getanirao/ghidra-bizhawk-mcp
GitHub: getanirao/ghidra-bizhawk-mcp
一个基于MCP协议的Ghidra无头逆向分析服务器,将多平台复古ROM的自动分诊、静态分析与P-code微模拟统一为AI可调用的工具链。
Stars: 0 | Forks: 0
# Ghidra BizHawk MCP
一个统一的 MCP (Model Context Protocol) 服务器,将 Ghidra 的无头静态分析与 BizHawk 的实时模拟连接起来——在同一会话中,可以在反编译 ROM 和在真实硬件上运行 ROM 之间切换。
## 安全模型
此服务器**完全通过标准进程 stdio 进行通信**——没有 HTTP 套接字,没有 TCP 监听器,也没有暴露的网络接口。它天生免疫局域网/广域网 (LAN/WAN) 暴露、SSRF 和未经身份验证的 API 攻击。与其交互的唯一方式是由 MCP 客户端将其作为子进程启动,并通过 stdin/stdout 进行通信。
## 硬件与复古生态系统集成
`ghidra-bizhawk-mcp` 包含开箱即用的原生支持,可通过 Ghidra 的静态分析实现复古逆向工程自动化 pipeline,并通过 BizHawk 的多系统模拟器进行实时模拟。该服务器捆绑了:
- **Nintendo Entertainment System (NES)**,通过 `GhidraNes`
- **Super Nintendo Entertainment System (SNES)**,通过原生 65816 内存映射
- **Game Boy Advance (GBA)**,通过 `gba-ghidra-loader`
- **Nintendo DS (NDS)**,通过 `NTRGhidra`
- **Nintendo Switch**,通过 `ghidra-switch-loader`
- **PlayStation 1 (PSX)**,通过 `ghidra_psx_ldr`
- **Sega Genesis / Mega Drive**,通过原生 68000 内存映射
- **Sega Master System / Game Gear**,通过 `Ghidra-SegaMasterSystem-Loader`
- **Sega Dreamcast**,通过原生 SuperH4 内存映射
### 零输入分析 — 示例操作 (GBA)
主要入口点是 `triage_and_load_retro_rom`。只需传入任意 ROM 路径,服务器即可处理其余操作:
```
# 自动检测 platform,映射 language,分配 session
triage_and_load_retro_rom(rom_path="/data/game.gba")
# → platform: "Game Boy Advance (GBA)"
# → loader: "GBA ROM Loader"
# → arch: "ARM:LE:32:v4t"
# 在同一个 session 中反编译 main entry point
decompile_function(address="0x00001c2c")
# → GBA ROM 入口例程的反编译 C 代码
# 搜索已知 pattern(例如 32-bit ARM store-multiple)
search_bytes(pattern="09 08 00 01")
# → 匹配的地址被标记为 "gba_ram_start"
```
### 执行链式流程
无需强制您的 AI agent 花费周期去手动识别架构映射、寄存器布局或内存段,直接链式调用自动化提取 pipeline 即可:
1. 传入目标文件路径调用 `triage_and_load_retro_rom`。
2. 服务器以无头模式解析二进制文件结构(`NES\x1a`, `NTR`, `NSO0`, `GBA`, SNES 标题向量, `PS-X EXE`, `SEGA`, `TMR SEGA`, `SEGA ENTERPRISES`),绑定匹配的 Ghidra 语言模块(`6502:LE:16`, `ARM:LE:32:v4t`, `AARCH64:LE:64`, `65816:LE:24`, `MIPS:LE:32`, `68000:BE:32`, `Z80:16`, `SuperH4:LE:32`),加载标准地址内存块,并链接自动化特征缓存数组。
3. 使用集成的 `emulate_slice` 或 `emulate_slice_with_taint` 工具来分析本地化的主机循环——无需物理主机硬件或开启 GDB 网络端口。
### 分析工具
| 工具 | 描述 |
|---|---|
| `triage_and_load_retro_rom` | 读取原始文件的 magic bytes 以检测 NES、SNES、GBA、NDS、Switch、PSX、Genesis、SMS 或 Dreamcast ROM。提供一个正确映射了语言的 Ghidra 会话并自动恢复缓存的函数签名。返回平台、loader、架构标签和映射的内存块。 |
## 快速开始
### 本地
```
pip install -e .
set GHIDRA_INSTALL_DIR=C:\path\to\ghidra # Windows
ghidra-bizhawk-mcp
```
### Docker
```
docker build -t ghidra-bizhawk-mcp .
docker run -i --rm -v /path/to/binaries:/data ghidra-bizhawk-mcp
```
该容器捆绑了 JDK 17、Ghidra 11.2 和服务器——除了 Docker 之外没有任何宿主机依赖。
## Claude Desktop 配置
```
{
"mcpServers": {
"ghidra-headless": {
"command": "ghidra-bizhawk-mcp",
"args": ["--ghidra-dir", "C:\\path\\to\\ghidra"],
"env": {}
}
}
}
```
## 工具
### 会话管理
| 工具 | 描述 |
|---|---|
| `analyze_binary` | 导入并分析二进制文件,返回一个 `session_id`。如果提供则重用该 ID,否则自动生成。 |
| `list_sessions` | 列出所有活跃的工作区及其会话 ID、二进制文件路径和加载时间。 |
| `close_session` | 关闭会话并释放其 Ghidra 项目资源。 |
大多数工具接受可选的 `session_id` 参数——省略它即可使用最近加载的会话。
### 读取 / 分析
| 工具 | 描述 |
|---|---|
| `decompile_function` | 按名称或地址反编译函数。 |
| `decompile_function_paginated` | 使用 `line_start`、`line_end`、`max_tokens`(token 预算截断)和 `summarize`(去除样板局部变量 + 折叠空行)进行反编译。防止上下文窗口耗尽。 |
| `get_data_types` | 列出程序中定义的所有数据类型。 |
| `get_cross_references` | 指向/来自某地址的交叉引用。 |
| `get_call_graph` | 函数的递归调用图和调用者。 |
| `analyze_and_decompile_entrypoints` | 复合操作——在一次调用中批量反编译所有入口点(程序入口、导出函数、`main`、`_start` 等)。 |
| `generate_workspace_report` | 生成当前活跃工作区的 Markdown 摘要——入口点、函数数量、自定义符号、恢复的结构、重命名的函数、注释。替代 GUI 的 CodeBrowser 窗口。 |
### 写入 / 修改
| 工具 | 描述 |
|---|---|
| `rename_symbol` | 重命名函数或标签。存储在 Ghidra 项目数据库中。 |
| `add_comment` | 附加注释(`plate`、`pre`、`post`、`eol`、`repeatable`)。 |
| `create_struct` | 从 JSON 成员布局 `[{offset, name, type}, ...]` 创建自定义结构化数据类型。偏移量是可选的。 |
| `retype_variable` | 重新定义局部变量或函数参数的类型(例如 `undefined4*` → `MyStruct*`)。 |
### 汇编层级
| 工具 | 描述 |
|---|---|
| `disassemble_range` | 在指定地址反汇编 N 条原始指令——返回助记符、操作数、十六进制字节和长度,用于精确的底层检查。 |
| `get_listing_range` | 字节范围的原始十六进制 + ASCII 转储,等同于 Ghidra 的 Listing 面板。对数据区域补充 `disassemble_range` 功能。 |
### 字节序列搜索
| 工具 | 描述 |
|---|---|
| `search_bytes` | 在整个二进制文件中搜索十六进制字节模式(例如 `09 08 00 01` 或 `F86D0003`)。返回匹配的地址及其上下文字节以及命中处的任何字符串标签。 |
### 二进制对比
| 工具 | 描述 |
|---|---|
| `diff_binaries` | 按函数名称和函数体大小比较两个已加载的会话。返回各自独有的函数以及已更改的函数。 |
## 工作区会话
每次 `analyze_binary` 调用都会创建一个命名会话。会话独立保持其 Ghidra 项目处于打开状态,因此可以并发加载多个二进制文件:
```
# 将两个 binary 加载到独立的 session 中
s1 = analyze_binary(binary_path="/bin/a.out") # auto session_id
s2 = analyze_binary(binary_path="/bin/b.out", session_id="my_session")
# 在特定的 session 上操作
decompile_function(function_name="main", session_id=s1.session_id)
# 对它们进行 Diff
diff_binaries(session_a=s1.session_id, session_b="my_session")
```
## 部署
### Docker(多用户 / CI)
```
docker build -t ghidra-bizhawk-mcp .
# 作为 MCP 子进程运行
docker run -i --rm \
-v /data/binaries:/data \
ghidra-bizhawk-mcp \
--ghidra-dir /opt/ghidra
```
`Dockerfile` 在精简的 Python 3.11 镜像中捆绑了 Ghidra 11.2 和 JDK 17。在运行时将您的二进制文件目录绑定挂载。
### MCP Bundle (MCPB — Claude Desktop / Smithery)
打包为可移植的 `.mcpb` bundle,可在 Claude Desktop 中一键安装或发布到 [Smithery](https://smithery.ai)。
**前置条件:** 安装 MCPB CLI:
```
npm install -g @anthropic-ai/mcpb
```
**构建 bundle:**
```
# 从 repo 根目录开始
scripts/build-mcpb.ps1
```
或者使用 `mcpb` 手动操作:
```
mcpb pack
```
输出的 `ghidra-bizhawk-mcp.mcpb` 使用一个 `manifest.json` 包装服务器,该清单会在安装时提示输入 `GHIDRA_INSTALL_DIR`(必填)以及可选的 `BIZHAWK_EXE_PATH`——无需手动编辑 JSON。
**发布到 Smithery:**
```
smithery mcp publish ./dist/ghidra-bizhawk-mcp.mcpb -n getanirao/ghidra-bizhawk-mcp
```
### P-code 微模拟
| 工具 | 描述 |
|---|---|
| `emulate_slice` | 无头执行 N 条指令。设置初始寄存器状态并获取寄存器变化的逐步 trace。 |
| `emulate_slice_with_taint` | 同 `emulate_slice`,但带有自动化污点追踪——指定一个污点寄存器(例如 `r0`),该工具将准确标记其值何时被修改或传播到其他寄存器。 |
| `emulate_slice_with_breakpoints` | 执行直到满足条件或次数到期。条件语法:`R0==0`、`R1>0xFF`、`R2!=R3`、`PC==0x1234`。在匹配指令之前或之后停止。 |
全部通过 Ghidra 的 `EmulatorHelper` 在 pyhidra 进程内运行——无需 GDB/LLDB,无需网络端口,无需 debugger stub。适用于 ARM、x86、MIPS 以及任何 Ghidra 支持的架构。
#### 操作示例 — 基于寄存器条件中断
假设您正在逆向一个 GBA ROM,并希望找出在 `0x08000100` 处的循环内 `r0` 第一次变为零的位置:
```
# 单步执行直到 r0 == 0,在匹配到该指令前停止
result = emulate_slice_with_breakpoints(
session_id="gba_v1",
start_address="0x08000100",
max_instructions=5000,
stop_condition="R0==0",
stop_mode="before"
)
# result.exit_reason → "R0==0"
# result.instructions_executed → 312
# result.trace → [步骤 311: r0 从 4 变为 2, 步骤 312: r0 从 2 变为 0]
# 检查 branch 后是否到达了特定地址
result = emulate_slice_with_breakpoints(
session_id="gba_v1",
start_address="0x08000100",
max_instructions=5000,
stop_condition="PC==0x08001234"
)
# result.exit_reason → "PC==0x08001234"
# 使用不等式来捕获 bounds check
result = emulate_slice_with_breakpoints(
session_id="gba_v1",
start_address="0x08000100",
max_instructions=5000,
stop_condition="R1>0xFF"
)
# result.exit_reason → "R1>0xFF"
# result.last_step["r1"] → 0x100
```
这对于识别复制循环边界 (`R3 >= R4`)、空指针路径 (`R0==0`) 或 switch-table 目标 (`PC==0x`) 特别有用。
### 函数指纹识别 / 签名转移
| 工具 | 描述 |
|---|---|
| `calculate_function_fingerprint` | 为函数生成结构化哈希(变量、参数、函数体大小、分支、调用的函数、嵌入的字符串、数字常量)。能抵抗编译器重新排序的影响。 |
| `export_signature_map` | 为当前二进制文件中的每个函数构建完整的 `{hash → name}` 映射。保存此 JSON 以便在不同版本间重用。 |
| `apply_signature_map` | 传入之前导出的签名映射;服务器将扫描二进制文件并自动重命名每个匹配的函数。 |
### 持久化签名存储(服务器端缓存)
| 工具 | 描述 |
|---|---|
| `save_active_binary_signature` | 对所有函数进行指纹识别,并将映射存储在 `lineage_group_id`(例如 `"my_firmware_v1"`)下。保存在 `~/.ghidra_bizhawk_mcp/signatures/` 中——无需管理 JSON 文件。 |
| `auto_restore_signatures_from_stash` | 通过 `lineage_group_id` 加载存储的映射,并自动重命名每个匹配的函数。 |
| `auto_stash_current_binary` | **零输入自动存储**——对二进制文件的前 4 KB 进行哈希处理,并在该哈希下保存映射。只需分析并调用即可。 |
| `auto_restore_current_binary` | **零输入自动恢复**——对二进制文件进行哈希处理,查找之前的存储,并重命名匹配项。不需要 group ID。 |
| `list_stashed_signature_groups` | 列出当前本地缓存中所有已存储的组。 |
**工作流 — 完全自动化的持久化:**
```
# 分析 v1 — 基于 binary 内容哈希自动 stash
s1 = analyze_binary(binary_path="/bin/v1.bin")
auto_stash_current_binary(session_id=s1.session_id)
# 之后,分析 v2 — 自动 restore
s2 = analyze_binary(binary_path="/bin/v2.bin")
auto_restore_current_binary(session_id=s2.session_id)
# → 142 个函数被重命名,无需手动处理 JSON
```
## 演示

*Claude Desktop:“反编译此 GBA ROM 的入口点并追踪 r0 的传播”——服务器自动检测 ARMv4t 语言,配置会话,并返回反编译的 C 代码 + 污点追踪。*

*来自 `triage_and_load_retro_rom` 的控制台输出,检测到 PlayStation 1 可执行文件(`PS-X EXE` magic),映射 MIPS:LE:32,并自动恢复缓存的签名。*
### 快速测试
```
# 安装
pip install ghidra-bizhawk-mcp
# 需要 Ghidra 12.x + pyghidra;请参见上方的 Quick Start。
# 启动服务器(stdio — 通过管道传递给 MCP client)
ghidra-bizhawk-mcp
```
配置 Claude Desktop:
```
{
"mcpServers": {
"ghidra-bizhawk": {
"command": "ghidra-bizhawk-mcp",
"args": ["--ghidra-dir", "C:\\path\\to\\ghidra"],
"env": {}
}
}
}
```
然后询问 Claude:
- *"加载此 GBA ROM 并反编译入口点。"*
- *"在这个 NDS 二进制文件中,哪些函数调用了 0x8001234?"*
- *"分析此 PSX EXE 并在前 20 条指令中追踪 r0。"*
- *"对比我打开的两个会话,并显示已更改的函数。"*
## 项目结构
```
ghidra-bizhawk-mcp/
├── Dockerfile
├── pyproject.toml
├── README.md
└── src/ghidra_bizhawk_mcp/
├── __init__.py
├── server.py # MCP server, tool registry, stdio transport
├── ghidra_bridge.py # GhidraSession — pyghidra wrapper, all Ghidra logic
├── lua/
│ └── bridge.lua # BizHawk-side Lua bridge for live emulation
└── tools/
├── __init__.py
├── bizhawk_bridge.py # TCP server bridging MCP ↔ BizHawk
└── ...
```
## 工作原理
1. `pyhidra.start()` 在服务器启动时一次性启动 Ghidra 的 JVM
2. 每次 `analyze_binary` 调用都会在其自身的命名会话中打开一个新的 Ghidra 项目
3. 读取/写入工具通过 `session_id`(或活动的默认会话)路由到请求的会话
4. 写入工具将更改直接应用到 Ghidra 程序数据库5. 会话在显式关闭之前会一直存在——从而实现多二进制工作流和 diffing
标签:Findomain, Ghidra, XSS注入, 云安全监控, 固件分析, 模型上下文协议, 游戏模拟器, 请求拦截, 逆向分析, 逆向工具, 静态分析