表达式、运算符和其他结构

支持的平台:

本文档包含的信息可帮助您使用表达式构建 YARA-L 规则和查询。

布尔表达式

布尔值表达式是指具有布尔值类型的表达式,包括比较表达式、函数表达式以及参考列表或数据表表达式。您可以在 YARA-L 规则或查询的 eventsoutcome 部分中使用布尔表达式。

比较表达式

比较表达式是指对两个表达式应用比较运算符的表达式。表达式可以是事件字段、变量、字面量或函数表达式。

示例:比较表达式

$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")

参考列表或数据表

您可以在 eventsoutcome 部分中使用参考列表或数据表。如需详细了解参考列表和数据表的行为和语法,请参阅参考列表使用数据表

示例:参考列表的语法

以下示例展示了查询中各种类型的参考列表的语法:

// 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

示例:在参考列表语法中使用 notnocase

// 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 部分中使用逻辑 andor 运算符。

示例:逻辑表达式

$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"

默认情况下,优先级从高到低的顺序是 notandor。例如,如果表达式中明确定义了运算符 orand,“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 事件字段中的特定值,以便在整个查询中引用和使用。它们用于将不同的事件关联在一起,尤其是在多事件查询中。通过为占位符分配一个共同值(例如 useridhostname),您可以在 match 部分中使用此占位符,以在指定时间窗口内对共享该值的事件进行分组。

您可以在 events 部分中定义占位变量,方法是将 UDM 字段的值分配给以 $ 为前缀的变量名称(例如:$targetUser = $e.target.user.userid)。

您还可以在以下部分中定义占位符变量:

  • condition 部分来指定匹配条件。
  • outcome 部分,用于执行计算、定义指标或从匹配的事件中提取特定数据点。
  • match 部分,按共同值对事件进行分组。

关键字

在 YARA-L 中,关键字是用于定义检测查询的结构和逻辑的预留字。它们用于指定查询的不同部分、执行逻辑和数学运算,以及定义匹配事件的条件。这些关键字不能用作查询、字符串或变量的标识符。

关键字不区分大小写(例如,andAND 是等效的)。

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 转换。
    • base64base64wide:在匹配之前应用 Base64 编码。
  • 逻辑运算符
    • andornot:用于组合条件的标准布尔逻辑运算符。
    • all ofany of:用于评估条件中的多个表达式。
  • 比较运算符和关系运算符
    • at:指定字符串匹配的确切偏移量。
    • contains:检查字符串是否包含子字符串。
    • startswithendswith:检查字符串是否以某个子字符串开头或结尾。
    • icontainsistartswithiendswithiequals:不区分大小写的版本。
    • matches:用于正则表达式匹配。
  • 数据类型和大小说明符
    • int8uint8int16uint16int32uint32:具有指定大小的整数类型。
    • int8beuint8beint16beuint16beint32beuint32be:整数类型的大端版本。
    • filesize:表示正在分析的文件的大小。
    • entrypoint:指可执行文件的入口点。

地图

YARA-L 支持用于 StructLabel 数据类型的映射,这些数据类型用于某些 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"]

不支持的地图使用方式

anyall 关键字与地图结合使用

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 专业人士的解答。