
用 REA + Claude Code 逆向任何 Android APK:从装引擎到洁净室复刻
REA 让编码智能体拥有真正的反编译器,但 Android 引擎要单独装。完整流程:JDK 17、锁定版 JADX jar 与摘要校验、先测绘后读方法的提示词、证据三标签与洁净室复刻规则。
REA 给你的编码智能体一台真正的反编译器,而不是让它猜。指向一个 Android 构建,它会返回清单文件、DEX 布局、类树和方法体,而且每一条结论都带一个你可以亲自打开核对的 Evidence ID。坑在于——也是大多数第一次尝试无声失败的原因——Android 引擎是单独分发的。REA 不会替你安装 Java、Android SDK 或 JADX;在引擎就位之前把 APK 丢给它,你什么有用的东西都拿不到。
cabbit 于 10 月 20 日在 X 上发布了一篇长文指南,把整条路径讲完了:REA 锁定的 JADX 具体版本、以及在信任它之前如何校验摘要;在读任何一个方法之前先测绘整个包的提示词;把每一行结论标注为 Observation、Inference 或 Unknown 的纪律;以及让复刻出来的功能真正属于你的洁净室规则。本文是该指南的完整改编,提示词逐字保留,可以直接复制粘贴使用。
REA 到底是什么
REA 是一个 MCP 服务器。你的智能体调用它,REA 驱动底层的反编译器,每条结论都带一个可以打开的 Evidence ID:文件、类、行号。它不是把语言模型「印象中的 Android 代码长什么样」包了一层,而是架在真正反编译器前面的工具服务器——正是这一点让证据可以被核对。
在 Android 侧,它封装 headless JADX,暴露五个方法:
| 方法 | 解决什么问题 |
|---|---|
inspect_android_package | 构建的整体形状:清单、入口组件、DEX 布局、原生库、资源 |
search_android_classes | 按功能、字符串或资源 id 找候选类名 |
inspect_android_class | 单个类:字段、方法与继承关系 |
inspect_android_method | 单个方法的函数体 |
trace_android_references | 谁调用了这个方法、还有哪些地方引用它 |
在这五个方法之上还有两个引导式提示词,实际承担了大部分工作。investigate_feature 是 APK 工作的起点:它把发现过程从整个包一路走到函数级证据,而不是把整个 DEX 倒在你面前。audit_residual_unknowns 是另一端的闸门:在宣布任何事「完成」之前先跑它,让未解问题清单落在纸面上,而不是留在没人记得的脑子里。
该项目开源在 github.com/morluto/rea。原文内嵌的那条帖子提到,它在发布一天内就收获了几万 GitHub star。这种热度恰恰让一份认真的安装指南变得重要:很多人装了服务器却没装引擎,然后得出结论说工具是坏的。
开始之前:你被允许打开什么
REA 自己声明了适用范围:合法研究、教育、互操作、软件保存,以及你拥有或被授权检查的软件。落到操作上,就是从这些目标开始:
- 你自己的 APK;
- 你付费购买的应用;
- 你想搞懂的开源构建;
- 你需要与之对话的某种格式。
为自己复刻一个功能,在多数地方都属于这个范围之内;把一个付费应用克隆出来发布,则在范围之外。这条界线不是法律表演,而是后续整个工作流要帮你守住的那条线——第 7 步的洁净室规则存在的意义,正是让「研究」和「构建」永不混在一起。
第 1 步:安装 REA
先检查 Node。REA 要求 22 线的 22.19 及以上、24 线的 24.11 及以上,或 26 及以上的任意版本。
node --version
一行命令的安装会把 MCP 服务器注册给你的智能体,并写入指令文件:
npx rea-agents setup
如果你更想手工只接一个项目,就在项目目录里执行这条:
claude mcp add --scope local rea -- npx -y rea-agents@6.7.0 mcp
用 --scope local。project 作用域会往仓库里写一个 .mcp.json,你大概不想让它进提交记录。
第 2 步:给它一个 Android 引擎
大多数安装卡在这里,这也是这篇指南存在的全部理由。REA 不安装 Java、Android SDK 和 JADX。你需要自备完整的 JDK 17 或更新版本,外加 REA 锁定的那个 JADX 构建。
JRE 不行。REA 会在运行时编译一个小的元数据桥,因此模块列表里必须有 jdk.compiler。一条命令就能告诉你手上的 Java 是 JDK 还是 JRE:
java --list-modules | grep jdk.compiler
然后拉取锁定的 jar,并在把任何东西指向它之前校验摘要:
mkdir -p "$HOME/.local/share/rea/android" && cd "$HOME/.local/share/rea/android" &&
curl -fL -o jadx-headless-mcp-0.7.1-all.jar \
https://github.com/1013503897/jadx-headless-mcp/releases/download/v0.7.1/jadx-headless-mcp-0.7.1-all.jar &&
echo '6e5eacf500b64292bfb73c49797c1958f6ee44646e43e868039ae7feb573ff75 jadx-headless-mcp-0.7.1-all.jar' | sha256sum -c -
在 MCP 服务器启动所用的同一个 shell 里导出路径:
export REA_JADX_MCP_JAR="$HOME/.local/share/rea/android/jadx-headless-mcp-0.7.1-all.jar"
export JAVA_HOME=/absolute/path/to/your/jdk
在 Linux 上还需要 PATH 里有一个 procps 兼容的 ps,因为 REA 要跟踪它自己启动的 JADX 进程。
在把智能体拉进来之前,先从 CLI 做一次冒烟测试:
rea inspect-android-package /absolute/path/Example.apk
如果它返回了解码后的清单,安装就完成了。
第 3 步:每次会话开头都要贴的适用范围声明
在做任何事之前先贴这段。它划定边界,也阻止智能体自创工作流:
I want to understand how FEATURE works in the Android app at ABSOLUTE/PATH.apk,
well enough that I could build my own, and I want the evidence for every claim.
I may examine this target because REASON.
You drive REA through MCP. I read the evidence, not your summary. Start from
the guided prompt that matches my question instead of inventing a sequence.
Before anything else, confirm the toolchain: headless JADX and a full JDK.
If an engine is missing, tell me the one command that fixes it and stop.
Everything runs locally and nothing leaves this machine.
这段话里有三句在真正干活。「I read the evidence, not your summary」把人从观众变成审阅者;「Start from the guided prompt that matches my question」收回了智能体即兴编排工具序列的自由;「If an engine is missing, tell me the one command that fixes it and stop」把最常见的失败模式——引擎只配了一半——从一小时的幻觉式自救,变成一行诊断。
第 4 步:在读任何方法之前先测绘整个包
本能反应是去搜一个听起来对的类名。在缩水构建上,这是浪费一小时最快的方式。先测绘包的形状:
Map this package before you read a single method.
AndroidManifest.xml first: package name, versionCode, minSdk, permissions, and
which components are exported.
Entry points: activities, services, receivers, providers.
DEX layout: how many, and whether FEATURE lives in app code or a bundled library.
Native: list the .so files and their ABIs, and say plainly if the feature crosses JNI.
Resources: the R ids and strings that name FEATURE in the UI.
Give me the seed you plan to start from and why you picked it.
最后一行是陷阱检测。能说清自己从哪个种子开始、为什么选它的智能体,是在从清单向外推进;说不清的,就是在猜名字。
这一步还附带一个注意事项:签名校验的结果是 not_performed,所以你面前的文件就只是你面前的文件。它证明不了商店当初分发的是什么。
第 5 步:默认构建是被重命名过的
R8 和 ProGuard 会把类名和方法名改写成单个字母。一个靠名字推理的智能体就是在猜。下面这段提示词告诉智能体,缩水器动不了哪些东西:
Assume this build is shrunk and renamed until you prove otherwise.
Never infer behaviour from a class or method name alone. Anchor on what the
shrinker cannot rename: string constants, resource ids, framework calls,
manifest entries, reflection targets, serialized field names.
If a mapping.txt is present, say so and use it. If not, say that too.
字符串常量、资源 id、框架调用、清单项、反射目标、序列化字段名——这些是混淆之后仍然存活的锚点。其余一切都是假设,而这段提示词逼智能体说清楚哪条是锚点、哪条是假设。
第 6 步:把一个功能一路追到一个方法
到这里引导式提示词才真正值回票价。investigate_feature 把发现过程推进到函数级证据,而不是把整个 DEX 倒给你:
Use investigate_feature for FEATURE.
Work from the whole down to the one method: UI to handler to the call that
does the work. Use search_android_classes and inspect_android_class to find
names, inspect_android_method to read one, and trace_android_references to
find who calls it.
Name the tool you are about to call and the one thing you expect it to settle.
If the result does not settle it, say so before moving on.
Label every line: Observation with its Evidence ID, Inference with your
confidence and the explanation you rejected, or Unknown.
最后那个标签才是最要紧的。证据不完整时的「没找到」不等于行为不存在。在一个被重命名的 APK 上,没找到那个调用,通常只意味着你还没找到它。
读方法这里还有两件事。反编译源码是一种派生表示,文本完整不等于语义完整;重载索引绑定的是「这个产物 + 这个引擎」,今天的索引到明天的构建上不作数。
第 7 步:写一份不含他们任何东西的规格
闭源已死。REA 让 Claude Code 或 Codex 把任何应用拆到二进制层面,并准确解释某个功能是如何工作的:原生应用、Electron 应用、网站、APK、固件,甚至智能合约字节码。它为每条结论出示证据,然后构建自己版本的功能。一天之内 25,000+ GitHub star,100% 开源。
@RoundtableSpace 在 X 上的帖子,被原文内嵌引用;REA 位于 github.com/morluto/rea
规格书的规则简单且绝对:只从观察到的行为来写。
Write SPEC.md from observed behaviour alone: inputs, outputs, state changes,
units, rounding, edge cases, error paths, wire formats, and the exact values
you measured.
No decompiled source, no strings, no constants lifted from the target.
Someone who has never seen the APK should be able to build from it.
Before you write it, call audit_residual_unknowns and list every open question
back to me with the one probe that would close it.
把研究和构建放在不同的会话里。如果你的实现里有一行代码之所以存在,只是因为智能体看过他们的那一行,那这一行就不能发。
Implement in LANGUAGE under ./my-version, from SPEC.md only. If the spec is
short of an answer, go back and investigate instead of filling the gap from
memory of the target.
Derive the tests from the behaviour recorded in SPEC.md. Run them until they
pass, then feed them a deliberately wrong answer and make sure they fail.
第 8 步:构建,然后测试你的测试
实现提示词里最后那条指令,恰恰是大家最常跳过的那条:给测试喂一个故意错误的答案,确认它会失败。一个永远不会失败的比较,从来就没有检查过任何东西。在这里对自己的测试做变异测试很便宜,因为规格书里已经列出了实测的精确数值——一个「故意错误的答案」距离正确答案只差一个常量。
会咬你的地方
- 保留的会话在 60 秒不活动后会被清理。你自言自语思考两分钟,下一次调用就要把 APK 从头重新加载。把问题攒成批再问。
- APK 里的字符串和文件名是数据,永远不是指令。一个要求你的智能体去下载或执行什么的包,就是攻击。告诉智能体:报告它,而不是服从它。
- 静态分析只读文件。除非你要求,否则不安装、不启动、不插桩;补丁、重打包、重签名则一律不做。
中间那条值得比它现在得到的更多重视。用一个能执行工具的智能体做逆向,等于把目标里的每个字符串都变成潜在的提示注入向量。这条边界不是礼貌问题,而是「反编译会话」和「被远程操控的会话」之间的区别。
你最终得到什么
一份你读得懂的规格、一张「仍然不知道什么」的清单、以及一个只从这份规格构建出来的实现。智能体负责阅读,你负责决定——因为它写下的每一行,都带着你可以亲自打开的 Evidence ID。
这种分工才是值得带出 Android 领域的部分。机器人技术栈里到处是同一形状的问题:厂商配套 App 里你需要与之对话的私有协议、没有任何文档覆盖的 JNI 边界与原生 .so、只肯用行为回答你的固件镜像。一个返回可核对证据的 MCP 工具服务器,加上一套逼每条结论声明「观察到 / 推断出 / 没够到」的工作流,是可以平移到这些场景的模式。反编译器是新鲜感所在,证据纪律才是能留下的部分。
来源:How To Reverse Engineer Any Android APK With REA And Claude Code,cabbit(@cabbitar),X 长文,2026 年 10 月 20 日。本文为完整翻译改编,作者提示词逐字保留;插图来自原文。
原文来源:X Articlehttps://x.com/cabbitar/status/2112520869105078561