内容
# unicornafl-mcp
> **将Claude Desktop转变为Unicorn仿真和AFL++模糊测试的实践驱动程序,用于二进制文件——并结合LLM引导的种子丰富和从ChatAFL(NDSS '24)改编的覆盖率平台突破。**
[]()
[]()
[]()
[]()
[]()
[]()
[]()
---
## ✨ 功能概述
通过与Claude在聊天中交流,并从同一对话中:
- 🎮 **模拟** — 通过Unicorn交互式地执行ARM64 / x86_64 / ARM / x86 / MIPS二进制文件
- 🔬 **分析** — 反汇编,提取神奇常量,扫描字符串,动态跟踪输入读取和比较以推断解析器结构
- 🌱 **生成种子** — 让Claude根据推断的结构(神奇字节,长度字段,调度器gmids…)设计种子,并随时注入
- 🐝 **模糊测试** — 启动AFL++ `unicorn-mode`活动;轮询`fuzzer_stats`和`plot_data`
- 📈 **突破平台瓶颈** — 检测覆盖率停滞并通过AFL的`-F` foreign-sync目录热注入新种子 - 无需重启活动
- 🔥 **分类崩溃** — 自动分类(`exec_unmapped`,`read_unmapped`,`cpu_exception`,…)并使用`afl-tmin`最小化
> [!IMPORTANT]
> **差异化因素:无需外部LLM API调用。** Claude本身进行推理,因此MCP只需公开*干净的信号*并接受*结构化的种子提案*。双向循环自然发生在聊天中。
---
## 🚀 快速入门
### 简介 — 三步
```bash
git clone https://github.com/skim1231234/unicornafl-mcp
cd unicornafl-mcp && ./setup.sh # 构建所有内容 + 自动在Claude Desktop注册
# 完全退出并重新启动Claude Desktop ← 唯一手动步骤
```
> [!IMPORTANT]
> 在`setup.sh`完成后**完全退出并重新启动Claude Desktop** - 这是唯一的手动步骤。
就是这样。`setup.sh`构建AFL++/Rust/unicornafl/venv **和**修补Claude Desktop的`claude_desktop_config.json`为您(保留现有的条目,并带有时间戳备份)。打开Claude Desktop的工具面板,您应该看到48个`unicornafl`工具。
### 要求
- macOS arm64(Apple Silicon)或Linux x86_64 — 其他平台未测试
- Python ≥ 3.11
- Homebrew(macOS)或您的发行版的包管理器
- git
- Claude Desktop
(Rust ≥ 1.87和AFL++如果缺少,则由`setup.sh`安装。)
### `setup.sh`的作用
1. 通过Homebrew安装**AFL++**
2. 安装/更新**Rust工具链**(rustc ≥ 1.87)
3. 克隆**unicornafl**源代码到`./vendor/unicornafl/`
4. 构建unicornafl轮子(`cargo build` + `maturin build`)
5. 创建`./.venv/`并安装`unicorn`,`unicornafl`,`mcp`,`capstone`
6. 运行导入烟雾测试
7. 将配置片段写入`./claude_desktop_config.local.json`(带有绝对路径)
8. **将该片段合并到Claude Desktop的`claude_desktop_config.json`中**,保留您已经拥有的其他`mcpServers`条目,并保存原始文件的带时间戳备份
> [!TIP]
> 幂等 — 随时重新运行。有关每个部分的详细信息,请参见[外部依赖项](#-外部依赖项)。
#### 标志和环境覆盖
```bash
./setup.sh # 默认:修补Claude Desktop配置前提示
./setup.sh --yes # 非交互式(CI等)
./setup.sh --no-register # 仅构建,保留Claude Desktop配置
UNICORNAFL_SRC=$HOME/src/unicornafl ./setup.sh # 使用现有的checkout
UNICORNAFL_REF=v3.0.0 ./setup.sh # 固定上游引用
PYTHON_BIN=python3.12 ./setup.sh # 不同解释器
VENV_DIR=$HOME/.venvs/unicornafl-mcp ./setup.sh # 不同venv位置
CLAUDE_CONFIG=/custom/path/claude_desktop_config.json ./setup.sh # 参见下文
```
| 变量 | 默认值 | 用途 |
|---|---|---|
| `UNICORNAFL_SRC` | `./vendor/unicornafl` | 使用现有的unicornafl checkout(跳过克隆) |
| `UNICORNAFL_REF` | `main` | 克隆的git引用(分支/标签/提交) |
| `PYTHON_BIN` | `python3.11` | 用于venv的Python解释器 |
| `VENV_DIR` | `./.venv` | 覆盖venv位置 |
| `CLAUDE_CONFIG` | (自动,参见下文) | Claude Desktop的`claude_desktop_config.json`路径 |
#### Claude Desktop配置路径
`setup.sh`按操作系统自动检测配置路径:
| 操作系统 | 默认配置路径 |
|---|---|
| **macOS** | `~/Library/Application Support/Claude/claude_desktop_config.json` |
| **Linux** | `~/.config/Claude/claude_desktop_config.json` |
| **Windows** | (未自动检测 — 显式传递`CLAUDE_CONFIG`) |
> [!NOTE]
> 默认值与Claude Desktop的标准安装匹配。**如果您的安装位于其他位置** - 例如便携式安装、容器化启动器、托管的企业目录或不同的操作系统/桌面变体 - 自动检测的路径将不正确。在这种情况下,请覆盖它:
>
> ```bash
> CLAUDE_CONFIG="$HOME/some/other/place/claude_desktop_config.json" ./setup.sh
> ```
要找出Claude Desktop在您的机器上实际保存其配置的位置:
- macOS:打开Finder → ⌘⇧G → 粘贴`~/Library/Application Support/Claude/`
- Linux:`find ~ -name claude_desktop_config.json 2>/dev/null`
- Windows:通常为`%APPDATA%\Claude\claude_desktop_config.json`
> [!TIP]
> 在修补之前,`setup.sh`会打印它即将写入的路径并等待确认 - 使用`n`中止并使用`CLAUDE_CONFIG=...`重新运行,如果路径不正确。
### 手动注册(仅当您运行 `--no-register`)
`setup.sh`将完全解析的配置片段写入`./claude_desktop_config.local.json`。将它的`mcpServers.unicornafl`块合并到您的Claude Desktop配置中 - 有关典型位置和如何找到它,请参见上面的Claude Desktop配置路径。
模板形式(带有占位符)位于[`claude_desktop_config.example.json`](claude_desktop_config.example.json):
```json
{
"mcpServers": {
"unicornafl": {
"command": "/path/to/unicornafl-mcp/.venv/bin/python",
"args": ["/path/to/unicornafl-mcp/server.py"],
"env": { "PYTHONUNBUFFERED": "1" }
}
}
}
```
> [!IMPORTANT]
> 完全退出并重新启动Claude Desktop - 所有48种工具都应在工具面板中出现。
### 卸载 / 注销
打开Claude Desktop的`claude_desktop_config.json`并删除`mcpServers.unicornafl`块(`setup.sh`制作的备份位于旁边,命名为`claude_desktop_config.json.bak.<timestamp>`)。然后`rm -rf`克隆的`unicornafl-mcp/`目录。
### 30秒烟雾测试
将此提示放入Claude:
> 创建一个arm64会话,映射一个r-x 4 KB区域到0x100000,写入字节
> `40 00 80 52 1f a8 00 71 3f 6a 00 71 1f 00 1f 72 c0 03 5f d6`(mov w0,#2;
> cmp w0,#0x2a; cmp w17,#0x1a; tst w0,#2; ret),并在[0x100000, 0x100014)上运行`find_immediates`
>
您应该会看到从`cmp`/`tst`指令中提取的三个常量:`0x2a`,`0x1a`和`0x2`。
---
## 🛠️ 工具目录(48种工具)
### 会话 / 内存 / 寄存器
`session_init`,`session_status`,`session_reset` ·
`mem_map`,`mem_unmap`,`mem_write`,`mem_write_file`,`mem_read`,`mem_regions` ·
`reg_write`,`reg_read`,`reg_dump`
### 代码加载 / 反汇编 / 钩子
`load_code`,`disasm`(capstone) ·
`hook_code`,`hook_mem`,`get_trace`,`remove_hook`
### 模拟 / 快照
`emu_start`,`emu_stop` ·
`snapshot_save`,`snapshot_load`,`snapshot_list`
### 模糊测试(基本)
`fuzz_configure`(input_kind = `memory` / `register`)· `fuzz_seed_corpus` · `fuzz_generate_harness` · `fuzz_test_harness` · `fuzz_start`(自动添加`-F <inject_dir>`)· `fuzz_status` · `fuzz_stop` · `fuzz_list_crashes` · `fuzz_replay_crash`
### LLM引导(★ ChatAFL改编)
| 阶段 | 工具 |
|---|---|
| **输入结构推断**(论文§IV-A) | `analyze_input_handling`,`find_immediates`,`find_strings`,`probe_input_access`,`probe_compare_log` |
| **种子丰富**(论文§IV-B) | `seed_describe`,`seed_add_many`,`template_seeds`,`seeds_from_struct_spec` ★ |
| **覆盖率平台突破**(论文§IV-C) | `fuzz_coverage_history`,`fuzz_plateau_check`,`fuzz_break_plateau` ★,`fuzz_inject_seed`,`fuzzing_advisor` |
| **崩溃分析** | `crash_summarize`,`crash_minimize` |
工具签名在Claude Desktop工具面板和`server.py`文档字符串中记录。
---
## 🔄 LLM引导的工作流程
```
┌──────────────────────────────────────────────┐
│ MCP服务器(Python) │
│ Unicorn会话 · AFL++子进程 │
└──────────┬─────────────────────▲─────────────┘
│ │
clean signals│ │ structured seeds
▼ │
┌──────────────────────────────────────────────┐
│ Claude │
│ (在聊天中 - 无外部LLM API) │
└──────────────────────────────────────────────┘
```
典型循环:
1. **引导**
- `session_init('arm64')`,`mem_map`,加载函数代码 + 输入缓冲区
- `analyze_input_handling(start, end, input_address, input_size)` - 结合反汇编 + 立即数 + 字符串 + 读取访问模式 + 比较日志
- Claude读取报告并形成结构假设(例如 *"12字节头部,魔术0xAB7EC0DE,6位gid,然后TLV有效载荷"*)
2. **丰富种子**
- `template_seeds(prefix_hex='<inferred header>', body_min, body_max, count)`,或
- `seed_add_many([{name, hex}, …])`用于挑选的结构种子
3. **运行模糊测试**
- `fuzz_generate_harness` → `fuzz_test_harness`以验证 → `fuzz_start`
4. **监控 / 突破平台瓶颈** - 每30秒到几分钟
- `fuzzing_advisor(job_id)` - 一次调用返回状态 + 平台判决 + 覆盖率尾部 + 崩溃 + 推荐的下一步行动
- 如果`plateau == True`,两条路径:
- **快速 / 机械** - `fuzz_break_plateau(job_id, code_start, code_end)`在典型头部偏移处叠加反汇编得出的魔术常量,探测长度字段边界,并拼接语料库对。 不需要Claude推理;毫秒级运行。
- **深入 / 理由** - 重新阅读反汇编代码段 → `seed_add_many(target='inject', …)`用于手工制作的种子。 在(1)没有帮助时使用。
5. **分类**
- `crash_summarize`以分类崩溃 → `crash_minimize`每个唯一类别一次
这镜像论文的算法1(`PlateauLen ≥ MaxPlateau → ChatNextMessage`),外部LLM调用被聊天中的Claude替换。
---
## 🤝 与[IDA Pro MCP](https://github.com/mrexodia/ida-pro-mcp)配合使用
当[`ida-pro-mcp`](https://github.com/mrexodia/ida-pro-mcp)与`unicornafl-mcp`一起注册到Claude Desktop时,平台突破会变得非常强大。两个服务器独立运行 - Claude在单个聊天中协调它们:IDA提供*结构化基础*,而unicornafl应用该知识到实时AFL活动。
### IDA解锁的内容(vs.原始内存分析)
| 信号 | `unicornafl-mcp`单独 | `+ ida-pro-mcp` |
|---|---|---|
| 函数边界 | 通过`code_start` / `code_end`猜测 | `lookup_funcs`返回prologue–epilogue确切 |
| 魔术常量 | `find_immediates`在内存范围内 | `decompile`显示`*(u32)msg == 0xAB7EC0DE`,以及其他解析器使用相同魔术 |
| 输入布局 | 从动态读取模式推断 | 有类型结构体(如果用户在IDA中输入)→ 偏移量/宽度准确 |
| 长度字段 | 偏移量猜测 `[4, 6, 8]` | `trace_data_flow` 跟随`memcpy`长度参数返回输入字节 |
| 未采取分支 | 不直接可见 | AFL bitmap × `basic_blocks` CFG → 确切的`b.ne`指令从未翻转 |
| 调度表 | 不直接可见 | `read_struct`超过`g_DISPATCH[64]` → 枚举每个有效的`(gid, mid)`对 |
### 工作流程
[plateau detected via fuzz_plateau_check]
↓
ida-pro-mcp__decompile(<parser_va>) # 可读的伪代码
ida-pro-mcp__list_globals(pattern=...) # 定位调度/消息定义表
ida-pro-mcp__read_struct(addr, stride, ...) # 枚举有效ID/字段
ida-pro-mcp__xrefs_to(<parser_va>) # 查找调用者上下文
↓ (Claude推理结构事实)
unicornafl-mcp__seeds_from_struct_spec(spec, target="inject", job_id=...)
↓
AFL通过foreign-sync选择它们,平台突破
### 具体示例 - 协议解析器平台瓶颈
场景:针对`Extract`解析器的活动已经停滞,因为机械魔术覆盖在调度门处不断被拒绝(`gid`不在调度表中)。
```python
# 1. IDA - 枚举调度表中的每个有效(gid, mid)对
ida__read_struct(addr=0x5BE05C8, stride=8, count=64) # group指针
for gp in active_groups:
msgdefs = ida__read_struct(addr=gp, stride=56, terminator="mid==0")
valid_pairs += [(gid, m.mid) for m in msgdefs]
# 2. unicornafl-mcp - 每个pair一个结构正确的种子
spec = {
"size": 12,
"fields": [
{"offset": 0, "kind": "u32_le", "value": 0xAB7EC0DE},
{"offset": 4, "kind": "u8", "values": [g << 3 for g, _ in valid_pairs]},
{"offset": 5, "kind": "u8", "value": 0},
{"offset": 6, "kind": "u16_le", "value": 0},
{"offset": 8, "kind": "u16_le", "values": [m << 6 for _, m in valid_pairs]},
{"offset": 10,"kind": "u16_le", "value": 0},
]
}
seeds_from_struct_spec(spec, count=200, target="inject", job_id=current_job)
```
生成的种子都通过了神奇的+大小+ gid + msgdef + mid门户,恰好进入每个组的原生处理程序 - 正是那里更深层次、测试较少的代码。
### 安装指针
安装`ida-pro-mcp`按照其 [自己的README](https://github.com/mrexodia/ida-pro-mcp) 并将其添加到Claude Desktop的`claude_desktop_config.json`中与`unicornafl`一起。
```json
{
"mcpServers": {
"unicornafl": { "command": "...", "args": ["..."] },
"ida": { "command": "...", "args": ["..."] }
}
}
```
两个服务器独立运行 - 在同一个聊天回合中不需要代码更改。
---
## 📁 工作目录布局
```
unicornafl-mcp/
├── setup.sh
├── server.py # MCP服务器(~1800行,48种工具)
├── harness_template.py # 由fuzz_generate_harness填充 → work/harness.py
├── claude_desktop_config.example.json # 模板,带有/path/to/...占位符
├── claude_desktop_config.local.json # 由setup.sh生成,实际路径(gitignored)
├── vendor/
│ └── unicornafl/ # 由setup.sh自动克隆(gitignored)
├── .venv/ # Python虚拟环境(gitignored)
└── work/ # 运行时工件(gitignored)
├── snapshots/<name>/{meta.json, region_*.bin}
├── harness.py # 每次fuzz_generate_harness覆盖
├── in/ # AFL种子语料库
├── inject/<job_id>/ # AFL -F 实时注入目录
├── out/ # AFL输出(out/default/{queue,crashes,fuzzer_stats,plot_data})
└── fuzz_<job_id>.log
```
---
## 📦 外部依赖项
unicornafl-mcp是三个外部项目的薄层封装。它们不被打包到此仓库中 - `setup.sh`会在第一次运行时获取并构建它们。
### unicornafl (单独的上游项目)
[unicornafl](https://github.com/AFLplusplus/unicornafl)(AFL++ ↔ Unicorn桥接器)是核心依赖项。其Python轮子不在PyPI上发布,因此必须从源代码构建。
| | |
|---|---|
| 上游 | `https://github.com/AFLplusplus/unicornafl` |
| 默认安装位置 | `./vendor/unicornafl/`(此仓库,gitignored) |
| 覆盖 | `UNICORNAFL_SRC=/path/to/checkout ./setup.sh` |
| 构建链 | Rust(`cargo build --release`)→ Python轮子(`maturin build --release`)→ `pip install`到`.venv` |
| 固定的引用 | 默认情况下为`main`;通过`UNICORNAFL_REF=v3.0.0 ./setup.sh`覆盖 |
如果您已经在其他地方维护unicornafl checkout,请通过`UNICORNAFL_SRC`指向它,`setup.sh`将直接从那里克隆并构建。
### AFL++
[AFL++](https://github.com/AFLplusplus/AFLplusplus) 提供`afl-fuzz`,`afl-tmin`和unicorn模式下的forkserver。通过Homebrew安装:
```bash
brew install afl++
```
`setup.sh`在`$PATH`上找不到`afl-fuzz`时会运行此操作。如果它已经存在于Linux上,您需要从源代码安装它并在之后重新运行`./setup.sh`。
### Unicorn Engine
[Unicorn Engine](https://github.com/unicorn-engine/unicorn) 通过Python包`unicorn>=2.1.3`(PyPI上的二进制轮子 - 无需原生构建)传递依赖。
### 其他Python依赖项(PyPI)
通过`setup.sh`安装到项目venv中:
- `mcp>=1.2.0` — 模型上下文协议Python SDK
- `capstone` — 用于`disasm`,`find_immediates`,`probe_compare_log`的反汇编器
- `maturin>=1.8,<2.0` — 用于构建unicornafl轮子的工具
- `pydantic>=2` — `mcp`的传递依赖
### 您需要自行安装的软件
在运行 `setup.sh` 之前,只需要确保以下前提条件存在:
| 工具 | macOS 安装方法 |
|---|---|
| Python ≥ 3.11 | `brew install python@3.11` |
| Homebrew | [brew.sh](https://brew.sh) |
| git | `xcode-select --install` |
其他所有内容(AFL++、Rust、unicornafl 源代码、Python 包、venv)都由 `setup.sh` 处理。
---
## 🏗️ 架构说明
- **会话范围是进程范围。** Claude Desktop 的 stdio MCP 服务器为单个客户端服务,因此全局 `SESSION` 足够。
- **仿真在进程内**(仅 Unicorn);**模糊测试在进程外** — `fuzz_generate_harness` 将实时会话快照到磁盘,并发出一个自包含的 Python 测试桩,然后由 `afl-fuzz` 启动。
- **实时种子注入** — `fuzz_start` 自动在 AFL 命令行中追加 `-F WORK_DIR/inject/<job_id>`。AFL++ 监视该目录作为外部队列,因此 `fuzz_inject_seed` 注入的任何内容都会流入正在运行的队列,无需重启。
- **统计信息是 AFL++ 的文件** — `out/default/fuzzer_stats`(键值对)和 `out/default/plot_data`(带有 `#` 头部的 CSV)。无自定义遥测。
- **支持持久模糊测试** — `harness_template.py` 捕获初始寄存器集和可写入内存,并在 `place_input` 回调中恢复。设置 `persistent_iters=0` 以获得论文推荐的吞吐量最优的无分叉循环。
---
## ⚠️ 限制 / 注意事项
> [!WARNING]
> **不是完整的 OS 模拟器。** 页表、MMIO、中断和系统调用必须手动存根。unicornafl 设计用于**函数隔离**的模糊测试,本项目亦如此。
> [!NOTE]
> 在 **macOS arm64** 上,unicornafl 轮子构建偶尔需要 Rust nightly 版本;`setup.sh` 首先尝试稳定版本,否则会打印指导信息。
> [!CAUTION]
> **macOS 上的 AFL++** 自动设置 `AFL_SKIP_CPUFREQ=1` 和 `AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES=1`,但您可能仍会在第一次运行时遇到一次性权限/核心转储提示。
> [!WARNING]
> **Claude Desktop 有消息长度限制。** 在一个非常大的函数上分析输入处理可能会被截断 — 请传递较小的 `count` 或 `size_hint` 值。
> [!NOTE]
> **arm64 thumb / mips64 / 等** 未经过测试 — 欢迎提交拉取请求。
---
## 📄 本文依据的论文
本工具移植了以下论文中的三种策略(语法提取 · 种子丰富 · 饱和处理程序)到**二进制模糊测试**,并使用**Claude 作为上下文 LLM**:
> **基于大型语言模型的协议模糊测试。**
> Ruijie Meng, Martin Mirchev, Marcel Böhme, Abhik Roychoudhury.
> *NDSS Symposium 2024.* [doi:10.14722/ndss.2024.24556](https://dx.doi.org/10.14722/ndss.2024.24556)
原始 ChatAFL 针对基于文本的协议(RTSP/FTP/SIP/…),并调用 OpenAI API。unicornafl-mcp 的不同之处在于:
| 方面 | ChatAFL | unicornafl-mcp |
|---|---|---|
| 目标 | 文本协议 | 二进制代码(ARM64优先) |
| LLM 调用 | OpenAI API | Claude Desktop 聊天上下文(无需外部调用) |
| 语法提取 | RFC NL → 消息语法 | 反汇编 + 立即数 + 字符串 + 动态读取模式 |
| 种子丰富 | LLM 生成缺少的消息类型 | Claude 设计种子 → `template_seeds` / `seed_add_many` |
| 平台突破 | LLM 提出下一个消息 | `fuzz_plateau_check` → Claude 推理 → `fuzz_inject_seed` |
---
## 🙏 依赖 / 致谢
- [Unicorn Engine](https://github.com/unicorn-engine/unicorn)
- [unicornafl](https://github.com/AFLplusplus/unicornafl) — AFL++ ↔ Unicorn 桥接
- [AFL++](https://github.com/AFLplusplus/AFLplusplus)
- [Capstone](https://github.com/capstone-engine/capstone)
- [模型上下文协议 Python SDK](https://github.com/modelcontextprotocol/python-sdk)
---
## 🤝 贡献
欢迎提交问题和拉取请求。在提交拉取请求之前,请确保:
- `python -m py_compile server.py harness_template.py` 通过
- 在此文件的 README 目录中添加新工具
- 如果可行,请附上一个小型冒烟测试,用来测试新工具与真实 Unicorn 实例的交互
---
## 📜 许可证
MIT — 见 [`LICENSE`](LICENSE)。
MCP 配置
以下是此 MCP Server 的配置信息,可直接复制到 Cursor 或其他 MCP 客户端中使用。
mcp.json
连接信息
猜你喜欢
暂无推荐服务器