编写yara规则,检测恶意软件(自定义yara规则)
Yara規則與C語言語法十分相像, 以下是一個簡單的規則, 這個規則沒有進行任何操作:
rule HelloRule {condition:false }規則標識符
規則標識符是上面簡單規則示例中跟在rule后的詞, 比如單詞"dummy"也可以是一個規則標識符, 標識符命名有如下要求:
- 是由英文字母或數字組成的字符串
- 可以使用下劃線字符
- 第一個字符不能是數字
- 對大小寫敏感
- 不能超出128個字符長度
Yara關鍵字
下面這些詞不能用作規則標識符, 因為這些單詞在yara語言里有特定用處
all, and, any, ascii, at, condition, contains entrypoint, false, filesize, fullword, for, global, in import, include, int8, nt16, int32, int8be, int16be int32be, matches, meta, nocase, not, or, of private, rule, strings, them, true, uint8, uint16 uint32, int8be, uint16be, uint32be, wide
通常yara規則有兩部分:?字符串定義和條件
rule HelloRule2 // This is an example {strings:$my_text_string = "text here"$my_hex_string = { E2 34 A1 C8 23 FB }condition:$my_text_string or $my_hex_string }當發現有規則里定義的任意字符串, 規則就會生效. 如你所見, 你還可以在規則里添加注釋.
十六進制字符串
通配符
十六進制字符串可以用通配符表示, 通配符符號用"?"表示
rule GambitWildcard {strings:$hex_string = { EF 44 ?? D8 A? FB }condition:$hex_string }這個規則可以匹配下面的兩個字符串
EF?44?01?D8 AA FB
EF?44?AA D8 AB FB
不定長通配符
不定長的字符串可以用下面這個方法表示
rule MarioJump {strings:$hex_string = { F4 23 [4-6] 62 B4 }condition:$hex_string }這個規則可以匹配下面的兩個字符串
F4?23?01?02?03?04?62?B4
F4?23?AA BB CC DD EE FF?62?B4
當然無限長的字符串也是可以的.
rule BuzzLightyear {strings:$hex_string = { F4 23 [-] 62 B4 }condition:$hex_string }這個規則可以匹配下面的兩個字符串
F4?23?AA FF?62?B4
F4?23?AA AA AA AA AA...FF FF?62?B4
有條件的字符串
你可以創建一個字符串應對多種情況
rule WorriedRabbit {strings:$hex_string = { BA 21 ( DA BC | C6 ) A5 }condition:$hex_string }這個規則可以匹配下面的兩個字符串
BA 21 DA BC A5
BA 21 C6 A5
混合
當然, 你也可以將上面這幾種方法結合起來.
rule WorriedGabmitLightyearJump {strings:$hex_string = { BA ?? ( DA [2-4] | C6 ) A5 }condition:$hex_string }這個規則可以匹配下面的三個字符串
BA 01 DA 01 02 03 04 A5
BA AA C6 A5
BA FF DA 01 02 A5
文本字符串
除開使用十六進制字符串, 我們也還可以使用文本字符串
rule KimPossible {strings:$alert_string = "Whats the Sitch"condition:$alert_string }你也可以像C語言那樣使用如下的轉義符:
\" 雙引號
\\ 反斜杠
\t 水平制表符
\n 換行符
\xdd 以十六進制表示的任何字節
修飾符
不區分大小寫的字符串
Yara規則默認對大小寫敏感, 但你可以使用修飾符將其關閉
rule ThickSkin {strings:$strong_string = "Iron" nocasecondition:$strong_string }寬字符串
wide修飾符可以用來搜尋以2字節表示1字符這種方式編碼的字符串, 這種寬字符串在許多二進制文件中都有出現. 如果字符串"FatTony"以2字節表示1字符的方式編碼并在二進制文件中出現, 我們就可以使用wide修飾符將其捕獲. 因為"FatTony"也可能是"fattony", 我們也可以添加nocase修飾符以免錯過.
rule FatTony {strings:$fat_villain = "FatTony" wide nocasecondition:$fat_villain }[!]重要提示: 請記住, 該修飾符只是將字符串中字符的ASCII碼和\x00交錯起來組成寬字符, 它并不支持包含非英文字符的UTF-16字符串. 要想對既有ASCII字符和寬字符的字符串進行搜索, 請使用如下命令:
rule ASCIIFatTony {strings:$fat_villain = "FatTony" wide ascii nocasecondition:$fat_villain }字符串默認是ASCII編碼, 所以如果你想單獨用ascii搜索"FatTony", 你并不需要添加ascii修飾符
rule ASCIIFatTony {strings:$fat_villain = "FatTony"condition:$fat_villain }如果你想在不使用wide和nocase修飾符的情況下進行搜索, 上述這個規則可以生效.
Fullwords修飾符
該修飾符可用于匹配那些前后沒有附加其他字符的單詞(全詞匹配).
rule ShadyDomain {strings:$shady_domain = "faceebook" fullwordcondition:$shady_domain }這個規則可以匹配下面的三個字符串
www.faceebook.com
www.myportal.faceebook.com
https://secure.faceebook.com
但這個規則不能匹配以下的字符串:
www.myfaceebook.com
thefaceebook.com
兩者區別在于匹配的全詞前后可以附加特殊字符, 不能是普通字符.
正則表達式
yara規則允許使用正則表達式, 不過要用正斜杠而非雙引號括起來使用(像Perl編程那樣)
rule RegularShow {strings:$re1 = /md5: [0-9a-fA-F]{32}/$re2 = /state: (on|off)/condition:$re1 and $re2 }該規則將捕獲任何狀態下找到的所有md5字符串.
你也可以在正則表達式中使用文本修飾符, 如nocase,ascii,wide和fullword.
元字符
元字符是一個字符對計算機程序有特定含義(而非字面含義)的字符. 在正則表達式中, 有以下含義:
**?引用下一個元字符
^ 匹配文件的開頭
$ 匹配文件的末尾
| 多選
() 分組
[] 方括號字符類
也可以使用以下量詞:
*?匹配0次或多次
+?匹配1次或多次
? 匹配0次或1次
{n} 只匹配n次
{n, } 至少匹配n次
{ ,m} 至多匹配m次
{n,m} 匹配n到m次
也可以使用以下的轉義符:
\t 水平制表符 (HT, TAB)
\n 換行符 (LF, NL)
\r 回車符 (CR)
\f 換頁符 (FF)
\a 響鈴
\xNN 十六進制代碼為NN的字符
也可以使用以下字符類:
\w 匹配單詞字符 (單詞可由字母數字加"_"組成) \W 匹配非單詞字符 \s 匹配空白符 \S 匹配非空白字符 \d 匹配一個十進制數字字符 \D 匹配一個非數字字符 \b 匹配單詞邊界 \B 匹配非單詞邊界字符串集
如果你想要中列表中選擇一定數量的字符串, 你可以執行以下操作:
rule MigosPresent {strings:$m1 = "Quavo"$m2 = "Offset"$m3 = "Takeoff"condition:2 of ($m1,$m2,$m3) }如果$m1,?$m2和$m3任意存在兩個, 那么就滿足上述規則中的條件.
你還可以使用通配符來表示一個字符集. 像如下這樣使用通配符*
rule MigosPresent {strings:$m1 = "Quavo"$m2 = "Offset"$m3 = "Takeoff"condition:2 of ($m*) }要表示strings中的所有變量, 你可以使用關鍵字them
rule ThreeRappersPresent {strings:$m1 = "Quavo"$m2 = "Offset"$m3 = "Takeoff"$q1 = "Cardi B"condition:3 of them // equivalent to 3 of ($*) }你可以使用任何返回數值的表達式. 以下是使用關鍵字any和all的一個示例
rule Squad {strings:$m1 = "Quavo"$m2 = "Offset"$m3 = "Takeoff"$q1 = "Cardi B"condition:3 of them // equivalent to 3 of ($*)all of themany of ($*) and 2 of ($*) // Fancy way of using any in a rule that requires 3. }帶有of和for...of的匿名字符串
如果你沒有專門引用字符串的事件, 你可以僅使用$來將它們全部引用.
rule AnonymousStrings {strings:$ = "dummy1"$ = "dummy2"condition:1 of them }條件
Yara規則允許通過and, or, 和not等相關運算符來表示布爾表達式, 算術運算符(+,-,*,%)和位運算符(&, |, <<, >>, ~, ^)也可用于數值表達式中.
布爾運算
字符串標識符也可在條件中充當布爾變量, 其值取決于文件中相關字符串是否存在.
rule Example {strings:$hero1a = "Batman"$hero1b = "Robin"$hero2a = "Edward"$hero2b = "Alphonse"condition:($hero1a or $hero1b) and ($hero2a or $hero2b) }計數字符串實例
有時我們不僅需要知道某個字符串是否存在, 還需要知道字符串在文件或進程內存中出現的次數. 每個字符串的出現次數由一個變量表示, 變量名是用#代替$的字符串標識符. 例如:
rule Ransomware {strings:$a = "encrypted"$b = "btc"condition:#a == 2 and #b > 2 }這個規則會匹配任何包含兩個字符串$a以及出現至少兩次字符串$b的文件或進程.
字符串偏移(虛擬地址)
在大多數情況下, 當在條件中使用字符串標識符, 我們都只需知道關聯的字符串是否在文件或進程內存內就行了. 但有時我們還是需要知道該字符串是否在文件的某個特定偏移處, 或是在進程地址空間的某個虛擬地址處. 在這種情況下, 我們就需要操作符at.
rule Offset {strings:$a = "encrypted"$b = "btc"condition:$a at 100 and $b at 200 }如果在文件的偏移100處(或者在一個正在運行的進程中, 位于虛擬地址100位置)發現了字符串$a, 我們的規則就能捕獲到該字符串. 當然字符串$b也要在偏移200位置上才行. 你也可以使用十六進制表示而不一定要十進制.
rule Offset {strings:$a = "encrypted"$b = "btc"condition:$a at 0x64 and $b at 0xC8 }at操作符指定到一個具體的偏移量, 而你可以使用操作符in來指定字符串的位置范圍.
rule InExample {strings:$a = "encrypted"$b = "btc"condition:$a in (0..100) and $b in (100..filesize) }字符串$a必須在偏移0-100之間才能找到, 而$b則必須是在偏移100到文件末尾位置(才能找到).
你也可以使用@a[i]來取得字符串$a第i個字符的偏移量或虛擬地址. 字符串索引以1開頭 , 故第1個字符是@a[1], 第2個是@[a2]并依此類推, 而不是以@a[0]開始. 如果你提供的索引值大過字符串總共出現的次數. 那結果就將是值NaN(Not a Number, 非數字).
匹配長度
對于包含跳轉的許多正則表達式和十六進制字符串, 匹配長度用一個變量表示. 如果你有一個正則表達式/fo*/, 可以匹配字符串fo,?foo和fooo, 那么各個的匹配長度都是不同的.
在字符串標識符前加一個!得到匹配長度, 你就可以將匹配長度作為你條件的一部分. 跟你獲取偏移時使用字符@類似,?!a[1]是第一個匹配到的字符串$a的長度, 而!a[2]就是第二個匹配到的字符串的長度, 依此類推.?!a是!a[1]的縮寫.
rule Hak5 {strings:$re1 = /hack*/ // Will catch on hacker, hacked, hack, hack*condition:!re1[1] == 4 and !re1[2] > 6 }該規則可以匹配如下字符串:
We hack things. We are hackers.第一個hack是re1[1]且其長度等于4. 第二個hack長度則至少為6
文件大小
字符串標識符并不是唯一可以在條件中出現的變量(實際上, 可以不定義任何字符串來編寫一個規則), 還可以使用其他變量.?filesize就保存著正在掃描的文件的大小. 大小以字節為單位.
rule FileSizeExample {condition:filesize > 200KB }我們可以使用后綴KB將文件大小設置為200KB, 它會自動將常量的值乘上1024, 后綴MB會可以將值乘以2^20. 這兩個后綴都只能用于十進制常量
[!]重要提示:?filesize僅在規則應用于文件的時候生效. 如果應用于正在運行的進程, 那么它會永遠都匹配不了.
可執行程序入口點
如果我們正掃描的文件是一個PE或ELF文件, 那么變量entry_point會存有可執行文件的入口點偏移值. 而如果我們正掃描一個運行的進程, 那么entry_point會存有可執行文件入口點的虛擬地址. 變量entry_point的經典用法是用于搜索入口點的一些pattern, 以檢測殼或簡單的感染病毒. 目前使用entry_point的方式是通過導入PE和/或ELF的庫并使用它們各自的功能. Yara的entrypoint函數自第3版開始就已經過時了. 以下是它在第3版之前的樣子.
rule EntryPointExample1 {strings:$a = { E8 00 00 00 00 }condition:$a at entrypoint }rule EntryPointExample2 {strings:$a = { 9C 50 66 A1 ?? ?? ?? 00 66 A9 ?? ?? 58 0F 85 }condition:$a in (entrypoint..entrypoint + 10) }[!]重要提示: 再次強調, 不要使用yara的entrypoint, 請在導入PE或ELF文件后使用對應的pe.entry_point和elf.entry_point
訪問指定位置的數據
如果你想從特定偏移位置讀取數據, 并將其存為一個變量. 那么你可以使用以下任何一個方式:
int8(<offset or virtual address>) int16(<offset or virtual address>) int32(<offset or virtual address>)uint8(<offset or virtual address>) uint16(<offset or virtual address>) uint32(<offset or virtual address>)int8be(<offset or virtual address>) int16be(<offset or virtual address>) int32be(<offset or virtual address>)uint8be(<offset or virtual address>) uint16be(<offset or virtual address>) uint32be(<offset or virtual address>)數據存儲默認以小端序, 如果你想要讀取大端序的整形數, 請使用下面幾個以be結尾的對應函數.
參數<offset or virtual address>可以是任何一個返回無符號整數的表達式, 包括可以是uintXX函數的返回值.
rule IsPE {condition:// MZ signature at offset 0 and ...uint16(0) == 0x5A4D and// ... PE signature at offset stored in MZ header at 0x3Cuint32(uint32(0x3C)) == 0x00004550 }for…of: 對許多字符串應用同一個條件
要用for循環來檢查一組字符串是否滿足特定條件, 請使用如下語法:
for?num of string_set : ( boolean_expression )
對每個string_set的字符串, 都會計算boolean_expression的值, 并且這些值必須至少有1個為真.
當然你也可以使用其他關鍵字, 如all或any代替num來使用.
for?any?of ($a,$b,$c) : ( $ at elf.entry_point? )
$表示集合中的所有字符串. 本例中, 它是字符串$a,?$b和$c.
你也可以使用符號#和@來引用每一個字符串的出現次數和首字符偏移量.
for all of them : ( # > 3 ) for all of ($a*) : ( @ > @b )迭代字符串出現次數
如果你想對偏移迭代并測試條件. 你可以如下操作:
rule Three_Peat {strings:$a = "dummy1"$b = "dummy2"condition:for all i in (1,2,3) : ( @a[i] + 10 == @b[i] ) }這個規則說的是,?$b出現前三個的字符串應當分別隔$a出現的前三個的字符串10個字節遠. 另外一種寫法如下:
for?all?i?in?(1..3) : ( @a[i]?+?10?==?@b[i] )
我們也可以使用表達式. 在本例中, 我們迭代每一次出現的$a(記住,?#a代表$a的出現次數). 該規則指定, 每一次$a都應當出現在文件的前100個字節內.
for?all?i?in?(1..#a) : ( @a[i] < 100 )
你也可以指定字符串的某一次出現需要滿足條件(而非全部).
for any i in (1..#a) : ( @a[i] < 100 ) for 2 i in (1..#a) : ( @a[i] < 100 )引用其他規則
就像C語言中引用函數那樣. 函數, 或是這里說的規則, 都必須在使用前進行定義.
rule Rule1 {strings:$a = "dummy1"condition:$a }rule Rule2 {strings:$a = "dummy2"condition:$a and Rule1 }Yara要點
全局規則
Yara規則允許用戶在所有規則中進行約束. 如果你希望所有規則都忽略掉那些超出特定大小限制的文件, 那么你可以對規則進行必要的修改, 或是編寫一條像以下這樣的全局規則:
global rule SizeLimit {condition:filesize < 2MB }你可以根據需要定義各種全局規則. 這些規則會在其他規則之前運行.
私有規則
私有規則在匹配時沒有任何輸出. 當和其它規則成對引用時, 這樣就可以使輸出更為清楚. 比如為了判斷文件是否惡意, 有這樣一條私有規則, 要求文件必須是ELF文件. 一旦滿足這個要求, 隨后就會執行下一條規則. 但我們在輸出里想看的并不是該文件它是不是ELF, 我們只想知道文件是否惡意, 那么私有規則就派上用場了. 要想創建一條私有規則, 只需要在rule前添加一個private即可.
private rule PrivateRule {... }規則標簽
如果你只想查看ruleName類型的規則輸出, 你可以對你的規則打上標簽
rule TagsExample1 : Foo Bar Baz {... }rule TagsExample2 : Bar {... }元數據
Yara允許在規則中存儲一些額外數據.
rule MetadataExample {meta:my_identifier_1 = "Some string data"my_identifier_2 = 24my_identifier_3 = truestrings:$my_text_string = "text here"$my_hex_string = { E2 34 A1 C8 23 FB }condition:$my_text_string or $my_hex_string }使用模塊
一些模塊由YARA官方發布, 比如PE和Cukoo模塊. 這些模塊就如python那樣導入即可, 不過在導入時模塊名需要添加雙引號
import "pe" import "cuckoo"一旦模塊成功導入, 你就可以在函數前加模塊名, 來使用這些功能.
pe.entry_point == 0x1000 cuckoo.http_request(/someregexp/)未定義的值
一些值在運行時保留為undefined. 如果以下規則在ELF文件上執行并找到對應的字符串, 那么它的結果相當于TRUE & Undefined.
import "pe"rule Test {strings:$a = "some string"condition:$a and pe.entry_point == 0x1000 }所以在用的時候要注意咯!
外部變量
外部變量允許你定義一些, 依賴于第三方提供值的規則.
rule ExternalVariable1 {condition:ext_var == 10 }ext_var是一個外部變量, 它在運行時會分配有一個值, (見命令行的-d選項以及yara-python中compile和match方法的參數). 外部變量可以是int,?str或boolean類型
外部變量可以和操作符contains和matches一起使用.?contains在字符串包含特定子串的情況下返回true. 而matches在字符串匹配給定的正則表達式時返回true.
rule ExternalVariable2 {condition:string_ext_var contains "text" }rule ExternalVariable3 {condition:string_ext_var matches /[a-z]+/ }你也可以將matches操作符和正則表達式一起使用
rule ExternalVariableExample5 {condition:/* case insensitive single-line mode */string_ext_var matches /[a-z]+/is }/[a-z]+/is中的i表示匹配時不區分大小寫.?s表示是在單行(single line)模式
記住, 你必須在運行時定義好所有的外部變量. 你可以使用-d參數來指定.
文件包含
當然在yara規則里你可以使用類似C語言的導入方式(#include, 不過yara中并不使用#號來包含所需的文件, 而是用雙引號引起來)來包含其他文件. 你可以在包含時使用相對路徑或絕對路徑. 如果是windows系統, 甚至還可以是驅動設備的路徑.
include "Migos.yar" include "../CardiB.yar" include "/home/user/yara/IsRapper.yar" include "c:\\yara\\includes\\oldRappers.yar" include "c://yara/includes/oldRappers.yar"總結
好吧. 現在你應該知道如何寫一些Yara規則了.
這里有一些惡意軟件的倉庫, 規則和工具, 可以讓你來生成yara規則. 如果你安裝了yarGem, 你只需要將它指向到惡意軟件, 它就會為該惡意軟件生成一個簽名. 如果你想捕捉一個惡意軟件家族, 你最好是將規則推廣到整個家族去.
總結
以上是生活随笔為你收集整理的编写yara规则,检测恶意软件(自定义yara规则)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python - os模块
- 下一篇: Windows系统自带WMI应用的查询使