yara规则学习笔记(一)

本文最后更新于:2021-09-23 上午

前言

yara是一种皆在帮助恶意软件研究人员识别和分类恶意软件样本的工具。每一个描述,也就是规则,由一组字符串和一个确定其逻辑布尔表达式组成。

官方文档地址:

https://yara.readthedocs.io/en/v4.1.2/index.html

windows端官方下载地址:

https://github.com/VirusTotal/yara/releases/tag/v4.1.2

安装

安装非常的简单,在windows下,可以直接访问https://github.com/VirusTotal/yara/releases/tag/v4.1.2来进行下载

下载解压之后就可以直接使用了,输入参数–help可以看到所有yara的参数。

中文翻译

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
-t --tag=tag只打印标记为tag的规则
-i --identifier=identifier只打印名为identifier的规则
-n --negate只打印不满足的规则(negate)
-D --打印模块数据打印模块数据
-g --打印标签打印标签
-m --打印元数据
-s --打印字符串打印匹配的字符串
-L --打印字符串长度打印匹配字符串的长度
-e --打印命名空间打印规则的命名空间
-p --threads=NUMBER使用指定的线程数扫描目录
-l --max rules=NUMBER匹配多个规则后中止扫描
-d VAR=值定义外部变量
-x MODULE=文件将文件内容作为额外数据传递到模块
-a --timeout=秒在给定秒数后中止扫描
-k --堆栈大小=插槽设置的最大堆栈大小(默认值=16384
-r --递归递归搜索目录
-f ——快速扫描快速匹配模式
-w --无警告禁用警告
--警告失败警告失败
-v --版本显示版本信息
-h --help显示此帮助并退出

编写yara规则

yara规则的语法类似于C语言,易于编写和理解,下面是官方给出的示例

1
2
3
4
5
6
7
8
9
10
11
12
13
rule silent_banker : banker
{
meta:
description = "This is just an example"
thread_level = 3
in_the_wild = true
strings:
$a = {6A 40 68 00 30 00 00 6A 14 8D 91}
$b = {8D 4D B0 2B C1 83 C0 27 99 6A 4E 59 F7 F9}
$c = "UVODFRYSIHLNWPEJXQZAKCBGMT"
condition:
$a or $b or $c
}

meta是一些描述信息,

strings定义了一些十六进制值和一个字符串

condition定义了匹配的条件,a,b,c三个条件任意一个都会被认定为是silent_banker : banker

下面来进行一下简单的测试。

创建一个文件写入字符串,再创建一个文件写入$a的十六进制值。

将它们放在test file文件夹下,进行检测,可以看到规则成功命中

yara的每条规则都以关键字rule开头,后面跟规则标识符。标识符必须遵循与C编程语言相同的词汇约定,它们可以包含任何字母数字字符和下划线字符串,但是第一个字符不能是数字,规则标识符是区分大小写的,不可以超过128个字符。以下关键字是保留的,不能用作标识符:

all and any ascii at base64 base64wide condition
contains endswith entrypoint false filesize for fullword global
import icontains iendswith in include int16 int16be int32
int32be int8 int8be istartwith matches meta nocase not
of or private rule startswith strings them true
uint16 uint16be uint32 uint32be uint8 uint8be wide xor

规则部分通常是由两部分组成:字符串定义和条件。如果规则不依赖于任何字符串,则可以省略字符串定义部分,但始终需要条件部分。字符串定义部分是定义成为规则一部分的字符串的地方。每个字符串都有一个标识符,由$字符后跟一系列字母数字字符和下划线组成,这些标识符可以用于条件部分以引用相应的字符串。字符串可以以文本或者十六进制形式定义,如下所示:

1
2
3
4
5
6
7
8
rule test
{
strings:
$test_string="this is a test"
$test_hex_value={11 22 33 44}
condition:
$test_string or $test_hex_value
}

文本字符串用双引号括起来,像在C语言中一样,十六进制数字用大括号括起来。

条件部分是规则逻辑所在。该部分必须包含一个布尔表达式,说明在何种情况下文件或者进程满足规则。通常。条件将通过使用它们的标识符来引用先前定义的字符串。在这种情况下,字符串标识符充当布尔变量,如果在文件或者进程内存中找到字符串,则该变量评估为真,否则为假。

注释

可以向规则中添加注释,像C一样,支持单行和多行注释。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
rule test
{
//这是单行注释
strings:
$test_string="this is a test"
$test_hex_value={11 22 33 44}
/*

这是一个
多行注释

*/
condition:
$test_string or $test_hex_value
}

字符串

yara中有三种类型的字符串:十六进制字符串、文本字符串和正则表达式。十六进制字符串用于定义原始字节序列,而文本字符串和正则表达式用于定义清晰文本的部分。然而,文本字符串和正则表达式也可用于转义序列来表示原始字节。

十六进制字符串

十六进制字符串允许三种特殊结构,使其更加灵活:通配符、跳转和替代。通配符只是一个占位符,表明某些字节是未知的,它们可以匹配任何内容。占位符是(?)。示例如下:

1
2
3
4
5
6
7
rule WildcardTest
{
strings:
$a = {31 32 33 34 ?? 36 3? 38 39}
condition:
$a
}

现在向test文件内写入“123456789”这段字符串,可以成功检测。

通配符在你知道字符串的可变块长度时很有用,然而,有时情况不是这样。某些情况下,可能需要定义具有可变内容和长度的块的字符串。在这些情况下可以使用跳转。

1
2
3
4
5
6
7
rule JumpTest
{
strings:
$a = {31 [0-7] 39}
condition:
$a
}

还是继续测试刚才的文件,可以看到成功检测到,提示了一个warning意思是可能会拖慢扫描速度,主要是规则编写的不到位,不够精准。

在上面的例子中,用方括号括起来0-7,这就是一个跳转,意思是任何0-7个字节的任意序列都可以占据此位置,比如这些字符都是可以匹配到当前规则的。

123456789

1xx9

19

1asdasd9

任何跳转的条件[X-Y]都必须满足Y>X的条件,如下是无效的

31 32 [10-2] 39

如果上下限相等,也就是说要匹配的数字的长度是固定的,可以写一个括在括号里的数字,如下所示

31 32 33 [2] 36

其等价于:

31 32 33 [2-2] 36

31 32 33 ?? ?? 36

从yara2.0开始还有无界跳转

31 32 [10-] 39

31 32 [-] 39

第一个的意思是10-无限,第二个意思是无限。

在某些情况下,可以使用正则表达式的语法

1
2
3
4
5
6
7
rule Test
{
strings:
$a = {31 32 (33 34 | 36 37) 39}
condition:
$a
}

此规则将会匹配1234912679

也可以将通配符结合起来,如下:

1
2
3
4
5
6
7
rule Test
{
strings:
$a = {31 32 (33 34 | 36 37 | 41 ?? 4?) 39}
condition:
$a
}

此时如果有一个字符串12A0G9的字符串也可以被检测到(这只是其中之一,可以根据条件自行构建字符串,并不是唯一的)。

文本字符串

文本字符串都是这样定义的

1
2
3
4
5
6
7
8
rule Test
{
strings:
$a = "this is a test"
condition:
$a
}

这是最简单的情况:ASCII编码、区分大小写。文本字符串还可以包含以下C语言中可用的转义字符:

\“ 双引号
\\ 反斜杠
\r 回车
\t 水平标签
\n 回车换行
\xdd 十六进制表示

文本字符串还可以附带一些有用的修饰符,这些修饰符可以改变字符串的解释方式。这些修饰符附加在由空格分隔的字符串定义的末尾。

不区分大小写

yara中默认文本字符串是区分大小写的,但是可以通过在字符串定义的末尾附加修饰符nocase将字符串转换为不区分大小写的模式。

1
2
3
4
5
6
7
8
rule Test
{
strings:
$a = "this is a test" nocase
condition:
$a
}

在测试文件中,改成大写的字符串

成功检出

宽字符串

wide可以用于搜索宽字符串

1
2
3
4
5
6
7
rule TestWide
{
strings:
$a = "this is a test" wide
condition:
$a
}

写一个小DEMO,定义一个宽字符串

1
2
wchar_t a[] = L"this is a test";
printf("%ws\n", a);

成功检出

如果想同时搜索ASCII和宽字节字符串,可以将asciiwide结合起来

1
2
3
4
5
6
7
rule TestWideAndAscii
{
strings:
$a = "this is a test" wide ascii
condition:
$a
}

在test.txt中写入this is a test,可以检测出宽字符的exe和ascii的txt

异或字符串

xor可以用于搜索字符串的单字节xor结果,比如以下规则可以搜索“this is a test”的每个字符的异或。

1
2
3
4
5
6
7
rule TestXor
{
strings:
$a = "this is a test" xor
condition:
$a
}

在test.txt中写入“dxyc0yc0q0ducd”,这是“this is a test”每个字符与0x10异或后的结果。

成功检出

上面这个逻辑是等价于

1
2
3
4
5
6
7
8
9
10
rule test
{
strings:
$a = "dxyc0yc0q0ducd"// this is a test 异或0x10结果
$b = "fz{a2{a2s2fwaf"// this is a test 异或0x12结果
$c = {8b 97 96 8c df 96 8c df 9e df 8b 9a 8c 8b}
//this is a test 异或0xFF结果
condition:
any of them
}

也可以将wideascii结合使用

1
2
3
4
5
6
7
8
rule testXorAsciiWide
{
strings:
$a = "this is a test" xor wide ascii

condition:
$a
}

从yara3.11开始,可以控制异或的范围,如下所示。

1
2
3
4
5
6
7
rule TestXor
{
strings:
$a = "this is a test" xor(0x01-0x10)
condition:
$a
}
Base64字符串

base64可以用于搜索被base64编码过后的字符串,以下规则将搜索“this is a test”base64编码后的结果。

1
2
3
4
5
6
7
rule testBase64
{
strings:
$a = "this is a test" base64
condition:
$a
}

this is a testbase64编码后结果是:dGhpcyBpcyBhIHRlc3Q=

base64wide是搜索宽字符

搜索完整词

fullword该修饰符保证字符串仅出现在由非字母数字字符分隔的文件中时才匹配。

1
2
3
4
5
6
7
rule testFullword
{
strings:
$a = "b1ackie" fullword
condition:
$a
}

这种时候,比如iamb1ackie和 www.b1ackieblog.com 都是不匹配的,但是可匹配 www.b1ackie.cnwww.b1ackie-blog.com

正则表达式

正则表达式是yara最强大的功能之一,它们的定义方式与文本字符串相同,使用正斜杠而不是双引号括起来。

1
2
3
4
5
6
7
rule test
{
strings:
$a = /md5: [0-9a-fA-F]{32}/
condition:
$a
}

也可以在正则表达式中使用文本修饰符,nocaseasciiwidefullword

正则表达式还可以识别以下元字符

\ 引用下一个元字符
^ 匹配文件的开头
$ 匹配文件的结尾
| 交替
() 分组
[] 带括号的字符类

也可以使用以下量词

* 匹配0次或多次
+ 匹配1次或多次
? 匹配0或1次
{n} 完全匹配n次
{n,} 至少匹配n次
{,m} 最多匹配n次
{n,m} 匹配n到m次

这些量词还有一个非贪婪的变体,后面跟一个问号(?)

*? 匹配0次或多次,非贪婪
+? 匹配1次或多次,非贪婪
?? 匹配0或1次,非贪婪
{n}? 完全匹配n次,非贪婪
{n,}? 至少匹配n次,非贪婪
{,m}? 最多匹配n次,非贪婪
{n,m}? 匹配n到m次,非贪婪

字符序列

\w 匹配一个单词字符(字母数字加_)
\W 匹配一个非单词字符
\s 匹配一个空白字符
\S 匹配一个非空白字符
\d 匹配一个十进制数字字符
\D 匹配一个非数字字符

私有字符串

yara中的所有字符串都可以标记为private,这意味着它们将永远不会出现在yara的输出之中,当使用-s参数时也无法看到回显。

1
2
3
4
5
6
7
rule test
{
strings:
$a = "asd" private
condition:
$a
}

参考

https://yara.readthedocs.io/en/v4.1.2/index.html

https://www.yuque.com/p1ut0/qtmgyx/eubd9v#4CpNb

https://bbs.pediy.com/thread-226011.htm#msg_header_h3_15