表达式、运算符和其他结构
本文档包含的信息可帮助您使用表达式构建 YARA-L 规则和查询。
布尔表达式
布尔表达式是布尔类型的表达式,包括比较表达式、函数表达式以及引用列表或数据表表达式。您可以在 YARA-L 规则或查询的 events 和 outcome 部分中使用布尔表达式。
比较表达式
比较表达式是指对两个表达式应用比较运算符的表达式。表达式可以是事件字段、变量、字面量或函数表达式。
示例:比较表达式
$e.source.hostname = "host1234"
$e.source.port < 1024
1024 < $e.source.port
$e1.source.hostname != $e2.target.hostname
$e1.metadata.collected_timestamp.seconds > $e2.metadata.collected_timestamp.seconds
$port >= 25
$host = $e2.target.hostname
"google-test" = strings.concat($e.principal.hostname, "-test")
"email@google.org" = re.replace($e.network.email.from, "com", "org")
函数表达式
某些函数表达式会返回布尔值,可用作 events 部分中的单个谓词,例如:
re.regex()
net.ip_in_range_cidr()
示例:函数表达式
re.regex($e.principal.hostname, `.*\.google\.com`)
net.ip_in_range_cidr($e.principal.ip, "192.0.2.0/24")
引用列表或数据表
您可以在 events 或 outcome 部分中使用引用列表或数据表。如需详细了解引用列表和数据表的行为和语法,请参阅引用列表和使用数据表。
示例:引用列表的语法
以下示例展示了查询中各种类型的引用列表的语法:
// STRING reference list $e.principal.hostname in %string_reference_list // Regular expression reference list $e.principal.hostname in regex %regex_reference_list // CIDR reference list $e.principal.ip in cidr %cidr_reference_list
示例:数据表的语法
// STRING data table $e.target.hostname in %data_table_name.column_name // Regular expression data table $e.target.hostname in regex %regex_table_name.column_name // CIDR data table $e.principal.ip in cidr %cidr_table_name.column_name
示例:在引用列表语法中使用 not 和 nocase
// Exclude events whose hostnames match substrings in my_regex_list. not $e.principal.hostname in regex %my_regex_list // Event hostnames must match at least 1 string in my_string_list (case insensitive). $e.principal.hostname in %my_string_list nocase
nocase 运算符与 STRING 列表和 REGEX 列表兼容。
出于性能方面的原因,引用列表和数据表的使用存在以下限制:
- 查询中
in语句的最大数量(无论是否包含特殊运算符):10 - 包含
regex运算符的in语句的最大数量:5 - 包含
cidr运算符的in语句的最大数量:5
逻辑表达式
您可以在 events 部分中使用逻辑 and 和 or 运算符。
示例:逻辑表达式
$e.metadata.event_type = "NETWORK_DNS" or $e.metadata.event_type = "NETWORK_DHCP"
($e.metadata.event_type = "NETWORK_DNS" and $e.principal.ip = "192.0.2.12") or ($e.metadata.event_type = "NETWORK_DHCP" and $e.principal.mac = "AB:CD:01:10:EF:22")
not $e.metadata.event_type = "NETWORK_DNS"
默认情况下,优先级从高到低的顺序是 not、and、or。例如,如果在表达式中明确定义了运算符 or 和 and,则“a or b and c”会被评估为“a or (b and c)”。
在 events 部分中,如果未明确定义运算符,则谓词会使用 and 运算符联接。如果表达式中隐含了 and 运算符,则评估顺序可能会有所不同。
请考虑以下比较表达式,其中明确定义了 or,而隐含了 and 运算符。
$e1.field = "bat" or $e1.field = "baz" $e2.field = "bar"
其解读如下:
($e1.field = "bat" or $e1.field = "baz") and ($e2.field = "bar")
由于明确定义了 or,因此系统会先对周围的谓词进行分组和评估。最后一个谓词是 $e2.field = "bar"。使用 and 隐式联接。结果是评估顺序发生了变化。
逻辑表达式中的数据表查找
在 events 部分中使用数据表查找(in %list 语法)时,适用以下语法限制:
类型独占:您无法在同一逻辑块中混用标准字段比较(例如
$field = "value")和数据表查找。运算符限制:数据表查找只能使用
OR运算符与其他数据表查找联接。这些表达式不支持AND运算符。使用
OR的事件变量独占:使用OR运算符组合数据表查找时,OR表达式中的所有查找都必须引用来自同一事件变量的字段。例如,($e.user in %table1 OR $e.group in %table2)是有效的。但是,不支持($e.user in %table OR $g.user in %table)等表达式。这是因为,在OR条件下,跨不同事件变量(例如$e和$g)的查找缺少共同的等式锚点,这会导致编译器在运行时失败,并显示RULE ERROR: error processing rule。
示例:语法错误导致结果无效
// Invalid: Mixing a field comparison with a data table lookup
($field = "value" OR $field in %list)
// Invalid: Using AND with data table lookups
($field_A in %list_A AND $field_B in %list_B)
示例:有效示例
// Valid: Multiple data table lookups joined by OR
($field in %list_A OR $field in %list_B)
示例:无效的跨变量 OR
// Invalid: OR condition across different event variables
($e.user in %table OR $g.user in %table)
如果您的逻辑既需要标准字段检查,又需要数据表查找,则必须在 events 部分中将它们定义为单独的独立谓词。
枚举类型
您可以将运算符与枚举类型搭配使用。它可以应用于规则,以简化和优化(使用运算符而不是引用列表)性能。
在以下示例中,“USER_UNCATEGORIZED”和“USER_RESOURCE_DELETION”分别对应于 15000 和 15014,因此该规则将查找所有列出的事件:
$e.metadata.event_type >= "USER_CATEGORIZED" and $e.metadata.event_type <= "USER_RESOURCE_DELETION"
Nocase 修饰符
如需在字符串值或正则表达式之间的比较表达式中忽略大小写,请在表达式末尾附加 nocase,如以下示例所示。
示例:nocase 修饰符
$e.principal.hostname != "http-server" nocase
$e1.principal.hostname = $e2.target.hostname nocase
$e.principal.hostname = /dns-server-[0-9]+/ nocase
re.regex($e.target.hostname, `client-[0-9]+`) nocase
当字段类型是枚举值时,不能使用 nocase 修饰符。以下示例无效,并会产生编译错误:
$e.metadata.event_type = "NETWORK_DNS" nocase
$e.network.ip_protocol = "TCP" nocase
评论
您可以在查询中使用评论来提供更多信息。您可以使用正斜杠字符来表示评论:
- 对于单行评论,请使用两个正斜杠字符 (
// comment)。 - 对于多行评论,请使用一个正斜杠字符和一个星号字符 (
/* comment */)。
字面量
YARA-L 支持非负整数和浮点数、字符串、布尔值和正则表达式字面量。字面量是查询条件中使用的固定值。YARA-L 还使用其他类似字面量的结构,例如用于模式匹配的正则表达式(用正斜杠括起来)和用于逻辑的布尔值 (true/false)。
字符串字面量
字符串字面量是用双引号 (") 或反引号 (`) 括起来的字符序列。字符串的解读方式因您使用的引号类型而异:
- 双引号 ("hello\tworld"):用于普通字符串;必须包含转义字符,其中 \t 会被解读为制表符。
- 反引号 (`hello\tworld`):用于按字面解释所有字符,其中 \t 不会被解读为制表符。
正则表达式字面量
对于正则表达式字面量,您有两种选择:
如果您想在不使用
re.regex()函数的情况下直接使用正则表达式,请使用/regex/作为正则表达式字面量。如果您想使用字符串字面量作为正则表达式字面量,请使用
re.regex()函数。请注意,对于双引号字符串字面量,您必须用反斜杠字符转义反斜杠字符,这看起来很奇怪。
以下示例展示了等效的正则表达式:
re.regex($e.network.email.from, `.*altostrat\.com`)
re.regex($e.network.email.from, ".*altostrat\\.com")
$e.network.email.from = /.*altostrat\.com/
Google 建议对正则表达式中的字符串使用英文反引号字符,以便于阅读。
运算符
| 运算符 | 说明 |
| = | 等于/声明 |
| != | 不等于 |
| < | 小于 |
| <= | 小于或等于 |
| > | 大于 |
| >= | 大于或等于 |
了解如何在 YARA-L 中使用变量
在 YARA-L 中,所有变量都使用语法 $<variable name>。本部分介绍了在 YARA-L 中使用的变量类型。
事件变量
事件变量表示事件组或实体事件。您可以使用名称、事件来源和事件字段在 events 部分中指定事件变量的条件。
事件来源为
udm(用于规范化事件)和graph(用于实体事件)。如果省略来源,则udm设置为默认来源。事件字段表示为 .<field name> 链(例如 $e.field1.field2),并且字段链始终从顶级来源(UDM 或实体)开始。
匹配变量
匹配变量用于 match 部分,以根据指定时间窗口内的通用值对事件进行分组。
它们会成为查询的分组字段,因为对于每组唯一的匹配变量(以及每个时间范围),都会返回一行。当查询找到匹配项时,将返回匹配变量值。
您可以在 events 部分中指定每个匹配变量表示的内容。
占位符变量
您可以使用占位符变量从 UDM 事件字段捕获和存储特定值,以便在整个查询中引用和使用。 您可以使用它们将分散的事件链接在一起,尤其是在多事件查询中。通过为占位符分配通用值(例如 userid 或 hostname),您可以在 match 部分中使用此占位符,以对指定时间窗口内共享该值的事件进行分组。
您可以在 events 部分中定义占位符变量,方法是将 UDM 字段的值分配给以 $ 为前缀的变量名称(例如 $targetUser = $e.target.user.userid)。
您还可以在以下部分中定义占位符变量:
函数到占位符的分配
在 YARA-L 中,函数到占位符的分配是指转换 UDM 事件数据并存储结果以供整个规则使用的过程。 此操作可让您在 events 部分中执行计算或数据操作,并在 match、condition 和 outcome 部分中引用这些结果。
以下是核心组件:
- **函数**:内置操作,用于对特定事件字段执行计算或数据转换(例如字符串操作或数学运算)。
- 占位符变量:用户定义的变量,以
$为前缀(例如$user、$ip),充当容器。它们存储直接从事件中提取的值或由函数输出生成的值。 - 分配:将函数的输出链接到占位符变量的逻辑,使转换后的数据可供其余检测逻辑访问。
限制
使用函数到占位符的分配时,存在以下两个限制:
您必须将每个占位符分配给包含事件字段的表达式。
有效示例
$ph1 = $e.principal.hostname $ph2 = $e.src.hostname // Both $ph1 and $ph2 have been assigned to an expression containing an event field. $ph1 = strings.concat($ph2, ".com")$ph1 = $e.network.email.from $ph2 = strings.concat($e.principal.hostname, "@gmail.com") // Both $ph1 and $ph2 have been assigned to an expression containing an event field. $ph1 = strings.to_lower($ph2)无效示例
$ph1 = strings.concat($e.principal.hostname, "foo") $ph2 = strings.concat($ph1, "bar") // $ph2 has NOT been assigned to an expression containing an event field.函数调用应依赖于一个且仅一个 事件。但是,函数调用参数中可以使用来自同一事件的多个字段。
有效示例
$ph = strings.concat($event.principal.hostname, "string2")$ph = strings.concat($event.principal.hostname, $event.src.hostname)无效示例
$ph = strings.concat("string1", "string2")$ph = strings.concat($event.principal.hostname, $anotherEvent.src.hostname)
使用关键字定义查询
在 YARA-L 中,关键字是保留字,用于定义检测查询的结构和逻辑。它们用于指定查询的不同部分、执行逻辑和数学运算,以及定义匹配事件的条件。这些关键字不能用作查询、字符串或变量的标识符。
关键字不区分大小写(例如,and 或 AND 是等效的)。
YARA-L 2.0 关键字的主要类别
此列表并不详尽,但涵盖了 YARA-L 2.0 中用于构建可靠检测查询的主要关键字。
- 查询定义:
rule:启动新 YARA-L 查询的定义。private:将查询指定为私有,防止其直接公开或在外部触发。global:将查询标记为全局,表示应广泛应用。
- 查询部分:
meta:引入元数据部分,用于提供有关查询的说明性信息。strings:表示定义字符串模式的部分。condition:指定包含查询触发的布尔逻辑的部分。events:定义用于指定事件变量及其条件的部分。match:引入用于在时间窗口内聚合值的部分。outcome:定义用于向触发的查询添加上下文和评分的部分。
- 字符串修饰符:
ascii:指定字符串应作为 ASCII 文本进行匹配。wide:表示字符串应作为宽字符 (UTF-16) 进行匹配。nocase:执行不区分大小写的字符串匹配。fullword:要求字符串作为完整字词进行匹配。xor:在匹配之前对字符串应用 XOR 转换。base64、base64wide:在匹配之前应用 Base64 编码。
- 逻辑运算符:
and、or、not:用于组合条件的标准布尔逻辑运算符。all of、any of:用于评估条件内的多个表达式。
- 比较和关系运算符:
at:指定字符串匹配的确切偏移量。contains:检查字符串是否包含子字符串。startswith、endswith:检查字符串是否以子字符串开头或结尾。icontains、istartswith、iendswith、iequals:不区分大小写的版本。matches:用于正则表达式匹配。
- 数据类型和大小说明符:
int8、uint8、int16、uint16、int32、uint32:具有指定大小的整数类型。int8be、uint8be、int16be、uint16be、int32be、uint32be:整数类型的大端版本。filesize:表示正在分析的文件的大小。entrypoint:指可执行文件的入口点。
YARA-L 映射支持
YARA-L 支持 Struct 和 Label 数据类型的映射,这些数据类型在某些 UDM 字段中使用。
如需在 Struct 和 Label 数据类型中搜索特定的键值对,请使用标准映射语法:
- Struct 字段语法:
$e.udm.additional.fields["pod_name"] = "kube-scheduler" - Label 字段语法:
$e.metadata.ingestion_labels["MetadataKeyDeletion"] = "startup-script"
示例:映射的有效和无效使用
以下示例展示了映射的有效和无效使用。
映射的有效使用
在 events 部分中使用 Struct 字段:
events: $e.udm.additional.fields["pod_name"] = "kube-scheduler"
在 outcome 部分中使用 Label 字段:
outcome: $value = array_distinct($e.metadata.ingestion_labels["MetadataKeyDeletion"])
将映射值分配给占位符:
$placeholder = $u1.metadata.ingestion_labels["MetadataKeyDeletion"]
在联接条件中使用映射字段:
// using a Struct field in a join condition between two udm events $u1 and $u2 $u1.metadata.event_type = $u2.udm.additional.fields["pod_name"]
不支持使用映射
将 any 或 all 关键字与映射组合
all $e.udm.additional.fields["pod_name"] = "kube-scheduler"
其他类型的值
映射语法只能返回字符串值。对于 [Struct](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#struct) 数据类型,映射语法只能访问其值为字符串的键。 无法访问其值为其他原始类型(例如整数)的键。
映射中的重复值处理
映射访问旨在检索与特定键关联的单个值。这是标准行为,也是预期行为。
但是,在极少数情况下,map access 的上下文可能会无意中指向多个值。在映射访问引用多个值的罕见极端情况下,map access 将确定性地返回第一个值。如果标签具有重复的键,或者标签具有祖先重复字段,则可能会发生这种情况。
标签具有重复的键
标签结构表示映射,但不强制执行键的唯一性。按照惯例,映射应具有唯一的键,因此 Google SecOps 不建议使用重复的键填充标签。
示例:具有重复键的标签
如果对以下数据示例运行查询文本 $e.metadata.ingestion_labels["dupe-key"],则会返回
第一个可能的值 val1:
// Disrecommended usage of label with a duplicate key:
event {
metadata{
ingestion_labels{
key: "dupe-key"
value: "val1" // This is the first possible value for "dupe-key"
}
ingestion_labels{
key: "dupe-key"
value: "val2"
}
}
}
标签具有祖先重复字段
重复字段可能包含作为子字段的标签。顶级重复字段中的两个不同条目可能包含具有相同键的标签。
示例:具有祖先重复字段的标签
如果对以下
数据示例运行查询文本 $e.security_result.rule_labels["key"]
,则会返回第一个可能的值 `val3`:
event {
// security_result is a repeated field.
security_result {
threat_name: "threat1"
rule_labels {
key: "key"
value: "val3" // This is the first possible value for "key"
}
}
security_result {
threat_name: "threat2"
rule_labels {
key: "key"
value: "val4"
}
}
}
访问映射中的结果变量
本部分介绍了如何以原始数据类型(例如整数、布尔值或这些类型的列表)访问映射中的结果变量,而不是仅以字符串形式访问。您可以使用此功能来提高查询逻辑的灵活性和准确性。
结果数据可在以下字段中找到:
- 结果值在
variables字段中保留其原始类型。 outcomes字段存储string版本,以实现向后兼容性。
您可以使用 variables 映射访问这些结果值,以检索特定类型或使用数组索引访问序列中的元素。您可以按索引访问序列中的特定项,也可以选择整个序列以单独评估每个值。
语法:
$d.detection.detection.variables[OUTCOME_NAME].TYPE_SUFFIX
序列的语法:
$d.detection.detection.variables[OUTCOME_NAME].SEQUENCE_TYPE_SUFFIX.TYPE_VALS_SUFFIX
示例:访问映射中的结果变量
访问字符串结果:
$my_string_outcome = $d.detection.detection.variables["outcome_ip"].string_val
此示例直接检索字符串值(例如,"1.1.1.1" 如果
outcome_ip 是单个字符串)。
访问整数结果
$my_int_outcome = $d.detection.detection.variables["outcome_port"].int64_value
此示例检索整数值(例如 30)。
使用 Int64Sequence 访问整数列表
$my_int_list = $d.detection.detection.variables["outcome_ports"].int64_seq.int64_vals
此示例检索完整的整数列表,并像
重复字段一样将其取消嵌套(例如 [2, 3, 4])。
从整数列表中访问特定元素
$first_int = $d.detection.detection.variables["outcome_ports"].int64_seq.int64_vals[0]
此示例从列表中检索第一个整数(例如 2)。
访问字符串列表 (StringSequence)
$my_string_list = $d.detection.detection.variables["outcome_ips"].string_seq.string_vals
此示例检索完整的字符串列表,并像
重复字段一样将其取消嵌套(例如["1.1.1.1", "2.2.2.2"])。
从字符串列表中访问特定元素
$first_ip = $d.detection.detection.variables["outcome_ips"].string_seq.string_vals[0]
此示例从列表中检索第一个 IP 地址(例如 "1.1.1.1")。
variables 的可用类型后缀
如需查看支持的后缀的完整列表,请参阅 FindingVariable。