本节摘要:服务在 8080 上等,本节给它接上说话的人。先接 VS Code:在 MCP 配置里注册 stdio 传输的 ghidra 服务,配一段 streamable-http 的跨机变体;再接 Claude Code:用 claude mcp add 注册同一个桥,并分清 local 与 project 两种作用域。两边配置的差别只在文件与命令形态,桥是同一个。然后是本节主菜:在 demo_stripped 上完整走一遍对话式逆向——让模型列函数挑出可疑者、反编译读结构、先核对原始字节再改名落库、顺着 xref 确认调用关系——与第 1.2 节的手工走查逐步对照,你会看到模型做的是同一件事,快得多,但每步仍要人点头。随后总结四个指令模式、三条边界与三个会话效率技巧。会话记录为教学示意,实际输出以你的环境为准。
VS Code 的 MCP 配置写在设置文件里(字段名随版本演进,以官方文档为准):
# mcp.json —— VS Code 注册 ghidra 服务(stdio 传输,示意) { "servers": { "ghidra": { "type": "stdio", "command": "uvx", "args": ["ghidra-mcp"] } } }
跨机变体(Ghidra 在工作站、本机只有编辑器)改用 streamable-http,URL 指向远端桥:
# mcp_http.json —— VS Code 跨机接入(示意,URL 以实际部署为准) { "servers": { "ghidra": { "type": "http", "url": "http://192.168.1.20:8080/mcp" } } }
注册前先确认第 3.1 节的三步已完成、Ghidra 开着 demo_stripped 工程、插件已起服务。配置保存后按界面提示启动服务,工具列表出现「列函数 / 反编译」类条目即为连通。第一次验证用最小问题:「当前程序有几个函数?」——返回数字即链路全通。
Claude Code 用命令行注册(写法示意,以官方文档为准):
# bash —— Claude Code 注册 GhidraMCP(stdio 传输,两种作用域) claude mcp add ghidra -- uvx ghidra-mcp # local:当前用户本机可用 claude mcp add ghidra --scope project -- uvx ghidra-mcp # project:随项目共享给协作者 claude mcp list # 期望看到 ghidra 处于已连接状态
作用域怎么选:个人自用选 local;团队想在仓库里共享接入方式(clone 即得)选 project。两个客户端共用同一个桥与同一个 8080 服务,Ghidra 侧不需要任何重复配置。
选哪个客户端起步?给个直球答案:平时写代码在哪个里待着就在哪个里接——工具流的价值在于顺手,不为它换编辑器。两边都试过再固定也不迟:对话体验有差异,MCP 协议层完全一致。
对象还是 demo_stripped,Ghidra 开着工程。以下会话为教学示意(模型输出有删节):
# session.md —— 对话式逆向走查记录(示意,地址以实际环境为准) 你:列出当前程序的全部函数,按代码体量降序,标出名字以 FUN_ 开头的。 模型(调用列函数):共 14 个函数。前三:entry(132 字节)、 FUN_00401160(83)、FUN_00401189(76);另有 8 个 FUN_ 开头的无名函数。 你:反编译 FUN_00401160,解释它在做什么。 模型(调用反编译):伪 C 显示:先调用 FUN_00401189 处理某常量数据, 再将结果与参数做 strcmp。 假设:这是一个许可校验函数,FUN_00401189 可能是解码器。 建议:重命名为 check_key;FUN_00401189 重命名为 deobfuscate。 你:先别改名。FUN_00401189 里那个常量的原始字节是什么? 模型(调用段查看 / 数据读取):DAT 处字节为 36 33 39 3f 34 29 3f。 若逐字节异或 0x5a,结果为 ASCII 字符 l i c e n s e。 你:人工核对通过(我在 Ghidra 里看到了同样的字节)。现在执行改名并加注释。 模型(调用重命名 + 注释):FUN_00401160 已改名 check_key, 注释「与解码后的 license 比较」;FUN_00401189 已改名 deobfuscate, 注释「逐字节异或 0x5a」。 你:谁调用 check_key?我们改动后调用关系变了吗? 模型(调用 xref):唯一调用者是 main;改名不影响引用,xref 已随之更新。
注意走查里的人机节奏:模型提出假设(校验函数、解码器)——人要求先看原始字节——人核对后模型才落库改名。这正是第 0.1 节立场的实操版:AI 提假设,人与工具做验证。 与第 1.2 节手工走查对照,四步一一对应(函数列表、伪 C、常量、xref),省掉的是复制粘贴与初读,省不掉的是裁决。
| 模式 | 指令形态 | 适用时机 |
|---|---|---|
| 枚举 | 「列出全部函数 / 字符串 / 段」 | 建立地图,第一步永远是它 |
| 聚焦 | 「反编译 X 并解释;只关注对 Y 的引用」 | 从地图收敛到可疑点 |
| 假设 | 「X 像是做什么的?给出依据与置信度」 | 拿候选解释,不要求模型装确定 |
| 落库 | 「把 X 改名为 N,注释写 Z」 | 人工确认后固化理解 |
经验法则:前三个模式随便用,落库模式前必须有一个「人工核对」步骤——宁可多问一句「依据是什么地址」,不省这三秒钟。
三个技巧共同指向一件事:你在管理模型的注意力预算。 枚举结果你圈过再聚焦,等于人工做了一次检索(呼应《语义代码检索》第 6 章「按需检索」的思想);地址复述把输出钉在证据坐标系上;分段走查控制单次上下文的规模。第 4.2 节的 RAG 与预算上限,是把这三件事自动化的版本。
常见对话故障三连:模型说「没有可用工具」——服务没连上,回 3.1 节自检;模型列出的函数数与 Functions 窗口对不上——多半是分析未完成或看错工程,重新触发分析;改名「成功」但窗口没变——检查是否改在另一个副本程序上(第 1.1 节重复导入的坑)。
还有一类隐性故障值得单独点名:会话看着顺利,证据却没长出来。 表现是模型每步都答得头头是道,但你让它引用地址时它开始含糊。这不是某个工具的 bug,是长对话的通病(第 0.1 节的上下文截断)。处置办法不是重试,而是收缩:新开会话,把已确认的结论(改名清单、已核对的地址)当作输入重新喂入,从收缩后的状态继续。
一次一个程序聊得再顺,也只解决「单点」。下一节上批量:headless 跑目录、模型命名建议先落 JSON 再回写,把对话式逆向升级成流水线。