IDA Pro MCP
# IDA Pro MCP
> 本仓库维护者:**Moer2831** · [GitHub @Moer2831](https://github.com/Moer2831) · 仓库:<https://github.com/Moer2831/ida-pro-mcp>
>
> 直接来源是增强版 [QiuChenly/ida-pro-mcp-enhancement](https://github.com/QiuChenly/ida-pro-mcp-enhancement),其上游为 [mrexodia/ida-pro-mcp](https://github.com/mrexodia/ida-pro-mcp)。本仓库在其基础上继续维护:保留 Broker 纯路由架构、客户端侧 SQLite 静态缓存接管层与严格类型协议,并新增 stdio 端自动拉起 Broker 等便利改动。
一套面向 IDA Pro 的 [MCP(Model Context Protocol)](https://modelcontextprotocol.io/introduction) 服务端,让大模型以结构化工具调用的方式读写 IDA IDB,用于逆向工程、二进制分析、Hook 开发等场景。
英文原版文档请参阅 [README.en.md](./README.en.md)。本 README 描述的是在上游之上增强过的 Broker 架构与 SQLite 静态缓存接管层。版本改动记录见 [CHANGELOG.md](./CHANGELOG.md)。
示例视频与 prompt:见 [mcp-reversing-dataset](https://github.com/mrexodia/mcp-reversing-dataset)。
---
## 快速开始(GUI IDA + 任意 MCP 客户端)
```text
① 装到本机 Python 环境 pip install -e . (见"三、安装")
② 部署 IDA 插件 见"三、安装 → 部署 / 更新插件" (Windows 必须手动铺一次)
③ 配置 MCP 客户端 见"七、使用方式" (DSH / Cursor / Claude 各有配方)
④ 打开 IDA 加载二进制 插件自动连 Broker,缓存自动开始构建(不需要按 Ctrl+Alt+M)
⑤ 在客户端里调用工具 mcp__ida__decompile / list_funcs / find_regex / cache_status ...
```
三个最容易踩的坑,先记住:
1. **IDA 只扫描自己的插件目录**(`%APPDATA%\Hex-Rays\IDA Pro\plugins`),仓库目录它不认识;
改了仓库代码**必须重新部署**,否则 IDA 跑的还是旧拷贝(这是"改了没生效"的头号原因)。
2. **Broker 由 MCP 客户端启动**:客户端以 stdio 启动 `ida-pro-mcp` 时会在回环地址自动拉起 Broker,
IDA 插件只负责注册与退避重连;没有客户端时请在终端手动 `ida-pro-mcp --broker`。
3. **用 Output 窗口确认版本**:出现 `[MCP] 插件代码: ... (缓存 schema v2)` 与
带配置后缀的 `[MCP][cache] 守护线程启动 ... (scope=..., chunk=...)` 才算新版生效。
---
## 一、项目亮点(本增强版 vs. 上游)
- **Broker 进程纯路由**:独立监听 `127.0.0.1:13337`,IDA 实例与所有 MCP 客户端都只和它交互;多 Cursor 窗口、多 IDA 同时挂载不再抢端口。
- **客户端侧 SQLite 静态缓存(`xxx.idb.mcp.sqlite`)**:IDA 插件在 IDA idle 时由守护线程把字符串、函数、全局、导入、交叉引用全量落地到 IDB 旁的 SQLite 文件。
- **缓存接管 tools/call**:`find_regex / entity_query / list_funcs / list_globals / imports / refresh_cache / cache_status` 共 7 个工具在 IDA 插件进程内部被 **直接用本地 SQLite 响应**,完全不占用 IDA 主线程。
- **严格类型协议**:全部新增 API 的请求/返回走 `TypedDict`(`FindRegexArgs / FindRegexResult / ToolSchema / McpToolCallResult` 等),消除"字段是否存在"之类的不确定性。
- **Broker 注入虚拟工具**:`refresh_cache` 与 `cache_status` 作为虚拟 `ToolSchema` 追加到 `tools/list` 结果中,模型可以直接看到并调用,但它们并不在 Broker 执行,最终仍被路由到指定 IDA 实例。
- **idalib 无头模式**:通过 `idalib-mcp` 运行纯 headless 服务,支持 `--isolated-contexts` 做严格的每连接上下文隔离。
- **stdio 端自动拉起 Broker(本仓库新增)**:MCP 客户端(Cursor / Grok / Claude / VS Code…)以 stdio 启动本进程时,若本机没有监听中的 Broker,会自动用隐藏窗口(Windows `Start-Process -WindowStyle Hidden`)/ 独立会话(POSIX `start_new_session=True`)拉起一个,避免"忘记先开 Broker"导致 `instance_list` 为空;该行为只对回环地址生效(`127.0.0.1` / `localhost` / `::1`),远程 Broker 不会被自动拉起,可用 `--no-auto-broker` 关闭。
- **大库内存重写(2.1.0)**:缓存构建从"整库物化成 Python 对象 + 单事务全量重写"改为**分块流式提取 + 影子表原子切换 + 表级指纹增量**,峰值内存 O(全库) → O(块),且块间让出 IDA 主线程不再卡界面;查询侧补齐 `ea` 索引、去掉多余的 `COUNT(*)` 全表扫描。完整清单见 [CHANGELOG.md](./CHANGELOG.md)。
- **零操作自启(2.1.0)**:缓存守护线程的生命周期绑定"当前 IDB"(`IDB_Hooks.loaded` 起、`closebase` 停),与是否连上 Broker 解耦 —— **打开 IDB 就开始建缓存,不需要按 Ctrl+Alt+M,也不需要设任何环境变量**;重连 Broker 也不会打断正在进行的构建。
---
## 二、环境要求
- Python 3.11+(建议使用 `idapyswitch` 切换到最新 Python)
- IDA Pro 8.3+(推荐 9.0+),**不支持 IDA Free**
- 支持任意标准 MCP 客户端:DSH(DeepSeek Harness)/ Cursor / Claude / Claude Code / Codex / VS Code / Gemini CLI / Cline 等
---
## 三、安装
```bash
pip uninstall ida-pro-mcp
pip install https://github.com/Moer2831/ida-pro-mcp/archive/refs/heads/main.zip
```
本地开发安装:
```bash
cd ida-pro-mcp && uv venv && uv pip install -e .
```
配置 MCP 客户端和 IDA 插件:
```bash
ida-pro-mcp --install
```
安装完成后请**完全重启** IDA 和 MCP 客户端。某些客户端(如 Claude Desktop)在后台常驻,需要从托盘图标退出。IDA 插件菜单需要先加载一个二进制文件才会出现。
### 部署 / 更新 IDA 插件(Windows)
IDA 只扫描两个插件目录,仓库目录它不认识:
- `<IDADIR>\plugins`(系统级)
- `%APPDATA%\Hex-Rays\IDA Pro\plugins`(用户级,免管理员)
`ida-pro-mcp --install` 会优先创建符号链接,失败则退化为**拷贝**(Windows 默认没有符号链接权限)。
因此**改了仓库代码必须重新部署**,否则 IDA 加载的仍是旧拷贝 —— 典型症状是"缓存还在用老实现 / 没有新增功能"。
```powershell
# 1) 完全退出 IDA(含 ida64 / idat64)
# 2) 删掉旧插件
$dep = "$env:APPDATA\Hex-Rays\IDA Pro\plugins"
Remove-Item "$dep\ida_mcp.py","$dep\ida_mcp","$dep\broker" -Recurse -Force -ErrorAction SilentlyContinue
# 3) 从仓库拷贝新版(把 $repo 换成你的克隆路径)
$repo = 'D:\path\to\ida-pro-mcp\src\ida_pro_mcp'
Copy-Item "$repo\ida_mcp.py" "$dep\ida_mcp.py" -Force
Copy-Item "$repo\ida_mcp","$repo\broker" $dep -Recurse -Force
# 4) 重新打开 IDA:Output 首行应出现 [MCP] 插件代码: ... (缓存 schema v2)
```
等价做法(会同时刷新 MCP 客户端配置):`ida-pro-mcp --install`。
---
## 四、总体架构
下面这张图描述本增强版的所有运行时组件与数据流。
```mermaid
flowchart LR
subgraph Clients[MCP 客户端]
CurA[Cursor 窗口 A]
CurB[Cursor 窗口 B]
Claude[Claude / Codex / VS Code ...]
end
subgraph MCPProc[MCP 进程 - 每个客户端各一份]
direction TB
MA[ida-pro-mcp 进程 A - stdio]
MB[ida-pro-mcp 进程 B - stdio]
DispatchProxy[dispatch_proxy - tools/list 注入虚拟工具 / tools/call 走路由]
end
subgraph BrokerProc[Broker 进程 - 127.0.0.1:13337 - 唯一监听]
Registry[IDA 实例注册表]
Router[纯路由 HTTP + SSE]
end
subgraph IDAProc[IDA 插件进程]
Plugin[ida_mcp 插件 - handle_mcp_request]
CacheHandlers[cache_handlers - 本地拦截 7 个缓存工具]
Daemon[sqlite_cache 守护线程 - idle 时刷新]
DB[(xxx.idb.mcp.sqlite)]
IDAAPI[IDA / Hex-Rays API]
end
CurA -- stdio --> MA
CurB -- stdio --> MB
Claude -. stdio .-> MA
MA -- HTTP JSON-RPC --> Router
MB -- HTTP JSON-RPC --> Router
MA --> DispatchProxy
MB --> DispatchProxy
Router <-- HTTP 注册 + SSE 推送 --> Plugin
Plugin -- 先命中? --> CacheHandlers
CacheHandlers -- 只读 --> DB
Plugin -- 未命中 --> IDAAPI
Daemon -- 采集 --> IDAAPI
Daemon -- 批量写入 --> DB
```
关键点:
- **MCP 进程不绑端口**:每个客户端窗口自己启动一份 `ida-pro-mcp`(stdio),它们全部把请求通过 HTTP 丢给 Broker。
- **Broker 只做路由**:它不读 IDB、不读 SQLite,完全不碰业务逻辑;只负责把 JSON-RPC 请求按 `instance_id` 扔给对应的 IDA 插件,并把 SSE 回写的响应拿回来。
- **SQLite 读写都在 IDA 进程内**:写由守护线程负责(idle 触发),读由 `handle_mcp_request` 的拦截层负责(命中就查 DB,不再走 IDA API)。Broker 进程绝不 import `sqlite_cache / sqlite_query`。
---
## 五、一次 tools/call 的完整时序
```mermaid
sequenceDiagram
autonumber
participant LLM as 大模型 / MCP 客户端
participant MCP as ida-pro-mcp 进程 (stdio)
participant BRK as Broker 进程 127.0.0.1:13337
participant IDA as IDA 插件 handle_mcp_request
participant CACHE as cache_handlers + sqlite_query
participant HR as IDA / Hex-Rays
LLM->>MCP: tools/call find_regex(instance_id=...)
Note over MCP: dispatch_proxy 判断工具名命中 IDA 白名单
MCP->>BRK: HTTP JSON-RPC 转发
Note over BRK: 按 instance_id 查注册表
BRK-->>IDA: 通过 SSE 通道下发请求
IDA->>CACHE: is_cache_tool(req)?
alt 命中缓存拦截名单
CACHE->>CACHE: 打开 .mcp.sqlite (只读) 并检查 meta.status
alt status == ready
CACHE->>CACHE: SQL + REGEXP 查询
CACHE-->>IDA: TypedDict 结构化结果
else status != ready 或 DB 缺失
CACHE-->>IDA: JSON-RPC error -32001 缓存未就绪,稍后重试或 refresh_cache
end
else 未命中 (普通 IDA 工具)
IDA->>HR: 走 ida_mcp 正常 dispatch
HR-->>IDA: 结果
end
IDA-->>BRK: JSON-RPC response
BRK-->>MCP: HTTP 响应
MCP-->>LLM: tools/call 结果
```
缓存未就绪时**不会回退到实时 IDA API**,而是直接向模型报错并提示稍后重试或先调用 `refresh_cache`。这是一种有意的硬性语义:避免在大模型未知状态下拿到"半新半旧"数据造成误判。
---
## 六、SQLite 缓存守护线程生命周期
```mermaid
stateDiagram-v2
[*] --> 未连接
未连接 --> 已连接: IDA 注册到 Broker
已连接 --> 首次写入: 守护线程检测 auto_is_ok() + hex-rays 初始化完成
首次写入 --> 写入中: status=building
写入中 --> 就绪: 全量写入完成 status=ready
就绪 --> 写入中: IDB 保存 / refresh_cache 触发 / 30 分钟兜底轮询
写入中 --> 错误: 采集或写入异常
错误 --> 写入中: 下一次 idle 自动重试
就绪 --> [*]: IDA 关闭 / 断开
写入中 --> [*]: IDA 关闭 / 断开
```
- 缓存文件名固定为 `<idb 路径>.mcp.sqlite`,随 IDB 一起落盘。
- `meta` 表记录 `status`(`building / ready`)、`last_updated` 等。
- 使用 WAL 模式,允许写入进行中仍被只读 `file:...?mode=ro` 连接查询(读到旧快照)。
- `cache_status` 查询不抛错:文件缺失时返回 `{exists: false, status: "missing"}`。
- **重新索引触发时机**(三种,任一满足即触发,触发后等待 IDA idle 再执行全量重建):
1. **IDB 保存**:IDA 每次保存数据库(Ctrl+S 或自动保存)时,`IDB_Hooks.savebase` 回调立即唤醒守护线程,确保重命名、新增函数等变更实时同步。
2. **主动调用 `refresh_cache`**:MCP 客户端显式触发,绕过所有检查直接重建。
3. **30 分钟兜底轮询**:定时唤醒时检查 IDB 文件 mtime,若与上次重建时一致则跳过,避免无意义的全量扫描。
---
## 七、使用方式(Broker 模式)
多个 IDA 实例、多个客户端窗口并用时,它们共享同一个 Broker(每个 IDA 用 `instance_id` 注册,互不抢端口)。
Broker 由**客户端进程**自动拉起,也可以手动常开:
> **现状说明**:Broker 没有空闲自动退出 —— IDA 全部关闭后它仍会驻留,下次客户端启动直接复用(不会反复重启)。
> 插件本身**不会**拉起 Broker,它只负责注册与退避重连。
```bash
# 1. 启动 Broker(可选:客户端启动时会自动拉起本机 Broker)
uv run ida-pro-mcp --broker
# 或自定义端口
uv run ida-pro-mcp --broker --port 13337
# 2. 启动 MCP 客户端(Cursor / Claude / VS Code / DSH…),它们会通过 stdio
# 启动自己的 ida-pro-mcp 进程,并向上面的 Broker 发请求
# 3. 打开 IDA、加载二进制 —— 插件会自动注册并开始建缓存
# (只有自动连接失败时才需要按 Ctrl+Alt+M 手动重连)
```
### 在 DSH(DeepSeek Harness)中配置
DSH 用 `@deepseek-ai/dsh-mcp-client` 把外部 MCP 服务器桥接成原生工具,工具名形如
`mcp__<serverName>__<tool>`。在 profile 的补丁层 `$DSH_HOME/profiles/<profile>/cordis.patch.yml`
里**必须用 `insert:` 包一层** —— 顶层直接写 `- id: ...` 会被当成"覆盖一个不存在的行"而被忽略
(`dsh --dump-config` 会打印 `patch: entry "..." not found`):
```yaml
- insert:
- id: mcp-ida
name: '@deepseek-ai/dsh-mcp-client'
config:
serverName: ida
transport: stdio
command: 'D:\path\to\ida-pro-mcp\.venv\Scripts\ida-pro-mcp.exe'
```
- `web` profile 是 `patchReload: live`:保存即生效,**不需要重启 DSH**。
- 配好后模型侧会出现 `mcp__ida__decompile`、`mcp__ida__list_funcs`、`mcp__ida__find_regex`、
`mcp__ida__cache_status` 等工具(本机实测 66 个)。
- 校验:`dsh --profile web --dump-config`(组合树里应出现 `mcp-ida` 且无 patch 警告)。
- 临时停用:给该行加 `disabled: true`。
### 远程访问
当 MCP 客户端(Cursor / Claude)运行在**另一台机器**上时,`--broker` 直接绑定 `0.0.0.0`,一个端口同时处理 IDA 插件和远程 MCP 客户端:
```bash
uv run ida-pro-mcp --broker --port 13337
```
在远程机器的 MCP 客户端配置中:
```json
{
"mcpServers": {
"ida-pro-mcp": {
"url": "http://<IDA机器的IP>:13337/mcp"
}
}
}
```
IDB 打开后,在 IDA 中按 Ctrl+Alt+M,连接地址填 `http://<机器IP>:13337`。
> **安全提示**:远程模式下 CORS 会自动放宽为 `*`。建议仅在可信网络中暴露端口,或配合 VPN/SSH 隧道使用。
### 多实例模式
同时分析多个二进制:打开多个 IDA,分别按 Ctrl+Alt+M 连上 Broker。
| 工具 | 说明 |
|------|------|
| `instance_list()` | 列出所有已连接 IDA 实例(`instance_id, name, binary_path, idb_path, base_addr`) |
| `instance_info(instance_id)` | 获取指定实例的详细信息 |
本增强版不再提供"当前活动实例"的隐式状态,也没有 `instance_switch / instance_current`。每次调用业务工具(如 `decompile`、`xrefs_to`、`find_regex` 等)时都**必须**在 `arguments` 里显式带 `instance_id`,由 Broker 精确路由到目标 IDA。这样做是为了避免多个 MCP 客户端共享同一个 Broker 时相互踩隐式状态。
---
## 八、命令行参数
| 参数 | 说明 |
|------|------|
| `--install` | 安装 IDA 插件 + 各 MCP 客户端配置 |
| `--uninstall` | 卸载 IDA 插件 + 各 MCP 客户端配置 |
| `--unsafe` | 启用调试器等不安全工具(`dbg_*`) |
| `--broker` | 启动 Broker HTTP 服务器(0.0.0.0),同时提供 MCP 协议端点和 IDA 注册端点 |
| `--broker-url URL` | 当前 MCP 进程要连的 Broker 地址,默认取环境变量 `IDA_MCP_BROKER_URL`,未设置时为 `http://127.0.0.1:13337` |
| `--no-auto-broker` | 关闭 stdio 模式下的 Broker 自动拉起(仅当 `--broker-url` 指向回环地址时才会触发自动拉起) |
| `--port PORT` | Broker 监听端口,默认 13337 |
| `--config` | 打印当前 MCP 配置 |
Broker 地址也可由环境变量指定:
```bash
IDA_MCP_BROKER_URL=http://127.0.0.1:13337 ida-pro-mcp
```
### 启用调试器工具
```json
{
"mcpServers": {
"ida-pro-mcp": {
"command": "uv",
"args": ["run", "ida-pro-mcp", "--unsafe"]
}
}
}
```
---
## 九、缓存相关工具
| 工具 | 语义 | 错误行为 |
|------|------|---------|
| `find_regex(instance_id, pattern, limit?, offset?, include_xrefs?)` | 正则搜索字符串表,含 xrefs | status != ready 时抛 `-32001` |
| `entity_query(instance_id, kind, name_pattern?, segment?, ...)` | 统一实体查询,kind ∈ `strings / functions / globals / imports` | 同上 |
| `list_funcs(instance_id, name_pattern?, ..., include_xrefs?)` | 函数列表,可带 xrefs | 同上 |
| `list_globals(instance_id, name_pattern?, ...)` | 全局变量列表 | 同上 |
| `imports(instance_id, name_pattern?, module_pattern?, ...)` | 导入表列表 | 同上 |
| `refresh_cache(instance_id)` | 唤醒目标 IDA 的缓存守护线程,立即返回 `{triggered, idb_path}` | 永不抛错 |
| `cache_status(instance_id)` | 查询缓存文件是否存在、`status`、各表计数 | 文件不存在时返回 `{exists: false, status: "missing"}` |
错误码约定:
- `-32001`:缓存未就绪 / 文件缺失
- `-32000`:未提供 `instance_id` 或没有活动 IDA 实例
- `-32602`:参数错误(如 `entity_query.kind` 非法)
- `-32603`:SQLite 查询内部异常
---
## 十、非缓存工具总览
以下工具仍走 IDA API 正常 dispatch,由插件进程通过 `@idasync` 在 IDA 主线程执行。
下列工具均为代码中真实注册的工具名(以仓库 `api_*.py` 中的 `@tool` 定义为准),若有出入请以源码为准。
### 核心查询
- `lookup_funcs(queries)` 按地址或名称获取函数
- `int_convert(inputs)` 十进制 / 十六进制 / 字节 / ASCII / 二进制互转
- `decompile(addr)` / `disasm(addr)` 反编译 / 反汇编
- `xrefs_to(addrs)` / `xref_query(queries)` / `xrefs_to_field(queries)` 交叉引用
- `callees(addrs)` 被调用函数
- `func_profile(queries)` 快速获取函数画像(prolog / 返回 / 基本块摘要等)
### 修改
- `set_comments(items)` 反汇编与伪代码视图同时写注释
- `patch_asm(items)` 汇编级补丁
- `declare_type(decls)` 在 IDB 本地类型库声明 C 类型
- `define_func(items)` / `define_code(items)` / `undefine(items)` 函数 / 代码定义控制
### 内存读取
- `get_bytes(addrs)` / `get_int(queries)` / `get_string(addrs)` / `get_global_value(queries)`
### 栈帧
- `stack_frame(addrs)` / `declare_stack(items)` / `delete_stack(items)`
### 结构体
- `read_struct(queries)` / `search_structs(filter)`
### 高级分析
- `py_eval(code)` 在 IDA 上下文执行任意 Python
- `analyze_function(addr, ...)` 单函数深入分析(反编译 + 汇编 + xrefs + 调用关系 + 基本块 + 常量 + 字符串)
- `analyze_batch(queries)` 批量版 `analyze_function`
- `analyze_component(...)` 以入口为根的组件级分析(调用树 + 数据流摘要)
- `diff_before_after(...)` 前后快照差异分析
- `trace_data_flow(...)` 数据流追踪
### 模式搜索
- `find_bytes(patterns)` 字节模式搜索(支持 `48 8B ?? ??`)
- `insn_query(queries)` 按助记符 / 操作数语义的指令序列查询
- `find(type, targets)` 立即值 / 字符串 / 数据与代码引用统一搜索
### 控制流 / 类型 / 导出 / 图
- `basic_blocks(addrs)`
- `set_type(edits)` / `infer_types(addrs)`
- `export_funcs(addrs, format)` 导出为 `json / c_header / prototypes`
- `callgraph(roots, max_depth)`
### 批量
- `rename(batch)` 函数 / 全局 / 局部 / 栈变量统一批量改名
- `patch(patches)` 批量字节修补
- `put_int(items)` 批量写整数
### 调试器(需 `--unsafe`)
- 控制:`dbg_start / dbg_exit / dbg_continue / dbg_run_to / dbg_step_into / dbg_step_over`
- 断点:`dbg_bps / dbg_add_bp / dbg_delete_bp / dbg_toggle_bp`
- 寄存器:`dbg_regs / dbg_regs_all / dbg_gpregs / dbg_regs_named / dbg_regs_remote / dbg_gpregs_remote / dbg_regs_named_remote`
- 栈 / 内存:`dbg_stacktrace / dbg_read / dbg_write`
---
## 十一、MCP 资源(只读状态)
按 MCP 规范暴露的 `ida://` 资源:
- `ida://idb/metadata` IDB 元数据(路径、架构、基址、哈希)
- `ida://idb/segments` 段与权限
- `ida://idb/entrypoints` 入口点(main / TLS 回调等)
- `ida://cursor` 当前光标 + 所在函数
- `ida://selection` 当前选区
- `ida://types` 本地类型
- `ida://structs` 所有结构 / 联合
- `ida://struct/{name}` 结构字段
- `ida://import/{name}` 按名查导入
- `ida://export/{name}` 按名查导出
- `ida://xrefs/from/{addr}` 从地址出发的交叉引用
---
## 十二、SSE 传输与无头 idalib
本增强版的 `ida-pro-mcp` 主入口默认通过 stdio 连接 MCP 客户端,Broker 模式 (`--broker`) 则直接提供 HTTP 端点(默认 `0.0.0.0:13337`),单端口承载全部功能:
```bash
uv run ida-pro-mcp --broker
```
该端口同时提供:
- `/mcp` — Streamable HTTP(MCP 客户端连接)
- `/sse` — MCP SSE 传输
- `/register`, `/events` 等 — IDA 插件注册与 SSE 通道
无头模式由 `idalib-mcp` 提供(需安装 [`idalib`](https://docs.hex-rays.com/user-guide/idalib))。可以启动时指定一个二进制:
```bash
uv run idalib-mcp --host 127.0.0.1 --port 8745 path/to/executable
```
也可以不带初始文件启动,之后用 `idalib_open(...)` / `idalib_close(...)` 动态打开和关闭数据库:
```bash
uv run idalib-mcp --host 127.0.0.1 --port 8745
```
stdio 客户端可使用:
```bash
uv run idalib-mcp --stdio
```
`idalib-mcp` 是一个 supervisor:每个打开的数据库由独立 idalib worker 进程承载。若请求的 IDB 已经在运行插件的 GUI IDA 中打开,`idalib-mcp` 会优先路由到该 GUI 实例;GUI 实例消失后,下次请求会在可行时回退到无头 worker。需要让回退看到 GUI 中的改动时,请先保存 IDB。
工具可通过当前 MCP 上下文绑定的数据库执行,也可以显式传 `database` 参数指定 session ID、文件名或输入路径:
```bash
uv run idalib-mcp --stdio --max-workers 4
```
每个 worker 都是独立进程、各自加载一整份数据库,默认上限 4(`--max-workers` / `IDA_MCP_MAX_WORKERS`)。历史上会话只在显式 `idalib_close` 时才释放,长期挂着的 worker 会一直占内存;现在可以开启空闲回收:
```bash
uv run idalib-mcp --max-workers 2 --idle-ttl 600 --idle-sweep 30
```
- `--idle-ttl SEC`(环境变量 `IDA_MCP_IDLE_TTL_SEC`,默认 `0` = 关闭):会话空闲超过该秒数后自动 `close_database()` 释放内存。
- `--idle-sweep SEC`(环境变量 `IDA_MCP_IDLE_SWEEP_SEC`,默认 `30`,最小 `1`):回收线程的扫描周期。
- 绑定在活跃上下文上的会话永远不会被回收(需先 `idalib_unbind()`);正在自动分析的会话也会跳过。
```python
idalib_open("/path/to/binary_a.exe", session_id="binary_a")
idalib_open("/path/to/library.dll", session_id="library")
decompile("main", database="binary_a")
xrefs_to("ImportantExport", database="library")
```
需要严格的每传输上下文隔离时启用 `--isolated-contexts`:
```bash
uv run idalib-mcp --isolated-contexts --host 127.0.0.1 --port 8745 path/to/executable
```
`--isolated-contexts` 的语义:
- 每个传输上下文(`/mcp` 的 `Mcp-Session-Id`、`/sse` 的 `session`、stdio 的 `stdio:default`)都有自己独立的 session 绑定。
- 未绑定上下文调用 IDB 依赖工具会直接失败,避免跨 Agent 误操作。
- 多 Agent 想共享同一 session 时,可以传 `database=...` 或通过 `idalib_switch(session_id)` 主动加入。
上下文管理工具:
- `idalib_open(input_path, ...)` 打开并绑定
- `idalib_switch(session_id)` 切换绑定
- `idalib_current()` 查当前绑定
- `idalib_unbind()` 解绑
- `idalib_list()` 列表,带 `is_active / is_current_context / bound_contexts / backend / pid`
worker 控制:
- `--max-workers N`:最大同时打开的数据库 worker 数(`0` 表示无限制,默认 `4`)
- `IDA_MCP_MAX_WORKERS`:`--max-workers` 的环境变量默认值
---
## 十三、提示工程建议
大模型在进制转换、数学计算、混淆代码上容易出错。务必:
- 明确要求使用 `int_convert` 工具做进制转换,不要让模型手算。
- 必要时配合 [math-mcp](https://github.com/EthanHenrickson/math-mcp) 做复杂运算。
- 混淆代码先做预处理再交给 LLM:字符串解密、导入哈希、控制流平坦化、代码加密、反反编译技巧。
- 用 Lumina 或 FLIRT 把开源库、C++ STL 先解掉。
一个适用于 crackme 场景的最小提示:
```md
你的任务是在 IDA Pro 中分析一个 crackme。你可以使用 MCP 工具获取信息。总体策略:
- 先用 decompile / disasm 审阅反编译与汇编
- 对可疑代码加注释,然后把变量、参数、函数重命名为具有描述性的名字
- 必要时修正类型(尤其是指针、数组)
- 绝对不要自己做进制转换,一律用 int_convert
- 不要暴力破解,只从反汇编和简单 python 脚本中推导结论
- 分析完成后写一份 report.md,最后把找到的密码交给用户确认
```
---
## 十四、常见问题
**Q:IDA 插件连接失败 / `instance_list` 空?**
1. 先单独启动 Broker:`uv run ida-pro-mcp --broker`(保持运行)
2. 再启动 Cursor / Claude / VS Code 等
3. 在 IDA 里按 Ctrl+Alt+M 连接
4. 如端口冲突:`ida-pro-mcp --broker --port 13338`,并确保 IDA 插件与 MCP 客户端的 `broker-url` 一致
**Q:调用 `find_regex / list_funcs` 等返回 `-32001`?**
说明本地 `.mcp.sqlite` 还没写好,属于正常初始化期。可以:
- 调用 `cache_status(instance_id=...)` 查看 `status` 与各表计数。
- 调用 `refresh_cache(instance_id=...)` 主动唤醒缓存守护线程。
- 稍等片刻后重试。
**Q:缓存文件在哪?能删吗?**
就在 IDB 旁边:`<idb 路径>.mcp.sqlite`(和 `.mcp.sqlite-wal / -shm` 同目录)。随时可删;下次 IDA idle 时会重建。
**Q:`uv pip install -e .` 提示 "Failed to clone files; falling back to full copy"?**
这只是 uv 的 warning(reflink 跨卷失败),构建其实成功。本项目 `pyproject.toml` 已内置 `[tool.uv] link-mode = "copy"` 消除该提示。
**Q:支持 IDA Free 吗?**
不支持,IDA Free 没有插件 API。
**Q:按 G 键跳转失败?**
请更新到最新版本后重启 IDA:
```bash
uv pip install -e .
```
**Q:IDA 里报 `[MCP] HTTP POST 失败 http://127.0.0.1:13337/register: <urlopen error [WinError 10061]>`?**
10061 = 连接被拒绝,即**本机此刻没有 Broker 在监听**,属于预期提示(不是插件故障):
- 启动 MCP 客户端(DSH / Cursor / Claude…)后它会自动拉起 Broker,插件会指数退避自动重连;
- 或者手动常开一个:`ida-pro-mcp --broker`;
- 确认是否在跑:`Get-NetTCPConnection -LocalPort 13337 -State Listen`(日志见 `~/.ida-pro-mcp/broker-<port>.log`)。
**Q:缓存报 `status=error, reason=Function can be called from the main thread only`?**
这是 2.1.0 之前的插件在 IDB 装载早期用 `is_idaq()` 误判"是否需要派发到主线程"导致的。
先确认 IDA 加载的是新版(Output 首行 `[MCP] 插件代码: ... (缓存 schema v2)`),否则按"三、安装 → 部署 / 更新 IDA 插件"重新铺一次。
**Q:改了仓库代码,但 IDA 行为没变?**
插件是**拷贝**部署到 `%APPDATA%\Hex-Rays\IDA Pro\plugins` 的,重新部署并重启 IDA 才会生效(同理,`schema v2` 这行是判断依据)。
---
## 十五、大库使用建议(内存与性能)
> **2.1.0 起,本节描述的实现已重写**:缓存构建改为分块流式 + 影子表原子切换 +
> 表级指纹增量,峰值内存从 O(全库) 降到 O(块)。下面的"机制"描述的是当前实现;
> 历史实现(整库物化 + 单事务全量重写)的问题见 [CHANGELOG.md](./CHANGELOG.md)。
**当前实现的内存模型**
- 提取在 `broker/cache_extract.py` 里按游标切片:每块经一次
`execute_sync(..., MFF_READ)` 派发到 IDA 主线程,块间让出消息循环,
因此 **GUI 不会长时间卡死**;块大小按实测耗时自适应(默认目标 150 ms/块)。
- 写入在 `broker/cache_writer.py` 里进影子表 `<table>__new`,最后一次事务内
`DROP` → `RENAME` → 建索引原子切换:读者要么看到旧快照、要么看到新快照,
**不存在"表被清空"的窗口**。
- 每个表组会先做一遍"只哈希不建对象"的指纹(`shape` 默认 / `full` 可选),
指纹未变则整组跳过写库。
- 触发时机仍是三个:插件连接 Broker 后首次 IDA idle、**每次保存 IDB**
(`IDB_Hooks.savebase()`)、30 分钟兜底轮询(IDB `mtime` 未变则跳过)。
守护线程本身在**打开 IDB 时**就会启动(`IDB_Hooks.loaded`),关闭库时停止,
与 Broker 连接无关 —— 所以默认用法下你什么都不用做。
- 缓存文件写在 IDB 旁边(`<xxx.i64>.mcp.sqlite` 及 `-wal` / `-shm`);
构建结束会做一次 `wal_checkpoint(TRUNCATE)`,WAL 不会长期留着。
**配置项(环境变量)**
| 变量 | 默认 | 说明 |
|------|------|------|
| `IDA_MCP_DISABLE_CACHE` | `0` | 设为 `1` 彻底关闭缓存守护线程(不建库、不提取);7 个缓存工具会返回 `-32001` |
| `IDA_MCP_CACHE_SCOPE` | `full` | `minimal` 只建 strings / functions / imports(不采集交叉引用与全局变量,更快更省内存)。scope 收窄时会清空范围外的表,避免返回过期交叉引用 |
| `IDA_MCP_CACHE_CHUNK_ROWS` | `20000` | 每块行数上限;峰值内存 ≈ 单块大小 |
| `IDA_MCP_CACHE_TARGET_CHUNK_MS` | `150` | 每块目标耗时,用于自适应调整块大小(越小越不卡 UI) |
| `IDA_MCP_CACHE_MAX_ROWS` | `0` | 单表行数上限;超限则放弃该表本轮刷新(保留旧快照)并标记 `partial` |
| `IDA_MCP_CACHE_MAX_RSS_MB` | `0` | 进程 RSS 上限;超限则停止本轮刷新(保留旧快照)并降级为 `degraded` |
| `IDA_MCP_CACHE_INCREMENTAL` | `1` | `0` = 关闭表级指纹增量,强制全量重建 |
| `IDA_MCP_CACHE_FINGERPRINT` | `shape` | `full` 会把字符串文本一起纳入指纹(更精确,建立指纹更慢) |
**建议**
1. **大库优先用 `minimal` 范围 + 关掉增量以外的默认值**:
`IDA_MCP_CACHE_SCOPE=minimal` 能直接砍掉最占空间的交叉引用表;
需要交叉引用时再切回 `full`(切换会自动重建)。
2. **内存吃紧就给护栏**:`IDA_MCP_CACHE_MAX_RSS_MB=2048`,
超限时本轮刷新会放弃并保留旧快照,而不是把 IDA 拖爆。
3. **不想建缓存就用开关,不要再用"把 .mcp.sqlite 变成目录"的偏方**:
设置 `IDA_MCP_DISABLE_CACHE=1` 即可,语义清晰且可观测。
4. **无头模式**(`idalib-mcp`)不启动缓存守护线程,内存主要取决于并发 worker 数
(默认 4,见 `--max-workers` / `IDA_MCP_MAX_WORKERS`):单库分析建议设为 1,
并开启空闲回收(`--idle-ttl 600 --idle-sweep 30`,见"十二、SSE 传输与无头 idalib")。
5. **让模型优先用分页工具**:7 个缓存工具都支持 `LIMIT/OFFSET`;
避免用 `py_eval` 在 IDA 里遍历全库,也不要一次性索取"全部函数 / 全部字符串"。
**排障与基准**
- `cache_status` 现在会回报 `progress`(阶段/表/已处理行/耗时/峰值 RSS)、
`partial`、`last_error`、`degraded_reason`、`tables_skipped`、`counts_source`
与 `schema_version`。`status=partial` 表示"还没有可用快照",
`status=ready` + `partial=1` 表示"有旧快照可用,但本轮刷新有问题"。
- 基准(不需要 IDA,可在 CI 里当门禁):
```bash
python -m ida_pro_mcp.benchmark --rows 200000 --legacy
python -m ida_pro_mcp.benchmark --rows 200000 --assert-peak-mb 64
```
输出会给出"分块(新)"与"全量物化(旧)"的 Python 峰值内存、耗时与查询延迟,
并说明 RSS 列是进程级读数(权威指标是 `tracemalloc` 峰值)。
---
## 十六、开发
核心实现位置:
- `src/ida_pro_mcp/server.py` 主 MCP 服务端入口(stdio / broker 双模式分发)
- `src/ida_pro_mcp/idalib_server.py` idalib 无头服务端
- `src/ida_pro_mcp/ida_mcp.py` IDA 插件入口与 `handle_mcp_request`
- `src/ida_pro_mcp/ida_mcp/api_*.py` 所有业务工具与资源(纯 IDA 侧)
- `src/ida_pro_mcp/broker/server.py` Broker HTTP + 注册表 + SSE
- `src/ida_pro_mcp/broker/manager.py` `dispatch_proxy` 路由 + 虚拟工具注入
- `src/ida_pro_mcp/broker/sqlite_cache.py` 插件侧 idle 守护 + 写入
- `src/ida_pro_mcp/broker/sqlite_query.py` 插件侧只读查询(强类型)
- `src/ida_pro_mcp/broker/cache_handlers.py` `tools/call` 的本地缓存拦截
- `src/ida_pro_mcp/broker/cache_types.py` 全部协议 `TypedDict`(`JsonRpcRequest / Response / Error / ToolSchema / *Args / *Result`)
- `src/ida_pro_mcp/broker/cache_config.py` 缓存构建配置与环境变量解析(纯逻辑)
- `src/ida_pro_mcp/broker/cache_extract.py` 游标式分块提取 + 表级指纹(无 IDA 依赖,可假后端单测)
- `src/ida_pro_mcp/broker/cache_backend.py` IDAPython 后端适配器与主线程派发(`run_on_ida_main`)
- `src/ida_pro_mcp/broker/cache_writer.py` 分块写入 + 影子表原子切换 + meta/进度
- `src/ida_pro_mcp/broker/cache_rss.py` 零依赖 RSS 读数(内存护栏用)
- `src/ida_pro_mcp/benchmark.py` 缓存层基准(峰值内存/耗时/查询延迟,可作 CI 门禁)
新增工具只需:
1. 在对应的 `api_*.py` 里写一个 `@tool` + `@idasync` 函数,带完整 Python 类型注解;
2. 用 `Annotated[...]` 写参数说明,函数 docstring 就是暴露给模型的 tool description;
3. MCP 服务端会自动扫描 `api_*.py` 并注册,无需手动改 schema。
运行测试:
```bash
uv run ida-mcp-test tests/crackme03.elf -q
uv run ida-mcp-test tests/typed_fixture.elf -q
```
MCP inspector 调试:
```bash
uv run mcp dev src/ida_pro_mcp/server.py
```
覆盖率:
```bash
uv run coverage erase
uv run coverage run -m ida_pro_mcp.test tests/crackme03.elf -q
uv run coverage run --append -m ida_pro_mcp.test tests/typed_fixture.elf -q
uv run coverage report --show-missing
```
不需要 IDA 的回归测试(CI 也是跑这一套):
```bash
cd tests && python -m unittest discover -s . -p "test_*.py" -v
python -m ida_pro_mcp.benchmark --rows 200000 --legacy --assert-peak-mb 64
```
真 IDA(idalib)端到端集成测试,需要 `IDADIR` 指向 IDA 安装目录,否则自动跳过:
```bash
set IDADIR=D:\IDA
cd tests && python -m unittest test_cache_idalib -v
```
---
## 十七、与其它 IDA MCP 的差异
市面上已有数个 IDA Pro MCP 实现,本仓库在上游 [mrexodia/ida-pro-mcp](https://github.com/mrexodia/ida-pro-mcp) 的基础上重点做了两件事:
- 把 Broker 做成纯路由,解决多客户端并发问题;
- 在客户端侧引入 SQLite 静态缓存,把高频只读查询从 IDA 主线程移走,让大模型在大规模分析场景下不再因 IDA API 往返而被拖慢。
其他实现(便于对比选型):
- https://github.com/mrexodia/ida-pro-mcp 上游
- https://github.com/QiuChenly/ida-pro-mcp-enhancement 上游增强版(本仓库的直接来源,Broker 与 SQLite 缓存即出自这里)
- https://github.com/taida957789/ida-mcp-server-plugin 仅 SSE,IDAPython 装依赖
- https://github.com/fdrechsler/mcp-server-idapro TypeScript,新增功能需大量样板
- https://github.com/MxIris-Reverse-Engineering/ida-mcp-server 自定义 socket,样板重
欢迎 PR 补充。
---
## 十八、许可证
见 [LICENSE](./LICENSE)。
---
## 署名
- **本仓库维护者**:[Moer2831](https://github.com/Moer2831)([Moer2831/ida-pro-mcp](https://github.com/Moer2831/ida-pro-mcp))
- **上游增强版作者**:[QiuChenly](https://github.com/QiuChenly)([ida-pro-mcp-enhancement](https://github.com/QiuChenly/ida-pro-mcp-enhancement))
- **上游原作者**:[mrexodia](https://github.com/mrexodia)([ida-pro-mcp](https://github.com/mrexodia/ida-pro-mcp))
本项目源自上游 `ida-pro-mcp`,由 QiuChenly 增强实现 Broker 路由架构、SQLite 静态缓存接管与严格类型协议等特性,本仓库在其基础上继续维护并新增 stdio 端自动拉起 Broker 等改动。如在论文、博客或工具中使用,请同时署名上游作者、上游增强版作者与本仓库维护者。
TDQS
Scored across 66 tools
Several tools appear to duplicate each other: instance_list vs list_instances both list IDA instances, imports vs imports_query, list_funcs vs func_query, and xrefs_to vs xref_query cover overlapping ground. Analysis tools (analyze_function, analyze_component, analyze_batch, func_profile, survey_binary) also overlap heavily, making selection between them uncertain.
The conventions are mixed: some tools are verb_noun (list_funcs, get_bytes, find_regex), others noun_verb (xrefs_to), others bare nouns (decompile, disasm, patch, rename, callees). The most glaring flaw is instance_list vs list_instances, the same concept with inverted word order.
At 66 tools this is far above a comfortable surface, and a meaningful subset are redundant query variants (imports_query, func_query, xref_query, entity_query) or near-duplicate instance/signature helpers. The domain is broad but the count is inflated by overlapping tools rather than distinct capabilities.
The surface is remarkably thorough for binary reverse engineering: decompilation, disassembly, xrefs, callgraphs, read/write/patch of memory and code, stack frames, type declaration, signatures, comments, renaming, and cache management are all present. Only minor lifecycle gaps (e.g. segment-level operations, richer diffing) seem missing.