表达式、运算符和其他结构
本文档包含的信息可帮助您使用表达式构建 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 // REGEX 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 // REGEX 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语句的最大数量(无论是否包含特殊运算符):7 - 含
regex运算符的in语句数上限:4 - 使用
cidr运算符的in语句数上限:2
逻辑表达式
您可以在 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 进行隐式联接。这样一来,评估顺序就会发生变化。
枚举类型
您可以将这些运算符与枚举类型搭配使用。它可以应用于规则,以简化和优化(使用运算符代替参考列表)性能。
在以下示例中,“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 中,所有变量均使用 $<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)。
您还可以在以下部分中定义占位符变量:
condition部分来指定匹配条件。outcome部分,用于执行计算、定义指标或从匹配的事件中提取特定数据点。match部分,按共同值对事件进行分组。
关键字
在 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 支持用于 Struct 和 Label 数据类型的映射,这些数据类型用于某些 UDM 字段。
如需在结构体和标签数据类型中搜索特定的键值对,请使用标准映射语法:
- 结构体字段语法:
$e.udm.additional.fields["pod_name"] = "kube-scheduler" - 标签字段语法:
$e.metadata.ingestion_labels["MetadataKeyDeletion"] = "startup-script"
示例:地图的有效使用和无效使用
以下示例展示了如何正确和错误地使用映射。
地图的有效使用
在“事件”部分中使用结构体字段:
events: $e.udm.additional.fields["pod_name"] = "kube-scheduler"
在结果部分中使用标签字段:
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
此示例直接检索字符串值(例如,如果 outcome_ip 是单个字符串,则检索 "1.1.1.1")。
访问整数结果
$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。
需要更多帮助?获得社区成员和 Google SecOps 专业人士的解答。