Repository navigation
Releases
v2.0.10 - broker 缓存层:修 entity_query 崩溃,扁平参数支持 kind
报障
{"instance_id":"ida-25956","name_pattern":"??_7*@ModifierDefinition@*","limit":10}Error: SQLite 缓存查询失败: dictionary update sequence element #0 has length 5; 2 is required
这里其实是两个独立的错误叠在一起。
一、dict(payload) 崩溃
cache_handlers._wrap_ok 用 dict(payload) 统一包装所有缓存工具返回值。entity_query 的每页有 5 个 key:
{"kind", "data", "next_offset", "total", "error"}dict() 会把它当成键值对序列去迭代,5 个 key 拆不成 2 元组 → ValueError。所以 entity_query 只要命中缓存就 100% 失败。
list_funcs 之所以没炸:它的页只有 2 个 key(data, next_offset),刚好能凑成一对,于是 dict() 静默成功并产出垃圾 {"data": "next_offset"}。两处都错,只是后者没崩出来。
修复:list payload 包成 {"result": [...]},与 _mcp_tools_call 对非 dict 结果的既有约定一致。
二、扁平参数查不到 globals
entity_query 的 kind / regex / segment 原本只能写在 queries 里:
- 扁平写法(
name_pattern+limit)永远只能查 functions 表 - 顶层传
kind会被当成未知参数拒绝
想查 globals 下的 vtable 符号,必须自己拼 queries 对象——而报障的调用正是扁平写法。
修复:
_ENTITY_QUERY_ARGS允许kind/regex/segment/include_xrefs_queries_from_args把这些扁平字段灌进 query(嵌套queries优先)- 其他工具(
list_funcs等)仍然拒绝kind,避免语义混淆 include_xrefs只在显式传参时才写入,否则None会被当成False
验证(真实 GameCore IDB):
name_pattern + kind=globals, ??_7*ModifierDefinition* -> 4 条
queries 形式 -> 3 条
两种写法结果一致。
三、顺带说明:你这个 pattern 本来就查不到东西
即使修好上面两点,??_7*@ModifierDefinition@* 也是 0 结果——没有 @ModifierDefinition@ 这个符号。真实的 vtable 是:
??_7SimpleModifierDefinition@GameEffects@@6B@
??_7?$_Ref_count@VSimpleModifierDefinition@GameEffects@@@std@@6B@
而且它们在 globals 表,不在 functions 表。用 ??_7*ModifierDefinition* + kind=globals 能查到 4 条。
四、broker 层此前没有任何测试
新增 tests/test_broker_cache_handlers.py(13 个用例)。已确认它们真的能抓到这两个 bug——把修复回滚后分别红 5 个和 2 个:
_wrap_ok 回滚成 dict(payload) -> 5 failed
删除扁平 kind 传播 -> 2 failed
测试
| 套件 | 结果 |
|---|---|
| host pytest | 181 passed, 1511 subtests |
| crackme03.elf | 361 passed, 1 skipped |
| typed_fixture.elf | 336 passed, 1 skipped |
升级
WHL=$(curl -sL https://api.github.com/repos/Sumcam-L/ida-pro-mcp-enhanced/releases/latest \
| grep -o 'https://[^"]*\.whl' | head -1)
pip install "$WHL"
ida-mcp-setup装完重启 IDA。
v2.0.9 - 修复 xrefs_to 截断丢数据
报障
同一 xrefs_to 查询三次分别返回 100 / 100 / 10 条。
根因一:预览层把每个 list 砍到固定 10 条(数据丢失)
rpc.py 的输出上限补丁在响应超过 50000 字符时,把 structuredContent 换成 _truncate_value() 的结果,而它对任何 list 都只保留前 10 项:
return [_truncate_value(item) for item in value[:OUTPUT_LIMIT_PREVIEW_ITEMS]]structuredContent 是权威数据、content 才是给人看的,于是客户端读到的是“只有 10 个 xref”。更糟的是截断标记只在 content[1],读 structuredContent 的调用方完全不知道自己看的是残缺数据。
实测(真实 IDA 实例):
| 传入 addrs | 字符数 | 保留条目 |
|---|---|---|
| 200 | 29317 | 200 |
| 350 | 超 50000 | 10 |
| 700 | 超 50000 | 10 |
改成按字符预算截断后:
| 传入 addrs | 保留条目 | 字符数 |
|---|---|---|
| 200 | 200 | 18601 |
| 350 | 350 | 32551 |
| 700 | 430 | 39991 |
| 2000 | 430 | 39991(附 download_hint) |
- 每个元素至少留 256 字符,避免长列表把单条记录切碎
- dict 按原尺寸比例分配,绝不丢 key(
total/more/error必须保留) - 嵌套超过 4 层改用均分,避免深树反复整树序列化
根因二:截断时没有机器可读标记
_build_download_meta 早就生成了 download_hint,却只塞进 content[1]。现在同时写 _meta.ida_mcp(total_chars / preview_chars / download_url)。
根因三:缓存层把 list payload 转成垃圾
dict([{'data': [...], 'next_offset': 2}]) -> {'data': 'next_offset'}
dict([{'a': 1}, {'a': 2}]) -> ValueError
list_funcs / list_globals / entity_query 走缓存时 structuredContent 一直是坏的。改成 {"result": [...]},与 _mcp_tools_call 对非 dict 结果的既有约定一致。
根因四:limit <= 0 被解释成上限而不是默认值
xrefs_to docstring 写 default 100 / max 1000,代码却是:
if limit <= 0 or limit > 1000:
limit = 1000传 0 得到 1000 条。callees / find_bytes / find 同样写法。统一改为 limit <= 0 → 文档里的默认值,再单独做上限截断。
测试基建
limit_response_size从闭包里的patched()提出来,成为可直接测试的函数;测试改为调用生产函数而不是复制一份逻辑(旧测试复制了补丁,rpc.py回归也照样绿)- 新增 6 个回归测试:保留条数、顺序、可重复性、dict 标量不被丢、深层嵌套不丢兄弟节点、
limit=0语义
测试
| 套件 | 结果 |
|---|---|
| host pytest | 168 passed, 1511 subtests |
| crackme03.elf | 361 passed, 1 skipped |
| typed_fixture.elf | 336 passed, 1 skipped |
升级
WHL=$(curl -sL https://api.github.com/repos/Sumcam-L/ida-pro-mcp-enhanced/releases/latest \
| grep -o 'https://[^"]*\.whl' | head -1)
pip install "$WHL"
ida-mcp-setup装完重启 IDA —— 插件模块只在启动时 import。
v2.0.8 - 修复模板类型写入与 schema 退化
这个版本修了什么
1. 模板实例化类型写不进 IDB,还会卡死 IDA
declare_type 的模板分支用 tinfo_t.parse() 解析成员类型,而 parse() 对任何含 <...> 的名字必定失败(同一个名字 get_named_type 能拿到 size=16,parse 却返回 empty)。
更糟的是没带 PT_SIL,失败时 IDA 侧可能弹模态框阻塞消息泵,插件线程卡死 —— 现场症状是 list_funcs 正常、type_inspect 全部超时,只能重启 IDA。
修复:
_resolve_base_type()对含<...>的名字改走get_named_type();先剥尾部*再create_ptr()还原,所以T *也能用于模板名- 普通拼写继续走
parse(),补上PT_SIL,永不弹框 _decl_needs_manual_udt()+_build_udt_by_hand():解析器失败且成员含<...>时,手工create_udt+set_named_typechar pad[8]的维度从名字剥出,改用create_array()重建
验证:GameEffects::Modifiers::DynamicModifier(47 成员 / 544 字节)一次写入成功,每个偏移对得上 ctor 反编译与 getter 反汇编:
0x000 __vftable 0x148 m_Owner (shared_ptr, 16B)
0x048 autoArchive[144] 0x168 m_Definition (16B)
0x0e8 m_id (int) 0x1b8 m_Effect (16B) <- ApplyEffect 的 [rbp+1B8h]
0x100 m_IsActivated 0x1c8 m_OwnerReqSet (16B) <- GetOwnerRequirementSet
0x118 m_WasActivated 0x1e0 m_StateString[32]
0x130 m_IsDisabled 0x210 Handle
2. 工具参数对模型退化成 unknown
参数写成 list[T] | T 会生成 anyOf,而客户端 schema 子集只认 type/oneOf/properties/required/additionalProperties/items/enum/const。一个 anyOf 就让整个参数对象变成 unknown,模型看不到参数名只能猜 —— 表现就是 search_structs(filter=..., limit=...)、type_inspect(name=...) 这类调用反复报 unexpected parameters。
本仓库 53 个工具是这种形状。改成 oneOf 后 87 个工具全部可渲染,unknown 从 55 降到 0。
3. 缓存工具返回形状不一致
缓存命中只给 content[0].text,原生路径给 structuredContent。现在两条路径一致。
4. type_inspect 标记空壳类型
PDB 空壳类表现为 exists: true + size: 1 + member_count: 0,和真实类型难以区分。新增 is_placeholder 字段。
5. idb_save 归入 --unsafe
idb_save / idalib_save 会直接写盘,现在需要 --unsafe。
注意:如果你的 broker 启动时没带
--unsafe,升级后idb_save会从工具列表消失。
6. 测试基建
- 新增
tests/test_schema_subset_compat.py:完整实现客户端 schema 子集校验,锁死anyOf不得回归 - 修 6 个因包名迁移早已无法收集的测试文件(引用已不存在的
src/ida_pro_mcp) - 新增 3 个模板成员/指针/数组回归测试
测试
| 套件 | 结果 |
|---|---|
| host pytest | 162 passed, 114 subtests |
| crackme03.elf | 361 passed, 1 skipped |
| typed_fixture.elf | 336 passed, 1 skipped |
升级
pip install https://github.com/Sumcam-L/ida-pro-mcp-enhanced/releases/download/v2.0.8/ida_pro_mcp_enhanced-2.0.8-py3-none-any.whl
ida-mcp-setup装完重启 IDA(或 Ctrl+Alt+M 重连)—— 插件模块只在启动时 import,热替换文件不会让已加载的模块重新执行。
v2.0.7 - 缓存层补参数校验
这个版本修了什么
缓存层对未知参数不再静默忽略。
现象:list_funcs 传 {"filter": "*TerrainElement*", "limit": "0:80"} 会原样返回全表前 N 条,filter 不生效、也不报错。
根因:缓存层插在 MCP dispatcher 之前,命中缓存工具就直接本地 SQLite 响应,绕过了 registry.dispatch 那套 unexpected parameters 校验。而 _queries_from_args() 只认 queries 和 name_pattern/offset/count/limit 这几个旧别名,filter 直接被丢弃 → 空查询 → 返回全表。
现在会明确报错并给出正确写法:
list_funcs: unrecognized parameter(s) ['filter'].
Hint - filter: wrap it: "queries": {"filter": ...} (or pass "queries": "*Pattern*")
Accepted parameters: ['count', 'instance_id', 'limit', 'name_pattern', 'offset', 'queries']
同时拦住 limit/count/offset 收到 "0:80" 这种分页简写的情况(那属于 queries)。
正确写法:
{"instance_id": "...", "queries": {"filter": "*TerrainElement*", "count": 20}}安装
pip install https://github.com/Sumcam-L/ida-pro-mcp-enhanced/releases/download/v2.0.7/ida_pro_mcp_enhanced-2.0.7-py3-none-any.whl
ida-mcp-setup装完在 IDA 里按 Ctrl+Alt+M 重连。
v2.0.6 - search_text pattern 语义提示
这个版本改了什么
search_text 的 pattern 语义提示(只改提示,不动匹配逻辑)。
现象:AI 传 pattern="TerrainElement|AssetCooker|TerrainStyle" 却没开 regex,拿到 n: 0 就判定「这些符号不存在」。
实际是默认按字面量子串匹配,竖线也被当成普通字符——搜的是那个 8 字符的整串,0 条完全正常。这是假阴性,比慢查询危险得多。
改动:
pattern/regex参数说明写清:要|表示 OR 必须传regex=true- docstring 增加 PATTERN SEMANTICS 段落,明确「0 命中不等于符号不存在」
- 新增
_regex_literal_hint():字面量 0 命中且 pattern 含强正则信号时,返回note解释
强信号只收竖线、反斜杠、锚点、.*、.+、(?:、[A-Z] 这类组合;不收裸的点号、星号、圆括号、方括号——Foo.Bar、operator() 这类正常搜索未命中时不会刷无用提示。
安装
pip install https://github.com/Sumcam-L/ida-pro-mcp-enhanced/releases/download/v2.0.6/ida_pro_mcp_enhanced-2.0.6-py3-none-any.whl
ida-mcp-setup装完在 IDA 里按 Ctrl+Alt+M 重连。
v2.0.5 — 缓存层 filter 语义对齐原生实现
这个版本修了什么
缓存层的 filter 语义和原生实现不一致,同一个 filter 走不同路径结果不同。
缓存接管的 7 个工具(find_regex / entity_query / list_funcs / list_globals / imports / imports_query / refresh_cache)另写了一套 filter→SQL 翻译,有三处偏差:
| pattern | 原生 | 缓存(修复前) | 原因 |
|---|---|---|---|
/create/ |
3 | 1 | 原生 flags or re.IGNORECASE,无 flag 时默认忽略大小写 |
/CREATE/ |
3 | 0 | 同上 |
[abc] |
1 | 0 | 缓存多判了 [,把字面量当字符类 |
a[bc]d |
1 | 0 | 同上 |
-1:10 |
filter | 分页 offset=-1 | 正则写成 (-?\d+) |
修复后 20 项 filter 对比 + 8 项分页对比全部一致。
安装
pip install https://github.com/Sumcam-L/ida-pro-mcp-enhanced/releases/download/v2.0.5/ida_pro_mcp_enhanced-2.0.5-py3-none-any.whl
ida-mcp-setup装完在 IDA 里按 Ctrl+Alt+M 重连(会连同 broker 包一起重新加载)。
v2.0.4 — 批量接口拼错键名不再静默失败
这个版本修了什么
批量接口拼错键名时不再静默返回 total: 0。
rename 一直返回 total: 0 的真实原因是 batch 键名拼错(写成 functions / locals 复数),而旧实现逐个 batch.get(key) 取值——拼错不报错,只是取不到东西。
现在会在解析前校验键名,不合法直接报错并回显合法键列表:
rename: unknown batch key(s) ['functions']; supported keys: ['allow_overwrite', 'data', 'dry_run', 'func', 'global', 'globals', 'local', 'stack', 'stop_on_error']
覆盖 rename 与 type_apply_batch 两个批量接口。
安装
pip install https://github.com/Sumcam-L/ida-pro-mcp-enhanced/releases/download/v2.0.4/ida_pro_mcp_enhanced-2.0.4-py3-none-any.whl
ida-mcp-setup装完在 IDA 里按 Ctrl+Alt+M 重连(或重启 IDA)即可生效。
v2.0.3 — 移除永远返回空的 list_instances
这个版本修了什么
list_instances 永远返回空数组。
它是 mrexodia 原始「每个 IDA 插件各开一个 HTTP 端口」时代的工具:内部调用 discover_instances() 扫本机端口。当前是 broker 纯路由架构,IDA 插件不再监听任何端口,只作为 SSE 客户端挂在 broker 上 → 扫不到东西 → 永远返回 []。
但它的对外语义是「列出所有 IDA 实例」,所以调用方会以为真的没有实例。
修复:从工具目录里剔除 list_instances / select_instance。实例列表请用 broker 侧的 instance_list / instance_info。
工具总数 66 → 64。
安装
pip install https://github.com/Sumcam-L/ida-pro-mcp-enhanced/releases/download/v2.0.3/ida_pro_mcp_enhanced-2.0.3-py3-none-any.whl
ida-mcp-setup装完重启 IDA 和 DSH。
详见 PATCHES.md。
v2.0.2 — 修复 broker 被误杀导致的间歇性断连
这个版本修了什么
间歇性断连:IDA 开着,用着用着突然连不上,过一会儿又自己好了。
根因是 broker 生命周期判断错了对象 —— state[clients] 只记录 MCP 客户端进程,不记录 IDA 实例。任何导致 launcher 退出的情况(DSH 重载插件、工具超时重连、DSH 重启)都会让 broker 被 SIGTERM,挂在上面的 IDA 全部掉线。
日志实测:broker 启动 222 次、IDA 连接 484 次、断开 334 次。
修复后:broker 上只要还有 IDA 实例就不许关;无客户端认领时打 orphaned 标记,交给 15 分钟空闲清理。MCP 客户端断开不再影响已连接的 IDA,DSH 重连直接复用现有 broker,没有空窗期。
安装
pip install https://github.com/Sumcam-L/ida-pro-mcp-enhanced/releases/download/v2.0.2/ida_pro_mcp_enhanced-2.0.2-py3-none-any.whl
ida-mcp-setup装完重启 IDA 和 DSH。
详见 PATCHES.md。
v2.0.1 — 可直接安装的 IDA Pro MCP
安装
pip install https://github.com/Sumcam-L/ida-pro-mcp-enhanced/releases/download/v2.0.1/ida_pro_mcp_enhanced-2.0.1-py3-none-any.whl
ida-mcp-setup这个版本修了什么
| 问题 | 上游 | 本版 |
|---|---|---|
search_text 搜不存在的 token |
~235 秒,broker 超时后 IDA 主线程仍在跑 | ~15 秒,带预算返回 partial 游标 |
search_text 搜常见符号 |
~70 秒 | ~15 秒 |
survey_binary 扫十万函数大库 |
主线程卡几十分钟且无法取消 | < 1 秒拒答,建议改用窄工具 |
| 工具超时 | sys.setprofile 递归 / PyThreadState_SetAsyncExc 锁死主线程 |
只有调用方放弃等待,主线程安全跑完 |
| 安装 | 需手工处理 site-packages 与插件目录两份副本 | pip install + ida-mcp-setup |
包名说明
PyPI 上的 ida-pro-mcp 是上游 mrexodia 的包(1.4.0,无 broker 架构),本发行版用不到那个名字。
- 发行包:
ida-pro-mcp-enhanced - Python 模块:
ida_pro_mcp_enhanced(与上游区分,避免覆盖) - DSH 插件:
dsh-ida-mcp(npm)
完整改动说明见 PATCHES.md。