yara规则小记

@lzeroyuee  July 10, 2020

yara

官方文档

yara启动参数

-f, --fast-scan             快速扫描
-r, --recursive             递归扫描目录
-s, --print-string          打印匹配的字符串
-n, --negate                只打印不匹配的结果
-p, --thread=NUMBER         设置线程数
-D, --print-module-data     打印模块数据

几个简单的例子

Example1

rule apt_CN_BlueTraveller {
    strings:
        // fullword - 完整匹配字符串
        // ascii - 多字节字符串
        $a1 = "PROXY_PROXY_PROXY_PROXY" fullword ascii
        $a2 = "0ROXY_TYPE" fullword ascii
        $b1 = "cmd.exe /c hostname" fullword ascii
        $b2 = "/O.htm" fullword ascii
        $b3 = "%s%04d/%s" fullword ascii
        $b4 = "http://%s/%s/%s/" fullword ascii
        $c1 = "cmd.exe /c" fullword ascii
        $c2 = "Upload failed..." fullword ascii
        $c3 = "Download OK!" fullword ascii
        $c4 = "-download" fullword ascii
        $c5 = "-exit" fullword ascii
        $c6 = "james" fullword ascii
    condition:
        // 文件开头是'MZ'标志 &&
        uint16(0) == 0x5A4D and
        // (匹配$a*中的任意个数规则 ||
        // 匹配$b中的任意2个规则 ||
        // 匹配$c中的任意4个规则) &&
        (any of ($a*) or 2 of ($b*) or 4 of ($c*))
        // 文件大小小于400000字节
        and filesize < 400000
}

Example2

rule apt_DiplomaticDuck {
    strings:
        $http = "http://%s" ascii
        $a1 = "IEXPLORE.EXE" fullword ascii
        $a2 = "%s\\cmd.exe" fullword ascii
        $a3 = "GetLastActivePopup"
        $a4 = "SleepEx" fullword ascii
        $a5 = "CryptDestroyKey" fullword ascii
        $a6 = "ShellExecuteA" fullword ascii
        $a7 = "PathFileExistsA" fullword ascii
    condition:
        uint16(0) == 0x5A4D
        // 找到$http的个数为5and (#http == 5 and 5 of ($a*))
        and filesize < 400000
}

编写规则注意点

  • 当字符串比较短的时候,使用fullword关键字
  • 尽量不要根据一些常见的导入/导出函数来指定规则
  • 记住要使用filesize
  • 总是要检查文件头部信息(PE、ELF等)
  • 不使用运行时期生成的字符串
  • 尽量不要把所有可能的strings部分作为必要条件

    • 比如使用5 of ...来代替all of ...

yara的模块

PE模块

使用import "pe"导入pe模块
以下使用pe模块的例子:

  • pe.characteristics & pe.DLL
  • pe.exports("SpLsaModeInitialize")
  • pe.imports(“kernel32.dll”, “WriteProcessMemory”)
  • pe.machine == pe.MACHINE_AMD64
  • pe.timestamp == 1434793542
  • pe.locale(0x0419) or pe.language(0x19)
  • pe.version_info["CompanyName"] contains "Adobe"
  • pe.imphash() == "b8bb385806b89680e13fc0cf24f4431e"
  • pe.sections[pe.section_index(".payload")].raw_data_offset
  • pe.sections[4].name == ".payload"
  • pe.signatures[1].serial == "d9:5d:2c:aa:09:3b:f4:3a:02:9f:7e:29:16:ea:e7:fb"

Hash模块

使用import "hash"导入hash模块

for any i in (0..pe.number_of_sections - 1):
    (hash.md5(pe.sections[i].raw_data_offset, pe.sections[i].raw_data_size)
        == "d41d8cd98f00b204e9800998ecf8427e")
        
for any i in (0..pe.number_of_resources - 1):
    (hash.sha256(pe.resources[i].offset, pe.resources[i].length)
        =="e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855

Time模块

使用import "Time"导入time模块

condition:
    pe.timestamp > time.now()
    
    pe.timestamp > time.now() - 86400   // 过去24小时内
    and pe.timestamp < time.now()

Math模块

使用import "math"导入math模块

condition:
    math.in_range(math.entropy(
        pe.sections[pe.section_index[".rsrc"]].raw_data_offset,
        pe.sections[pe.section_index[".rsrc"]].raw_data_size,
    ), 6.66, 8.0)

yara编写总结

字符串

// 不加修饰的字符串
$string="mystring"

// 完全匹配字符串
$fullword="mystring" fullword

// 宽字符
$wide="mystring" wide 

// ascii的宽字符
$wide_ascii="mystring" wide ascii

// 忽略大小写
$case_sensitive="MyString" nocase

// 混合使用:完全匹配、ascii、宽字符、忽略大小写
$mix="mystring" fullword ascii wide nocase

// 16进制的字符串
$hex_string={01 02 03 04 05}

// 带有通配符,?占位一个
$hex_wildcards={01 ?? ?3 04 05}

// 中间跳过2-10个字节
$hex_jump={01 [2-10] 04 05}

// 中间跳过2个及以上的字节,即[2-infinite]
$hex_infinite={01 [2-] 04 05}

// 即[0-infinite]
$hex_no_bound={01 [-] 04 05}

// 或
$hex_str_or={01 (02 03 | 03 04) 05}
$hex_str_mult_or={01 (02 | 03 | 04 ?? 05) 06}

// 正则
$regex=/regexp .* blah/
$md5_regex = /[A-Za-z0-9]{32}/

字符串匹配条件

// $a在偏移0处
($a at 0)

// $a在PE的入口点上
($a at pe.entry_point)

// $a在$b后的16偏移处,@代表位置
$a at (@b+16)

// 没有匹配到$b
not $b

// $a匹配到的个数为5,$b匹配到的个数大于7,#代表个数
(#a == 5) or (#b > 7)

// $a在0-100之间,$b在100-filesize之间
$a in (0..100) and $b in (100..filesize)

// $a在PE入口点到入口点后偏移10位置之间
$a in (pe.entry_point .. pe.entry_point + 10)

// 其中匹配到2个
2 of ($a,$b,$c)

// 它们之间匹配到2个
2 of them

// 在任意的$a*中,匹配到2个
2 of ($a*)

// 它们中的所有都匹配到
all of them

// 它们中匹配到任意个
any of them

给定位置的整型

uint8(offs) == 255
uint16(offs) == 8192
uint32(offs) == 0xcafebabe
int8(offs) == -12
int16(offs) == -12000
int32(offs) == -1200768

// 以下有be后缀的为大端表示
int16be(offs) == -44777
uint16be(offs) == 0x4D5A
uint32be(offs) == 0xD0CF11E0

文件检查

// PE格式
uint16(0) == 0x5A4D     // 好的,效率高
($mz at 0)              // 坏的,效率低

// ELF或者Mac
(uint32(0) == 0x464c457f)     // ELF
(uint32(0) == 0xfeedfacf) or (uint32(0) == 0xcffaedfe) or (uint32(0) == 0xfeedface) or (uint32(0) == 0xcefaedfe)     // Mac

// 文件大小检查
(filesize > 512) or (filesize < 5000000) or (filesize < 5MB)

pe模块

// 是dll
pe.characteristics & pe.DLL

// 拥有导出名SpLsaModeInitialize
pe.exports("SpLsaModeInitialize")

// 有导入函数kernel32中的WriteProcessMemory
pe.imports("kernel32.dll", "WriteProcessMemory")

// 是64位的PE文件
pe.machine == pe.MACHINE_AMD64

//时间戳
pe.timestamp == 1434793542

// 地区和语言
pe.locale(0x0419) or pe.language(0x19)

// 节区数量大于5
pe.number_of_sections > 5

// 索引为4的节的名字位.payload
pe.sections[4].name == ".payload"

// 公司组织名包含Adobe
pe.version_info["CompanyName"] contains "Adobe"

// .payload节的.raw_data_offset
pe.sections[pe.section_index(".payload")].raw_data_offset

// 资源数量大于5
pe.number_of_resources > 5

// 索引为1的资源名为RSRCNAME
pe.resources[1].name_string == "RSRCNAME"

添加新评论

  1. 让内鬼康康又发了什么

    Reply