REA 如何让编码 Agent 接手逆向工程:从一个 mjs 分发器到引擎里的驻留插件
REA(npm 包名 rea-agents)是给 AI 编码 Agent 用的开源逆向工程工具:一条命令装好,就能在本地驱动 Hopper、Ghidra、IDA 分析原生二进制、Electron/JS 应用、.NET 程序集、安卓 APK 与固件,每个结论附带可复现的证据。适合想把"搞懂别人二进制怎么实现"交给 Agent 的工程师。
它要解决的真实痛点
没有 REA 时,逆向活基本卡在三个地方:
- Agent 够不到二进制。 Claude Code、Codex 这类编码 Agent 读的是仓库源码。目标如果是已发布的二进制或 Electron 应用,得人肉打开 Hopper/Ghidra 反编译,把伪代码复制进对话框,Agent 只能靠上下文猜调用关系。
- 各引擎接口互不相通。 Hopper 走 Python 脚本,Ghidra 走 Headless 脚本,JADX 又是另一套 headless 机制,生命周期、参数、错误格式各不同。每接一个引擎都要重写一层驱动,上层工作流跟着换。
- 结论无法审计。 反编译天然不完整,模型会自行脑补;没有地址、偏移和"不确定"标注的结果,事后既不能复核也不能对比两次运行的差异。
接入 REA 后,流程变成:Agent 通过 MCP 调工具 → REA 返回带伪代码、交叉引用和局限性的结果 → Agent 追问、解释行为或直接写实现。终端里跑同样的命令走的是同一套工作流,行为一致。
拆开看:核心设计
入口调度层:一个 mjs 分流两套运行时
npm 包同时注册 rea 和 rea-agents 两个命令,都指向 scripts/rea.mjs。这个文件只做路由:先校验 dist/ 下编译产物是否存在(缺失时直接给出构建指引退出),再按参数分流。裸 rea mcp 进入长会话通道,加载 src/main.ts 编译出的 dist/mcp/main.js,在 stdio 上起 MCP 服务器(加 --http 切 streamable HTTP);analyze、doctor、setup 等一次性命令走 CLI 通道,加载 dist/cli-runtime/cli.js。CLI 侧基于 incur 构建,src/cli.ts 注册 src/cli/ 下 75 个文件提供的几十个命令模块;每个命令模块只做两件事:注册子命令,然后把调用交给 composition。
src/composition/ 是装配线:directAnalysis.ts 按目标类型懒加载会话工厂(binary.js、android.ts、firmware.ts 等动态 import),用不到的引擎代码不进当前进程。
引擎与驻留插件层:把代码送进对方进程住下来
Node.js 直接指挥不了 Hopper 和 Ghidra,REA 的解法是往对方进程里注入一段认证过的代码。bridge/hopper_bridge.py(1300 多行)经 Hopper 的 --python 参数执行,固定在 Hopper 专用 Python 线程上——文件头注释明确警告把 dispatch 挪到 worker 线程会让 Hopper 死锁。启动前 REA 注入 REA_SOCKET 和随机 REA_TOKEN,桥接端用 HMAC 签名与本地 unix socket 对话,不是谁都能连的裸套接字。bridge/ghidra/ReaGhidraBridge.java 是同款安排的 Java 版(5000 行),打包成 Ghidra HeadlessScript,负责清单、反编译、交叉引用和控制流图;Android 侧由 ReaJadxBridge.java 驱动自带 headless JADX。
所有引擎都以子进程身份运行。src/process/ 统一接管进程组、私有目录、超时与清理:OwnedLease 用私有目录加 socket 租约防止同一引擎实例被重复占用,LinuxResourceLimits、HopperLease 等文件负责资源限额和生命周期回收,分析工具不会失控。
证据与契约层:让结果可复现、可审计
所有工具的输入输出 Schema 集中在 src/contracts/ 用 Zod 校验,默认使用 compact 输入 schema profile 降低 Agent 的 context 开销。src/domain/ 三百多个文件是无副作用的纯函数层,负责证据比较、摘要计算和应用图。按 docs/mcp-contracts.md 的约定,产证工具返回 normalized_result 加 evidence_id,完整记录保留在会话 bundle 里,analyze_function 的 Evidence 可直接传给 compare_functions;记录不完整时拒绝而不是猜测。教 Agent 何时连 REA、失败时按阶梯恢复的指令包在 .agents/skills/reverse-engineer-anything/SKILL.md,随包发布。
跑起来:最小可用路径
npx rea-agents setup。前提:Node.js 22.23.4+ / 24+ / 26.12.1+ 与 npm,以及支持本地 MCP 的编码 Agent。为所选 Agent 写入 REA 的 MCP 注册和工作流指令,原有配置自动备份,审批变更后重启 Agent。npx -y rea-agents@latest analyze-javascript-application /绝对路径/应用 --json。前提:纯静态分析,不需要任何原生引擎(Linux 需 PATH 上有 procps 兼容的ps)。输出 JSON:模块、导入、Electron 边界及其证据。rea doctor --json。前提:已通过 npm 全局安装。按任务范围做引擎与注册状态自检,必填检查失败时退出码为 1,可直接当 CI 门禁。
设计取舍
- 驻留插件而非改造引擎。 收益是不动外部工具、换引擎不碰上层;代价是每个引擎都要维护一份自己语言、自己版本的驻留脚本,且 Hopper 桥被钉死在单线程上,并发余地小。
- 延迟启动的双运行时。 src/main.ts 检测到已录制的工具目录(RECORDED_MCP_CATALOG_SHA256)时走 runDeferred,不起分析栈直接服务工具列表,显著缩短
tools/list冷启动;代价是 deferred 与 eager 两条运行时路径必须长期保持一致,目录摘要与运行时对不上就回退。 - 证据优先,体积换可信。 每个结论都带证据、局限性和未知项,输出天然偏重,所以才有输入/输出 schema profile 可调,以及 retained evidence 机制——
analyze_function默认只回摘要,完整卷宗留在会话里按需分页。这是"给 Agent 用"与"给人看"的本质差别。
把"开引擎、反编译、复制"压缩成一组带身份标识的工具调用后,REA 实际定义的是一套新的接口:逆向引擎负责产出,上层只消费带证据的结论。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考




