内容
# ghidra-mcp
**无头 [Ghidra](https://ghidra-sre.org/) 作为 MCP 服务器。** 一个单独的 TypeScript 守护进程暴露了 Ghidra 的逆向工程能力 — 反编译、反汇编、交叉引用、类型/结构编辑、调用图、数据流跟踪、脚本编写等 — 作为 [模型上下文协议](https://modelcontextprotocol.io) 工具,通过 **SSE** 和 **可流式 HTTP** 提供服务。每个会话都由一个真正的 Java Ghidra 进程驱动,因此结果与 Ghidra 生成的结果完全相同,而不是近似值。
它有两种运行方式:
- **本地** 为单个用户 — 绑定到环回地址,无身份验证,一条命令即可启动。
- **云原生** 作为 OAuth 安全的 **claude.ai 连接器**:一个守护进程容器,创建一个 Ghidra 工作进程容器,每个会话使用一个 Ghidra 工作进程容器,与 **共享的 Ghidra 服务器** 通信。
## 概念
Ghidra 是一个重量级的桌面逆向工程套件。ghidra-mcp 以无头模式运行,并在其前面添加了一个薄而快速的协议,以便 LLM 代理可以驱动分析,就像人类在 GUI 中一样 — 打开程序,反编译函数,重命名变量,定义结构体,跟踪交叉引用 — 但可以编程和大规模地执行。
两个进程,一个协议边界:
- **守护进程** (Node/TypeScript)— MCP 服务器,传输,OAuth 授权服务器,会话管理器和工作进程池。本身不持有任何 Ghidra 状态。
- **工作进程** (Java)— 一个 `com.ghidramcp.Worker` JVM,带有 Ghidra 在其类路径上。每个会话一个。打开程序并执行实际的分析命令。
守护进程通过可插拔的 **后端** 启动工作进程:本地子进程(默认)或 **一个 Kubernetes 容器组一个工作进程** (`GHIDRA_MCP_WORKER_BACKEND=k8s`)。
## 架构

> 图表由 [LikeC4](https://likec4.dev) 模型生成,位于
> [`docs/architecture/ghidra-mcp.c4`](docs/architecture/ghidra-mcp.c4)
> (`npx likec4 serve docs/architecture` 以交互方式探索)。
### 本地模式
```
MCP 客户端 --stdio/HTTP--> 守护进程 (Node) --spawns--> Java 工作进程 (子进程)
|
v
本地 .gpr 项目 / 二进制文件
```
`ghidra --stdio` 运行一个_stdio_桥接器,用于桌面 MCP 客户端;或者在端口上运行守护进程,并将 MCP 客户端指向 `/mcp` 或 `/sse`。
### 云原生模式
守护进程容器只是 Node 进程 — 它 **不** 在进程中运行 Ghidra。当客户端打开程序时,守护进程 **创建工作进程容器** 通过 Kubernetes API。工作进程容器运行相同的镜像(JVM 入口点),连接到守护进程容器上的集群内服务,打开 **共享 Ghidra 服务器** 中的程序,并提供分析。会话结束时,容器将被删除,如果守护进程本身死亡,则通过 `ownerReferences` 进行垃圾回收。

在 Kubernetes 中,上述每个容器都映射到一个容器组:**守护进程** 是长期运行的部署,每个 **Ghidra 工作进程** 是一个短期容器组,由守护进程通过 Kubernetes API 创建(每个会话一个),在会话结束时回收。
```
claude.ai --HTTPS--> Ingress --> ghidra-mcp 服务 --> 守护进程容器 (Node, OAuth)
| create/delete/watch
v (k8s API, namespaced RBAC)
工作进程容器 工作进程容器 工作进程容器 ...
(Java JVM) (Java JVM) (Java JVM)
| 每个都连接到守护进程服务,
v 然后从以下位置打开其程序:
共享 Ghidra 服务器 (RMI :13100-13102)
```
为什么一个工作进程一个容器组:每个 Ghidra JVM 都很耗内存,并且持有进程范围的锁。将工作进程隔离在它们自己的容器组中(每个容器组都有自己的内存限制和临时卷),可以消除将所有 JVM 塞进一个进程中的堆/锁争用问题,并允许并发性随着集群容量而不是单个容器组的 RAM 扩展。
### 共享 Ghidra 服务器(云原生模式必填)
云原生模式围绕一个 **协作式 [Ghidra 服务器](https://ghidra-sre.org/InstallationGuide.html#GhidraServer)** (RMI 存储库服务器,端口 13100-13102)构建,作为程序的真实来源:
- 客户端通过 **存储库路径** 打开程序(例如 `MyRepo/path/to/binary.exe`);守护进程会添加配置的 `GHIDRA_SERVER_HOST:PORT` 前缀。凭证始终来自环境,永不来自 URL。
- 多个工作进程(和人类 Ghidra GUI 用户)共享同一个存储库,因此分析 — 重命名、结构体、注释、书签 — 被提交回去并在会话之间持续。
- 工作进程容器的捆绑 Ghidra 版本 **必须与** Ghidra 服务器版本匹配 — RMI 握手会检查版本。
如果没有共享服务器,您仍然可以在本地模式下运行针对本地二进制文件/项目的本地模式,但多容器组、多用户连接器模型假设存在该服务器。
## 软件包
| 软件包 | 角色 |
|-|-|
| `@ghidra-mcp/shared` | 类型,工作进程<->守护进程协议,平台/路径检测,后端选择 |
| `@ghidra-mcp/mcp` | MCP 服务器 + 工具定义 + 工具->守护进程调度器 |
| `@ghidra-mcp/daemon` | HTTP/SSE/Streamable 服务器,OAuth 授权服务器,会话管理器,工作进程池,k8s 启动器,状态数据库 |
| `@ghidra-mcp/cli` | `ghidra` 二进制文件:启动守护进程或 MCP 客户端的_stdio_桥接器 |
| `@ghidra-mcp/dashboard` | React 监控 UI(在 `/dashboard` 处提供) |
`ghidra-worker/` 目录是一个 Gradle 构建的 Java 胖 JAR(`com.ghidramcp.Worker`),针对本地 Ghidra 安装编译。
## 构建
```bash
npm install
npm run install:ghidra # 下载 Ghidra(scripts/install-ghidra.sh)
npm run install:worker # gradle 构建 -> ghidra-worker.jar
npm run build # 构建所有 TS 软件包
```
或者,使用 `Dockerfile` 构建所有-in-one 容器(Node + JRE + Ghidra + 工作进程 JAR)。
## 本地运行
```bash
node packages/cli/dist/bin.js --port 8432 # 启动守护进程
node packages/cli/dist/bin.js --stdio # MCP 客户端的 stdio 桥接器
```
在本地,守护进程绑定到 `127.0.0.1` 并要求 **无身份验证**。
## 作为远程 claude.ai 连接器运行
设置 `GHIDRA_MCP_PUBLIC_URL` **和** `GHIDRA_MCP_AUTH_SECRET`,守护进程成为一个自包含的 OAuth 2.1 授权服务器(动态客户端注册 + PKCE),控制 MCP 端点。可选地,将人类登录联合到上游 OIDC 提供者,使用 `GHIDRA_MCP_OIDC_*` 变量(仅允许列出的用户获得令牌)。
| 环境变量 | 目的 | 默认值 |
|-|-|-|
| `GHIDRA_MCP_HOST` | 绑定地址 — 在容器中设置为 `0.0.0.0` | `127.0.0.1` |
| `GHIDRA_MCP_PORT` | 监听端口 | `8432` |
| `GHIDRA_MCP_PUBLIC_URL` | 公共 HTTPS URL(OAuth 发行者/资源 ID) | — |
| `GHIDRA_MCP_AUTH_SECRET` | 同意屏幕上显示的连接器密码 | — |
| `GHIDRA_MCP_OAUTH_SCOPES` | 支持的范围(空格/逗号分隔) | `ghidra` |
| `GHIDRA_MCP_ACCESS_TTL` | 访问令牌生命周期(秒) | `3600` |
| `GHIDRA_MCP_REFRESH_TTL` | 刷新令牌生命周期(秒) | `2592000` |
| `GHIDRA_MCP_OIDC_ISSUER` | 上游 OIDC 发行者(启用联合登录) | — |
| `GHIDRA_MCP_OIDC_CLIENT_ID` / `_CLIENT_SECRET` | OIDC 客户端凭证 | — |
| `GHIDRA_MCP_OIDC_ALLOWED_USERS` | OIDC 用户名允许列表 | — |
| `GHIDRA_MCP_WORKER_SECRET` | `/internal/*` 工作进程控制平面共享密钥 | 自动生成,在数据目录中持久化 |
**共享 Ghidra 服务器 + 工作进程后端:**
| 环境变量 | 目的 | 默认值 |
|-|-|-|
| `GHIDRA_SERVER_HOST` / `GHIDRA_SERVER_PORT` | 守护进程连接到的默认 Ghidra 服务器 | — / `13100` |
| `GHIDRA_SERVER_USER` / `GHIDRA_SERVER_PASSWORD` | 工作进程的 Ghidra 服务器凭证 | — |
| `GHIDRA_MCP_WORKER_BACKEND` | `process`(本地子进程)或 `k8s`(每个工作进程一个容器组) | `process` |
| `GHIDRA_MCP_WORKER_DAEMON_URL` | 工作进程调用回的集群内服务 URL(k8s) | — |
| `GHIDRA_MCP_WORKER_IMAGE` | 工作进程容器镜像(k8s) | 继承守护进程自己的镜像 |
| `GHIDRA_MCP_NAMESPACE` | 创建工作进程容器的命名空间(k8s) | `ghidra-mcp` |
| `GHIDRA_MCP_MEMORY` | 工作进程 JVM 堆(容器限制 ~ 堆 + 512Mi) | — |
| `GHIDRA_MCP_MAX_WORKERS` | 最大并发工作进程容器/进程 | — |
| `GHIDRA_HOME` | Ghidra 安装目录 | 平台默认值 |
当设置了 `GHIDRA_MCP_PUBLIC_URL` **和** `GHIDRA_MCP_AUTH_SECRET` 时,OAuth 被激活。将 `<GHIDRA_MCP_PUBLIC_URL>/mcp` 添加为 claude.ai 中的自定义连接器;它会发现 OAuth 端点,通过 DCR 注册,并提示连接器密码。
### 端点
- MCP:`POST/GET/DELETE /mcp`(可流式 HTTP),`GET /sse` + `POST /sse/messages`(SSE)— **当启用 OAuth 时进行令牌控制**
- OAuth:`/.well-known/oauth-authorization-server`,`/.well-known/oauth-protected-resource`,`/authorize`,`/token`,`/register`,`/revoke`,`/oauth/consent`
- 打开:`/health`,`/status`,`/dashboard`
- 内部(工作进程控制平面):`/internal/worker/:id/*` + 日志 WebSocket — 使用每个守护进程的 `GHIDRA_MCP_WORKER_SECRET` 进行身份验证。将其远离入口;仅环回流量/容器内流量应到达它。
## Kubernetes
一个自我托管的 **kustomize 模板** 位于 [`deploy/k8s/`](deploy/k8s/) — 基础(部署,服务,命名空间 RBAC,用于容器组创建/删除/监视,PVC)加上一个示例叠加层(入口,镜像标签,`config.env`)。参见 [`deploy/k8s/README.md`](deploy/k8s/README.md) 以获取机密设置和守护进程->工作进程模型。部署与此存储库故意解耦;CI 将镜像发布到 `ghcr.io`;使用您的 GitOps 工具(例如 ArgoCD Image Updater,或您的基础设施存储库中的 `newTag` 增量)进行部署。
```bash
kubectl apply -k deploy/k8s/overlays/example
```
## 测试
```bash
npm run test:unit
npm run test:e2e
```
## 许可证
MIT — 参见 [LICENSE](LICENSE)。