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 分析并获取返回的反编译函数](https://static.pigsec.cn/wp-content/uploads/repos/cas/6b/6b7fa434f92a8b80aab02d9bf1a12e49ffcae424e4013a1c4f68b67e3d2bbcd0.png) *Claude Desktop:“反编译此 GBA ROM 的入口点并追踪 r0 的传播”——服务器自动检测 ARMv4t 语言,配置会话,并返回反编译的 C 代码 + 污点追踪。* ![终端输出显示 triage_and_load_retro_rom 检测到 PSX EXE](https://static.pigsec.cn/wp-content/uploads/repos/cas/6b/6b7fa434f92a8b80aab02d9bf1a12e49ffcae424e4013a1c4f68b67e3d2bbcd0.png) *来自 `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注入, 云安全监控, 固件分析, 模型上下文协议, 游戏模拟器, 请求拦截, 逆向分析, 逆向工具, 静态分析