YARA

YARA说明手册
yara下载地址

推荐YARA相关文章

《看雪 - [翻译]编写Yara规则检测恶意软件 》

通用

文件属性

SIZE(如66KB)

文件大小判断:filesize < 100KB
注意,因为想当然,容易把区间写成“33KB < filesize < 99KB”,会报错。
正确的区间写法:(33KB < filesize and filesize < 99KB)

  1. condition:
  2. 33KB < filesize and filesize < 99KB

条件(Condition)

字符串多次出现

字符串出现的次数可以由一个变量表示,变量名是用#代替$的字符串标识符

  1. condition:
  2. //包含String
  3. $String
  4. //String出现2次以上,字符串符号$改为变量符号#
  5. #String > 2

字符串位置

地址

yara中数值默认是十进制的,如果是十六进制的需要在数字前加上“0x”。

具体位置

有时知道字符串是否位于特定的位置,或偏移量在文件或进程地址空间内的某个虚拟地址上时,可通过at进行匹配:

  1. rule AtAddress
  2. {
  3. strings:
  4. $Address0x100 = "Address0x100"
  5. condition:
  6. $Address0x100 at 0x100
  7. }

位置区间

当字符串Digits2偏移量在0-100之间,而字符串Above100位于100到文件末尾之间的偏移量处时:

  1. rule InInterval
  2. {
  3. strings:
  4. $Digits2= "0-99"
  5. $Above100 = "100<"
  6. condition:
  7. $Digits2 in (0..100) and $Above100 in (100..filesize)
  8. }

偏移

如果确定某字符串出现在另一字符串固定位置的偏移处,可以通过@取得地址值表示,格式为@String1[Num]。
[Num]表示的是,对应字符串第一次出现的位置,并且Num计数是从1开始的
当字符串String1地址往后偏移0x11处为String2时:

  1. condition:
  2. @String1[1] + 0x11 == @String2[1]

除此之外,也可以通过加减运算和比较运算符表示。如:

  1. condition:
  2. //取值是支持负数的
  3. (@String1[1] - @String2[1] == -0x11)
  4. //或者也可以写为
  5. (@String2[1] - @String1[1] > 0x11)

PE模块

需要对PE结构有比较深的认识和理解,包括分析时对数据的敏感性。
下面仅对常见的一些字段进行介绍,但是一些小众冷门的字段,可能效用更强,如证书(signatures),链接器版本(linker_version),节表(sections),资源(resources)等。
使用该模块之前需要导入PE模块(注意PE必须小写):

  1. import "pe"
  2. condition:
  3. pe.is_dll()
  4. pe.is_32bit()
  5. pe.is_64bit()
  6. pe.pdb_path == "Y:\建瓯最坏\ProjectName\2021\Project2021RAT.pdb"
  7. pe.imphash() == "b8bb385806b89680e13fc0cf24f4431e"
  8. pe.imports("kernel32.dll", "WriteProcessMemory")
  9. pe.exports("ExportTableName")

文件类型判断

Dll文件:pe.is_dll()
32位:pe.is_32bit()
64位:pe.is_64bit()

导入表

导入表哈希值:pe.imphash() == “b8bb385806b89680e13fc0cf24f4431e”
*注意值要小写

  • 导入表中的函数:pe.imports(“kernel32.dll”, “WriteProcessMemory”),尽量找比较特殊的函数
  • 导入表Library的个数:pe.number_of_imports == 6

    延迟导入表

    在编写海莲花后门Salgorea发现一个比较特殊的情况,有个样本的导入表函数条件匹配不上:
    1. pe.imports( "Shell32.dll" , "ShellExecuteW" )
    IDA的导入表内却又有“ShellExecuteW”,定位来源发现不同处为,匹配不上的函数显示来自延迟导入表(Delayed imports from SHELL32.dll):
    【更新:v4.2.0-rc1】YARA - APT / 病毒检测 | 常用模块和字段 - 图1

    搜了很久都没有找到YARA怎么匹配延迟导入表中函数的写法。

    22.03.10更新:4.2.0支持啦 image.png

换个思路,暂时选择通过增加其他的函数条件来检测。方式为:对比两种样本的函数,找到相同且比较特殊的函数、
从导入表中,复制所有列:
【更新:v4.2.0-rc1】YARA - APT / 病毒检测 | 常用模块和字段 - 图3

利用Excel或其他方式(如文本对比工具,Linux的命令uniq),选中范围后,设置高亮Name列的重复项:

【更新:v4.2.0-rc1】YARA - APT / 病毒检测 | 常用模块和字段 - 图4

我个人还是更倾向于Excel:
【更新:v4.2.0-rc1】YARA - APT / 病毒检测 | 常用模块和字段 - 图5

Office的Excel好像没有提取重复数据,WPS这个功能要钱,那么推荐安装Git或者在Linux下执行命令求交sort Data.txt | uniq -dsort Data1.txt Data2.txt | uniq -d
编写好后,使用YARA规则命令复查一下是否有无法匹配的样本:

  1. yara.exe .\YaraRule.txt -r -n [存放需要匹配样本的目录]

导出表

导出表中的函数名:pe.exports(“ExportTableName”)
导出表中的函数个数等于明确数字:pe.number_of_exports == [导出表最下方的数字减去1]

【更新:v4.2.0-rc1】YARA - APT / 病毒检测 | 常用模块和字段 - 图6

导出表中的函数个数处于某个区间:(200 < pe.number_of_exports and pe.number_of_exports < 300)
还可以匹配正则等,检测单个用名称匹配比较多,预测性用正则

PDB

PDB:pe.pdb_path == "Y:\建瓯最坏\ProjectName\2021\Project2021RAT.pdb"
只单个样本可以考虑
但是从APT跟踪角度来说,一般以字符串的方式+正则匹配用来预测可能变化的情况。
分解上面的PDB分别是:

  • 盘符:一个大写英文,[A-Z]{1}
  • 用户名:固定长度的中文[\u4e00-\u9fa5]{4}
  • 项目名:不定长(暂取Max50)大小写英文,[a-zA-Z]{,50}

  • 年份:定长数字,20XX年,20[0-9]{2};201X年,201[0-9]{1};202X年,202[0-9]{1}

  • 源文件名称:当做是随机搭配的数字和大小写英文,[\w]{,50}
    可以写成以下几种格式搭配,一般建议写宽松写,或者根据历史样本的变化规律可以对应的修改。

    1. rule PDB
    2. {
    3. strings:
    4. //如果以“建瓯最坏”不变匹配:建瓯最坏\([路径(包括大小写英文和\)]).pdb
    5. $StringsPDB_JianOu = /[\u4e00-\u9fa5]{4}[\w\\]{,50}.pdb/
    6. //ProjectName\[不同年份]\Project2021RAT.pdb
    7. $StringsPDB_Year = /ProjectName\\20[0-9]{2}\\Project2021RAT.pdb/
    8. //[不同项目名]\2021\[不同源文件名].pdb
    9. $StringsPDB_ProjectName = /[a-zA-Z]{,50}\\20[0-9]{2}\\[\w]{,50}.pdb/
    10. condition:
    11. all of them
    12. }

    资源

    如iCon:

    1. import "pe"
    2. import "hash"
    3. condition:
    4. for any i in (0..pe.number_of_resources) :
    5. (
    6. ( pe.resources[i].type == pe.RESOURCE_TYPE_ICON )
    7. and
    8. ( hash.md5(pe.resources[i].offset,pe.resources[i].length) == "834c709455bfefb9b0e8976bad13a8f4" )
    9. )

    我一般判断的值是导入“hash”计算的MD5,VT等平台用的SHA-256比较多(hash.sha256),自己注意:
    image.png

    证书

    以APT组织白象的木马从21年7月捕获的样本使用的证书为例(值要小写):
    image.png ```python import “pe”

rule APT_HangOver_Signatures_AccelerateTechnologies_21July { condition: for any i in ( 0 .. pe.number_of_signatures -1 ): ( pe.signatures[i].thumbprint == “83e2488e16fd5d1ca018ff0c04e826ec27f4a2a0” ) }

  1. <a name="94c24e5f"></a>
  2. # 写YARA规则的一些思路
  3. <a name="3d74084d"></a>
  4. ## IDA导出表窗口
  5. ```yaml
  6. condition:
  7. pe.exports("ExportTableName")

除非是比较特殊,而且长期大量的样本都是同一个名称,不然其实检测的精度比较高,但是通用性较低:【更新:v4.2.0-rc1】YARA - APT / 病毒检测 | 常用模块和字段 - 图9

字符串

IDA的字符串窗口

PDB

在IDA的字符串窗口搜索PDB,没有就算了
有的话参考上述的规则,判断PDB路径结构,对一些变量(如年份2018和日期/版本号0328)替换为正则

【更新:v4.2.0-rc1】YARA - APT / 病毒检测 | 常用模块和字段 - 图10

这种通用性较低,没有比较特殊的字符串,勉强可以写一个,条件判断里可能也不会作为主要逻辑:

  1. strings:
  2. $StringsPDB = /F:\\[\w\\]{36,60}\\Doc.pdb/

字符型格式符

在IDA的字符串窗口搜索字符型格式符,如%s,基本会有一些比较自定义的字符串:
【更新:v4.2.0-rc1】YARA - APT / 病毒检测 | 常用模块和字段 - 图11

函数参数

还有部分函数的参数需要传入字符串,可以试着从这里面看看有无比较特殊的字符串或者格式。

创建(Create)

此类函数,需要传入创建的名字:

【更新:v4.2.0-rc1】YARA - APT / 病毒检测 | 常用模块和字段 - 图12

CreateDirectoryA:

【更新:v4.2.0-rc1】YARA - APT / 病毒检测 | 常用模块和字段 - 图13

设置(Set)

如RegSetValueExA:

【更新:v4.2.0-rc1】YARA - APT / 病毒检测 | 常用模块和字段 - 图14

获取(Get)

大多数获取功能的函数是没有什么参数,但是要处理获取到的数据,此时会有一些参与处理的字符串:

【更新:v4.2.0-rc1】YARA - APT / 病毒检测 | 常用模块和字段 - 图15

文件(File)

文件操作类大多数需要带文件路径或者文件名,如打开某文件,或者遍历某文件等:

【更新:v4.2.0-rc1】YARA - APT / 病毒检测 | 常用模块和字段 - 图16

字符串

字符串操作(str)

字符串操作那必然是有字符串了,找找有无比较特殊的字符串:
【更新:v4.2.0-rc1】YARA - APT / 病毒检测 | 常用模块和字段 - 图17

格式化输出函数(printf)

和字符串操作同理:
【更新:v4.2.0-rc1】YARA - APT / 病毒检测 | 常用模块和字段 - 图18

实例

字符串多次出现

考虑到误报率和检测率,可以增加一些检测条件,下图中的“%ls”可以看出有多次出现:

【更新:v4.2.0-rc1】YARA - APT / 病毒检测 | 常用模块和字段 - 图19

但是需要通过Hex十六进制窗口确认一下字符串的类型,此部分一共有1个“%ls”和两个宽字节的“%ls”:【更新:v4.2.0-rc1】YARA - APT / 病毒检测 | 常用模块和字段 - 图20

或者通过浮IDA提示的字符串类型判断,char为普通字符串:【更新:v4.2.0-rc1】YARA - APT / 病毒检测 | 常用模块和字段 - 图21

wchar为宽字符。需要将字符串加上wide 定义:
【更新:v4.2.0-rc1】YARA - APT / 病毒检测 | 常用模块和字段 - 图22

可以写字符串多次出现的规则检测如下:

  1. strings:
  2. $LS = "%ls" nocase
  3. $LS_Wide = "%ls" nocase wide
  4. condition:
  5. $LS and #LS_Wide > 1
  6. //或者如果字符串多,可以:
  7. //all of them and #LS_Wide > 1

image.png

不过大于小于的偏差值可能会比较大,YARA有一参数-s:

-s, —print-strings print matching strings
打印出匹配的字符串

在数量不多的情况下,可以作为计数方式使用:
【更新:v4.2.0-rc1】YARA - APT / 病毒检测 | 常用模块和字段 - 图24

上图可知“%ls”出现3次,宽字节的“%ls”出现5次,加上精确数字,规则可以写为:

  1. strings:
  2. $LS = "%ls" nocase
  3. $LS_Wide = "%ls" nocase wide
  4. condition:
  5. #LS ==3 and #LS_Wide == 5

【更新:v4.2.0-rc1】YARA - APT / 病毒检测 | 常用模块和字段 - 图25

特殊函数

有些恶意软件,可能字符串常见,但是用的函数很特殊,或者这两个叠加的集合比较准确,可以考虑在条件处增加函数条件。格式为:pe.imports(dll_name, function_name),如:pe.imports(“Kernel32.dll”, “Sleep”)

恶意软件常通过创建互斥体,保持运行的唯一性,这本质是通过创建锁来形成的,故除了互斥体外,还有其他的方式,如:临界区,事件和信号量。

如APT蔓灵花的常用下载器使用的方式是调用“CreateSemaphoreA”创建信号量

【更新:v4.2.0-rc1】YARA - APT / 病毒检测 | 常用模块和字段 - 图26

  1. import "pe"
  2. rule APT_Bitter_ArtraDownloader_Jul20
  3. {
  4. meta:
  5. author = "建瓯最坏"
  6. description = "蔓灵花ArtraDownloader - 2020.07新变种"
  7. sampleMD5 = "cgc.exe - 808849DE500179EC0FBC82C862F62333"
  8. strings:
  9. $Strings = "ExRng" nocase
  10. $StringsPost1 = "info" nocase
  11. $StringsPost2 = "Test" nocase
  12. $StringsPHP = ".php" nocase
  13. $StringsFile = "wb" nocase
  14. $StringsFileOpen = "Open" nocase
  15. condition:
  16. all of them
  17. and pe.is_32bit()
  18. and pe.imports("Kernel32.dll","CreateSemaphoreA") //创建信号量
  19. and pe.imports("SHLWAPI.dll","PathAddBackslashA") //比较特殊
  20. and pe.imports("WS2_32.dll","connect")
  21. and pe.imports("SHELL32.dll","ShellExecuteA")
  22. }

函数多次出现

竟然木有这种语法,希望后续版本中会有。

特殊代码

分离赋值-StrongPity

APT组织StrongPity常用的Exfiltrate模块中有字符串分离赋值的特殊代码如下:

【更新:v4.2.0-rc1】YARA - APT / 病毒检测 | 常用模块和字段 - 图27

上面截图中是一个很有特殊性的代码,Exfiltrate模块会拼接卷序列号和分离赋值的版本字符串。在后续载荷中,代码的具体实现稍有不同,直接通过“GetVolumeInformationW”快速定位到了此处:

【更新:v4.2.0-rc1】YARA - APT / 病毒检测 | 常用模块和字段 - 图28

可以发现有规律为通过分离赋值的方式,拼接处“v[1-9][0-9]{2}kt[0-9]{2}p[0-9]”,那么一定有对“v”“_”“kt”“p”的赋值,对应着二进制代码如下:

  1. strings:
  2. //v赋值,push 0x76
  3. $HexVerNumV = { 6A 76 }
  4. //下划线
  5. $HexVerNumUnderscore = { 6A 5F }
  6. //k
  7. $HexVerNumK = { 6A 6B }
  8. //p
  9. $HexVerNumP = { 6A 70 }

对目前已知发现的版本检测率为100%:

【更新:v4.2.0-rc1】YARA - APT / 病毒检测 | 常用模块和字段 - 图29

又考虑到赋值都在一定范围的代码处,可以再加上字符串地址取值和偏移量判断,但又不是非常固定,是处于一个比较临近的区间,比如“v”和“_”偏移,在V20版本为0xD的,在V21版本为0x11的,所以取一个比较宽松的值为0x33(3是我的幸运数字):

  1. import "pe"
  2. rule APT_StrongPity_Exfiltrate_Dec20
  3. {
  4. meta:
  5. author = "建瓯最坏"
  6. description = "2020-12-25,StrongPity联网插件"
  7. sample = "81390CE601D34F384BFF9198EEF793A9"
  8. strings:
  9. //版本号标识,格式如"v33_kt33p3"
  10. $HexVerNumV = { 6A 76 }
  11. $HexVerNumUnderscore = { 6A 5F }
  12. $HexVerNumK = { 6A 6B }
  13. $HexVerNumP = { 6A 70 }
  14. $StringsName = "name=%ls" nocase
  15. $StringsDel = "delete=" nocase
  16. //有些版本SFT字符串被加密处理了,无法通用
  17. //$StringsSFT = "*.sft" nocase wide
  18. condition:
  19. all of them
  20. and ( @HexVerNumUnderscore[1] - @HexVerNumV[1] < 0x33 )
  21. and pe.imports("Kernel32.dll","GetVolumeInformationW")
  22. and pe.imports("Kernel32.dll","ReadFile")
  23. and pe.imports("Kernel32.dll","CreateProcessW")
  24. and pe.imports("WinHTTP.dll","WinHttpSendRequest")
  25. }

如果仅根据上面两个版本的偏移数字,甚至可以写作:

  1. condition:
  2. ( 0xD <= @HexVerNumUnderscore[1] - @HexVerNumV[1] )
  3. and
  4. ( @HexVerNumUnderscore[1] - @HexVerNumV[1] <= 0x11 )
  5. }

但是对于示例的这个样本,不是很有必要,因为这些分离赋值的Hex值都比较特殊,每个在IDA里面搜索后的结果都只有一个:
【更新:v4.2.0-rc1】YARA - APT / 病毒检测 | 常用模块和字段 - 图30

直接通过定义String,即可以扫描到几乎所有的对应样本。
偏移计算对特殊的字符串意义不大,不过对于通用的字符串,可以大大的提高准确率。

版本更新

v4.2.0-rc1

新增

  • 用于在偏移范围内计算字符串出现次数的新语法。示例:#a in (0..100)#1565)。
  • 用于检查是否在偏移范围内找到一组字符串的新语法all of them in (0..100)( #1554 )。
  • of运算符现在接受规则集,例如:2 of (rule1, rule2, rule3)2 of (rule*)(#1597 )
  • 新的语法糖允许none of ($a*)写成(0 of ($a)#1559 )。
  • %字符串集的新运算符。示例:20% of them#1434)。(“将 ‘percent’ 关键字替换为 ‘%’”看不懂,也不知道怎么用)
  • 新运算符defined#1529)。
  • 新运算符iequals#1536)。(不区分大小写的字符串比较)
  • match模块添加了函数abscountpercentagemode#1483
  • 添加了新console模块(#1594)。(🥳🥳🥳调试/测试好用)
  • 添加了对**pe**模块延迟导入的支持(#1523)。(椰丝椰丝🥳🥳🥳)
  • 在 Linux ( #1470 )中扫描进程内存时减少内存压力。
  • 在匹配某些十六进制字符串(#1526#1552)时提高性能。
  • 在 Windows ( #1491 )中实现对 unicode 文件名的支持。
  • 添加新的 API 函数yr_get_configuration_uintXXyr_set_configuration_uintXX( #1621 )。
  • 添加--max-process-memory-chunk用于在扫描进程内存时控制块大小的选项(#1393)。
  • 添加--skip-larger用于在扫描目录时跳过大于特定大小的文件的选项。

    修复

  • BUGFIX:fullword修饰符在所有语言环境下都不能正常工作(#1544)。

  • BUGFIX:当文件具有被解释为 PID 编号的数字名称时修复边缘情况(#1541)。
  • BUGFIX:修复模块中的内存泄漏magic。