YARA 的C语言完整集成与开发方法

以下是 YARA 的 C 语言完整集成与开发方法,包括:

  • YARA C API 的使用

  • 规则编译、文件扫描、回调处理

  • 完整的 C 示例代码

  • 编译与链接方法

  • 高级用法(自定义外部变量、模块支持等)

🧰 一、前置条件

1. 安装 YARA 开发库

Ubuntu/Debian

1
2
sudo apt-get install libyara-dev yara

CentOS/RHEL

1
2
3
4
sudo yum install yara-devel
# 或使用 dnf(新版本)
sudo dnf install yara-devel

macOS

1
2
brew install yara

确保 yara 命令可用,并且头文件(yara.h)和库文件(libyara.so / libyara.a)已安装。

📦 二、YARA C API 核心概念

data-ad-format="fluid" data-ad-layout-key="-7k+ex-4a-9w+4a">

YARA C API 主要包含以下组件:

组件说明YR_COMPILER用于编译 YARA 规则YR_RULES编译后的规则集合yr_rules_scan_*(…)扫描文件/内存/文件描述符YR_CALLBACK_FUNC匹配回调函数(接收匹配结果)

🧪 三、完整 C 示例代码:扫描文件并输出匹配结果

文件:yara_scan.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <yara.h>

// 回调函数:处理匹配结果
int callback_function(YR_SCAN_CONTEXT* context, int message, void* message_data, void* user_data)
{
YR_RULE* rule;
YR_STRING* string;
YR_MATCH* match;

switch (message)
{
case CALLBACK_MSG_RULE_MATCHING:
rule = (YR_RULE*) message_data;
printf("&#91;+] 匹配规则: %s", rule->identifier);

if (rule->tags&#91;0] != NULL)
{
printf(" (标签: ");
for (int i = 0; rule->tags&#91;i] != NULL; i++)
{
if (i > 0) printf(", ");
printf("%s", rule->tags&#91;i]);
}
printf(")");
}
printf("\n");

// 打印匹配的字符串
yr_rule_strings_foreach(rule, string)
{
yr_string_matches_foreach(context, string, match)
{
printf(" ├─ 字符串: %s = \"%.*s\" @ 0x%llx\n",
string->identifier,
match->data_length,
match->data,
match->base + match->offset);
}
}
break;

case CALLBACK_MSG_RULE_NOT_MATCHING:
// 可选:打印未匹配的规则
// rule = (YR_RULE*) message_data;
// printf("&#91;-] 未匹配: %s\n", rule->identifier);
break;

case CALLBACK_MSG_SCAN_FINISHED:
// 扫描完成
break;

default:
break;
}

return CALLBACK_CONTINUE; // 继续扫描
}

int main(int argc, char** argv)
{
if (argc != 3)
{
fprintf(stderr, "用法: %s <规则文件.yar> <目标文件>\n", argv&#91;0]);
return 1;
}

const char* rules_file = argv&#91;1];
const char* target_file = argv&#91;2];

YR_COMPILER* compiler = NULL;
YR_RULES* rules = NULL;
FILE* rule_fh = NULL;
int result;

// 初始化 YARA
yr_initialize();

// 创建编译器
result = yr_compiler_create(&compiler);
if (result != ERROR_SUCCESS)
{
fprintf(stderr, "无法创建编译器: %d\n", result);
goto cleanup;
}

// 打开规则文件
rule_fh = fopen(rules_file, "r");
if (!rule_fh)
{
perror("无法打开规则文件");
result = -1;
goto cleanup;
}

// 编译规则(从文件)
yr_compiler_add_file(compiler, rule_fh, NULL, rules_file);

// 检查编译错误
if (compiler->errors > 0)
{
fprintf(stderr, "规则编译失败!\n");
goto cleanup;
}

// 获取编译后的规则
result = yr_compiler_get_rules(compiler, &rules);
if (result != ERROR_SUCCESS)
{
fprintf(stderr, "无法获取规则: %d\n", result);
goto cleanup;
}

// 扫描目标文件
printf("🔍 开始扫描文件: %s\n", target_file);
result = yr_rules_scan_file(rules, target_file, 0, callback_function, NULL, 0);

if (result != ERROR_SUCCESS)
{
fprintf(stderr, "扫描失败: %d\n", result);
}

cleanup:
// 清理资源
if (rule_fh) fclose(rule_fh);
if (rules) yr_rules_destroy(rules);
if (compiler) yr_compiler_destroy(compiler);
yr_finalize();

return result == ERROR_SUCCESS ? 0 : 1;
}

🔧 四、编写测试 YARA 规则文件

文件:test.yar

1
2
3
4
5
6
7
8
9
10
11
12
rule HelloWorldRule
{
tags: test demo

strings:
$greeting = "Hello, World!" ascii
$number = { 48 65 6C 6C 6F } // "Hello" in hex

condition:
all of them
}

🛠️ 五、编译 C 程序

方法 1:使用 gcc 直接编译

1
2
gcc -o yara_scan yara_scan.c -lyara

如果提示找不到 -lyara,请确认 libyara.so 在 /usr/lib 或 /usr/local/lib。

方法 2:指定路径(如自定义安装)

1
2
3
4
5
gcc -o yara_scan yara_scan.c \
-I/usr/local/include \
-L/usr/local/lib \
-lyara

▶️ 六、运行测试

1
2
3
4
5
6
7
# 创建测试文件
echo "Hello, World!" > test.txt

# 编译并运行
gcc -o yara_scan yara_scan.c -lyara
./yara_scan test.yar test.txt

预期输出:

1
2
3
4
5
🔍 开始扫描文件: test.txt
&#91;+] 匹配规则: HelloWorldRule (标签: test, demo)
├─ 字符串: $greeting = "Hello, World!" @ 0x0
├─ 字符串: $number = "Hello" @ 0x0

⚙️ 七、高级功能集成

1. 使用外部变量(External Variables)

修改规则:

1
2
3
4
5
6
rule HasKeyword
{
condition:
contains(text, keyword)
}

在 C 中设置外部变量:

1
2
3
4
5
6
7
8
9
YR_EXTERNAL_VARIABLE ext_vars&#91;] = {
{ .identifier = "keyword", .type = YE_STRING, .value = {.ss = "malware"} },
{ .identifier = "text", .type = YE_STRING, .value = {.ss = "this is a malware sample"} },
{ .identifier = NULL } // 结束标记
};

// 扫描时传入
yr_rules_scan_mem(..., callback, NULL, 0, ext_vars);

2. 扫描内存缓冲区

1
2
3
4
5
char buffer&#91;] = "Hello, World!";
size_t buffer_len = strlen(buffer);

yr_rules_scan_mem(rules, buffer, buffer_len, 0, callback_function, NULL, 0);

3. 启用内置模块(如 hash, pe, elf)

1
2
3
4
5
6
7
8
9
10
// 启用 PE 模块
yr_rules_load(rules, "compiled_rules.bin"); // 可选:保存编译后的规则
yr_initialize();

// 启用模块(必须在扫描前)
yr_enable_features(YARA_FEATURE_EXTERNAL_VARIABLES | YARA_FEATURE_MODULES);

// 或使用宏控制
#define YR_ENABLE_CRYPTO

注意:使用 pe, hash 等模块需在编译 YARA 时启用对应功能。

📁 八、保存/加载编译后的规则(提高性能)

1
2
3
4
5
6
7
8
9
10
11
12
13
// 保存编译后的规则到文件
yr_rules_save(rules, "compiled.yarc");

// 加载已编译规则
YR_RULES* loaded_rules;
yr_rules_load("compiled.yarc", &loaded_rules);

// 直接扫描
yr_rules_scan_file(loaded_rules, target_file, 0, callback, NULL, 0);

// 销毁
yr_rules_destroy(loaded_rules);

适用于规则不变、频繁扫描的场景(如安全网关、EDR)。

🧩 九、常见错误与解决

错误原因解决undefined reference to ‘yr_initialize’未链接 -lyara添加 -lyaracannot open shared object libyara.so动态库未找到sudo ldconfig 或设置 LD_LIBRARY_PATH编译规则失败语法错误使用 yara test.yar /bin/ls 测试规则内存扫描崩溃缓冲区为空或未初始化检查指针和长度

📚 十、官方文档与资源

✅ 总结

通过 C 语言集成 YARA,你可以:

  • 将 YARA 深度嵌入到安全产品中(如杀毒引擎、EDR、防火墙)

  • 实现高性能、低延迟的实时扫描

  • 与自定义解析器、沙箱、驱动联动

  • 支持跨平台(Linux、Windows、macOS)

data-ad-format="auto" data-full-width-responsive="true">