rea-reverse

JADX MCP Server 配置教程:让 Claude Code / Codex 读 APK 反编译代码(含实例)

基于 REA 6.7.0发布者 rea-reverse更新于 约 6 分钟读完
本页目录

想让 Claude Code 或 Codex 直接读 APK 的反编译代码,做法是把 JADX 以无头方式跑在一个 MCP Server 后面,给 agent 一组「传 APK 路径就能查」的工具。本文用的 Server 是第三方开源工具 REA,配合 jadx-headless-mcp 0.7.1 的 JAR:在 Server 的 env 里设好 REA_JADX_MCP_JAR(可选 JAVA_HOME),重启客户端,agent 就多了 5 个 Android 工具,不需要一直开着 jadx-gui。

下文所有命令都在 macOS 上实际跑过:npm 版 REA 6.7.0、Temurin JDK 17.0.18、0.7.1 的 JAR,Claude Code 和 Codex 都用全新配置。请只分析你有权分析的 APK,本文两个示例都是开源应用。

无头 JADX 和 jadx-ai-mcp 的区别

另一个常见方案 jadx-ai-mcp 是 JADX 图形界面的插件,外加一个独立的 Python MCP Server。你在 jadx-gui 里打开 APK,agent 读的是 GUI 当前加载的内容,适合边点边看的交互式场景。

REA + jadx-headless-mcp jadx-ai-mcp
是否需要 GUI 不需要 需要 jadx-gui 加插件
怎么指定 APK 每次调用都带 APK 路径 GUI 里当前打开的那个
输出 带 APK 和引擎 JAR 摘要的 Evidence 插件返回的内容
同一 Server 能否分析其他目标 原生、JavaScript、.NET、Web、固件 只有 Android

无头方案的好处是结果可复现,CI 或直接用 rea 命令行也能跑。

需要准备什么

  1. 完整的 JDK 17 或更高版本。 REA 启动时要编译一个小的 Java 元数据桥,需要 jdk.compiler 模块,JRE 没有。
  2. jadx-headless-mcp 0.7.1,v0.7.1 release 里的 -all.jar,SHA-256 为 6e5eacf500b64292bfb73c49797c1958f6ee44646e43e868039ae7feb573ff75。
  3. REA 6.7.0,已注册到你的 agent(见 安装指南)。
  4. Linux、macOS 或 Windows x64。其他平台(如 Windows arm64)会返回 unsupported_host。

这些 REA 都不替你装。每次调用 JADX 工具时,它会检查平台、找到 JAR 和 java、执行 java --list-modules,再读 JAR 的 ZIP 目录确认桥接所需的类都在。任一步失败都返回 capability_unavailable,JADX 不会启动。

第 1 步:检查 JDK

java --list-modules | grep -E '^(java\.base|jdk\.compiler)@'

要有 jdk.compiler 这一行,且 java.base@ 版本不低于 17。没有 jdk.compiler 就说明装的是 JRE。装了多个 Java 时用 JAVA_HOME 指定,必须是绝对路径。

第 2 步:下载并校验 JAR

shasum -a 256 jadx-headless-mcp-0.7.1-all.jar
# Windows PowerShell:Get-FileHash .\jadx-headless-mcp-0.7.1-all.jar -Algorithm SHA256

一定要用 -all 的 JAR。REA 会检查里面有没有 jadx-headless-mcp 的会话类、JADX 的 JadxDecompiler、Gson 和 Kotlin MCP SDK 的 Server 类,普通 JADX 发行包或瘦 JAR 都过不了。放到固定位置,比如 ~/tools/jadx/。

第 3 步:先在终端里跑通

改 agent 配置之前,先确认 Java 和 JAR 没问题。以 F-Droid 客户端 的 APK 为例:

export REA_JADX_MCP_JAR="$HOME/tools/jadx/jadx-headless-mcp-0.7.1-all.jar"
export JAVA_HOME=/absolute/path/to/jdk-21   # 可选

rea doctor --provider jadx --json
rea inspect-android-package /absolute/path/F-Droid.apk --json

doctor 的输出(节选):

"name": "jadx", "ok": true, "classification": "healthy",
"detail": "JADX MCP bridge JAR validated at /…/jadx-headless-mcp-0.7.1-all.jar; its path is recorded for agent registrations. This checks configuration, not whether a selected APK fits in the worker's memory."

我们 2026-10-20 下载的 F-Droid 是 2.0.2(SHA-256 19d51fad…7b52),耗时 4.5 秒,报告包名 org.fdroid.fdroid、min SDK 24、target SDK 37、31 个权限、11,960 个类。这一步通了,之后 MCP 再出问题就只可能是 agent 配置。

第 4 步:在 Claude Code 里加 env

在已经设好 REA_JADX_MCP_JAR 的终端里运行 setup,它会把变量写进注册信息(JAVA_HOME、REA_JADX_HEAP_MIB、REA_JADX_ACTIVE_PROCESSOR_COUNT、REA_JADX_OPERATION_TIMEOUT_MS 设了的话也一并写入):

export REA_JADX_MCP_JAR="$HOME/tools/jadx/jadx-headless-mcp-0.7.1-all.jar"
npx [email protected] setup --client claude_code --client codex --yes

加 --dry-run 会先打印 Detected provider environment: JAVA_HOME=…, REA_JADX_MCP_JAR=…,不写文件。MCP 客户端启动 Server 时用的是配置文件里的环境变量,不是你终端的,所以要打开文件确认。Claude Code 的配置在 ~/.claude.json:

{
  "mcpServers": {
    "rea": {
      "command": "npx",
      "args": ["-y", "[email protected]", "mcp"],
      "env": {
        "JAVA_HOME": "/absolute/path/to/jdk-17",
        "REA_JADX_MCP_JAR": "/Users/you/tools/jadx/jadx-headless-mcp-0.7.1-all.jar"
      }
    }
  }
}

如果运行 setup 时没设变量,就手动补上 env。然后彻底退出 Claude Code 再开新会话(正在运行的客户端会沿用旧的 Server 进程和旧环境),用 /mcp 确认 rea 已连接。以后重跑 setup 不会覆盖你已有的 env。

第 5 步:在 Codex 里加 env

Codex 读 ~/.codex/config.toml(或 $CODEX_HOME/config.toml)。setup 写入的内容:

[mcp_servers.rea]
command = "npx"
args = [ "-y", "[email protected]", "mcp" ]
startup_timeout_sec = 30
tool_timeout_sec = 450

[mcp_servers.rea.env]
JAVA_HOME = "/absolute/path/to/jdk-17"
REA_JADX_MCP_JAR = "/home/you/tools/jadx/jadx-headless-mcp-0.7.1-all.jar"

Windows 上路径用 TOML 字面量字符串,例如 'D:\tools\jadx-headless-mcp-0.7.1-all.jar';setup 写的 command 保持原样。改完开一个新的 Codex 会话。

第 6 步:大 APK 的可选限制

变量 作用 示例
REA_JADX_HEAP_MIB -Xmx<value>m 8192
REA_JADX_ACTIVE_PROCESSOR_COUNT -XX:ActiveProcessorCount=<value> 4
REA_JADX_OPERATION_TIMEOUT_MS 单次操作超时(默认 120 秒) 300000

三个都只接受纯整数,堆写 8g、超时写 5m 都被拒绝了。大 APK 跑不动时,加大堆内存通常比反复重试有效。

5 个工具与提问方式

MCP 工具 CLI 命令 用途
inspect_android_package rea inspect-android-package Manifest、SDK、权限、数量统计
search_android_classes rea search-android-classes 按类名子串搜索(区分大小写)
inspect_android_class rea inspect-android-class 字段、方法、重载序号
inspect_android_method rea inspect-android-method 反编译单个方法(必要时回退 smali)
trace_android_references rea trace-android-references 静态入向引用

每次调用都带 APK 的绝对路径 path,不存在「先打开」这一步。提问时说清目标和想要的证据,不用规定工具顺序:

Using REA's Android tools, inspect /absolute/path/F-Droid.apk.
1. Report the package name, minSdk/targetSdk and the declared permissions.
2. Find the class registered as android:name on <application> in the manifest,
   list its methods, and decompile onCreate.
3. Tell me which statements come from decompiled code and which are your
   inference. List anything you could not determine.

从 manifest 入手,agent 拿到的是真实类名。发布版常被压缩和重命名,只靠类名搜索容易漏。要求列出「无法确定的部分」,能让 agent 把观察到的事实和推测分开。

实例:Aegis 的六位验证码是怎么算出来的

目标是 Aegis Authenticator v3.4.3(GPL-3.0),从 GitHub release 下载。先记下摘要,每个结果都会带上它:

shasum -a 256 aegis-v3.4.3.apk
# fa091c91912c0d56ec3553394fac4158f9a9d178e93bcbb35419601d788cb528  (6,881,897 字节)

F-Droid 上的版本由 F-Droid 自己构建签名,字节和摘要都不一样。下面是 agent 通过 MCP 会做的同一组调用,这里用 CLI 演示。

包信息。 rea inspect-android-package /absolute/path/aegis-v3.4.3.apk --json 用时 3.8 秒,normalized_result 节选:

"package_name": "com.beemdevelopment.aegis",
"version_name": "3.4.3",
"version_code": "82",
"min_sdk": "23",
"target_sdk": "36",
"class_count": 3077,
"resource_count": 1106,
"signature_verification": "not_performed"

注意 version_code 和 SDK 级别是字符串。这一步不验签名,也不会运行应用。

通过 MCP 分析大 APK 时传 detail: "summary":Aegis 的响应从 26,991 个字符降到 4,634 个,去掉的是解码后的 manifest 文本。之后要看 manifest,把摘要里的 parent_evidence_id 传给 inspect_analysis_view,参数为 {"source": {"kind": "retained-evidence", "evidence_id": "<parent_evidence_id>"}, "view": {"kind": "android", "facet": "manifest", "offset": 0, "limit": 4000}},分页读取(offset 按 UTF-16 码元计,Aegis 的 manifest 共 10,988)。这个 ID 只在产生它的会话里有效,拿到别的会话里用会返回 evidence_integrity_mismatch。

搜索类名。

rea search-android-classes /absolute/path/aegis-v3.4.3.apk Totp

三个结果:otp.TotpInfo、ui.views.TotpProgressBar、importers.TotpAuthenticatorImporter(省略了包名前缀),只有第一个像是计算逻辑。

列成员,再读指定重载。

rea inspect-android-class /absolute/path/aegis-v3.4.3.apk com.beemdevelopment.aegis.otp.TotpInfo
rea inspect-android-method /absolute/path/aegis-v3.4.3.apk \
  com.beemdevelopment.aegis.otp.TotpInfo getOtp --overload-index 1

getOtp() 是 0 号重载,getOtp(long) 是 1 号。不给序号 REA 会直接拒绝,不会替你猜。序号只对这个 APK 和引擎有效,换了构建不要沿用。

找调用者。

rea trace-android-references /absolute/path/aegis-v3.4.3.apk \
  com.beemdevelopment.aegis.otp.OtpInfo --method-name getOtp

两个调用者:ui.views.EntryHolder.getOtp(显示验证码)和 ui.MainActivity.copyEntryCode(复制验证码)。每条引用都带 direct_invokes 列表,包含操作码(如 INVOKE_VIRTUAL)、DEX 方法索引和代码单元偏移。OtpInfo.getOtp 只有一个重载;追踪 TotpInfo.getOtp 时不带 --overload-index 会被拒绝:Select one of 2 overloads: 0: getOtp(): java.lang.String; 1: getOtp(long): java.lang.String。

怎么看这些结果。

  • 反编译出的 Java 是推导出来的视图,文本完整不代表语义完整。静态初始化块(<clinit>)返回的是 smali。
  • 引用是静态的入向关系,不是运行时实际发生的调用,反射和动态加载仍然未知。
  • 压缩过的构建里类名不可信:Aegis 的 OTP 生成逻辑出现在 kotlin.ExceptionsKt 下,所以要从已知入口顺着调用往下跟。

资源、设备和 Flutter 相关的工具见 用 REA 分析 Android APK。

连不上时先查这些

以下都是我们自己踩到过的:

  • Server 读不到 REA_JADX_MCP_JAR。 没写进 Server 的 env,或者写的是相对路径。在终端里 export 影响不到客户端启动的 Server。
  • JAVA_HOME 是相对路径或用了 ~。 设成 JAVA_HOME=jdk17 时,6.7.0 的 Server 打印 REA configuration is invalid: JAVA_HOME must be absolute.,不响应 initialize,/mcp 里 rea 显示失败。请写字面量绝对路径。
  • 装的是 JRE。 元数据桥编译失败,把 JAVA_HOME 指向完整 JDK。
  • JAR 不对。 非 all 的包或 JADX 发行包通不过类检查。
  • 堆大小带了单位。 报错 REA_JADX_HEAP_MIB must be a positive safe integer; received 8g.
  • 大 APK 超时。 加大堆内存、缩小请求范围,超大应用不要用空查询列全部类。
  • 手里是 .aab。 App Bundle 会被拒绝,请用独立 APK(见 APK 与 AAB 解包)。

Windows 我们没有测试。Windows x64 上如果 Server 找不到 Java,就在 env 里设 JAVA_HOME;也可以在 WSL 里配 Linux 版 JDK。