本文由 莫潇羽@无忆研习社(www.wuyi.wiki)撰写,转载请注明出处。
快速摘要
REA(Reverse Engineer Anything)不是一个"一键抄软件"的破解器,它本质上是把 Ghidra、IDA、Hopper、JADX 这类专业逆向工具,通过 MCP 协议打包成 AI Agent 能直接调用的工作台。 一条 npx rea-agents setup 就能接进 Claude Code、Codex、Cursor 等客户端;之后你对着一个闭源程序说"这个搜索功能怎么实现的",Agent 会自己去反编译、翻字符串、追调用链,然后把结论连同证据和局限一起交回来。但请务必记住三件事:第一,它拆的是"跑在你机器上的那一半",服务端逻辑、云端算法、风控模型永远拆不到;第二,加壳、混淆、虚拟化保护依然能把它挡在门外,VMP 级别的高强度保护目前基本啃不动;第三,也是最容易被忽略的——"研究"和"照着做一个"在法律上是两件事,逆向研究的豁免拿不到"重建"头上。
这篇文章我会从原理讲到实操:REA 的三阶段工作模型、能拆的目标清单、完整安装配置、两个可复现的实操案例(桌面客户端和 Android 端)、逆向攻防的技术底牌,以及我自己整理的一套"目标分级 + 净室隔离"合规工作流。全程不吹"扒光商业软件"这种话,我只讲能落地、且不会给自己找麻烦的做法。
想看完整拆解,往下翻。
一、先把话说清楚:REA 到底是什么,不是什么
1.1 从 MCP 讲起:给 AI 装一个"万能插排"
要理解 REA,得先说清楚 MCP 是什么。
MCP 是 "Model Context Protocol"(模型上下文协议)的缩写。它是一套开源标准,解决的问题很朴素:让 AI 应用能统一地连接外部系统——本地文件、数据库、搜索引擎、专业软件,都可以。
打个比方你就懂了。以前每个家电(电视、空调、音响)都有自己的专属遥控器和接口,AI 想控制它们,就得为每一个单独写一套对接代码。MCP 就像给 AI 配了一个万能插排:不管后面插的是什么设备,前面的接口长得都一样。AI 客户端拿到一份"这个插排上有哪些按钮"的清单,就能按需去按。
这套架构里有三个角色:
|
角色 |
干的事 |
类比 |
|
MCP Host |
运行客户端的那个 AI 应用,比如 Claude Code、Cursor |
拿着遥控器的人 |
|
MCP Client |
Host 内部的中介,负责发请求、接结果 |
遥控器的红外发射头 |
|
MCP Server |
真正提供能力的服务端,响应调用、协调外部工具 |
电视里的接收电路板 |
MCP Server 通常对外暴露三类东西:
- Tools(工具):能执行的动作,比如"反编译这个函数"、"读取这份文件的字符串"。模型根据上下文自己判断该调哪个。
- Resources(资源):能读取的数据,比如文件内容、数据库记录。
- Prompts(提示模板):预先写好的工作流模板。
REA 就是一个 MCP Server。它把你的 Agent 和一堆逆向工具连了起来。
1.2 REA 真正的定位:它是"调度员",不是"反编译器"
这一点特别重要,很多自媒体文章都讲歪了。
REA 自己不做反编译。 它是一个编排层(orchestration layer),坐在你的 Agent 和真正的分析工具之间。它对外提供统一的分析工具箱,对内去调用:
- 深度分析提供方:Hopper、Ghidra(只读模式,需要你自己装)、IDA
- 产物解析提供方:ZIP、APK、IPA、ASAR、MSIX 这类打包文件的清单解析
- 浏览器提供方:通过 Chrome DevTools Protocol 被动观察网页行为
- 进程捕获提供方:在受控的终端会话里记录程序行为
- 原生平台工具:macOS 侧的 Mach-O 解析、代码签名、plist 读取、Swift 符号还原
所以它的技术含量不在"反编译算法"上——那些算法是 Ghidra 和 IDA 二十多年的积累。REA 的创新在工程整合和证据管理上,这一点下面会展开。
用一句话概括:REA 不发明工具,它发明的是"让 Agent 会用工具"这件事。
1.3 三种交付形态,一条命令起步
REA 是 MIT 协议的开源项目,用 TypeScript 写成,代码托管在 GitHub 的 morluto/rea 仓库里。它对外提供三种使用方式:
形态一:CLI(命令行)
在终端直接跑分析,适合脚本化和自动化
命令前缀:rea 或 npx rea-agents
形态二:MCP Server
Agent(Claude Code / Codex / Cursor / Gemini CLI 等)直接调用
这是它最主流的用法
形态三:配套的 Agent 调查技能
setup 时会一并装上,教 Agent"该按什么顺序提问"
安装和使用是这样的:
# 一条命令完成配置(会检测你装了哪些客户端,逐步确认)
npx rea-agents setup
# 只想先看看它打算改哪些文件,用 dry-run
npx rea-agents setup --dry-run
# 装完检查环境,看有没有缺件或配错
rea doctor
安装过程有一点设计我觉得很值得学:它默认是"No"。它会先检测你机器上装了哪些 Agent 客户端,问你要开启哪些能力,把即将修改的文件列给你看,你点头才动手,而且会先备份已有配置。这种"默认保守"的交互,在这类会改配置文件、又会动系统工具的项目里,算是比较少见的克制。
1.4 三阶段工作模型:反编译、理解、重建
REA 把每一次调查都拆成固定的三段,这个框架我建议你记下来,因为它其实就是逆向工程的标准心法:
|
阶段 |
英文 |
做什么 |
产出 |
|
第一阶段 |
Decompile |
打开目标,还原可读代码、字符串、符号名、其他线索 |
伪代码片段、字符串表、符号列表 |
|
第二阶段 |
Understand |
顺着线索从一个模块追到另一个模块,直到能解释清楚功能怎么跑 |
调用关系图、数据流向说明 |
|
第三阶段 |
Recreate |
把学到的东西变成你自己项目里的功能,用你的语言和框架 |
可运行的实现代码 |
REA 自己负责前两阶段,第三阶段交回给你原来的 Agent——因为写代码本来就是 Claude Code、Codex 这些工具擅长的事。
这里要划一条清晰的线:REA 明确说了,它不声称能还原原始源码,也不克隆应用。它给的是伪代码、汇编、符号、字符串和它们之间的关系,并且会标注每条结论背后的证据。
1.5 三个常见误解,先破掉
新手看到"AI 逆向一切"这种标题,很容易脑补出三件事。我把它们提前澄清:
- 误解一:装完就能"扒光"任何软件。 实际它受限于目标有没有加壳、混淆强度、以及你本机装了什么分析工具。没装 Ghidra 或 Hopper,原生二进制的深度分析就是空的。
- 误解二:分析结果就是原始源码。 伪代码是"编译器优化后的逻辑的近似转写",变量名大多是
v1、a2、sub_401230,跟人写的源码差得远。 - 误解三:本地分析就等于不外发任何数据。 工具确实在你本机跑,但 Agent 要理解这些结果,就得把反编译片段、调用关系、字符串发给背后的模型服务商。这一条牵扯到很多合规问题,第八章会细讲。
理解了这三点,你再看后面所有讨论,就不会跑偏了。
二、为什么这波和以前不一样:门槛砸到地板,风险抬到天花板
2.1 变的不是技术,是"用的人"
业内有一个很犀利的判断:REA 的技术不新,新的是用它的人。
让 AI 接上逆向工具,这件事在 REA 走红之前一年就已经有人在做了。IDA Pro MCP、GhidraMCP、Binary Ninja 的 MCP 桥接、JADX 的 MCP 插件、Hex-Rays 官方的 MCP 支持,一个都不缺。REA 走红前几天,还有逆向工具的开发者公开说,他用 IDA 的 MCP 插件,大约一小时就解完了当年 Flare-On 逆向挑战赛的全部题目。
Flare-On 是谷歌旗下 FLARE 团队每年举办的逆向挑战赛,题目大多取材自真实的恶意软件样本。能一小时全通,这水平当然很高,但请注意:这类人本来就懂逆向,MCP 只是让他打字更少了。
REA 干的事不一样,它把这套东西变成了一行命令。
|
维度 |
传统路径 |
接上 REA 之后的路径 |
|
前置技能 |
会反汇编、看得懂汇编、熟悉 IDA 的 API |
会用自然语言提问 |
|
准备时间 |
装 IDA、配插件、找教程,几小时到几天 |
|
|
提问方式 |
自己知道该看哪个函数、哪个字符串 |
描述目标功能,Agent 自己找线索 |
|
工具切换 |
在反编译器、调试器、抓包工具之间来回倒 |
都在一个 Agent 会话里完成 |
|
结果形式 |
自己整理笔记 |
结构化的证据记录,可导出、可对比 |
门槛下降这件事,本身不坏。工具平民化一直是技术进步的常态。
问题在于:门槛下降的速度,远远快于规则科普的速度。
以前你想让 AI 帮你逆向,你得先是个会逆向的人——装好 IDA,配好插件,知道自己该问什么。这一路上无数个坎,其实都在筛掉"只是好奇想看看"的人。现在这些坎没了,直接涌入的是大量没有受过任何合规训练的普通开发者。
2.2 星数增长有多夸张(附一个提醒)
REA 项目的热度,写稿时看已经属于现象级:从最初几千颗星,不到一周就冲到了六位数,并登上了 GitHub Trending 榜首。不同媒体的统计口径不太一样——有的按"累计星数"算,有的按"新增星数"算,还有的报道成文时项目又已经涨了一大截。
所以这里我要提醒一句:任何具体到个位的星数、fork 数、下载量,都只是那一瞬间的快照,参考价值有限。 这类数据变动极快,你要真关心,直接去看仓库首页的实时数字。同样,本文涉及的一切版本号、平台支持范围、定价信息,都以写稿时的公开信息为准,请以官方文档的实时内容为准。
热度之外,真正让普通人第一次"看见"AI 逆向的,其实是游戏圈。
2026 年 10 月初,一批老游戏突然能在浏览器里直接玩了起来。随后有公开报道提到,一位逆向开发者用十几个 Claude Code 和 Codex 智能体并行作业,把一款射击游戏的绝大多数函数还原成了 C++ 源码,其中相当大比例的编译结果与原程序达到字节级一致。注意:他走的是 Hex-Rays 官方提供的 MCP 通道,不是 REA。 也就是说,REA 只是这波浪潮里名字最响的那一个,真正在动的是一整片水面。
2.3 圈内人的分歧:一句"炒作"和一句"挡不住"
有意思的是,职业逆向工程师群体对这件事普遍没那么兴奋。
我看到的公开讨论里,有两种针锋相对的说法:
一种说法是"炒作"。 持这种看法的人(包括几位写过逆向教材的作者)认为,让模型接工具这件事早就有人做了,REA 只是重新包装了一遍;而且,给模型暴露的工具越多,它不一定表现越好——现在已经有 REA 用户抱怨过,它塞给智能体的工具说明太长,用不了多久就把上下文窗口挤满了。
另一种说法是"能拖慢,挡不住"。 支持者认为,混淆和加壳会增加 AI 的分析成本,但并不能从根上阻止它——因为人写混淆规则的速度,赶不上模型试错的次数。
这两种说法我觉得都对,只是说的不是同一件事。前者说的是"能力上限",后者说的是"经济性"。而现实里,决定一个技术会不会普及的,往往不是上限,是经济性。
2.4 别忽略那道"护栏"
还有一个大家讨论得不多、但实操中一定会撞上的东西:模型服务商自己的限制。
有博主公开分享过,他用 Agent 去拆某款国产剪辑软件时,Claude 拆到一半会卡住,也会被限制继续。这类拦截不是说服务商在替你判断"这件事合不合法",而是他们在按自己的内容策略处理——反编译产物里可能包含敏感字符串、可能的绕过逻辑、甚至被误判的恶意代码特征。
这道护栏的存在,对你来说意味着三件事:
- 不透明:你事先不知道边界在哪,只能撞上才知道。
- 不稳定:今天能跑通的流程,明天可能就被拦。
- 不可依赖:别把关键业务流程建立在"某个模型不拦我"这个假设上。
我在自己的实践里,慢慢养成一个习惯:把分析产生的原始产物(伪代码、字符串表)当作敏感数据来对待,而不是随手丢进对话里。这个习惯后面第九节会展开成一套完整做法。
三、能力清单:REA 能拆什么,又永远拆不到什么
3.1 一张总表:覆盖范围与依赖条件
先把 REA 官方文档里列出的目标类型汇总成一张表。这张表我建议你打印出来贴在显示器边上,因为它直接决定了"你手上的问题能不能用它解决"。
|
目标类型 |
能拿到什么 |
依赖条件 |
典型场景 |
|
原生二进制(exe / so / dylib) |
伪代码、汇编、字符串、符号、调用与引用关系 |
本机装 Hopper、Ghidra 或 IDA |
分析无源码的桌面工具、闭源库 |
|
JavaScript / Electron 应用 |
模块、导入关系、source map、路由、IPC 边界、原生插件 |
只需要 Node.js |
分析 Electron 客户端的行为与接口 |
|
.NET 程序集 |
元数据、CIL 指令、原生依赖、版本对比 |
纯静态,无需额外引擎 |
分析企业内的小工具、插件 |
|
网站页面 |
页面结构、脚本、网络观测、截图 |
Chrome 系浏览器 |
弄清某个前端交互怎么做的 |
|
Android APK |
Manifest 声明、类结构、方法级反编译、引用关系 |
无头 JADX + JDK |
分析竞品 App 的功能实现 |
|
Android 设备 |
进程、日志、内存 dump、屏幕捕获 |
adb 连接真机 |
观察运行时的实际行为 |
|
WASM / EVM 字节码 |
校验信息、导入导出、WAT 反汇编、分发选择器 |
WABT 或本地解析 |
链上合约与前端 WASM 分析 |
|
固件镜像 |
区域划分、文件提取、原生分析交接 |
Binwalk / Unblob |
物联网设备固件研究 |
|
网络抓包(HAR) |
请求响应、暴露的载荷、源码位置 |
可选 mitmdump |
分析客户端与后端的通信协议 |
|
Go 二进制 |
函数、字符串、引用关系 |
内置支持 |
分析 Go 写的服务端工具 |
3.2 逐类说说我实际怎么用
原生二进制是最硬核的一档。 给一个没有源码的可执行文件或动态库,它能出伪代码、汇编、字符串、符号表、调用和引用关系。反编译引擎用你机器上装的哪一家都行——Hopper、Ghidra、IDA,三选一。我自己的习惯是:先用字符串搜线索,再用引用关系锁定函数,最后才看伪代码。 很多新手一上来就盯着伪代码看,结果被几千行 v1、a2 淹没,半天没有进展。字符串是导航,伪代码是终点,顺序别搞反。
Electron 应用是性价比最高的一档。 因为 Electron 把渲染层代码打包成 ASAR 归档,本质上是 JavaScript,静态分析就能梳理出模块、imports、路由、IPC 通道和原生 add-on 的关系,根本不用把应用跑起来。对一个做前端出身的人来说,这一档的学习曲线几乎为零。
Android APK 走无头 JADX。 它能把 APK 反编译到方法级,看清 manifest 里声明了什么、类结构长什么样、每个方法里的逻辑是什么。如果接上 adb,还能直接操作真机:看安装包、看进程、拉日志、截屏幕。
网站分析走的是被动观测。 通过 Chrome DevTools Protocol 观察页面结构、脚本加载、网络请求,最后还能截图。要注意"被动"这两个字——它记录的是页面本来就在做的事,不是往页面里注入什么。
WASM、EVM 字节码、固件、崩溃转储这些属于长尾。 平时用不上,但真遇到的时候,能在同一个 Agent 会话里顺手处理掉,省掉了专门去装一套工具链的功夫。
3.3 它永远拆不到的三样东西
这一段是我认为全文最重要的部分之一,因为它决定了你对这个工具的期望值该定在哪。
第一样:服务端逻辑。
REA 分析的是跑在你手里的那一半。你装在本机的客户端能拆开,但别人机房里的服务端你碰不到。这就是为什么很多人幻想"拆了国民级 App 就能拿到推荐算法"——做不到。推荐算法、支付风控、账户体系、社交关系链,这些全都跑在云端。抓包能看到请求和响应,但看不到产生这些响应的逻辑。
第二样:被高强度保护的核心逻辑。
加壳、代码虚拟化、反调试、完整性校验,这些手段本身就是为对抗逆向而生。强壳保护下的函数,反编译出来往往是一片无法识别的字节码。第七章会专门讲这块的技术细节。
第三样:意图和权衡。
就算你把某个功能的实现完整还原出来了,你也还原不出它的作者当时为什么这么选。为什么这个缓存策略定成 5 分钟而不是 30 分钟?为什么这里用轮询不用推送?——这些决策背后的业务约束、历史包袱、性能实测数据,都不在二进制里。
换句话说:逆向能给你"是什么"和"怎么做",给不了"为什么"。 而真正值钱的产品判断,恰恰在最后一个问题上。
3.4 "本地分析"到底意味着什么
REA 的说明里写着,所有分析在本机执行,你的目标文件不会离开你的机器。这句话是真的,但它只说了半句。
完整的版本应该是:分析引擎在本机跑,但分析结果的一部分会流向模型服务商。
为什么?因为 Agent 要理解分析结果,就必须把这些内容放进模型的上下文:反编译出来的代码片段、函数调用关系、提取到的字符串常量。这些东西要发到云端模型那边才能被"读懂"。
所以真实的数据流向是这样的:
你的机器 云端
+---------------------+ +------------------+
| 目标程序(不动) | | |
| | | | |
| v | | |
| 本机分析引擎 | | |
| (Ghidra/IDA/Hopper) | | |
| | | | |
| v | 外发 | |
| 分析产物 -----------|--------->| 模型服务商 |
| (伪代码/字符串/调用图) | | (有独立数据策略) |
+---------------------+ +------------------+
如果你拆的是自己买来的、随便研究的软件,这一步问题不大——顶多是你的使用习惯被看到了。
但如果你拆的是客户交付给你的软件、或者签过保密协议的东西,这一步就可能已经构成违约了。因为保密协议约束的通常是"你不得向第三方披露",而不是"你不得上传到云端"。这两件事在很多法务眼里是等价的。
所以我的第一个实操建议就是:拆之前先问一句,这个目标的代码能不能离开这台机器。 不能的话,就该考虑离线部署的模型方案,或者干脆不做。
四、环境准备与安装:把 REA 接进你的 Agent
4.1 装之前先确认这几件事
我见过太多人卡在第一步,其实都是环境问题。把下面这张表过一遍,能省掉你半小时。
|
检查项 |
要求 |
说明 |
|
Node.js |
建议 20 LTS 或更高 |
REA 的 CLI 走 npm 分发,Node 版本太低会直接报错 |
|
磁盘空间 |
预留几个 GB |
分析产物、证据包、反编译中间文件都会占地方 |
|
Agent 客户端 |
至少装一个 |
Claude Code、Codex、Cursor、Gemini CLI、Windsurf 等都可以 |
|
深度分析引擎 |
按需 |
要拆原生二进制,才需要装 Hopper / Ghidra / IDA 中的一个 |
|
JDK |
按需 |
只在你分析 APK 时才需要,无头 JADX 依赖它 |
|
目标样本 |
必须是你能合法获取的 |
这条是底线,不是建议 |
关于"目标样本必须合法获取"这句,我要再说一遍。 后面第八章会讲清楚法律细节,但这里先把话放前面:来路不正的样本,即使你只是"研究研究",也不会因为你用了 AI 就变得没问题。这是能力和授权的区别,两码事。
4.2 安装流程:四步走
安装本身设计得挺克制,但第一次用还是建议按顺序来,别跳步。
第一步:先跑一次 dry-run。
npx rea-agents setup --dry-run
这一步只做一件事:告诉你它打算改哪些文件。 它不会真的动手。
我强烈建议第一次一定先跑这个。原因很简单——这类工具会往你的 Agent 配置里写 MCP 连接信息,如果你同时用着好几个客户端,动手之前最好心里有数。
第二步:正式配置。
npx rea-agents setup
它会做这么几件事:
- 检测你机器上装了哪些 Agent 客户端
- 问你要开启哪些能力
- 列出即将修改的文件清单
- 先备份已有配置
- 你确认后才应用更改
注意这里有一个默认行为:所有确认项的默认值都是"No"。 这是好设计,你多按几次回车不会误开一堆能力——虽然有点烦,但比"手一抖开了权限"强多了。
如果你已经知道要用哪个客户端,可以直接指定,跳过交互:
# 客户端参数用下划线命名,例如 Claude Code 就是 claude_code
npx rea-agents setup --client claude_code
各主流客户端对应的参数值如下:
|
客户端 |
|
|
Claude Code |
|
|
Claude Desktop |
|
|
Codex |
|
|
Cursor |
|
|
Gemini CLI |
|
|
Windsurf |
|
|
VS Code |
|
|
GitHub Copilot CLI |
|
|
OpenCode |
|
|
Devin |
|
第三步:重启客户端。
这一步特别容易被忽略,而且忽略之后的症状是"装了但好像没生效"。
注意不同客户端的"重启"定义不一样:
桌面应用(Claude Desktop / Cursor / Windsurf / VS Code)
必须完全退出进程再打开,只关窗口不算
macOS:按 ⌘ + Q
Windows:如果有托盘图标,也要从托盘退出
终端类工具(Claude Code / Codex / Gemini CLI)
退出当前会话,重新启动一次即可
第四步:体检。
rea doctor
这个命令会告诉你:有没有缺件、有没有配错、你这台机器上有没有不支持的能力。装完必跑,报错先看它的输出。
4.3 配置文件长什么样
很多人装完之后完全不知道它动了什么。我建议你自己看一眼生成的配置,心里有个底。
MCP 连接信息通常长这样(不同客户端字段名略有差异):
{
"mcpServers": {
"rea": {
"command": "npx",
"args": ["-y", "rea-agents@latest", "mcp"],
"env": {
"REA_WORKSPACE": "/path/to/your/rea-workspace"
}
}
}
}
除了 MCP 连接,setup 还会装一份配套的调查技能(agent skill)。这份技能的作用是教 Agent"按什么顺序提问"——先开目标、再看概览、再搜字符串、再追引用、最后才反编译。这个顺序很重要,下一章我会具体解释为什么。
4.4 目录结构建议
REA 会把分析产物落在工作目录里。我自己的习惯是把每个目标单独放一个目录,方便日后对比不同版本:
rea-workspace/
├── targets/
│ ├── notely-desktop-2.3.1/ # 目标名 + 版本号
│ │ ├── sample/ # 样本本体(不提交到版本库)
│ │ ├── evidence/ # 证据包导出
│ │ ├── notes/ # 我自己的分析笔记
│ │ └── findings.md # 结论汇总
│ └── notely-desktop-2.4.0/
├── templates/
│ └── feature-spec-template.md # 功能说明书模板
└── policies/
└── target-whitelist.md # 允许分析的目标清单
四个约定,你可以照抄:
- 目录名带版本号,因为分析产物能跨版本对比,这个能力很有用
sample/目录加进.gitignore,别把别人的软件提交到你的版本库policies/target-whitelist.md这份文件的意义,到第九章你会明白——它是你日后证明"我只拆了该拆的"的第一手材料- 每个目标单独一份
findings.md,别混在一起写
4.5 常见报错速查
|
症状 |
可能原因 |
处理办法 |
|
装完 Agent 里看不到 rea 工具 |
客户端没完全重启 |
彻底退出进程再打开,Windows 记得看托盘 |
|
提示 Node 版本不兼容 |
Node 太旧 |
升到 20 LTS 或以上 |
|
拆原生二进制时没有伪代码 |
没装 Hopper / Ghidra / IDA |
|
|
分析 APK 报 JADX 相关错误 |
缺 JDK |
装 JDK 后重试 |
|
Agent 聊到一半说上下文满了 |
工具说明太长 |
新开会话;只开本次需要的能力,别全开 |
|
分析到一半模型不往下走了 |
撞上了服务商的内容限制 |
换思路、缩小范围,或改用离线模型 |
最后一行那个问题值得单独说一下。很多人第一次撞上模型拦截时会以为是工具坏了,其实不是。 那道护栏在服务商那边,你改不了。能做的只有两件事:把问题切小,或者换一条不敏感的分析路径。
比如"给我这个软件的完整伪代码"就很容易被拦;而"帮我确认这个方法里数据是怎么流转的"通常能顺利通过。问得越具体、越像正常的工程问题,越不容易撞墙。 这不只是规避风控,它本身也是更好的提问方式——范围小的问题,模型的回答质量也更高。
五、实操一:拆一个桌面客户端的数据同步逻辑
5.1 先说明案例性质
这一节和下一节的案例,都是我为了讲解方便自己编出来的虚构目标,产品名、目录结构、代码片段全部是示意,不对应任何真实软件。我写它们的目的是把"分析流程长什么样"讲清楚,你自己动手时把路径和问题换成你的真实目标就行。
先把话说在这里,免得后面读串了:第五、第六两章里的目标、路径、Agent 输出内容,通通是虚构示意;但穿插在案例中间的那些经验型段落(比如该按什么顺序提问、上下文为什么会莫名其妙被塞满)是我自己在真实项目里踩出来的,属于实操总结。下文出现"我试过""我的习惯是""我的做法是"这类表述时,指的都是后者,跟虚构案例无关。
不要把这些示例的结论直接拿去套真实产品。 真实产品的实现千差万别,而且分析它们之前,你得先确认自己有那个权利——第八章会讲。
5.2 目标设定
假设你手上有一个虚构的桌面笔记应用 NotesLab。让你好奇的地方有好几个:
- 离线全文搜索很快,你怀疑它做了本地索引,但不知道用什么结构
- 多个设备同时编辑同一条笔记时,它处理冲突的方式很顺手
- 有个"批量导出"功能,能导出成好几种格式,你好奇它怎么组织的
这一节我们只挑第 2 个走完整个流程,也就是多设备同步冲突的处理策略。原因很实在:全文检索这一类功能的技术路线就那么几条,看多了容易腻;而"冲突处理策略"背后藏着一个更值得学的设计取舍——什么情况下该让用户决定,什么情况下该让系统悄悄合并。
你只想搞清楚它是怎么做的,然后在自己项目里实现一个思路不同的版本,一行代码都不抄。这就是一个典型的、合规区间内的研究目标。
因为 NotesLab 是 Electron 应用,我们先走 JavaScript 这条最好走的路。
5.3 第一步:找到应用本体
Electron 应用一般会把它打包的代码放在 resources 目录下的 app.asar 归档里。
# macOS 常见位置
/Applications/NotesLab.app/Contents/Resources/app.asar
# Windows 常见位置
C:\Users\<你的用户名>\AppData\Local\Programs\NotesLab\resources\app.asar
# Linux(AppImage 解开后)
squashfs-root/resources/app.asar
如果你不确定路径,用 Agent 直接问也行——它可以调用系统工具去定位。
5.4 第二步:用 CLI 做一次静态分析
这一档的好处是不需要把程序跑起来。用 CLI 直接分析目录或 ASAR 文件:
npx -y rea-agents@latest analyze-javascript-application \
"/Applications/NotesLab.app/Contents/Resources/app.asar" \
--json
加 --json 是为了把结果输出成结构化数据,方便你后面做对比。路径里有空格的话一定要加引号,这个坑我踩过,报错信息还挺难懂的。
这一步能拿到什么?大致是这些:
- 模块清单和它们之间的导入关系
- 路由表(Electron 里通常对应窗口和页面)
- IPC 通道列表——这是关键,主进程和渲染进程之间的通信接口全在这里
- 原生 add-on(
.node文件)的清单
IPC 通道列表为什么关键? 因为它等于一张"功能地图"。功能名往往直接写在通道名里,比如 notes:index:rebuild、sync:merge:resolve 这种。有了这张地图,你就知道该往哪个方向深挖了,而不是在几千个文件里瞎翻。
5.5 第三步:让 Agent 带着具体问题进场
这一步是 REA 的主场。关键是把问题问具体。
这套提问模板我在无忆研习社的内部工作流里用了挺久,前前后后改了五六版,现在长这样:
目标:/Applications/NotesLab.app/Contents/Resources/app.asar
问题:这个应用在多个设备同时编辑一条笔记时,
是用什么策略解决冲突的?
请按这个顺序做:
1. 先列出所有和 sync 相关的 IPC 通道和模块
2. 找出处理冲突的核心函数
3. 说明它用的是哪种策略(时间戳 / 版本向量 / 操作变换 / 其他)
4. 给出证据:涉及的文件、函数名、关键代码位置
5. 明确说清楚哪些结论是静态推断,哪些有直接代码依据
不要给我完整伪代码,我只要策略和证据。
这个模板里有几个设计,都是我试错试出来的:
|
设计 |
为什么这么写 |
|
明确给出目标路径 |
不给路径它会到处找,浪费上下文 |
|
限定"一个问题" |
一次问三四个问题,回答质量会明显下降 |
|
指定分析顺序 |
防止它一上来就钻细节,跳过全局 |
|
要求给出证据 |
这是防"一本正经地胡说"最有效的办法 |
|
明确"不要完整伪代码" |
反编译产物极长,全塞进上下文会挤爆窗口 |
|
要求区分推断和依据 |
逆向场景里,这条能帮你避开很多坑 |
最后两条特别重要。REA 在这一点上的设计我是认可的:它明确区分静态推断和运行时观测,数据不完整就报不完整,不把猜测当事实。 但你自己也得有意识地去追问,别看到一段像模像样的伪代码就信了。
5.6 Agent 实际会调用哪些工具
你发出上面那个问题之后,Agent 内部大致会走这么一串:
1. 打开归档,建立索引
-> open_archive / list_modules
2. 搜索字符串线索
-> search_strings("conflict")
-> search_strings("merge")
-> search_strings("version")
3. 搜索过程/函数名
-> search_procedures("sync")
4. 列出模块与导出
-> list_names
5. 顺着线索追引用关系
-> find_xrefs_to_name("resolveConflict")
-> xrefs(...)
6. 重建调用关系
-> get_call_graph(...)
-> procedure_callers(...)
7. 只对关键函数做反编译(范围受控)
-> procedure_pseudo_code(<关键函数>)
8. 汇总,按你要的格式输出
注意第 7 步是受控的——只反编译关键的那几个函数。这是整个流程里最省钱、也最不容易把上下文撑爆的做法。
整条链路的逻辑,用图表示更清楚:
flowchart TD
A[目标归档 app.asar] --> B[建立索引 / 列模块]
B --> C[字符串与符号搜索]
C --> D{找到相关线索?}
D -- 否 --> E[调整关键词, 回到 C]
D -- 是 --> F[追交叉引用, 锁定函数]
F --> G[重建调用图]
G --> H[对关键函数做反编译]
H --> I[生成带证据的结论]
I --> J[人工复核证据链]
J --> K[写成功能说明书]
5.7 怎么读 Agent 交回来的报告
假设它回来了,结论大概是这个形状(以下是我按常见输出结构示意,非真实结果):
结论:目标采用"最后写入优先 + 字段级合并"的混合策略,静态推断。
依据:
[1] 模块 src/sync/merger.js(文件已从 ASAR 还原)
导出函数 resolveConflict(remote, local)
[2] 该函数引用了 compareTimestamps 与 mergeFields
[3] IPC 通道 sync:merge:resolve 指向该模块
[4] 字符串常量包含 "field_level_merge"
置信度:中
局限:
- 以上为静态分析结论,未做运行时验证
- 存在一个混淆过的辅助模块,未能解析
- 服务端是否参与冲突判定,本地无法确定
我读这类报告的第一件事,不是看结论,是看"局限"那一栏。
如果一份分析报告的局限栏是空的,我基本不信它的结论。因为静态分析一定有盲区——动态加载的模块、运行时生成的代码、服务端的行为,这些都不可能从静态文件里看出来。一份诚实的报告一定会告诉你它哪里不确定。
第二件事是自己抽验一条证据。随便挑一条依据,比如 [3] 那个 IPC 通道,自己去搜一遍,看是不是真的存在。抽一条就够了——如果这一条对,说明整个证据链的可信度还行;如果这一条都编的,那整份报告直接作废。
5.8 这个案例的两个实用技巧
技巧一:先问"有哪些功能",再问"某个功能怎么做"。
很多人一上来就钻细节。更好的顺序是先要一张功能全景图(模块、IPC 通道、路由),拿着这张图自己挑感兴趣的点,再单独开一轮分析。这样你对整体的理解会扎实得多,也不容易被模型的措辞带着走。
技巧二:一次只开一个目标。
这一条跟上面那个虚构案例无关,是我自己真实项目里的经验。我同时把两个目标丢进同一个会话让它对比过,结果上下文很快就被塞满,回答质量断崖式下降——前一个目标的字符串表、调用关系还挂在上下文里,它回答第二个目标时明显在"串味",把两边的函数名混着说。一个会话一个目标,这是硬纪律。 需要对比两个目标的话,分两轮做完,然后把结论放在一张表里自己比,比让它在同一轮里比要靠谱得多。
六、实操二:Android APK 与原生二进制的分析姿势
6.1 APK 这一档:无头 JADX 是怎么工作的
Android 应用的分析走无头 JADX(headless,即不带图形界面,直接用命令驱动)。所谓"无头",就是没有那个让你点点点的界面,一切靠命令和 API。
它能做到方法级反编译,也就是把一个方法里的逻辑还原成可读的 Java 风格代码。配合分析,你能拿到:
AndroidManifest.xml里的声明:权限、组件、入口 Activity、接收的 Intent- 类结构和继承关系
- 每个方法的反编译结果
- 方法之间的引用关系
# 分析一个 APK(示意命令,具体参数以官方文档为准)
npx -y rea-agents@latest analyze-apk \
"/path/to/target.apk" \
--json
如果接上 adb,还能连真机:
adb devices # 确认设备已连接
adb shell pm list packages | grep <关键词> # 看装了哪些包
adb shell ps | grep <包名> # 看进程
adb logcat -d > runtime.log # 导出运行日志
「真机 + 静态分析」这两条路结合起来用,效果会好很多。静态分析告诉你"代码里写了什么",运行时观测告诉你"实际跑了什么"。有些功能是靠动态下发配置开关的,静态看永远是关着的那一条分支。
这里有一个必须提醒的点:APK 里往往包含第三方 SDK、图片素材、字体文件。 你在分析过程中会接触到这些东西。如果你后面要用到其中任何素材(图标、字体、音频),一定要记住:商用需要取得正版授权。 从别人 APK 里扒出来的素材,即使技术上拿到了,也不等于你有权使用。这条界限一定要清楚。
6.2 原生二进制这一档:三件套怎么配合
原生二进制是"最硬核"的一档,因为它离机器最近。这里的反编译引擎要看你自己装了什么:
|
分析引擎 |
授权模式 |
优势 |
我的使用感受 |
|
Ghidra |
开源免费 |
反编译器质量高,插件生态活跃 |
免费里最能打的,适合日常 |
|
IDA |
商业授权(有免费版) |
反汇编视图成熟,脚本能力强 |
老牌,界面和快捷键需要适应 |
|
Hopper |
商业授权 |
macOS 上体验顺滑 |
界面友好,适合新手入门 |
如果你刚开始,我的建议很简单:从 Ghidra 开始。 免费、跨平台、社区资料多,遇到问题容易搜到答案。等到你确实觉得某些场景需要更顺手的工具了,再考虑付费的那两个。
6.3 看原生二进制的正确顺序
不管用哪个引擎,分析的顺序是共通的。这套顺序我用了很多次,基本没变过:
|
顺序 |
做什么 |
为什么放在这个位置 |
|
1 |
看文件基本信息(架构、位数、是否加壳、是否有符号) |
决定后面值不值得投入时间 |
|
2 |
搜字符串 |
字符串是最高效的导航,界面文案、错误提示、日志前缀都在这里 |
|
3 |
用字符串反推函数(交叉引用) |
从"用户能看到的话"跳到"产生这句话的代码" |
|
4 |
看调用图,理清层级 |
避免陷在单个函数里 |
|
5 |
只对关键函数看伪代码 |
伪代码是成本最高的资源,最后才用 |
|
6 |
做动态验证 |
用调试器或运行时观测确认静态推断 |
第 1 步为什么重要? 因为如果目标是高强度加壳的,后面几步基本都可以省了——你会在"搜字符串"这一步就卡住,因为字符串被加密了。第七章细讲这个。
6.4 伪代码到底能信到什么程度
这是新手最容易误判的一环。我举一个示意性的对比。假设某个函数在源码里是这样:
int clamp_value(int v, int lo, int hi) {
if (v < lo) return lo;
if (v > hi) return hi;
return v;
}
经过编译器优化、再被反编译出来,你看到的可能长这样:
// 伪代码示意,非真实输出
int __fastcall sub_401230(int a1, int a2, int a3)
{
int v3;
v3 = a2;
if (a1 >= a2) {
v3 = a3;
if (a1 <= a3)
v3 = a1;
}
return v3;
}
这两段在逻辑上等价,但你如果直接照抄下面这段去重构,会写出很难维护的代码。 因为编译器已经把原来的两个 if 合并成了一个嵌套分支,变量名也没了。
所以用伪代码的正确姿势是:把它当作"逻辑等价物"来理解,而不是当作"源码"来搬运。 你要从里面看出的是意图(这是个范围钳制),而不是它的具体写法。
还有一层坑:内联和优化会"骗"你。 一个函数在源码里只有三行,编译后可能被内联到十几个调用点,你看到的就是十几段重复代码。反过来,一个被编译器合并的大函数,在伪代码里长得像个巨型循环。看到这类反常结构,第一反应应该是"这可能是编译器优化的产物",而不是"原作者写得很烂"。
6.5 案例复盘的三个通用结论
把上面两节合起来,我总结了三条通用结论,你可以直接当作工作原则:
- 字符串先行,伪代码压轴。 搜索成本低的东西先做,反编译成本高的东西后做。这条能帮你节省大量时间。
- 静态和动态必须交叉。 只有静态,你会漏掉运行时行为;只有动态,你会看不懂协议的来龙去脉。两条腿走路。
- 结论必须带证据和局限。 这条既是 REA 的设计原则,也应该是你自己的写作纪律。你交给同事的分析报告,如果局限栏是空的,那就是一份不合格的报告。
七、逆向攻防:为什么"能拆"和"拆得动"是两回事
7.1 先把"保护手段"这个词拆开
很多人把"加壳""混淆""加密"当成一回事,其实它们是不同层级的对抗手段,成本和效果差得很远。我把常见的手段整理成一张表:
|
保护手段 |
原理 |
对抗静态分析 |
对抗 AI Agent |
对性能的影响 |
|
符号剥离 |
编译时去掉函数名、变量名 |
弱 |
弱 |
无 |
|
名称混淆 |
把类名、方法名改成 |
中 |
弱到中 |
无 |
|
控制流混淆 |
打乱分支结构、插入无效跳转 |
中 |
中 |
小到中 |
|
字符串加密 |
字符串常量加密,运行时才解密 |
中到强 |
中到强 |
小 |
|
加壳(压缩/加密) |
整个程序被包一层,运行时解压到内存 |
强 |
强 |
小到中 |
|
代码虚拟化(VMP) |
把关键代码翻译成自定义字节码,在自带虚拟机上执行 |
很强 |
很强 |
中到大 |
|
反调试 |
检测调试器存在,发现就改变行为或退出 |
只对抗动态 |
中 |
小 |
|
完整性校验 |
校验自身代码有没有被改过 |
中 |
中 |
小 |
关键结论在最后两行之外的那一行:代码虚拟化是目前公开技术里性价比最高的强保护。 它也是你判断"这个目标值不值得投入时间"的最重要信号。
7.2 代码虚拟化为什么这么难啃
我用尽量白话的方式解释一遍它的原理。
正常编译是这样的:你的 C 代码 → 机器指令 → CPU 直接执行。
代码虚拟化插了一刀:你的关键代码 → 被翻译成一套只有它自己认识的字节码 → 由程序里自带的一个"虚拟机"来解释执行这些字节码。
这套自带的虚拟机和字节码指令集,是保护方案自己设计的,跟标准 CPU 架构没有关系。于是逆向者面对的情况变成:
- 你看到的不是
mov eax, ecx,而是一段段循环,每段循环在处理一个字节 - 每个"字节"的含义,取决于虚拟机自己的操作码表,而你手上没有这张表
- 操作码表还可能每个版本重新随机生成
公开的技术资料里描述过这个机制的细节:虚拟机会为进程创建新的节区来存放虚拟机代码和虚拟字节码,关键代码的入口被替换成跳转指令,原来的代码位置用垃圾数据填充。虚拟机内部的指令处理程序(handler)还会用大量逻辑门运算来隐藏原本的运算逻辑,再配合对标志寄存器状态的精细控制,让控制流更加难以还原。
这就是为什么它对 AI 特别有效。 AI 的优势是"量"——它可以快速试错成千上万次。但虚拟化保护的问题不是量的问题,是"信息缺失"的问题:在拿到操作码表和虚拟机语义之前,你手上的字节码就是一堆没有意义的数字,试再多次也试不出来。 你得先做一件需要人来判断的事:把虚拟机的语义逆向出来。
7.3 一个容易被忽略的成本:体积
代码虚拟化还有一个副作用:体积会暴涨。下面这组是公开技术博客与安全社区在给 VMProtect 做体积实测时汇总出来的对比数据,我把它整理成表,方便你看比例:
|
测试程序 |
原始体积 |
虚拟化保护后体积 |
膨胀倍数 |
|
计算器小程序 |
约 27 KB |
约 3.5 MB |
约 130 倍 |
|
中型测试程序 |
约 192 KB |
约 3.8 MB |
约 20 倍 |
|
迷宫小程序 |
约 8 KB |
约 3.43 MB |
约 440 倍 |
关于这组数据要说明三点。 第一,它不是厂商发布的官方基准,而是社区与个人博主在多轮实测中汇总出的结果,换一个保护工具的版本、换一组保护选项(比如是否勾选压缩、保护哪些函数),数字都会有出入,所以量级可以参考,具体数值别当标准答案。第二,它和你熟悉的压缩壳方向正好相反——UPX 那类壳是把体积压小,虚拟化壳是把体积撑大,因为虚拟机本体和虚拟字节码都要一起打包进你的程序里。第三,唯一稳定的规律是"至少涨一个数量级":上面三条里涨得最少的也有二十倍,你完全可以拿这一点当信号用。
这条数据对你的实际意义是:体积异常膨胀本身就是一个信号。 一个功能明明很简单的小工具,装完之后体积大得离谱,那大概率是上了虚拟化保护。你可以据此快速判断"要不要继续投入"。
7.4 "壳强不强"取决于怎么用,不看工具名字
这里要纠正一个常见误解:不是"用了某某加壳工具"就等于安全。
公开的技术文章里总结过,如果只是简单套一层壳、不做虚拟化处理、核心逻辑还是明文,那依然有可能被绕过。真正提高门槛的是组合用法:
- 把核心逻辑(比如授权校验、密钥算法)做代码虚拟化
- 加上反调试检测:API Hook 检查、硬件断点扫描、调试器特征识别
- 做流程混淆和指令重排
- 加上动态解密,防止内存直接 dump
- 做硬件绑定授权
五个都做齐,破解成本才会真正抬高。 只做其中一个,尤其是只做前两个,基本上就是"防君子不防小人"。
还有一个现实情况:网上确实能找到一些自动化的脱壳脚本,但它们大多只对早期版本有效。对于启用了完整虚拟化加授权模块的较新版本,还是得结合动态调试、内存分析、语义重构、函数伪代码还原一步步来,几乎没法"通杀"。
7.5 那 AI 到底能不能啃动强壳?
我看到的公开讨论里,圈内人的说法其实比较一致,我把它概括成一句:
"能拆"和"拆得动"之间,差的是成本曲线,不是能力开关。
具体到实际表现,比较有共识的判断大概是这个梯度:
无保护 / 仅符号剥离 -> AI 基本能拿下来,速度很快
轻度混淆 -> 能拿下来,需要多几轮引导
中度混淆 + 字符串加密 -> 拿得下来,但要人工介入拆解关键点
加壳(常见壳) -> 困难的开始,AI 容易卡在壳的处理上
完整代码虚拟化(强壳) -> 目前基本啃不动,需要人类专家深度参与
服务端逻辑 -> 无论什么工具都拿不到
注意最后两行。 一行是技术边界,一行是物理边界。很多文章把这两行混在一起讲,导致读者对能力产生了严重误判。
加壳(常见壳) 那一行也值得说一句。有做逆向产品的公司在公开渠道提到过,碰到加壳样本,模型分析照样会卡住。这说明当前的能力状态是"能处理容易的那一头",公开的成功案例也大多落在这一侧。
7.6 工具越多越好吗?一个反直觉的发现
这一条挺有意思,值得单独说。
我们通常会以为"给 AI 更多工具,它就更强"。但一位调试器与逆向工具的开发者公开表示过相反的看法:他不认为给模型暴露更多工具能让它表现更好。
为什么?因为每个工具的描述、参数说明都要占用上下文。工具越多,光是把工具说明读完就消耗掉大量空间,留给真正分析任务的余量就少了。现在也确实有用户抱怨,工具说明太长,聊不了多久上下文就满了。
这给你的实操启发是:按需开启能力,不要全开。
只分析 Electron 应用?那就别开原生二进制那一堆。只拆 APK?那也不需要 Hopper 相关的工具。每次新开会话、只开这一轮需要的,比"全开然后抱怨它变笨"要有效得多。
7.7 我的三条判断
写到这儿,我的看法可以总结成三句:
- 护城河正在从"藏"转向"贵"。 以前大家相信只要不给源码就安全。现在的事实是:只要程序跑在用户手里,就能被拆开。所以软件公司的应对方向在变化——不是把代码藏得更深,而是让"拆了也没用"。比如把关键逻辑放服务端、用云端签名、把数据和算法绑在一起。一位写过逆向教材的作者给出的建议就很直接:只要程序跑在用户手里,就能被拆开,唯一稳妥的办法是把关键