- 网络安全
- 模式匹配
【免费下载链接】yara
The pattern matching swiss knife
导读
Magic 模块是 YARA 提供的官方模块之一,它把 Unix 标准命令file背后的 libmagic 库能力直接接入 YARA 规则语言,让你在规则条件中直接用magic.type()判断目标文件类型(如 "PDF document, version 1.5")、用magic.mime_type()获取其 MIME 类型(如 "application/pdf")。本文基于当前仓库的官方文档 docs/modules/magic.rst 与源码 libyara/modules/magic/magic.c 展开,读完你将掌握:Magic 模块的两个函数怎么用、与file命令输出的对应关系、如何把它编译进 YARA、底层实现原理与缓存机制,以及来自 tests/test-magic.c 的真实检测用例。
该模块自 YARA 3.1.0 起提供(见 docs/modules/magic.rst)。
一、Magic 模块是什么
Magic 模块允许你基于file命令的输出识别文件类型。file是 Unix 上最经典的文件类型识别工具,其底层依赖 libmagic 库——通过读取文件头部字节、比对内置的“魔法字节”(magic numbers)数据库,来判断文件真实类型,而不是简单地看扩展名。
在 YARA 中引入 Magic 模块后,规则编写者就能把这种类型识别能力作为规则条件的一部分,例如“命中所有 PDF 文件”或“命中所有 PE 可执行文件”,而不必手动编写繁琐的十六进制头字节匹配。
重要前提
- 默认不编译:Magic 模块和 Cuckoo 模块一样,并不默认构建进 YARA。必须在使用
configure配置时显式加上--enable-magic(详见下文编译章节)。 - 不支持 Windows:官方文档明确说明,该模块不支持在 Windows 上运行。这是因为 libmagic 在 Windows 平台上不可用/不常用(源码 libyara/modules/magic/magic.c 直接
#include <magic.h>,依赖系统 libmagic 头文件与链接库)。 - 不适用于进程内存扫描:从源码看,两个函数在
SCAN_FLAGS_PROCESS_MEMORY(进程内存扫描)模式下都会直接返回YR_UNDEFINED(见 libyara/modules/magic/magic.c 与 L135-L136),即规则条件中将视为未定义值。
二、模块的两个函数:type 与 mime_type
Magic 模块只暴露两个函数(见源码 libyara/modules/magic/magic.c 的声明注册):
| 函数 | 返回类型 | 返回内容 | 对应file命令 |
|---|---|---|---|
magic.type() | 字符串 | libmagic 对文件类型的人类可读描述 | 直接运行file <文件> |
magic.mime_type() | 字符串 | 文件的 MIME 类型(不含 charset 部分) | file --mime <文件> |
2.1 magic.type():描述性类型字符串
magic.type()返回与file命令一致的描述性字符串。例如对一份 PDF 文档运行file:
$ file some.pdf some.pdf: PDF document, version 1.5那么在规则中调用magic.type()就会返回"PDF document, version 1.5"。
典型规则用法(官方文档示例):
import "magic" rule IsPDF { condition: magic.type() contains "PDF" }2.2 magic.mime_type():MIME 类型
magic.mime_type()相当于把--mime参数传给file:
$ file --mime some.pdf some.pdf: application/pdf; charset=binary不同之处在于:magic.mime_type()只返回 MIME 类型本体,不包含; charset=...部分。上例中它返回的是"application/pdf"。
典型规则用法:
import "magic" rule IsPDFByMime { condition: magic.mime_type() == "application/pdf" }2.3 常见文件类型的输出示例
为了帮助你预期不同类型文件的检测结果,官方文档给出了一些通过实际运行file观察到的典型输出:
JPEG image data, JFIF standard 1.01PE32 executable for MS Windows (GUI) Intel 80386 32-bitPNG image data, 1240 x 1753, 8-bit/color RGBA, non-interlacedASCII text, with no line terminatorsZip archive data, at least v2.0 to extract
建议的做法是:针对你关心的样本,先手工运行file/file --mime观察真实输出,再据此编写contains或==匹配条件,避免猜测输出文本。
三、编译 Magic 模块
3.1 configure 开关
Magic 模块默认不编译。在源码根目录执行./configure时需要传入--enable-magic(见 docs/gettingstarted.rst):
$ ./configure --enable-magic也可以与其他模块组合开启:
$ ./configure --enable-cuckoo --enable-magic3.2 依赖 libmagic
configure.ac中对该开关的处理逻辑(configure.ac)会做以下检查:
- 检查头文件
magic.h:若缺失则报错please install libmagic library; - 检查链接库符号
magic_open:若缺失同样报错; - 通过后定义编译宏
MAGIC_MODULE,并把-lmagic加入PC_LIBS_PRIVATE(pkg-config 私有链接库)。
因此编译前必须先安装 libmagic 开发包。官方文档(docs/gettingstarted.rst)说明:Ubuntu、Debian 和 CentOS 均提供libmagic-dev软件包,例如:
$ sudo apt-get install libmagic-dev在构建系统中,Makefile.am中也有对应接线(Makefile.am 的MODULES += libyara/modules/magic/magic.c以及 L416-L419 的test-magic测试目标),确认模块源文件参与编译,并有对应测试程序tests/test-magic.c。
四、libmagic 数据库与 MAGIC 环境变量
libmagic 需要一份编译好的文件类型数据库才能工作。官方文档说明:
- 默认情况下,libmagic 尝试从
/etc/magic.mgc读取编译后的类型数据库; - 如果该文件不存在,可以通过环境变量
MAGIC指定一个magic.mgc文件的路径,libmagic 会转而从那里加载:- 若系统已有标准安装(通常 Debian/Ubuntu 的
libmagic1/libmagic-dev会提供/usr/share/misc/magic.mgc),可将MAGIC指向它; - 也可以指向自己编译的
file工具产生的数据库。
- 若系统已有标准安装(通常 Debian/Ubuntu 的
从源码实现看,模块调用的是magic_open(0)+magic_load(cookie, NULL)(libyara/modules/magic/magic.c),其中第二个参数传NULL即表示使用 libmagic 的默认数据库加载路径——这正是文档所述/etc/magic.mgc或MAGIC环境变量所影响的加载行为。
五、源码级实现原理
5.1 基于“首个内存块”的类型判定
两个函数的实现非常简洁(libyara/modules/magic/magic.c),核心逻辑一致:
- 通过
first_memory_block(context)取得被扫描文件的第一个内存块; - 用
yr_fetch_block_data(block)取得该块数据与大小; - 调用 libmagic 的
magic_buffer(cookie, block_data, block->size)对块内容进行识别。
也就是说,Magic 模块实际只读取被扫描文件的首个内存块做识别(与file工具读取文件头判断类型的思路一致)。若首个块为空,则返回YR_UNDEFINED。
两者的区别仅在于传给 libmagic 的 flags:
magic.type():magic_setflags(cookie, 0),输出完整描述文本;magic.mime_type():magic_setflags(cookie, MAGIC_MIME_TYPE),输出 MIME 类型。
5.2 线程本地缓存与“同一次扫描只算一次”
源码中值得注意的优化点是缓存机制:
- 模块定义了一个线程本地存储键
magic_tls,每个线程通过get_cache()惰性创建并缓存一个magic_tcookie(libyara/modules/magic/magic.c),避免每条规则重复magic_open/magic_load的开销; - 在
magic_type/magic_mime_type中,cached_type/cached_mime_type一旦计算过,同一次扫描中后续调用直接复用结果(L101-L102 与 L140-L141),且内存用yr_strdup复制并由module_unload统一释放(L198-L215)。
由此可以推断:在同一次扫描中多次使用magic.type()并不会多次执行 libmagic 分析,性能开销只有一次;同时由于缓存挂在线程本地,多线程扫描时各线程各自维护自己的 cookie,互不干扰。
5.3 模块生命周期
module_initialize创建线程本地存储键(L171-L174);module_load/module_unload在每次扫描开始/结束时调用,负责释放缓存的字符串(L189-L215);module_finalize关闭 cookie 并销毁线程本地键(L176-L187)。
六、实战:来自官方测试的检测规则
仓库自带的测试程序 tests/test-magic.c 提供了三个可直接借鉴的真实检测场景(该测试通过assert_true_rule_blob把规则直接应用于内置测试样本):
1. 检测 ELF 文件:
import "magic" rule test { condition: magic.type() contains "ELF" }2. 检测 PE 可执行文件(兼容描述文本差异):
import "magic" rule test { condition: ( magic.type() contains "MS-DOS executable" or magic.type() contains "PE32+ executable" or magic.type() contains "PE32 executable") and ( magic.mime_type() == "application/x-dosexec" or magic.mime_type() == "application/vnd.microsoft.portable-executable" ) }这个用例很有实战参考价值:同一类文件在不同 libmagic 版本/数据库下,type()的描述文本可能不同(如区分 32 位 PE32 与 64 位 PE32+),因此用contains组合多个关键词比精确==更稳健;同时可结合mime_type()的 MIME 判定做双重确认。
3. 检测 Mach-O 文件(对应 GitHub issue #1663 的回归测试):
import "magic" rule test { condition: magic.type() contains "Mach-O" and (magic.mime_type() == "application/x-mach-binary" or magic.mime_type() == "application/octet-stream") and magic.type() contains "Mach-O" }该用例说明:某些平台上 Mach-O 文件的 MIME 类型可能被识别为application/octet-stream,因此判定时需要同时兼容两种 MIME 结果。
这些测试目标均通过make check或单独构建test-magic运行(见 Makefile.am),可作为你验证本机 libmagic 输出与规则兼容性的最小实验。
七、使用建议与注意事项
- 先跑
file再写规则:libmagic 的输出随版本和数据库变化,写死精确字符串前,先对样本执行file与file --mime确认实际输出,再决定用contains还是==。 - 注意大小写与格式:
type()返回文本包含逗号、数字等细节(如PNG image data, 1240 x 1753, 8-bit/color RGBA, non-interlaced),匹配时建议用contains聚焦关键子串。 - 进程内存扫描不可用:在
SCAN_FLAGS_PROCESS_MEMORY场景下两个函数返回未定义值,因此该模块只适用于文件扫描(这从 libyara/modules/magic/magic.c 可以确认)。 - 平台限制:仅限类 Unix 平台(Linux/macOS 等),Windows 上不可用。
- 数据库缺失时:确保系统存在
/etc/magic.mgc,否则通过MAGIC环境变量指向可用的magic.mgc,否则 libmagic 加载会失败,模块初始化时报ERROR_INTERNAL_FATAL_ERROR(对应 magic.c 的magic_load失败分支)。
八、小结
Magic 模块用两个简单函数把成熟的 libmagic 类型识别能力带进了 YARA 规则语言:magic.type()给出人类可读的文件类型描述,magic.mime_type()给出 MIME 类型。它适合作为规则的第一道“类型过滤器”,配合其他模块(如pe、elf、dotnet)与字符串/十六进制条件,可以构建出先判类型、再深入分析的层级化检测逻辑。使用时注意三点:编译时显式--enable-magic、目标平台为类 Unix 且装有 libmagic、先运行file命令校准预期输出。
- 网络安全
- 模式匹配
【免费下载链接】yara
The pattern matching swiss knife
相关推荐
nhost 中基于 Magic Number 的 MIME 类型检测:179 种文件格式识别能力全解析
nhost 中基于 Magic Number 的 MIME 类型检测:179 种文件格式识别能力全解析 mimetype 是 nhost 仓库(Open Sou
后端认证鉴权数据库无服务开发工具云原生Mermaid Live Editor:在浏览器里实时画出可分享的图表
Mermaid Live Editor:在浏览器里实时画出可分享的图表 Mermaid Live Editor 是 Mermaid 官方的浏览器图表编辑器:输入
前端开发者工具数据可视化YARA PE 模块完全指南:基于 PE 结构编写精细化恶意软件检测规则
YARA PE 模块完全指南:基于 PE 结构编写精细化恶意软件检测规则 PE 模块( pe )是 YARA 内置模块之一,它把 Windows PE 文件(P
网络安全模式匹配
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考