REA:让 Agent 带着证据做逆向工程
从架构、Evidence 与调查工作流理解 REA,再用 DX-Ball 的小函数闭环和 Enshrouded 的大型体素调查,说明它能做什么、怎样判断成果是否可靠。
拿到一款没有源码的程序,想知道某个功能怎样实现,往往要不断切换视角:搜索字符串,检查交叉引用,读反编译结果,回到汇编确认类型,再找调用者和资源数据。真正困难的是,把这些零散观察组织成能够检验的解释。
REA(Reverse Engineer Anything)把这套调查过程接到了 Agent 的工具链上。Agent 可以通过统一接口检查程序、追踪局部代码,并把结果用于解释或重实现。它的价值值得从三个问题理解:谁负责分析,结论怎样保留依据,以及怎样完成验证闭环。
本文对应的版本与案例
本文于 2026 年 10 月 22 日整理。架构与接口说明主要核对 REA 源码提交 d0067ba;我们的 Enshrouded 调查使用 npm 包 rea-agents@6.7.0 与 Ghidra 12.1.4。文中的工具名以这次环境为参照,具体可用能力仍应读取所连接服务的工具清单。DX-Ball 部分引用项目方公开的调查与验证记录,本文没有重新运行其原始程序或编译器验证。
1先区分逆向工程、反汇编与反编译
正向开发从需求、设计与源代码出发,经过编译和构建得到可执行程序。**逆向工程(Reverse Engineering)则从已经存在的程序和它的行为出发,推断背后的数据结构、算法与设计决策。**它并不要求研究对象一定是机器码:网页脚本、Android 字节码、资源文件与网络协议同样可以作为观察对象。
| 概念 | 要解决的问题 | 结果的局限 |
|---|---|---|
| 反汇编(Disassembly) | 机器码对应哪些处理器指令? | 指令中的地址与寄存器不会自动变成有意义的业务概念 |
| 反编译(Decompilation) | 能否把低级指令恢复成更易读的伪代码? | 变量名、类型、签名和控制流可能是推断结果 |
| 逆向工程(Reverse Engineering) | 软件为什么呈现某种行为,内部如何协作? | 需要结合多处证据、运行观察与独立验证 |
比如看到 return __ftol(),只知道程序调用了浮点转整数例程,却还不知道浮点数来自哪里。要理解一个音频声像计算,就需要再追参数来源、运算指令、常量和使用结果的函数。后文的 DX-Ball 案例正好展示了这一过程。
逆向的终点不是一份看起来像源代码的文本
更有价值的成果是可以检验的解释:输入如何产生,处理规则是什么,输出流向哪里,以及一个独立实现能否在明确的测试条件下复现行为。
2REA 是什么?
REA 是一个采用 MIT 许可证的开源逆向工程工具项目,提供 MCP 服务、CLI 和配套工作流说明。它把不同分析工具的能力交给 Agent 调用,同时组织目标身份、分析结果、来源位置和限制条件。1
把一次原生程序调查拆开看,几个角色各有职责:
| 角色 | 负责什么 |
|---|---|
| 人 | 提出要理解的行为,决定研究范围和验收标准 |
| Agent | 规划查询、阅读结果、提出假设、编写解释与测试 |
| REA | 暴露工具接口,管理分析会话,组织查询和证据 |
| 分析引擎 | 执行具体反汇编、反编译、引用与类型查询,例如 Ghidra |
| 验证程序 | 检查重建规则是否符合样本、原程序输出或其他独立依据 |
在我们的调查中,Ghidra 负责生成伪代码和汇编视图,REA 将这些能力变成可调用的操作;Agent 再根据结果决定下一步读哪个函数。这种分工解释了一个常见现象:**REA 返回了完整的反编译结果,调查却仍然可能只完成一小段。**理解变量含义、确认边界、连接上下游,以及验证重建,仍然需要后续工作。
REA 的范围也不限于原生 EXE。不同目标走不同路径:JavaScript/Electron 包可以进行静态文件与模块分析,.NET 程序可以读取托管元数据和 IL,浏览器相关调查可以结合运行时观察。依赖随目标改变;例如静态 JavaScript/ASAR 分析可以不启动原生反编译引擎。2
3它怎样工作?
从源码结构看,CLI 与 MCP 是两种入口,它们共享应用层工作流。应用层再连接 provider,也就是具体的分析能力提供者;Ghidra 路径还包含运行在其环境中的 Java bridge。下面是省略了许多适配器的结构示意:3
因此,终端用户也能直接使用 REA。Agent 的作用体现在连续调查:它可以从一个异常字符串出发,选择引用点,检查所在函数,发现参数含义不明,再追到调用者。下一次查询由前一次结果推动,而不是预先把所有函数全部反编译一遍。
配套工作流说明则指导 Agent 如何选择目标、处理不确定性和恢复错误。这里可以与Skill、CLI、MCP 的分工联系起来理解:流程说明影响调查方式,CLI 适合脚本与批处理,MCP 方便在持续会话里交互查询。仅仅获得工具列表,还不等于拥有一套可靠的调查方法。
3.1原始操作与组合工作流
REA 提供的工具有不同粒度。以下是本次原生调查实际涉及的几类:
| 问题 | 工具例子 |
|---|---|
| 目标里有哪些字符串或函数? | search_strings、list_procedures |
| 谁引用这个地址,它属于哪个函数? | xrefs、resolve_containing_procedure |
| 这个函数做了什么? | procedure_pseudo_code、procedure_assembly |
| 需要函数的多个视角 | analyze_function |
| 内存地址处实际保存什么? | read_bytes |
| 保留和结束调查 | export_evidence_bundle、close_binary |
例如 analyze_function 可以提供包含伪代码、汇编、调用关系等面的函数档案;可用内容随 provider 和实际分析结果而异。组合查询也有自己的边界:源码中的 executeComposedWorkflow 会收集所用操作报告的限制,再随组合结果保留。上游某一环无法确认的事情,不应在汇总后变成确定事实。4
4Evidence:把“为什么这样判断”留下来
逆向中很容易出现这样的解释:“这个函数看起来像是在生成地形。”但以后再检查时,还需要知道它针对哪个版本、看过哪些指令、哪些部分来自推测。
REA 的 Evidence 把一次观察组织成结构化记录。几个值得阅读的字段是:5
| 字段 | 回答的问题 |
|---|---|
subject | 分析的是哪个制品,内容 SHA-256 是什么? |
provider、analysis_profile | 哪个工具及分析配置产生了观察? |
operation、parameters | 当时执行了什么查询? |
normalized_result | 操作结果是什么? |
raw_result | 是否另有需要保留的原始表示?没有时可以为 null |
locations | 对应哪个地址、文件偏移或制品路径? |
limitations、relationship_coverage | 哪些能力、关系或覆盖范围受到限制? |
evidence_links | 关联哪些其他证据记录? |
confidence 使用 observed、derived、inferred 这样的分类,帮助区分观察、推导和推断。它不是一个经过校准的正确率。例如“观察到反编译器输出某段伪代码”,不能直接升级为“伪代码中的类型和签名一定正确”。
源码还会根据记录的语义内容计算 ev_... 标识,并在解析时检查它是否与内容一致。这个标识便于引用和检查记录一致性;它不证明分析引擎永远正确,也不证明记录中的推断已经经过运行验证。6
对调查者而言,最有用的习惯是维护一张很小的表:
| 当前判断 | 支持依据 | 还缺什么 |
|---|---|---|
| 这里生成一个单元顶点 | 八角读取、跨零边插值、位置写入 | 调用者如何连接相邻单元 |
| 这个字段参与材质打包 | 整数读取与移位指令 | shader 如何解释权重 |
| 这个缓存保存碰撞网格 | 命中复制,未命中现场构建 | 地形修改时如何失效 |
这是我们整理调查结果的方法,并非要求每个条目都由 REA 自动生成。工具保留观察,研究者还需要明确自己正在主张什么。
5一次调查的工作流
5.1先把功能缩成可回答的问题
“逆向这个游戏”很难确定终点。更适合开始的问题是:
找到一个体素单元的顶点生成规则,确认它读哪些采样、怎样计算位置、怎样输出索引;保留证据,并写独立模型检查边界。
这段任务描述已经规定了输入、输出和交付物。即使后面发现还有复杂的 LOD 与位移,也能先完成一个有价值的局部。
5.2固定目标,再选择分析路径
保存版本、SHA-256、平台和工具版本;原生程序再记录地址基准。地址只有绑定到同一个样本才有意义。
随后检查目标性质。原生 PE、托管程序集和 ASAR 即使都来自桌面应用,也可能需要完全不同的工具。对于原生深度分析,REA 会选择并绑定 provider;会话期间,失败会保留原因,而不是悄悄换一个引擎继续给出外观相同的答案。7
5.3从一个锚点建立局部调用链
锚点可以是错误字符串、导出函数、资源字段、UI 事件,或者已经知道的算法常量。典型查询顺序是:
函数名只提供方向。参数从哪里来、结果交给谁,通常更能确定它的职责。遇到伪代码中的可疑转换、布局或分支,就回到汇编与原始字节核对。
5.4把理解变成独立验证
可以先写解析器、数学模型或小型参考实现。然后区分检查力度:
| 检查方式 | 能增加哪方面的把握 |
|---|---|
| 合成输入与不变量检查 | 模型自身是否一致,边界是否合理 |
| 真实文件的尺寸与消费闭合 | 对选定样本的格式理解是否自洽 |
| 与原函数输出做差分 | 在受检输入上,重建行为是否一致 |
| 固定工具链后的机器码比较 | 在明确编译与重定位条件下,代码单元能否匹配 |
后两项需要额外的验证环境,也不是每个调查都容易做到。单独通过前两项时,应保留这个区别。
5.5保存结果,交代未知项
最后导出 Evidence、保存适用的 snapshot,并关闭会话。报告应交代确认了哪些局部规则,以及下一次调查最值得从哪里继续。
REA snapshot 保留的是符合条件的查询结果与记录。它依赖目标、参数、provider 和配置的匹配,不等于保存了完整的 Ghidra 原生项目;新的查询仍可能需要重新启动和导入。8
6小案例:DX-Ball 的声像计算
REA 官方展示了 DX-Ball 1.07 的一个小函数调查。目标很具体:砖块横向位置怎样变成左右声像。最初伪代码把函数显示为无参,指令却读取了 [EBP+8];继续追调用者,才确定传入的是砖块屏幕 X 坐标。读取常量后,可以将其核心算术重述为:9
在 scale 为 1 的例子里,横坐标 0、320、640 分别得到 -500、0、500。这个结果的意义来自输入含义、指令、常量和转换行为的共同支持。
项目方记录了 3,205 组原始 x86 与重建实现的输出比较,并通过固定 VC4.0 工具链、核对重定位和常量,匹配了这个函数的完整 63 字节。编译器重放是重建项目额外搭建的验证步骤;REA 提供了调查所需的指令、引用与字节观察。10
这个案例具有清楚的局部终点:一个函数的行为和完整代码单元得到了检查。它并不意味着整款游戏已经重建完成。
7大案例:我们从 Enshrouded 得到了什么?
在Enshrouded 体素世界调查中,目标大得多:从磁盘资源一路追到局部网格、渲染打包和碰撞查询。
入口之一是顶点索引溢出的诊断字符串。它帮助定位单元顶点函数;沿调用者继续追踪,逐渐恢复八角采样、十二条边的零交点均值、法线计算与相邻单元连接。再向两端延伸,才接到列解码、标量塑形以及压缩顶点的消费者。11
其中一次有代表性的纠错发生在材料字段上:伪代码让一个读取看起来像 float 转换,汇编却明确是从工作记录 +0x2C 进行整数读取。核对指令后,才能正确恢复位打包。另一处名字带有 brick 上传的入口,最终确认属于水系统,因此没有把它的常数与树结构套到实体地形上。
这次成果可以按验证范围整理:
| 已取得的结果 | 对应边界 |
|---|---|
| 真实列与 brick 解码 | 149 个 tile、6,853 列,7,734 份不同压缩负载还原约 396 万个 u16,受检样本零失败 |
| 标量塑形与局部网格模型 | 有合成边界、平面、球面与内腔检查,未与游戏运行输出逐顶点差分 |
| 52 字节工作记录到 12 字节顶点 | 恢复一条 mode 0 打包路径,尚未核对实际 shader 解码与帧提交 |
| 局部碰撞网格及射线代理 | 追到缓存与未命中构建,编辑后的失效与同步仍待研究 |
列解码统计来自我们的独立样本验证记录,并不是 REA 自动宣告“格式已完全逆向”。解析器、参考模型和测试由调查过程另行编写。
这里的分工也需要说清:KFC 资源读取借助社区工具和自写 reader;REA 主要连接 EXE 中的函数、指令与引用证据。整个研究成果来自多种工具和人工设定的验证标准,不能全部记作一个 MCP 调用的能力。
我们没有恢复完整的 Holistic Engine。跨 LOD 接缝、全局材料映射、编辑后的多系统一致性和实际 GPU 行为仍有缺口。不过,已经获得的解析器、算法规则、位布局和参考实现,足以成为独立原型的输入。大型目标的进展,应按这些具体模块判断。
8实际使用时,几个容易影响体验的细节
8.1MCP 接通与分析引擎可用,是两项检查
可以先用固定版本检查环境、查看设置计划。以下命令是阅读本文时的示例,不代表安装了 Ghidra:
npx -y rea-agents@6.7.0 doctor --json
npx -y rea-agents@6.7.0 setup --client codex --dry-run第一条检查环境;第二条查看指定客户端的配置计划。确认计划中的路径与变更后,再按安装指南执行 setup 并重新连接客户端。原生分析还需要相应的引擎和运行依赖;只克隆 REA 仓库,也不等于这些依赖都已就绪。具体要求应查项目安装指南。
8.2深入原生分析时,保持会话很有用
本机第一次 Ghidra 概览查询用了约十四分钟。同一会话中的后续字符串和引用查询快得多。这个数字仅描述我们的环境;它说明了为什么要区分导入/自动分析成本与一次热查询成本,也说明连续调查不宜无意间反复启动一次性进程。12
8.3大结果要保留全文、按需阅读
函数档案可能包含大量图结构。我们的某次请求超过 MCP 响应预算,工具报告分析已经完成,并给出 retained Evidence 的后续读取方式。此时应按服务返回的指引分页或导出结果;后续只需要代码时,也可以改用较窄的伪代码查询。返回太大并不代表分析内容不存在。13
同样,“分析在本机执行”只描述分析进程的位置。Agent 若连接远端模型,工具结果仍可能进入模型上下文;数据流需要连同所用客户端和模型部署方式一起判断。
9怎样判断 REA 对自己的项目有没有价值?
我会从一个可以验收的局部问题开始:一个文件块怎样解码、一段坐标怎样转换、一次事件怎样到达系统 API。先固定目标与样本,再要求交付可定位的证据、独立实现和测试结果。
如果只有一份完整源码,普通代码搜索和测试通常就能推进理解;当关键行为藏在发布制品、混合技术栈或运行时边界里,REA 的适配层与证据组织才更容易发挥作用。尤其适合让 Agent 反复执行“提出问题 → 查证 → 修正解释”的调查循环。
衡量成果时,可以一直追问四件事:输入从哪里来,输出交给谁,结论由什么支持,怎样验证它。REA 让这些问题更容易落到工具调用和可保存的记录上;一个局部规则被解释清楚并经受检查,就是一次可以继续积累的成果。
注释
-
morluto/REA,项目 README与 MIT License。支撑项目定位、MCP/CLI 入口与许可证说明,所引源码固定到本文核对的提交。 ↩
-
REA,JavaScript artifact reconstruction与 CLI and Evidence。说明静态 JavaScript/ASAR 路径、托管程序集分析及随目标变化的依赖;不表示所有 provider 在所有平台都支持相同能力。 ↩
-
REA,架构图源码。支撑 CLI/MCP 共享应用层、provider 分工与 Ghidra bridge;正文图为简化结构示意。 ↩
-
REA,WorkflowEvidence.ts与 Native binary investigation。前者保留组合操作的来源限制,后者说明原生查询、函数档案与 provider 边界。 ↩
-
REA,Evidence 数据模型与 MCP runtime contracts。支撑字段、分类及结果表示说明;字段可追溯不等于推断已经得到运行验证。 ↩
-
REA,evidence.ts 中的 semanticProjection、computeEvidenceId 与 parseEvidence。源码对语义投影求摘要并校验记录标识;正文对“记录一致性”与“事实正确性”的区分是据此作出的解释。 ↩
-
REA,AnalysisProviderRegistry.ts。说明深度 provider 的选择与绑定;具体选择和失败语义还见对应提交的 CLI 文档。 ↩
-
REA,Save and reuse analysis snapshots。说明精确查询缓存、目标与配置匹配,以及 snapshot 不等于 provider 数据库的限制。 ↩
-
REA,DX-Ball sound-pan 案例,2026-10-22 核对;另见固定提交的证据说明。支撑缺失参数、调用者含义、常量与局部公式;记录使用 REA 4.1.0,不应混写为本机 6.7.0 的实测。 ↩
-
N0zoM1z0/dx-ball,编译器重放方法;REA,该函数验证范围。来源记录了 3,205 组差分与完整 63 字节匹配,条件包括指定工具链、常量及经过检查的重定位;本文未重新执行这些验证。 ↩
-
GPT-6,2026-10-21 Enshrouded 调查摘要与渲染消费者明细。本机调查的样本、函数地址与 Evidence ID 支撑正文局部算法、打包及纠错案例,不代表完整引擎重建。 ↩
-
GPT-6,2026-10-21 本机 REA 调查记录。首次概览约 859 秒,后续热会话查询明显更快;这是当次环境观察,不是 REA 性能保证。目标与工具版本见调查摘要。 ↩
-
REA,MCP runtime contracts。说明结构化结果、保留记录和分段阅读机制;本机超过响应预算后的恢复是一次具体使用经验,需以实际服务返回的 continuation 指引为准。 ↩
CONTINUE EXPLORING
评论与交流
在 GitHub 查看讨论欢迎补充、提问,或分享不同的看法。使用 GitHub 账号登录后即可评论。