rules.py — YARA 规则管理
RulesManagerYARA
apkid/rules.py 定义 RulesManager——负责 YARA 规则的发现、编译、加载、哈希。它把"规则是数据"这件事封装干净。
📋 文件内容
只有一个类 RulesManager。
🧱 RulesManager
构造
python
def __init__(self, rules_dir=None, rules_ext='.yara'):
if not rules_dir:
rules_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'rules')
self.rules_dir = rules_dir # apkid/rules/
self.rules_path = os.path.join(rules_dir, 'rules.yarc') # 编译产物路径
self.rules_ext = '.yara'
self.rules = None # 加载后的 yara.Rules
self.rules_hash = None # SHA-256(惰性)默认 rules_dir 是 apkid/rules/(相对本文件定位),可传入自定义目录(测试用)。
load()
python
def load(self) -> yara.Rules:
self.rules = yara.load(self.rules_path)
return self.rules加载预编译的 rules.yarc。运行时只调这个,毫秒级。如果 rules.yarc 不存在会抛异常——所以源码安装后必须先 prep-release.py。
_collect_yara_files()
python
def _collect_yara_files(self) -> Dict[str, str]:
files = {}
for root, dirnames, filenames in os.walk(self.rules_dir):
for filename in filenames:
if not filename.lower().endswith(self.rules_ext):
continue
path = os.path.join(root, filename)
files[path] = path
return files递归收集所有 .yara 源文件。返回 Dict[路径, 路径](key=value,供 yara.compile(filepaths=...) 用)。rules.yarc 不以 .yara 结尾,自动被排除。
compile()
python
def compile(self) -> yara.Rules:
yara_files = self._collect_yara_files()
self.rules = yara.compile(filepaths=yara_files)
return self.rules把所有源 .yara 编译成 yara.Rules。只在开发/打包时调,较慢(要解析全部规则 + DEX 模块)。
save()
python
def save(self) -> int:
self.rules.save(self.rules_path)
return len(set(r.identifier for r in self.rules)) # 去重后的规则数写出 rules.yarc,返回规则数(去重)。prep-release.py 和 apkid-ai-cli rules compile 都走 compile() + save()。
hash(property)
python
@property
def hash(self) -> str:
if not self.rules_hash:
h = hashlib.sha256()
for file_path in self._collect_yara_files():
with open(file_path, 'rb') as f:
h.update(f.read())
self.rules_hash = h.hexdigest()
return self.rules_hash对所有源 .yara 文件算 SHA-256(按收集顺序,非排序)。惰性计算,首次访问后缓存。
哈希的含义
它反映的是 源规则 的内容指纹,不是 rules.yarc 的指纹。改一条规则 → 哈希变 → 每次扫描输出的 rules_sha256 变。可用于"这次扫描用的是哪版规则"溯源。
注意:哈希基于 os.walk 的遍历顺序,跨平台可能不同(Linux 通常稳定,Windows 看文件系统)。所以它适合 同一环境的版本对比,不适合跨平台精确比对。
🔄 完整生命周期
开发 运行
│ │
▼ ▼
编辑 apkid/rules/*.yara RulesManager()
│ │
▼ ▼
.compile() ← yara.compile .load() ← yara.load('rules.yarc')
│ │
▼ ▼
.save() → rules.yarc self.rules(毫秒级)
│ │
▼ ▼
gitignore,随包分发 Scanner 用它匹配🔗 谁用它
Options构造时self.rules_manager = RulesManager(),但Options不主动load()——由 Scanner 调用方决定。common.make_scanner():RulesManager().load()后传给Scanner。cmd_info/ MCPinfo:取.hash和.load()后的规则数。cmd_rules/ MCPrules:compile()+save()。- 经典
main.py:options.rules_manager.load()。