指导编写用于恶意软件识别的高质量 YARA-X 检测规则。适用于编写、审查或优化 YARA 规则等场景。涵盖命名规范、字符串提取与筛选、性能优化、传统 YARA 规则迁移以及降低误报率。触发词:YARA、YARA-X、恶意软件检测、威胁猎捕、IOC、特征码、crx 模块、dex 模块。
YARA-X Rule Authoring
写出既能精准缉捕恶意软件、又不会让你淹没在误报海洋里的检测规则。
本 Skill 针对 YARA-X——基于 Rust 编写的新一代 YARA。YARA-X 为 VirusTotal 生产系统提供动力,也是官方推荐的实现版本。如果你手中已有旧版规则,请参考 从旧版 YARA 迁移。
核心原则
-
字符串必须能生成优质 Atom(原子) — YARA 会提取 4 字节子串用于快速匹配。如果字符串包含重复字节、通用序列,或者长度不足 4 字节,会导致大量文件陷入低效的字节码验证流程。
-
锁定具体家族,而非泛泛的类别 — “检测勒索软件” 相当于啥都没检出;“检测 LockBit 3.0 配置提取 Routine” 才能精准命中目标。
-
上线部署前务必在白样本(Goodware)上测试 — 一条在 Windows 系统文件上乱报的规则毫无价值。上线前请在 VirusTotal 的白样本库或你自有的干净文件集中进行验证。
-
优先使用低开销检查进行 Short-circuit(短路逻辑) — 把
filesize < 10MB and uint16(0) == 0x5A4D放在昂贵的字符串搜索或模块调用之前。 -
Metadata 就是文档 — 未来的你(以及你的团队)需要清楚知道这条规则抓的是什么、为什么要抓、以及样本来自何处。
适用场景
- 编写用于恶意软件检测的新 YARA-X 规则
- 审查现有规则的质量或性能问题
- 优化运行缓慢的规则集
- 将 IOC 或威胁情报转化为检测特征码(Signature)
- 排查与调试误报问题
- 准备将规则部署上线到生产环境
- 将旧版 YARA 规则迁移至 YARA-X
- 分析 Chrome 扩展程序(crx 模块)
- 分析 Android 应用(dex 模块)
禁用场景
- 需要反汇编的静态分析 → 请使用 Ghidra/IDA 相关的 Skill
- 动态恶意软件分析 → 请使用沙盒分析(Sandbox analysis)相关的 Skill
- 基于网络的检测 → 请使用 Suricata/Snort 相关的 Skill
- 使用 Volatility 进行内存取证 → 请使用内存取证相关的 Skill
- 简单的 Hash 匹配检测 → 直接使用 Hash 清单即可
YARA-X 概览
YARA-X 是基于 Rust 重构的新一代 YARA:正则匹配速度提升 5~10 倍、报错提示更友好、内置代码格式化工具、校验更严格、新增支持模块(crx、dex),且保持了 99% 的规则兼容性。
安装方法: brew install yara-x (macOS) 或 cargo install yara-x
常用命令: yr scan, yr check, yr fmt, yr dump
平台考量
YARA 支持处理任何文件类型。请根据目标平台灵活调整匹配模式:
| 平台 | Magic Bytes(魔数) | 不推荐的字符串 | 推荐的字符串 |
|---|---|---|---|
| Windows PE | uint16(0) == 0x5A4D |
API 名称、Windows 常用路径 | Mutex(互斥量)名称、PDB 路径 |
| macOS Mach-O | uint32(0) == 0xFEEDFACE (32位), 0xFEEDFACF (64位), 0xCAFEBABE (通用/Fat) |
通用 Obj-C 方法名 | 键盘记录器特征串、持久化路径 |
| JavaScript/Node | (无需指定) | require, fetch, axios |
混淆器签名、eval+decode 链 |
| npm/pip 包 | (无需指定) | postinstall, dependencies |
可疑包名、数据外泄(Exfil)URL |
| Office 文档 | uint32(0) == 0x504B0304 |
VBA 关键字 | 宏自动执行逻辑、编码后的 Payload |
| VS Code 插件 | (无需指定) | vscode.workspace |
罕见的 activationEvents、隐藏文件访问逻辑 |
| Chrome 扩展 | 使用 crx 模块 |
常用 Chrome API | 权限滥用特征、manifest 异常配置 |
| Android 应用 | 使用 dex 模块 |
标准 DEX 结构 | 被混淆的类名、可疑权限申请 |
macOS 恶意软件检测
目前尚无专属的 Mach-O 模块。可通过魔数校验 + 字符串特征组合实现检测:
魔数(Magic bytes):
// Mach-O 32-bit
uint32(0) == 0xFEEDFACE
// Mach-O 64-bit
uint32(0) == 0xFEEDFACF
// Universal binary (fat binary)
uint32(0) == 0xCAFEBABE or uint32(0) == 0xBEBAFECA
macOS 恶意软件优质特征:
- 键盘记录器留下的迹象:
CGEventTapCreate,kCGEventKeyDown - SSH 隧道相关字符串:
ssh -D,tunnel,socks - 持久化路径:
~/Library/LaunchAgents,/Library/LaunchDaemons - 凭据窃取:
security find-generic-password,keychain
来自 Airbnb BinaryAlert 的示例模式:
rule SUSP_Mac_ProtonRAT
{
strings:
// 动态库特征
$lib1 = "SRWebSocket" ascii
$lib2 = "SocketRocket" ascii
// 行为特征
$behav1 = "SSH tunnel not launched" ascii
$behav2 = "Keylogger" ascii
condition:
(uint32(0) == 0xFEEDFACF or uint32(0) == 0xCAFEBABE) and
any of ($lib*) and any of ($behav*)
}
JavaScript 检测决策树
编写 JavaScript 规则?
├─ npm 包?
│ ├─ 检查 package.json 中的模式
│ ├─ 排查 postinstall/preinstall 钩子
│ └─ 锁定外泄模式:fetch + 环境变量访问 + 凭据路径
├─ 浏览器扩展?
│ ├─ Chrome:使用 crx 模块
│ └─ 其他:锁定 manifest 配置文件模式、后台脚本(background script)行为
├─ 独立 JS 文件?
│ ├─ 排查混淆特征:eval+atob, fromCharCode 拼接链
│ ├─ 锁定独特的函数/变量名(即使压缩后往往也能留存)
│ └─ 检查加壳/编码后的 Payload
└─ 压缩/webpack 打包代码?
├─ 锁定打包后依然留存的独有字符串(URL、魔数/固定值)
└─ 避开函数名(大部分会被混淆/破坏)
JavaScript 推荐的优质字符串:
- 以太坊函数选择器(Function Selectors):
{ 70 a0 82 31 }(transfer) - 零宽字符(隐写术):
{ E2 80 8B E2 80 8C } - 混淆工具签名:
_0x,var _0x - 具体的 C2 特征:域名、Webhook URL
JavaScript 不推荐的字符串:
require,fetch,axios— 太过常见Buffer,crypto— 正常代码里到处都是- 单独的
process.env— 必须配合具体的环境变量名称
必备工具箱
| 工具 | 用途 |
|---|---|
| yarGen | 提取候选字符串:yarGen.py -m samples/ --excludegood → 再用 yr check 校验 |
| FLOSS | 提取混淆/栈字符串:floss sample.exe(当 yarGen 无效时使用) |
| yr CLI | 校验规则:yr check,扫描:yr scan -s,转储查看:yr dump -m pe |
| signature-base | 学习参考高质量示例规则 |
| YARA-CI | 上线前在白样本库上进行持续集成测试 |
掌握这 5 个就够了,不要在繁复的工具列表中迷失方向。
应该摒弃的妥协念头(误区避坑)
当你产生以下想法时,请立刻打住并重新思考:
| 错误念头 | 专家解答 |
|---|---|
| “这个通用的字符串应该足够独特了” | 先拿白样本跑测试再说。你的直觉大概率是错的。 |
| “yarGen 帮我提出了这些字符串” | yarGen 只负责建议,你需要负责校验。请逐个人工核对。 |
| “在我手头的 10 个样本上跑得挺好” | 10 个样本 ≠ 生产环境。请使用 VirusTotal 的白样本库验证。 |
| “写一条规则通杀所有变种” | 这会导致误报刷屏。请精准锁定具体家族。 |
| “万一真误报了,我再去把规则改精准点” | 规则上线前就要写严谨。误报会极大地消耗团队信任度。 |
| “这个十六进制模式绝对独特” | 在单一样本里独特 ≠ 在整个恶意软件生态中独特。 |
| “性能慢点无所谓” | 只要有一条慢规则,就会拖慢整个规则集。请优化 Atom。 |
| “PEiD 规则现在还能用” | 早过时了。32 位加壳工具在现代威胁中已无实际参考价值。 |
| “我以后再补充更多条件” | 带着缺陷上线规则等于埋雷。 |
| "这只是用来做 Threat Hunting(威胁猎捕)的" | Hunting 规则最终都会变成检测规则,质量标准应完全一致。 |
| “这个 API 名称证明它就是恶意软件” | 合法软件也会使用相同的 API。需要结合上下文行为判断。 |
| “对于这些通用字符串,用 any of 就挺好” | 通用字符串 + any of = 误报灾难。any of 只能用于本身就足够独特的字符串。 |
| “这个正则表达式足够精准了” | /fetch.*token/ 会匹配到所有的鉴权代码。必须限定外泄目的地要求。 |
| “这段 JavaScript 看着挺干净的” | 攻击者会向合法代码中注入恶意代码。仔细检查是否存在 eval+decode 链。 |
| “用 .* 匹配更灵活” | 无界正则表达式 = 性能崩盘 + 内存爆炸。请使用 .{0,30} 限定范围。 |
| “我在所有地方都加上 --relaxed-re-syntax” | 这会掩盖真实的逻辑 Bug。请去修复正则本身,而不是掩耳盗盗。 |
决策树
这个字符串够好吗?
这个字符串够好吗?
├─ 长度小于 4 字节?
│ └─ 否 — 寻找更长的字符串
├─ 包含重复字节 (如 0000, 9090)?
│ └─ 否 — 补充上下文环境信息
├─ 是 API 名称 (如 VirtualAlloc, CreateRemoteThread)?
│ └─ 否 — 改用调用位置(Call site)的十六进制模式
├─ 出现在 Windows 系统文件中?
│ └─ 否 — 太过通用,去寻找独特的特征
├─ 是常用路径 (如 C:\Windows\, cmd.exe)?
│ └─ 否 — 去寻找该恶意软件独有的路径
├─ 该恶意软件家族独有?
│ └─ 是 — 直接使用
└─ 也出现在其他恶意软件中?
└─ 也许 — 结合家族专属的标记组合使用
什么时候用 "all of" vs "any of"
应该要求匹配所有字符串 (all of) 还是允许任意匹配 (any of)?
├─ 字符串单独提取出来就属于该恶意软件的独有特征?
│ └─ any of them(每个字符串单独看就很可疑)
├─ 字符串很常见,但组合起来就很可疑?
│ └─ all of them(要求命中完整模式)
├─ 字符串的置信度不同?
│ └─ 分组结合:all of ($core_*) and any of ($variant_*)
└─ 遇到了大量误报?
└─ 收紧条件:将 any 改为 all,增加更多必需字符串
来自生产实践的教训: 曾有规则使用了 any of ($network_*),而字符串列表中包含了 "fetch"、"axios"、"http",结果几乎匹配了所有的 Web 应用程序。后来将其改为“必须同时包含凭据路径 AND 网络调用 AND 数据外泄目的地”,直接消除了所有误报。
什么时候该放弃当前规则思路?
出现以下情况时,请及时止损并切换思路:
-
yarGen 提取出来的全是 API 名称和通用路径 → 参见 当字符串失效时,转向结构分析
-
找不到 3 个独特字符串 → 很可能加壳了。寻找解壳后的版本,或直接针对加壳工具做检测。
-
规则命中白样本文件 → 字符串不够独特。1~2 个命中 = 排查并收紧规则;3~5 个命中 = 寻找其他特征指标;6 个以上命中 = 推翻重来。
-
即使优化后性能依然极差 → 属于架构问题。拆分为多条聚焦的细分规则,或添加严格的前置预过滤条件。
-
很难写出 Description 说明 → 说明规则逻辑太模糊。如果你连它抓的是什么都说不清,那它抓到的东西绝对太多了。
排查与调试误报
误报排查流程:
│
├─ 1. 到底是哪个字符串命中了?
│ 运行:yr scan -s rule.yar false_positive.exe
│
├─ 2. 存在于合法的第三方库中吗?
│ └─ 解决:添加 not $fp_vendor_string 排除条件
│
├─ 3. 是常见的开发代码模式吗?
│ └─ 解决:寻找更具体的特征指标,替换掉该字符串
│
├─ 4. 是否多个通用字符串同时命中了?
│ └─ 解决:收紧条件要求全匹配 (all) + 添加独有的标志位
│
└─ 5. 恶意软件使用了通用技术手段吗?
└─ 解决:锁定恶意软件具体的实现细节,而不是技术手段本身
十六进制 (Hex) vs 文本 (Text) vs 正则 (Regex)
我应该使用哪种字符串类型?
│
├─ 精确的 ASCII/Unicode 文本?
│ └─ 文本:$s = "MutexName" ascii wide
│
├─ 具体的字节序列?
│ └─ 十六进制:$h = { 4D 5A 90 00 }
│
├─ 带有通配/变动的字节序列?
│ └─ 带通配符的十六进制:{ 4D 5A ?? ?? 50 45 }
│
├─ 具有结构特征的模式 (URL、路径)?
│ └─ 有界正则表达式:/https:\/\/[a-z]{5,20}\.onion/
│
└─ 未知编码 (XOR, base64)?
└─ 带有修饰符的文本:$s = "config" xor(0x00-0xFF)
样本加壳了吗?(优先检查)
在编写任何基于字符串的规则之前:
样本加壳了吗?
├─ 熵 (Entropy) > 7.0?
│ └─ 很可能加壳了 — 先提取未加壳的 Payload 层
├─ 几乎没有可读字符串?
│ └─ 很可能加壳了 — 利用熵、PE 结构或壳特征码
├─ 检出了 UPX/MPRESS/自定义壳?
│ └─ 针对解壳后的 Payload 编写规则,或者直接检测壳本身
└─ 存在丰富的可读字符串?
└─ 继续使用基于字符串的检测方案
专家建议: 不要针对壳代码层写检测规则。壳经常变,但里面的 Payload 不会变。
当字符串失效时,转向结构分析
如果 yarGen 提取出来的全是 API 名称和通用路径:
St
<!-- truncated for translation batch; full body continues in source -->






