逆向工程 · 模型能力 · 精华速读

两次卡住的函数,都靠「换一种表达」拿到完整匹配

在固定编译器、结果可逐条比对的约束下,重建逆向工程(RE)本身就是一台训练环境:模型提出源码假设,编译器给出可重复的反馈。作者的判断是,真正该学的是反馈之后下一步做哪个实验——而检查器自己的盲区,会和模型一起决定学到的答案。

来源 · rea.tools《Does reverse engineering need a bigger model?》 · 阅读约 6 分钟

读目标 编译 比对 结果决定下一步实验 换一种表达 检索 01目标函数固定工具链 02源码假设一种表达形式 03编译并逐条比对指令级差异 04读反馈下一步选什么 关键改变表达方式打开新的搜索空间 仓库记忆源码 · 失败实验 · 编译器经验
Fig. 1同一条「假设 → 编译 → 比对」循环里,进步来自两处:仓库里已经记下的经验,以及模型把周围代码换一种表达。作者想知道两者各占多少。

30 秒速览

记住这 5 句就够了

  • 表达

    把真实运算合进一个表达式,临时值同时存活,编译器无法再复用同一个栈槽——TH09 攻击函数因此完整匹配。

  • 结构

    改掉分支周围的控制流图,让主路径汇入共享的收尾与返回,编译器才按原版的方式合并分支。

  • 记忆

    仓库里的源码、失败实验和编译器经验可以当检索记忆用:起点变好,而不必改模型权重。

  • 盲区

    检查器(oracle)有覆盖范围。TH08 里已有函数带着 1.0f 与 32.0f 的差别通过比对。

  • 结论

    改进学习器和改进 oracle 属于同一个研究问题。

第 1 层 · 为什么能突破

突破不在算法,在源码怎么写

分组 分离 ① 语句分开写 ② 合成一个表达式 ③ 碰撞检查值独立 槽 0 槽 1 槽 2 槽 3 槽 0 槽 1 槽 2 槽 3 槽 0 槽 1 槽 2 槽 3 临时值 A 临时值 B 碰撞检查值 c 槽被复用,与原版不符 临时值 A 临时值 B 临时值 C 碰撞值 c 仍有一个槽共用 临时值 A 临时值 B 临时值 C 碰撞检查值 c 干净重编:完整匹配
Fig. 2改的只有「临时值活多久」。在 C++ 里临时值活到使用它的表达式结束,所以把真实运算合成一个表达式,编译器就再也不能把它们塞进同一个栈槽——槽位分配随之与原版一致。
修改前 修改后 入口 分支 1 分支 2 分支 3 收尾 X 收尾 Y 入口 分支 1 分支 2 分支 3 收尾 + 返回
Fig. 3分支在哪里合流属于源码结构。改前分支汇入不同的收尾点,改后主路径汇入同一处收尾与返回,编译器随后以与原版相同的方式合并分支。

两个 TH09 函数的行为早就重建完了,剩下的差异都在存储与分支的写法上。GPT-6.1-Sol 找到的方向是临时值的存活时间:把真实运算合进一个表达式,临时结果就同时存活,编译器无法再共用一个栈槽;最后只剩一个槽与之后碰撞检查用的值重叠,给它独立的生命周期后,干净重编确认完整匹配。

第二个函数卡在分支的收尾方式。GPT-6.1-Sol 已经让部分匹配,GPT-6-Astra 改掉分支周围的控制流图、让主路径汇入共享的收尾与返回之后,完整匹配才出现。

归因要小心

后续会话是从先前的源码和失败记录出发的,累积的工作本身就是优势的一部分。要分离「模型能力」和「继承的经验」,需要受控对照。

第 2 层 · 专才与记忆

编译器怪癖很窄,但可以被记住

试过,无效 有效线索 移动声明位置布局仍不符 复用命名模式变量名影响槽位 VC7.1标识符 → 栈槽 Supervisor::LoadDat匹配,不加存储
Fig. 4TH095 里连局部变量叫什么都会改变 VC7.1 选择的栈槽。一个更早的后台函数暴露了命名模式,把它复用到文件大小变量上,就拿到了所需布局而没有多占存储。

这类细节通用模型到场时通常不知道。TH09 敌人生成函数的重建里,一次 GPT-6-Luna-max 会话正是从 TH08 的同源家族取来假设,再用 TH09 自己的证据补上细节。作者更愿意先把经验留在仓库里:检索能解决「模型不知道这个怪癖」,但解决不了「知道却用不起来」——把事实变成候选,以及候选失败后怎么读反馈,仍然要模型自己完成。所以对照实验里应该放一个「小模型 + 好检索」。

检索可解

不知道这个编译器怪癖

检索 仓库记忆 候选假设
训练要补

知道事实,却用不起来

未打通 已知事实 候选假设
训练目标

候选失败后选下一步

决策 观察结果 下一步实验
Fig. 5三张图用同一组坐标。检索补上缺失的事实;训练要补的是把事实变成候选、以及读反馈后决定走哪一步。

同一位作者(ZUN)写的代码习惯相似,这只是先验,每个目标自己决定先验是否成立。

TH04

DOS 时代的编译器。玩家重建需要不同的源码模式假设,还牵扯编译产物如何组合成可执行文件。

TH08

比 TH095、TH09 更早的 Visual C++ 版本;浮点常量的处理方式也在这里埋过一个坑。

TH095 · TH09

VC7.1。在固定的版本与选项下,源码的小改动会带来可重复的机器码差异。

第 3 层 · 训练什么

该学的是反馈之后选哪个实验

最终源码只记录成功的那一次改动,实验序列才记录每次观察如何决定下一次。保留无效变体同样重要:它告诉后来的 agent,哪些假设在同一个上下文里已经试过了。

选自攻击函数重建的实验序列,按「它教会了我们什么」排列。
实验结果告诉我们什么
换一种复制结果的方式复制代码变了,函数仍然不同。
直接使用返回的值复制方式与原版一致了,临时存储仍然不同。
让临时结果同时存活各自拿到独立存储,但仍有一个与之后碰撞检查用的值重叠。
让碰撞检查值独立干净重编确认完整匹配。
Fig. 6一步步缩小可能性,而不是一次猜中。每一行都改变了下一次要试什么。
反馈怎么读

中性结果说明该换变体;差异变了说明可能还有别的原因;当局部改动不再提供信息时,应该回去重看源码表达或证据。这需要自主性——有时正确的下一步是去查另一个调用者,而不是再编译一次。

第 4 层 · 奖励可信吗

oracle 会错,奖励会学会它的盲区

检查 跳过 精确比对通过≠ 结果一定正确 已记录地址的值:检查比较器先重定位地址 其他字面量:跳过例:1.0f ≠ 原版 32.0f
Fig. 7作者把参考或检查器称为 oracle。它给出可重复的答案,但答案覆盖到哪里是另一回事:TH08 里已有通过比对的函数带着错误的浮点常量。

比较器会先调整重定位地址,只有当某个地址被显式记录用于验证时,才检查它指向的值;其余字面量可以不看就通过。一次更广的审计因此在已经接受的函数里找到了错误常量。功能测试有同样的盲区:针对 LLM 反编译器的差分模糊测试发现了测试用例之外的行为差异。

用之前先打它一顿

用故意失败测试检查器,并把最终验收放在候选可编辑的源码之外——否则一段从目标拷来的字节数组就能骗过朴素的评分。用旧检查器当奖励,等于把它的盲区写进学习环境。

第 5 层 · 怎么验证

在固定的 VC7.1 任务族上做对照

固定条件 相同的目标证据与工具;训练类比较还固定基座模型与可用知识 强通用模型基线 小模型基线 小模型 + 仓库检索检索带来什么 专精模型监督 / RL 训练 完整且独立验证的匹配数 · 相同的候选与编译预算耗时与总成本(含编译)· 训练成本单独记账
Fig. 8四组拿到同样的目标证据、工具、候选与编译预算。留出集必须按源码家族切分——只留出游戏名不够,因为同引擎的函数几乎相同。

两类起点要分开测:从指令重建整个函数,和把维护中的源码收尾到完全匹配。专才可能很擅长第二类,而通用模型承担了走到这一步所需的大部分工作。重复多次同样重要,否则分不清稳定策略和一次运气。

别奖励「看起来更近」

一个 TH09 replay-save 候选编译后大小符合预期,完整比对仍发现大量差异;指令排布的变化也会让候选显得更近。这类诊断只能指导下一个实验,任何「更近了」的奖励都要拿完整验收来校准。更便宜的建议如果引来大量无信息的编译,反而变成更贵的工作流。

风险与边界

这四条会悄悄改写结论

归因混淆

优势不全来自模型

继承 先前会话 本次匹配

后续会话继承先前的源码与失败记录。

oracle 盲区

通过比对也算错

未检查 1.0f 32.0f

已接受的函数可能带着错误的常量。

看似更近

大小对了,差异还在

误导 候选 完整比对

编译后大小符合预期,仍可能大量不符。

上下文迁徙

同作者只是先验

需确认 同作者先验 目标确认

TH04、TH08、TH095 用的编译器都不同。

Fig. 9四条边界都指向同一件事:结论必须绑在具体的编译器版本、上下文和被检查的字节上。

作者最后倾向的形态是「宽能力的 agent + 可调用的专才」:任务变成熟悉的编译器搜索时,向专才要候选;结果开始说不通时,回到目标证据或去检查测量本身。专精由此变成调查过程中可以选用的一项工具,而不是固定的流水线。

来源

原文:Does reverse engineering need a bigger model?(rea.tools;文内第一节标题为 “What better models changed”,源码与匹配记录于 2026-10-13 复核)。详细证据笔记:evidence/reverse-engineering-models.md。相关研究:LLM4Decompile、AutoDecompiler、针对 LLM 反编译器的差分模糊测试。

本篇为约 6 分钟的精华版,删去了逐条提交链接、仓库检索的完整细节与模型标签说明;函数、常量、编译器版本与对照条件均按原文保留,未引入外部信息。TH09 的 gpt-dots 标签对应 GPT-6-Astra;gpt-web 随当时所用模型变化,不能单独标识某次运行。