Skip to main content
Glama
imjdl

ghidra-fleet

by imjdl
README.md
# ghidra-fleet

**一个 MCP 服务器,同时分析任意多个二进制文件,零端口占用。**

解决传统 GhidraMCP 的痛点:"1 个二进制 = 1 个 GUI 实例 = 1 个端口 = 1 份 MCP 配置"。
ghidra-fleet 完全无 GUI 运行(headless),AI 配置一次即可批量导入、并行分析、
跨样本交叉查询,分析结果落在标准 Ghidra 项目里,随时可以打开 GUI 人工复核。

```
MCP 客户端 ──stdio── ghidra-fleet (fastmcp)
                       ├─ core/runtime.py   PyGhidra 内嵌 JVM + 共享 .gpr 项目
                       │                    + 程序句柄懒加载 / LRU 逐出(控内存)
                       ├─ core/analyzer.py  批量分析:线程池并行、sha256 去重复用、
                       │                    后台作业 + 轮询(不受 MCP 30s 超时影响)
                       └─ core/queries.py   反编译/交叉引用/列表/重命名/跨样本搜索
```

## 安装

前置:已安装 Ghidra + Python 3.10+。

> 实测环境:Windows 11 + Ghidra 12.1 + Python 3.14。Linux/macOS 提供了对应的
> 安装脚本与跨平台测试(测试样本自动按平台选取),但未在 CI 上持续验证。

```bat
:: Windows
scripts\setup_env.bat <你的 Ghidra 安装目录>
```

```bash
# Linux / macOS
GHIDRA_INSTALL_DIR=/path/to/ghidra scripts/setup_env.sh
```

脚本会创建 `.venv` 并安装:fastmcp(PyPI)+ pyghidra(**Ghidra 安装目录自带的
wheel,版本严格匹配**)。若自带 jpype1 wheel 不覆盖你的 Python 版本,会自动
改用 PyPI 的兼容版 jpype1。

## 配置到 MCP 客户端

以 ZCode 为例,`~/.zcode/cli/config.json` 的 `mcp.servers` 里加(用户级),
路径换成你自己的:

```json
"ghidra-fleet": {
  "type": "stdio",
  "command": "<repo>\\.venv\\Scripts\\python.exe",
  "args": ["-m", "ghidra_fleet"],
  "cwd": "<repo>",
  "env": {
    "GHIDRA_INSTALL_DIR": "<你的 Ghidra 安装目录>"
  },
  "timeoutMs": 120000
}
```

Linux/macOS 下 `command` 用 `<repo>/.venv/bin/python`。
`timeoutMs` 建议保留:首次调用要启动 JVM + Ghidra(约 10~30 秒),之后都是毫秒级。
旧的单文件 GUI 版 MCP 无需删除,两者共存。

## 工具一览(23 个)

所有查询/改名工具的第一个参数都是 `binary`,接受:
完整 program_name(`ab12cd34__sample.exe`)/ 原始路径 / 唯一文件名 / sha256 前 8 位。
用 `list_binaries` 随时查看全部。

| 类别 | 工具 | 说明 |
|---|---|---|
| 舰队 | `analyze_binaries` | 批量导入+并行分析(目录也可,单批上限 1000 个文件),立即返回 job_id |
| | `analysis_status` | 轮询作业进度(含每个文件的分析百分比/阶段,已完成的可立刻查询) |
| | `list_binaries` | 所有二进制 + 架构/大小/哈希/状态(missing = 幽灵记录) |
| | `delete_binary` | 删除一个二进制(项目文件 + manifest;也能清理幽灵记录) |
| | `find_in_all_binaries` | **跨全部样本**搜同名函数/同字符串(找共享代码/家族特征,最多扫 200 个) |
| 查询 | `list_functions` / `list_methods` / `search_functions_by_name` | 函数列表/搜索 |
| | `decompile_function` / `decompile_functions` | 反编译 C(单个 / 批量最多 50 个,错误内联) |
| | `disassemble_function` | 反汇编 |
| | `function_info` | 签名、参数、调用约定等 |
| | `get_xrefs_to` / `get_xrefs_from` / `get_function_xrefs` | 交叉引用 |
| | `list_imports` / `list_exports` / `list_segments` / `list_strings` | 导入导出/内存段/字符串 |
| 修改 | `rename_function` / `rename_data` / `rename_variable` | 重命名(即时保存) |
| | `set_function_prototype` / `set_local_variable_type` | 签名/变量类型 |
| | `set_comment` | 注释(plate/pre/eol/post/repeatable) |

## 典型工作流

```
用户: 把 C:\samples\ 下的 20 个样本都分析了,找出它们的共同特征

AI:  1. analyze_binaries(["C:\\samples"], recursive=true)   → job_id
     2. analysis_status(job_id)                              → 轮询到完成(旧样本秒级跳过)
     3. list_binaries()                                      → 拿到全部 program_name
     4. find_in_all_binaries("string", "http://")            → 跨样本特征
        find_in_all_binaries("function", "encrypt")          → 共享函数
     5. decompile_function("ab12cd34__x.exe", "FUN_00401000")
        rename_function("ab12cd34__x.exe", "FUN_00401000", "c2_beacon_decode")
```

改名/注释/类型修改即时保存进项目;用 Ghidra GUI 打开
`<项目目录>/fleet.gpr`(File → Open Project)即可看到 AI 的全部标注。

## 环境变量

| 变量 | 默认 | 说明 |
|---|---|---|
| `GHIDRA_INSTALL_DIR` | (必填) | Ghidra 安装目录 |
| `GHIDRA_FLEET_PROJECT_DIR` | `~/.ghidra_fleet/projects` | Ghidra 项目目录 |
| `GHIDRA_FLEET_PROJECT_NAME` | `fleet` | 项目名 |
| `GHIDRA_FLEET_MAX_WORKERS` | `min(4, CPU)` | 并行分析线程数;遇到分析器并发问题时设 `1` |
| `GHIDRA_FLEET_MAX_OPEN` | `6` | 同时保持打开的程序数(LRU 逐出) |
| `GHIDRA_FLEET_XMX` | `4G` | JVM 堆上限;设空串用 Ghidra 默认 |
| `GHIDRA_FLEET_LOG` | `INFO` | 日志级别 |

## 测试

```bash
GHIDRA_INSTALL_DIR=<Ghidra 安装目录> .venv/bin/python tests/smoke_test.py [样本1 样本2 ...]
GHIDRA_INSTALL_DIR=<Ghidra 安装目录> .venv/bin/python tests/stdio_test.py
```

不传样本时自动挑选 PATH 上的系统小工具(Windows 用 where/robocopy 等,
Linux/macOS 用 ls/true 等)。覆盖:并行分析、二次提交缓存跳过、重复内容去重、
各类查询、改名/签名/类型后逐出重开的持久性、跨样本搜索、stdio 协议层。

## 已知边界

- **同一项目目录同一时间只允许一个 server 实例**(Ghidra 项目锁):开多个 MCP 会话时,
  第一个实例占用项目,其余实例会收到明确的锁冲突提示。多会话请错开使用,
  或给不同会话配置不同的 `GHIDRA_FLEET_PROJECT_DIR`。关闭占用方后即可恢复。
- **不要把 server 的启动脚本放在名为 `ghidra` 的目录下运行**(也不要让 sys.path[0]
  指向这种目录):Python 会把它当成 `ghidra` 命名空间包,与 pyghidra 的 Java 包
  导入钩子冲突(递归爆栈)。用 `python -m ghidra_fleet` 从任意 cwd 启动是安全的。
- 进程内并行分析在极少数格式上可能触发 Ghidra 分析器并发问题:把
  `GHIDRA_FLEET_MAX_WORKERS` 设为 `1` 即退化为顺序分析,接口不变。
- 分析进度按文件粒度上报百分比;部分 Ghidra 分析阶段本身不提供总量,
  此时只显示阶段名(progress_pct 为 null)。

TDQS

C2.7/5.0

Scored across 23 tools

Disambiguation3/5

Several tools overlap: list_methods is an explicit alias of list_functions, search_functions_by_name overlaps with filtered list_functions, and get_function_xrefs overlaps with get_xrefs_to on function entries. find_in_all_binaries is also adjacent to single-binary search tools. Descriptions help clarify intent, but an agent may still hesitate between similar query paths.

Naming Consistency4/5

Most names follow a consistent snake_case verb_noun style such as analyze_binaries, list_functions, decompile_function, and rename_variable. Minor deviations like analysis_status and function_info are noun-first, and list_methods introduces an alternate noun for the same concept, but the overall convention is predictable.

Tool Count3/5

At 23 tools, the surface is at the heavy end for a single MCP server, though the Ghidra reverse-engineering domain is broad. The explicit list_methods alias adds redundancy, and some list/search/xref tools could potentially be consolidated. It is usable but borderline heavy.

Completeness4/5

The surface covers batch analysis, status polling, binary listing, cross-binary search, function listing/search, decompilation, disassembly, xrefs, imports/exports, segments, strings, and several mutation operations. Minor gaps remain around deletion/project lifecycle and listing variables or data beyond rename operations, but core workflows are well covered.

Maintenance

ActivityMaintained
ResponsivenessNo issues