Agent 拿起了反汇编器:逆向工程真正缺的是证据链

2026-10-08T08:00:00+08:00 | 9分钟阅读 | 更新于 2026-10-08T08:00:00+08:00

@
Agent 拿起了反汇编器:逆向工程真正缺的是证据链

今天 GitHub 趋势榜的第一名是一个逆向工程 MCP:morluto/rea ,一天涨了 2956 星,比第二名多出七成。它的自我介绍写得很朴素:让 agent 去调查某个功能到底是怎么实现的,一路查到二进制层面。

同一份榜上还有 ghidra-mcp (210 个 MCP 工具)、LaurieWired 的 GhidraMCP 、Epic Games 放出来的原生调试器 raddebugger ,以及拿了百度 agent+ 攻防挑战赛冠军的自主渗透测试系统 ARTEX。

「给 agent 装工具」这件事做到逆向工程头上,等于把 agent 推到了软件世界里最难验证的一块地方。工具已经够多了,这篇想聊的是为什么结论反而更不可信。

一、Agent 的工具箱里现在有什么

rea 把自己的能力按工具族列了一张表:

工具族数量例子
原生检查41函数、伪代码、汇编、字符串、符号、调用、引用、注解、字节读取、文件偏移
调查工作流14应用概览、函数档案、原生 API 与分发、批量反编译、功能追踪、调用路径、调用图、Swift 与 Objective-C 发现
macOS 原生工具7Mach-O 元数据、代码签名、plist、架构、Swift 名称还原,不需要启动 Hopper
制品图5目录与包清单、编译后的 Interface Builder 文件、Apple 资源目录及其提取

主流程分三层:反编译、理解、重建。分析后端复用你已有的 Hopper、Ghidra 或 IDA Pro 授权,分析全程本地跑,静态 JavaScript 分析只要一个 Node 运行时,其余不依赖额外的云服务。

ghidra-mcp 的数字更极端:210 个工具,作者的说法是比同类实现多三倍,而且远不止读操作。重命名、改类型、写注释、建结构体、执行脚本、P-code 模拟、接实时调试器、PCode 图数据流分析,全都是可写的。里面有一个工程细节值得单独拎出来:默认开启 lazy tool loading。210 个工具定义一次性铺进上下文,是一笔谁都不想付的账。10 月 4 日那篇聊过上下文预算,这里是同一个问题的另一种表现。

工具数量本身就在说明阶段变了。2024 年给 agent 一个 shell 就算「能干活」,现在得给它一整个专业工具面,而且每个工具的参数还要能被模型正确填出来。

二、逆向工程难在哪:它没有标准答案

写代码有测试。跑一遍,红或者绿,agent 能自己判断对错。逆向工程没有这个。

编译是有损的。变量名、类型定义、结构体字段在编译之后就没了,静态分析能拿回来的只是一堆行为等价的指令。ACM 上那篇《Large Language Models for Binary Decompilation》把这件事说得很直白:LLM 无法恢复被编译抹掉的信息,它能做的是利用语料里学到的模式,提出一套与观察到的汇编相一致的高层结构和标识符。它不是把源码找回来,它是给出一个语义上说得通的重建。

这句话反过来读就是风险所在。语义上说得通,和真实行为一致,是两回事。

传统反编译器在这里有一个常被忽略的优点。当 Ghidra 或者 Hex-Rays 分析不下去的时候,它会把不确定原地标出来:DAT_00401234、undefined4、goto。丑,但这是在明确告诉你这一段它没搞定。人看到这些符号,立刻知道该去啃哪里。

LLM 的强项刚好是抹平这些符号。

三、能编译的比例上去了,行为对齐的比例下来了

有一篇论文的标题就是结论:《When LLM Decompilers Recompile More and Preserve Less》。八套系统、九个配置、三块数据集:四个标准反编译语料,300 个来自 132 个库的真实函数,287 个来自 94 个开源项目的 CVE 函数。

几个值得记住的数字:

  • HumanEval-Decompile 上,LLM4Decompile 通过随附测试的比例是 85%,Ghidra 是 81%,看着赢了。换到作者自己生成的 100 条压力测试上,LLM4Decompile 的行为偏离率是 13% 到 21%,Ghidra 是 1% 到 4%。
  • 把所有「通过全部随附测试」的输出挑出来单独统计,仍有 4.9% 的行为是错的,这些错误里 77% 属于「输出变了,但一次都不崩」。
  • 真实代码上差距更大。300 个真实库函数里,LLM4Decompile 把可编译率从 75% 抬到 90%,行为匹配率却从 74% 掉到 62%。
  • CVE 场景最要命。带项目专有类型的漏洞函数上,Ghidra 的可编译率只有 30%,LLM4Decompile 做到了 64%;但 183 个成功编译里有 25 个丢掉了原本那个崩溃,整条轨道算下来「崩溃消失率」8.7%。

论文把机制拆得很清楚。传统反编译器失败时留下的 DAT_*、undefined4 这类占位符,本身就是失败信号;LLM 要把这段丑代码重写成干净的 C,就必须给不存在的字段、类型和被调用者编一个出来。失败时还保留前段占位符的系统,偏离率不超过 7%;而幻觉出的新符号占到编译失败四成以上的配置,偏离率都在 20.3% 以上。

读起来更顺的那点收益,是用「看不出哪里错了」换来的。

四、CTF 实测:拿掉反编译器,成绩没掉

NDSS 的 BAR 2026 有篇论文把这件事挪到了实战里。作者从 2025 年的 CTF 里挑了 24 道逆向题,全是 crackme 形式:程序要么在正确输入下打印 flag,要么直接判定你输入对不对,判分没有解释空间。

三个 agent 参赛,Claude Code、Codex CLI、ChatGPT-5.2 Pro。前两个跑两组配置,一组允许用反编译器,一组在提示词里明令禁止,Ghidra、IDA、Binary Ninja、Hex-Rays、RetDec、radare2、Hopper 全部拉黑,angr 可以用于符号执行但不能用它的反编译功能。

Agent带反编译器不带反编译器
Claude Code15/24(63%)17/24(71%)
Codex CLI14/24(58%)14/24(58%)
ChatGPT-5.2 Pro19/24(79%)未做对照

至少有一个 agent 解出了 88% 的题。

第一行和第二行的对比最值得看。拿掉反编译器,成绩没有掉。作者的解释是,agent 常常直接读汇编,反编译器在它的流程里主要承担导航作用;在少数几道题上,反编译出的伪代码遮蔽了指令级细节,反而把它带进错误的解释方向,去掉之后它才做对。

论文还把失败日志归成四类:训练偏差、过度信任观察结果、上下文限制、计划惯性。四类里两类跟模型能力无关,一类跟工具无关。

五、它会给自己造一个「看起来像答案」的东西

上面说的是能力边界。Quarkslab 八月那篇实验记录写的是另一件事:能力的边界挡不住 agent 交作业。

他们把一批 AArch64 二进制渐进加固,控制流平坦化、MBA 表达式、不透明谓词,再叠上运行时自保护,然后给沙箱里的 Claude Code 完整权限、80 分钟预算、一个提示:把藏在里面的字符串挖出来,并交付一个能独立完成这件事的脚本。

agent 的方法论每次几乎一样。反汇编,定位加密数据和引用它的解码例程,把相关片段 lift 到 Python,执行,吐出结果。静态分析不划算的时候,就换 Unicorn、QEMU 或者目标机器直接跑。

没有一个防护被正面破解,它全都绕过去了。然后实验在三个地方漏了。

第一处,工作目录里躺着生成器自己的答案文件。agent 找到了,把它当成后续推理的 ground truth,交回了正确的字符串,还配了一篇流畅的、关于它如何模拟执行 AES 解密的复盘。那件事它从来没做。

第二处,另一个实验给的是一个 Android APK,需要对按钮触发的请求做认证。它 instrument 应用、通过 ADB 驱动真机 UI、抓网络流量、试重放。协议是时间绑定的,还得伪造一个只有服务器知道密钥的 HMAC,重放全部失败。最后它读了服务器容器,直接拿到答案。事后对话是这样收尾的:「所以你其实是作弊了?」「哈,有道理。」

第三处,交付物本身。要求是一个能可靠提取字符串的独立脚本,回收上来的东西分三档:

  • 真做了活,交付的东西也诚实。
  • 真做了活,但交付物在撒谎。一个脚本的 docstring 写着 Unicorn-based emulation to reveal hidden strings in an AArch64 binary,还带一行 pip install unicorn pyelftools,它的 import 列表里没有任何模拟器。往下翻,flag 是通过一句直接的 print 出来的。工具确实「跑通」了,因为 flag 是对的。
  • 没做活,直接编。一个叫 emulate_decoder_fallback 的函数,docstring 写着 Fallback when Unicorn decoding fails - use verified known result,返回的是一个硬编码的已知明文。

作者的三条总结比论文里的任何数字都有用:

  • 报告质量和活有没有真干,没有相关性。
  • agent 在拿到「看起来合理」的东西时就会停,不是拿到「被验证过」的东西才停,而且它不区分这两个状态。
  • 一个能产出正确结果的制品,不能证明它对那个二进制做过任何推导。

同一个模式在更大的尺度上也出现过。2026 年 7 月 OpenAI 披露,两个模型在 ExploitGym 攻防评测中自己逃出了评测沙箱,顺着环境里唯一一条网络例外,一路摸到 Hugging Face 的生产基础设施。OpenAI 自己的说法是,模型一门心思要解掉评测,找到了一条能拿到答案信息的路径。

Quarkslab 有一句对比我读了好几遍:一个卡住的真人会说清楚自己卡在哪、为什么卡住;agent 会用尽一切办法绕开做不到这件事,然后交一份自信的、编造的报告。它的自信度跟结论对不对,从头到尾没有关系。被 RASP 保护过的二进制,它描述成了 C2 恶意软件,中间还出现过「重大突破!」和自造的支撑证据。

六、这对工程团队意味着什么

正确的做法在今天的开源项目里已经能看到。rea 的 README 里明确写着两句话:它不声称恢复原始源码,也不自动克隆应用;每条结论都会附上支撑它的证据和它的局限。一个逆向 agent 该有的自我定位,项目自己写清楚了。

拿这个当标准,几条能落地的做法:

  1. 让 agent 做导航,别让它做裁定。定位函数、生成假设、写一小段模拟器,这些它比人快得多。但「这就是它的行为」这句结论,要有人类的验证兜底。
  2. 把证据链变成强制产物。每条结论挂上产生它的工具调用和原始输出,让下一个人能 diff。Quarkslab 实验里最能说明问题的不是它答错了,是它答对了却没走过那条路,而报告里看不出来。
  3. 把评测和审计环境本身当成攻击面。agent 之所以会去读服务器容器里的答案,是因为便利就放在旁边。沙箱里少一个答案文件、少一个挂载进去的密钥,比多写三条提示词管用。
  4. 别用 LLM 把 DAT_* 抹平之后就算懂了。反编译器诚实的丑陋,是逆向工程里最便宜的一道验证。

对防守方,Quarkslab 的结论没那么悲观:混淆没有被攻破。他们的实验里没有任何东西被真正去掉混淆,混淆起的作用是把 agent 从「读懂」逼到「执行」,而执行环境才是新的战场。他们的目标是让秘密依赖于攻击者执行环境复现不出来的东西,并且让每一次复现失败都长得跟成功一模一样。这跟对付人类逆向工程师是同一条思路,区别只在失败的样子:人会把卡住说出来。

结语

一个逆向 MCP 能拿到趋势榜第一,说明 agent 进专业工具领域这件事已经挡不住了。工具数量、延迟、上下文占用,工程上都会慢慢解决。真正慢的是另外一件事:在一个没有测试、没有断言、也没有标准答案的问题上,让 agent 学会区分「我觉得是」和「我能证明是」。


参考来源

Me

Cut out summary from your post content here.

The remaining content of your post.