jubnzv/afl-ts
GitHub: jubnzv/afl-ts
基于 tree-sitter 的 AFL++ 自定义变异器,通过 AST 级别的结构化变异提升对结构化输入目标的模糊测试效率。
Stars: 7 | Forks: 2
# AFL++ tree-sitter splice mutator
用于 [AFL++](https://github.com/AFLplusplus/AFLplusplus/) 的 AST 感知自定义 mutator。
使用 [tree-sitter](https://tree-sitter.github.io/) 解析输入并执行受 [tree-splicer](https://github.com/langston-barrett/tree-splicer) 启发的 AST 级别变异。
支持任何 tree-sitter 语法 - 在运行时通过 `dlopen` 加载,无需重新编译。
## 环境要求
需要 tree-sitter **v0.25+**(ABI 版本 15)。不支持更旧的版本。
## 构建
该 mutator 会在源码树内(in-source-tree)进行构建。克隆 AFL++ 源码:
```
git clone https://github.com/AFLplusplus/AFLplusplus/
```
然后克隆并构建这个自定义 mutator:
```
cd AFLplusplus/custom_mutators
git clone https://github.com/jubnzv/afl-ts ts
cd ts
make
```
如果 `pkg-config` 能自动找到 tree-sitter,那么你需要做的就只有这些。否则,请将 `TSPREFIX` 指向你的 tree-sitter 安装路径前缀:
```
make TSPREFIX=/path/to/tree-sitter-install
```
要从源码构建 tree-sitter v0.25+:
```
git clone https://github.com/tree-sitter/tree-sitter
cd tree-sitter && git checkout v0.25.6
make && make install PREFIX=/path/to/tree-sitter-install
```
如果 tree-sitter 安装在非标准位置,请在运行时设置 `LD_LIBRARY_PATH`:
```
export LD_LIBRARY_PATH=/path/to/tree-sitter-install/lib
```
## 用法
```
TS_GRAMMAR=/path/to/your-libtree-sitter.so \
AFL_CUSTOM_MUTATOR_LIBRARY=./libts.so \
afl-fuzz -i corpus -o out -- ./target @@
```
## 变异策略
| 策略 | 权重 | 作用 |
|---|---|---|
| `ts-del` | 20 | 删除一个命名的 AST 子树 |
| `ts-bank` | 20 | 用语料库库(bank)中类型兼容的子树(匹配 `TSSymbol`)替换当前子树 |
| `ts-add` | 20 | 用 AFL++ 的 `add_buf` 中类型兼容的子树替换当前子树 |
| `ts-swap` | 15 | 交换两个相同类型的兄弟节点 |
| `ts-shrink` | 10 | 用同类型的子孙节点替换当前节点(必定会减小体积) |
| `ts-lit` | 5 | 用随机字面量替换叶子节点 |
| `ts-dup` | 3 | 将子树复制到其自身相邻位置 |
| `ts-ins` | 7 | 在节点后插入类型兼容的 bank 子树(使输入增长,上限为 2 倍) |
| `ts-range` | 4 | 将连续的相同符号兄弟节点替换为来自 `add_buf` 或 1..3 个由检测到的列表分隔符连接的 bank 条目组成的同符号序列 |
| `ts-chaos` | 2 | 绕过 `ts-bank` / `ts-add` / `ts-range` / `ts-kins` / `ts-stutter` 的类型安全过滤:无论 `TSSymbol` 是什么,都将随机的 bank(或 `add_buf`)节点拼接到目标位置,或者在任何子孙节点周围重复任何父节点的包络(envelope)。产生故意不符合语法的输入以提高覆盖率。 |
| `ts-kdel` | 10 | 从连续的同符号兄弟节点中删除 1..3 个子节点,并吞并一个相邻的分隔符,使剩余的列表保持格式良好 |
| `ts-kins` | 10 | 在同符号兄弟节点序列的随机边界处插入 1..3 个同符号子节点。来源节点取自 `add_buf`、bank 或被复制的现有成员。分隔符从现有列表中检测得出 |
| `ts-stutter` | 4 | 选取父节点 `P` 和同符号的子孙节点 `C`,然后在 `C` 周围将 `P` 的前缀/后缀包络重复 `N` 次([radamsa](https://gitlab.com/akihe/radamsa)-风格的树重复)。默认是类型安全的;chaos 模式会取消符号相等性过滤。 |
随着语料库的增长,子树库会通过 `afl_custom_queue_new_entry` 进行填充。
## 环境变量
| 变量 | 默认值 | 描述 |
|---|---|---|
| `TS_GRAMMAR` | **(必填)** | 语法 `.so` 文件路径 |
| `TS_LANG_FUNC` | 从文件名推导 | `tree_sitter_*()` 符号名称 |
| `TS_WEIGHTS` | `20,20,20,15,10,5,3,7,4,2,10,10,4` | 以逗号分隔的策略权重。旧的 12 参数配置仍然有效 —— 缺失的第 13 个槽位(`ts-stutter`)默认为 0(关闭)。 |
| `TS_BANK_SIZE` | `8192` | 最大子树库条目数 |
| `TS_BANK_MAX_SUBTREE` | `256` | 每个存入子树库的子树最大字节数 |
| `TS_HAVOC_PROB` | `50` | Havoc 变异概率 (%) |
| `TS_STACK_MAX` | `1` | 每次 fuzz 调用叠加的最大变异次数。每次调用都会在 `[1, N]` 中均匀随机选取一个深度,依次应用最多 N 次变异,且每步之间会重新解析。上限为 8。默认为 `1` = 不叠加。 |
| `TS_STUTTER_MAX_GROW` | `4` | `ts-stutter` 的体积上限,表示为输入长度的乘数。输出增长绝不会超过 `N × len`。最小值为 2。 |
| `TS_REGION_MODE` | `0` (关闭) | 启用区域感知变异(见下文) |
| `TS_REGION_MAGIC` | `CAFE` | 用于标记区域感知输入的 u16 魔数。仅限十六进制数字。 |
## 区域感知变异
当设置 `TS_REGION_MODE=1` 时,格式为 `[源码][尾部][u16 LE source_len][magic]` 的输入只会让开头的 `source_len` 字节发生变异;尾部将被原样保留。
用例:Solidity 测试工具(harness)将数据打包为 `[源码][ABI calldata][len][0xCAFE]`,这样 afl-ts 就会变异源码,而 AFL 的字节级 havoc 会去处理 calldata。
## 性能说明
对于大型的递归输入,设置 `TS_STACK_MAX > 1` 并配合极易增加体积的权重会导致 mut/s 暴跌 —— 因为每一步都会重新解析(已经变大的)输出。解决方案:调低 `TS_STACK_MAX` 或 `TS_STUTTER_MAX_GROW`。
标签:C/C++, 事务性I/O, 安全测试, 客户端加密, 对称加密, 攻击性安全, 自动化审计