ida-mcp-rs如何安全编辑二进制:rename、注释、类型与patch的精确目标指南
ida-mcp-rs如何安全编辑二进制:rename、注释、类型与patch的精确目标指南
ida-mcp-rs 是一款 Headless IDA Pro MCP Server,让 AI 代理能够无头驱动 IDA 完成逆向工程。当代理开始"动刀"——用 rename 改名符号、set_comments 加注释、apply_types 应用类型、patch 修改字节时,最大的风险就是改错目标。本文带你完整理解它的"精确目标(exact-target)"机制,让每一次二进制编辑都精确落在你指定的那一个地址上,绝不"差不多就行"。
🎯 核心原理:每个编辑操作都必须命中"精确目标"
ida-mcp-rs 中所有会修改数据库的工具(rename、set_comments、patch、patch_asm、apply_types、declare_stack、delete_stack、lumina_apply 等)都遵循同一条铁律:
只接受"一个地址"或"一个与符号名完全一致(区分大小写)的名字",且两者只能二选一。
这套逻辑集中在 target.rs 的 resolve_mutation_target 函数中实现。它的设计哲学写在源码注释里:
发现类工具可以"模糊"地解析名字,但修改类工具绝对不行。名字必须逐字符、区分大小写地匹配,否则宁可失败,也绝不替你"猜一个最像的"。
三种目标方式的正确姿势
| 传参方式 | 示例 | 说明 |
|---|---|---|
| 只传地址 | address: "0x100000f00" |
最直接,永不歧义 |
| 只传精确名称 | target_name: "_main" |
必须与数据库中符号逐字符一致(Mach-O 的符号常带下划线前缀,别漏掉!) |
| 名称 + 偏移 | target_name: "main", offset: 8 |
先解析名称得到基准地址,再加偏移;结果中 base 与 address 分开记录 |
任何一次成功的编辑都会返回一条 target 记录,包含:数据库路径、解析出的符号(无名地址为 null)、基准地址(base)、你请求的地址(requested_address)和实际生效地址(address)。这是你核对"改对了没有"的唯一依据。
🧩 四类编辑工具逐个拆解
1️⃣ rename:给符号换一个新名字
rename 支持用地址或 current_name(当前精确名称)定位目标,实现在 annotations.rs。
rename(current_name: "interesting_function", name: "domain_check")
返回值中 target.symbol 记录的是改名前的旧名字,方便你回溯。
改伪代码里的局部变量请用 rename_lvar:先 list_lvars 拿到不透明的 lvar_locator,再传给 rename_lvar(lvars.rs)。为什么不能直接用 v1、v2 这类显示名?因为函数原型一变,这些名字可能漂到别的变量上;而 locator 失效或歧义时会直接报错、不做任何修改——宁可失败,不动错变量。
2️⃣ 注释:set_comments 与 set_pseudocode_comment
两者作用面不同,别用错:
set_comments:写反汇编视图的注释(annotations.rs)。空字符串即清除注释;repeatable: true设为可重复注释。set_pseudocode_comment:写 Hex-Rays 伪代码行尾注释,用list_pseudocode_comments返回的comment_locator精确定位某一行。过期的 locator 会失败而不改动,重新分析后需重新列出。
list_pseudocode_comments(target_name: "interesting_function")
set_pseudocode_comment(target_name: "interesting_function",
comment_locator: "<来自上一步的locator>", comment: "validated input")
3️⃣ 类型:apply_types、declare_stack 与 set_lvar_type
类型编辑同样走精确目标(types.rs):
apply_types:把命名类型或 C 声明应用到某个地址/符号;配合stack_offset/stack_name可应用到栈变量。declare_stack/delete_stack:声明/删除函数帧中的栈变量,注意这里的offset是栈偏移(局部变量为负),不是目标偏移,语义完全不同。set_lvar_type:给反编译局部变量设类型,定位方式与rename_lvar相同(locator 优先)。
所有类型编辑失败时(类型非法、声明解析不过)都会直接报错,不会留下半吊子状态。
4️⃣ patch:真正的"动字节"
patch 写入原始字节,patch_asm 传入汇编语句由 IDA 现场汇编(memory.rs):
patch(address: "0x100000f04", bytes: "1f 20 03 d5") # 写入 ARM64 NOP
patch_asm(name: "interesting_function", line: "nop") # 汇编后写入
改完之后用 list_patches 审计:它只读不改,把全部补丁按连续区间合并返回,相当于你数据库的"修改 diff"。
⚠️ 特别提醒:patch 修改的是 IDB 数据库中的补丁字节,原文件不会被动。补丁同样要 save_idb 才会落盘。
🛡️ 四重安全网:为什么可以放心让 AI"动刀"
第一重:拒绝模糊匹配。 名称匹配必须逐字符、区分大小写完全相等,见 target.rs 的 match_name。Main 匹配不到 main,_main 和 main 是两个完全不同的符号。
第二重:near-miss 给建议但不代选。 名字对不上时,工具返回错误并附带最多 8 个相似符号及其地址("no symbol is named exactly \"main\"; similar names: ..."),一个字节都不改。你可以拿建议纠正参数后重试,这个行为被 test/stdio_mutation_targets.sh 的自动化测试逐条验证。
第三重:单目标强约束。 同时传地址和名称 → 拒绝(not both);传多个地址的数组 → 拒绝(exactly one address)。杜绝"批量误伤"。
第四重:save_idb 检查点 + 崩溃即弃。 所有编辑在 save_idb 之前都只存在于内存;若 IDA 调用中途崩溃(SIGSEGV/SIGBUS)或超时,服务器会丢弃未保存的改动并重建 worker,绝不让损坏的状态污染数据库。也就是说,save_idb 是唯一"落锤"的动作。若只想让代理只读,启动时加 --read-only,所有编辑工具会被直接移除。
✅ 推荐的安全编辑工作流
- 先发现:
list_functions/resolve_function确认符号的精确名称和地址; - 再编辑:传地址最稳;用名称时带上完整前缀(如
_main),必要时加offset; - 核对 target 记录:确认
target.address与预期一致; - 验证改动:
list_patches、get_bytes或重新decompile复查; - 落盘检查点:一批编辑完成后调用
save_idb,把风险窗口控制在最小。
工具全目录可查阅 docs/TOOLS.md 的 Editing 分类,各工具的完整参数与示例在 src/tool_registry.rs 中有 example 字段可直接参考。
📂 延伸阅读
- 精确目标解析:src/ida/handlers/target.rs
- 重命名与注释实现:src/ida/handlers/annotations.rs
- patch / patch_asm 实现:src/ida/handlers/memory.rs
- 局部变量精确编辑:src/ida/handlers/lvars.rs
- 精确目标自动化测试:test/stdio_mutation_targets.sh
- 保存与崩溃处理说明:README.md
记住一句话:ida-mcp-rs 的编辑工具宁可失败,也绝不猜。 读懂 target 记录、用好 save_idb 检查点,你就能放心地把二进制编辑交给 AI 代理。