YARA Magic 模块完全指南:用 libmagic 文件类型识别能力武装你的检测规则
2026/9/24 13:58:37 网站建设 项目流程
  • 网络安全
  • 模式匹配

【免费下载链接】yara

The pattern matching swiss knife

项目地址:https://gitcode.com/gh_mirrors/ya/yara
点击查看免费下载

导读

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.01
  • PE32 executable for MS Windows (GUI) Intel 80386 32-bit
  • PNG image data, 1240 x 1753, 8-bit/color RGBA, non-interlaced
  • ASCII text, with no line terminators
  • Zip 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-magic

3.2 依赖 libmagic

configure.ac中对该开关的处理逻辑(configure.ac)会做以下检查:

  1. 检查头文件magic.h:若缺失则报错please install libmagic library
  2. 检查链接库符号magic_open:若缺失同样报错;
  3. 通过后定义编译宏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工具产生的数据库。

从源码实现看,模块调用的是magic_open(0)+magic_load(cookie, NULL)(libyara/modules/magic/magic.c),其中第二个参数传NULL即表示使用 libmagic 的默认数据库加载路径——这正是文档所述/etc/magic.mgcMAGIC环境变量所影响的加载行为。

五、源码级实现原理

5.1 基于“首个内存块”的类型判定

两个函数的实现非常简洁(libyara/modules/magic/magic.c),核心逻辑一致:

  1. 通过first_memory_block(context)取得被扫描文件的第一个内存块
  2. yr_fetch_block_data(block)取得该块数据与大小;
  3. 调用 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 的输出随版本和数据库变化,写死精确字符串前,先对样本执行filefile --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 类型。它适合作为规则的第一道“类型过滤器”,配合其他模块(如peelfdotnet)与字符串/十六进制条件,可以构建出先判类型、再深入分析的层级化检测逻辑。使用时注意三点:编译时显式--enable-magic、目标平台为类 Unix 且装有 libmagic、先运行file命令校准预期输出。

  • 网络安全
  • 模式匹配

【免费下载链接】yara

The pattern matching swiss knife

项目地址:https://gitcode.com/gh_mirrors/ya/yara
点击查看免费下载

相关推荐

上一篇:Flutter Unity View Widget:在Flutter中嵌入Unity游戏引擎视图
下一篇:startbootstrap-agency联系表单集成:SB Forms配置与使用教程

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询