目录
1.2 字符串偏移或者虚拟地址(String offset or virtual addresses)
1.5 可执行入口点(Executable entry point)
1.6 访问给定位置的数据(Accessing data at a given position)
1.8 多个字符串应用相同条件(Applying the same condition to many strings)
1.9 字符串迭代(Iterating over string occurrences)
1.11 引用其它规则(Reference other rules)
1. 条件块
Yara中条件块和编程语言中的布尔表达式类似,例如if语句。它可以包含一些常见的布尔运算符,例如"and"、"or"、"not";关系运算符,例如">="、"<="、">"、"<"、"=="、"!=";算术运算符,例如"+"、"-"、"*"、"\"、"%";移位运算符,例如"&"、"|"、"<<"、"??"、"~"、"^";这些运算符可以组成一个数值表达式。
在条件块中,所有整数定义为64比特(8字节)长,即使一些函数的返回值是uint8、uint16、uint32,一律会被提升为uint64类型。因此在进行位运算时,例如对0x01进行取反操作时,结果不是0xFE,而是0xFFFFFFFFFFFFFFFE。
下面是条件块的示例:
rule Example
{
strings:
$a = "text1"
$b = "text2"
$c = "text3"
$d = "text4"
condition:
// 当前规则命中条件是字符串$a和$b至少命中一个,并且字符串$c和$d也至少命中一个。
($a or $b) and ($c or $d)
}
1.1 字符串计数(Counting Strings)
当我们不仅需要知道文件或者进程内存中是否包含某个字符串,还需要知道字符串的数量,那么我们可以使用关键字"#"和字符串标识符来表示字符串出现的数量。示例如下:
rule CountExample
{
strings:
$a = "dummy1"
$b = "dummy2"
condition:
// #a表示字符串"dummy1"在文本或进程内存出现的数量。
// #b表示字符串"dummy2"在文本或进程内存出现的数量。
// 下面条件组合表达的含义是当在文本或者进程内存中"dummy1"字符串出现6次,"dummy2"字符串出现超过10次则命中当前规则。
#a == 6 and #b > 10
}
1.2 字符串偏移或者虚拟地址(String offset or virtual addresses)
如果说我们知道某些字符串在文件或者进程内存中的确切或者大概位置,可以使用关键字"at"和偏移量数值来表达确切位置,关键字"in"和范围来表达大概位置。
下面是关键字"at"的使用示例:
rule AtExample
{
strings:
$a = "dummy1"
$b = "dummy2"
condition:
// 下面条件表达式的含义是,如果字符串"dummy1"位于文件的第100个字节处或者进程虚拟地址100(0x64)处,
// 同时字符串"dummy2"位于文件的第200个字节处或者进程虚拟地址200(0xC8)处,则命中当前规则。
$a at 100 and $b at 200
}
下面是关键字"in"的使用示例:
rule InExample
{
strings:
$a = "dummy1"
$b = "dummy2"
condition:
// 下面条件表达式的含义是,如果字符串"dummy1"位于文件的0-100字节内或者进程虚拟地址0-100(0x64)内,
// 同时字符串"dummy2"位于文件的第200-300字节内或者进程虚拟地址200(0xC8)-300(0x12C)内,则命中当前规则。
$a in (0..100) and $b in (200..300)
}
当知道某些字符串距离文件末尾大概长度时,可以使用关键字"filesize"用来表示当前正在检测文件的大小。示例如下:
rule InExample
{
strings:
$a = "dummy1"
condition:
// 下面条件表达式的含义是,如果字符串"dummy1"在文件的100字节开始到文件末尾的范围内出现,则命中当前规则。
$a in (100..filesize)
}
除此之外,如果想知道某个字符串在文件或者进程内存中第n次命中时距离文件开始或者进程0地址偏移量大小,可以使用关键字"@"和字符串标识符标识,通过"[]"中标明数值来表示第几次匹配,需要注意的是"[]"中数值从1开始,表示字符串第一次匹配时的位置。如果说"[]"中的索引值大于该字符串在文件中出现的次数,则结果将为NaN值。示例如下:
rule InExample
{
strings:
$a = "dummy1"
condition:
// 下面条件表达式的含义是,如果字符串"dummy1"第2次在文件中出现的位置位于文件的第100字节到文件末尾之间,则命中当前规则。
@a[2] in (100..filesize)
}
1.3 长度匹配(Match length)
对于正则表达式(例如使用具有贪婪特性的符号)和包含跳转信息的十六进制字符串(例如[4-10])时,匹配的字符串长度是可变的。如果想知道某个字符串被规则匹配时对应的长度,则可以使用关键字"!"和字符串标识符作为长度表示,也可以使用关键字"[]"来表示文件中第几次所匹配的字符串长度。示例如下:
rule InExample
{
strings:
$a = /dummy1*/
condition:
// 下面条件表达式的含义是,如果字符串$a第2次在文件中第2次出现时,且匹配字符串长度大于8字节,则命中当前规则。
!a[2] > 8
}
1.4 文件大小(File size)
如果说所检测的文件大小也是检测条件之一,可以使用关键字"filesize"表示当前待检测文件的大小。此关键字只能作用于文件,对于进程内存则用于不匹配。示例如下:
rule Example
{
strings:
$a = "dummy1"
condition:
// 下面条件表达式的含义是,当前所匹配的文件大小要求大于200KB,并且文件中包含字符串"dummy1",则命中当前规则。
filesize > 200KB and $a
}
1.5 可执行入口点(Executable entry point)
关键字"entrypoint"可用于表示可执行文件例如PE(Portable Executable)和ELF(Executable and Linkable Format)类型文件入口点的偏移量。如果说Yara正在扫描正在运行的进程,那么此关键字表示可执行程序的入口点的虚拟地址。此关键字适用于从可执行文件或者可执行程序的入口点检测是否遭受感染或者被篡改。使用示例如下:
rule EntryPointExample1
{
strings:
$a = { E8 00 00 00 00 }
condition:
// 所扫描的可执行文件或者程序的入口点开始的5个字节为E8 00 00 00 00,则命中此规则。
$a at entrypoint
}
rule EntryPointExample2
{
strings:
$a = { 9C 50 66 A1 ?? ?? ?? 00 66 A9 ?? ?? 58 0F 85 }
condition:
// 所扫描的可执行文件或者程序的入口点开始到10个字节范围内包含字符串$a所表示信息,则命中此规则。
$a in (entrypoint..entrypoint + 10)
}
注意:此变量只针对可执行程序或者可执行文件生效,其它类型文件针对此规则返回false。除此之外,此关键字不赞成在实际规则中使用。在Yara3.0开始,使用此关键字会产生告警,未来版本会删除此关键字。建议使用PE模块来替代此变量表示。
1.6 访问给定位置的数据(Accessing data at a given position)
在很多情况下,匹配规则是否需要继续匹配取决于文件或虚拟内存中特定位置的数值用来表示是否是需要扫描的文件类型或者目标文件或者目标进程。Yara支持以下关键字来确定文件或者虚拟内存中特定位置下的值:
// 从文件开始指定偏移位置或者虚拟内存指定地址中取长度为8、16或者32比特的有符号数值。(小端模式)
int8(<offset or virtual address>)
int16(<offset or virtual address>)
int32(<offset or virtual address>)
// 从文件开始指定偏移位置或者虚拟内存指定地址中取长度为8、16或者32比特的无符号数值。(小端模式)
uint8(<offset or virtual address>)
uint16(<offset or virtual address>)
uint32(<offset or virtual address>)
// 从文件开始指定偏移位置或者虚拟内存指定地址中取长度为8、16或者32比特的有符号数值。(大端模式)
int8be(<offset or virtual address>)
int16be(<offset or virtual address>)
int32be(<offset or virtual address>)
// 从文件开始指定偏移位置或者虚拟内存指定地址中取长度为8、16或者32比特的无符号数值。(大端模式)
uint8be(<offset or virtual address>)
uint16be(<offset or virtual address>)
uint32be(<offset or virtual address>)
使用示例如下:
rule IsPE
{
meta:
description = "此条件块用于判定文件类型是否为PE类型。"
condition:
// PE类型文件开头两个字节使用0x5A4D进行标识。
uint16(0) == 0x5A4D and
// PE类型文件在0x3C位置包含四个字节的标识为0x00004550。
uint32(uint32(0x3C)) == 0x00004550
}
1.7 字符串集(Sets of strings)
Yara支持将多个字符串作为一个集合的形式,配合"of"、"any"、"none"等关键字来表示更丰富的条件。可以通过关键字"()"和字符串标识符组合的形式表示字符串集,示例如下:
rule OfExample1
{
strings:
$a = "dummy1"
$b = "dummy2"
$c = "dummy3"
condition:
// 将字符串$a,$b,$c作为一个集合,要求文件中必须存在集合中至少两个不同的字符串算作命中当前规则。
2 of ($a,$b,$c)
}
可以使用通配符"*"来表示具有共同前缀的一类字符串标识符,从而降低规则编写的工作量,提高可读性。示例如下:
rule OfExample1
{
strings:
$foo1 = "foo1"
$foo2 = "foo2"
$foo3 = "foo3"
condition:
// 下面条件等同于 2 of ($foo1,$foo2,$foo3)
2 of ($foo*)
}
rule OfExample2
{
strings:
$foo1 = "foo1"
$foo2 = "foo2"
$bar1 = "bar1"
$bar2 = "bar2"
condition:
// 带通配符的字符串标识符可以和其它标识符配合使用。
3 of ($foo*,$bar1,$bar2)
}
可以使用关键字"them"来标识字符串块中所有的字符串内容,也可以使用"$*"表示。
rule OfExample1
{
strings:
$a = "dummy1"
$b = "dummy2"
$c = "dummy3"
condition:
// 下面规则等价于 1 of ($*)
1 of them
}
还有一些关于字符串集灵活的使用方法,示例如下:
all of them // 字符串块中所有字符串匹配成功则命中当前规则。
any of them // 字符串块中任何字符串匹配成功则命中当前规则。
all of ($a*) // 字符串块中所有带有"a"前缀的字符串匹配成功则命中当前规则。
any of ($a,$b,$c) // 字符串$a, $b or $c中任何字符串匹配成功则命中当前规则。
1 of ($*) // 字符串块中任何字符串匹配成功则命中当前规则。
none of ($b*) // 字符串块中所有带有"b"前缀的字符串匹配不成功则命中当前规则。
Yara4.2.0版本后支持在特定范围内匹配一组字符串,示例如下:
all of ($a*) in (filesize-500..filesize)
any of ($a*, $b*) in (1000..2000)
Yara4.3.0版本后支持在特定偏移处匹配一组字符串,示例如下:
any of ($a*) at 0
1.8 多个字符串应用相同条件(Applying the same condition to many strings)
"for..of"关键字的应用会使匹配条件更加灵活。下面是此关键字的使用语法:
for expression of string_set : ( boolean_expression )
// expression为表示式,可以是any等。
// boolean_expression是布尔表达式,用于做前面所列出的字符串匹配条件。
下面是使用示例:
// 下面条件的含义是字符串$a,$b,$c中任何一个字符串在PE文件的入口处即为命中当前规则。
// 其中布尔表达式中"$"作为一个占位符,表示前面所迭代字符。本例中它将分别代表
// $a,然后是$b,最后是$c。
for any of ($a,$b,$c) : ( $ at pe.entry_point )
布尔表达式中也可以使用"#"、"@"、"!"关键字分别表示字符串匹配数量,字符串匹配位置以及字符串匹配长度。示例如下:
// 字符串块中所有的字符串在文件中匹配中数量超过3次即命中当前规则。
for all of them : ( # > 3 )
// 字符串块中所有以$"a"为前缀的字符串在文件中出现的位置大于字符串$b所在的位置即命中当前规则。
for all of ($a*) : ( @ > @b )
Yara4.3.0开始支持临时文本字符串集合,示例如下:
// 其中pe.imphash()函数的作用是针对PE类型文件,提取其导入表,然后计算哈希值。如果所计算的哈希值与前面表达式中包含的两个哈希值
// 任意一个相同,则命中当前规则。其中字符"s"用于承载临时字符串值用于匹配使用。
for any s in ("71b36345516e076a0663e0bea97759e4", "1e7f7edeb06de02f2c2a9319de99e033") : ( pe.imphash() == s )
1.9 字符串迭代(Iterating over string occurrences)
编写规则匹配条件时,可以使用迭代的方式来满足指定的匹配条件。示例如下:
rule Occurrences
{
strings:
$a = "dummy1"
$b = "dummy2"
condition:
// 下面条件要求第1、2、3个字符串$a在文件中位置加上10个字节后分别等于第1、2、3个字符串$b在文件中位置。
// 其中变量i在迭代过程中分别等于1、2、3。
for all i in (1,2,3) : ( @a[i] + 10 == @b[i] )
}
上面示例中给定了变量i的边界范围,即分别等于1、2、3。当然,可以使用关键字"#"和字符串标识符组合的形式自动适应所匹配的文件实际情况进行匹配。示例如下:
// 其中变量i的边界值取决于所检测的文件中包含字符串$a数量,即#a。
for all i in (1..#a) : ( @a[i] < 100 )
通过修改表达式也可以限制字符串匹配的数量,示例如下:
// 文件中至少有两个字符串$a,且在文件100个字节范围内即命中当前规则。
for 2 i in (1..#a) : ( @a[i] < 100 )
注意:for..in和for..of功能很相似,但是for..of用于迭代给定的字符串集,for..in可以迭代一个范围(ranges)、枚举(enumerations)、数组(array)和字典(dictionaries)。
1.10 迭代器(Iterators)
Yara4.0版本开始for..in操作符功能提升,不仅可以迭代整形枚举、范围,还可以迭代任意可迭代的数据类型,例如Yara模块中定义的数组、字典等。示例如下:
// pe.sections返回一个可迭代的数组列表,列表中每个元素为PE文件中所包含的section信息。
// for any section in pe.sections是迭代取出PE文件section列表中每个section信息。
// section.name == ".text"是判断所迭代的当前section中name属性是否为.text。
for any section in pe.sections : ( section.name == ".text" )
迭代字典对象时,需要提供两个变量名分别保存字典中每个条目的键和值,示例如下:
// some_dict是一个可迭代字典对象,下面条件是判断字典中是否包含键为"foo"并且值为"bar"的条目,如果有则命中当前规则。
for any k,v in some_dict : ( k == "foo" and v == "bar" )
1.11 引用其它规则(Reference other rules)
在编写规则匹配条件时,可以引用其它规则,需要注意的是,引用的其它规则要在当前规则之前定义。示例如下:
rule Rule1
{
strings:
$a = "dummy1"
condition:
$a
}
rule Rule2
{
strings:
$a = "dummy2"
condition:
// 下面条件的含义是文档中存在字符串"dummy2",并且Rule1规定的文件中存在字符串"dummy1"也成立,命中当前规则。
$a and Rule1
}
Yara4.2.0版本引入规则集,其操作方式和字符串集类似,需要注意的是,规则集中所有规则的定义要在包含规则集规则前面定义。示例如下:
rule Rule1
{
strings:
$a = "dummy1"
condition:
$a
}
rule Rule2
{
strings:
$a = "dummy2"
condition:
$a
}
rule MainRule
{
strings:
$a = "dummy2"
condition:
any of (Rule*)
}