apk-reversing — APK 脱壳与全量反编译还原
定位:这是
android-security-audit的上游准备技能——把 APK 变成"可挖的全量源码",交付标准目录后android-security-audit直接开挖(密钥追踪/组件安全)。本技能只负责还原,不挖洞;漏洞挖掘见android-security-audit。只有 APK 有加固壳时才必须完整走本技能;无壳 APK 直接 JADX 反编译即可开工。
快筛前置(省时):无壳大包(>100MB)可先用
asc-fast-hunt秒级快筛(Manifest 组件面 + 密钥/签名/接口定位),确认值得深挖再付全量反编译成本;已知有壳则直接走本技能——壳包的 DEX 里只有 stub,用 ASC 搜不出东西属正常现象,不要误判为"没有密钥"。
何时调用(触发条件)
- 拿到 APK 但 JADX 打开是 stub/空壳、看不到真实代码
- 入口类是
com.stub.StubApp/com.secneo.apkwrapper等加固特征类 lib/下只有壳 so(libshella、libjiagu、libSecShell 等)- 需要提取 so、H5/assets、资源、签名等全量产物
- 为
android-security-audit准备标准输入(jadx_out/+apktool_out/+lib/+assets/) - 无壳但包很大(>100MB),想先快筛再决定是否值得全量还原 → 先走
asc-fast-hunt(秒级定位密钥/接口)
标准产物(交付标准)
reversing/<package>/
├── jadx_out/ # Java 源码(JADX 反编译,必须是真实代码而非 stub)
├── apktool_out/ # smali + 资源 + AndroidManifest(apktool)
├── dex/ # 脱壳后的 dex(未合并进 jadx 时单独留档)
├── lib/ # 全部 so(arm64-v8a 优先,可 IDA/Ghidra 打开)
├── assets/ # H5/JS/证书/密钥/配置等资源
├── origin.apk # 原始 APK 存档
└── report.md # 壳类型 / 脱壳方式 / 产物清单 / 未还原项
交付检查:jadx_out 里能 grep 到真实特征串(appKey/secret/接口域名)、lib/ 的 so 可被 IDA/Ghidra 打开搜导出表、assets/ 的 H5 完整——满足即交付 android-security-audit。
一、APK 获取
# 已安装设备上提取
adb shell pm list packages | grep <关键词> # 找包名
adb shell pm path <package> # 拿到 APK 路径
adb pull <路径> origin.apk # 拉取
# 模拟器/真机均可;也可应用商店、官网、第三方平台、抓包拦截下载地址
- 优先取最新版本(厂商 SRC 要求应用商店最新版)
- 同一 App 多渠道包(小米/华为/Google Play)差异可能影响脱壳难度,可换渠道试
二、壳识别(先识别再脱壳)
快速判断:
# 1. 解压看 lib 与入口
unzip -l origin.apk | grep -iE "\.so$|stub|wrapper"
# 2. JADX 打开看 Application/入口类
jadx -d /tmp/quick origin.apk && grep -rn "StubApp\|class.*Application" /tmp/quick/sources/
常见壳特征表:
| 加固 | 特征(lib/入口类) | 脱壳方式 |
|---|---|---|
| 腾讯乐固 | libshella-*.so / libtprt.so / com.stub.StubApp |
frida-dexdump / 模拟器运行 dump |
| 梆梆 | libSecShell.so / SecShell |
frida-dexdump / 反射大师 |
| 爱加密 | libexec.so / libexecmain.so / com.secneo.apkwrapper |
frida-dexdump |
| 360 | libjiagu.so / libjiagu_x86.so |
360 脱壳 / 反射大师 |
| 娜迦/顶象 | libchaosvmp.so / libddog.so |
内存 dump + dex 修复 |
| 腾讯御安全/其他 | libtosprotection.so 等 |
frida-dexdump / 运行 dump |
| DCloud uni-app | 无壳 so,assets/apps/<appid>/www/ |
无需脱壳,直接提取 www/ 即前端源码 |
关键判断:JADX 打开后全是 stub 包装类 = 有壳,必须脱壳;能直接看到业务类 = 无壳,跳过脱壳直接反编译。
三、脱壳(核心)
方式 1:模拟器 + Frida dump(最通用,推荐)
# 1. 准备 root 模拟器(MuMu/夜神/雷电)并安装 frida-server(架构匹配)
adb push frida-server /data/local/tmp/
adb shell "chmod 755 /data/local/tmp/frida-server && /data/local/tmp/frida-server &"
# 2. 安装目标 App 并启动(dex 解密加载后才能 dump)
adb install origin.apk
# 3. dump 内存中的 dex
frida-dexdump -U -f <package> -o dex/
# 4. 或 frida-unpack 脚本(hook DEX 加载点,dump 更全)
- 脱壳时机:App 启动后 dex 才在内存解密,必须先运行再 dump
- 多个 dex 分片 → 合并后统一交给 JADX
- dump 的 dex 可能头部损坏 → 用 dex 修复工具(如 dexfixer)修复后再反编译
方式 2:在线脱壳服务(快速,适合简单壳)
- 上传 APK 到在线脱壳平台拿还原后的 dex(仅限自有授权目标,注意上传合规)
方式 3:非 root 场景(真机/普通模拟器)
- 部分旧壳可
adb backup -f app.ab <package>提取(API 23- 适用) - 或换 root 模拟器走方式 1
方式 4:H5 / uni-app 类(无需脱壳)
# DCloud uni-app:assets/apps/<appid>/www/ 直接是前端源码
unzip -o origin.apk -d apktool_out && ls apktool_out/assets/apps/*/www/
# 微信小程序内嵌:见 miniprogram-security
四、反编译与产物还原
# JADX(无壳:直接反编译 APK;有壳:反编译脱壳后的 dex)
jadx -d jadx_out origin.apk
# 或
jadx -d jadx_out dex/
# apktool(Manifest / smali / 资源 / lib)
apktool d origin.apk -o apktool_out
# so 提取(apktool_out/lib/ 或直接解压)
cp -r apktool_out/lib lib/
# H5/assets 提取
cp -r apktool_out/assets assets/
- so 层:
lib/arm64-v8a/优先;Ghidra + GhidraMCP 静态优先(list_functions定位 JNI 导出 →decompile_function拿伪 C),Radare2(izz/afl)做轻量侦察;导出表搜Java_前缀(静态注册)或JNI_OnLoad/RegisterNatives(动态注册)——对应android-security-audit一、1.3 SO 层追踪 - H5 层:
assets/下 HTML/JS 搜加密库(JSEncrypt/CryptoJS)与密钥常量;动态加载的远程 H5 记录加载 URL——对应android-security-audit一、1.4 H5/JS 层追踪 - 签名/证书:
META-INF/*.RSA、res/raw/下的证书与密钥,供重打包/校验分析
五、Ghidra + GhidraMCP 环境准备(AI 分析 so 的启动路径)
目标:让 Agent 能自主驱动 Ghidra 分析 .so(定位 JNI 导出 → 反编译伪 C → 读算法重写)。GhidraMCP 操作的是"已导入并分析完的程序",所以 .so 必须先导入 Ghidra 并跑完自动分析。
5.1 安装与启动
- Java 21+(Ghidra 11.x 要求);macOS:
brew install --cask ghidra或官网下载 - 装 GhidraMCP 插件:下载 LaurieWired/GhidraMCP 的 Ghidra 扩展 zip → Ghidra
File → Install Extensions安装 → 重启 Ghidra - 启动 MCP server:Ghidra 内
Window → Script Manager运行ghidra_mcp.py→ 起本地 HTTP MCP server(默认端口 8192,SSE 端点;端口/工具名以仓库 README 为准)
5.2 导入 .so(两种方式,任选)
方式 A(GUI 手动):File → New Project 建工程 → File → Import File 选 libxxx.so → Import → 勾选 Auto Analysis → 等分析跑完
方式 B(命令行,Agent 可代劳):
# 建工程 + 导入 + 自动分析(一次完成)
analyzeHeadless /tmp/ghidra_proj proj -import lib/arm64-v8a/libxxx.so -overwrite
# 之后 GUI `File → Open Project` 打开该工程,GhidraMCP 即挂到已分析程序上
5.3 Agent 连接 MCP
# Claude Code 示例
claude mcp add ghidra -sse http://localhost:8192/mcp
# Codex 等其他框架按各自 MCP 配置方式指向同一端点
5.4 AI 驱动分析路径(工具调用序列)
1. list_functions → 定位 JNI 导出(Java_ 前缀 / JNI_OnLoad)
2. get_function_by_name <签名函数>
3. decompile_function → 拿伪 C → 识别算法(MD5/SHA/AES)与拼接顺序 → Python 重写
4. get_strings → 提取硬编码密钥/常量线索
5. 伪 C 读不动(ollvm/VMP/字符串加密)→ 记录"需动态路径",回落 android-security-audit 1.3 抓包兜底
5.5 注意事项
- 首次自动分析大 .so 需要几分钟,等跑完再调工具
arm64-v8a原生支持;x86/x86_64 模拟器 so 也可分析- 分析对象是从 APK 提取的具体 .so(
apk-reversing产物lib/arm64-v8a/),不是整个 APK - 没有 GhidraMCP 时:Radare2(
izz/afl/pdf)做轻量无头侦察作为降级
六、质量检查(保证"拿全部")
- 入口类真实:JADX 打开 Application/入口 Activity 是业务代码,非 stub
- 特征串可命中:
grep -rniE "appKey|secret|sign|接口域名" jadx_out/sources/有结果 - so 完整:
lib/arm64-v8a/下 so 齐全,IDA 可打开、导出表可搜 - H5 完整:assets/www 或远程加载地址已记录
- dex 合并:多分片 dex 已合并,无遗漏
- report.md 已写:壳类型、脱壳方式、产物清单、未还原项(如实记录,不编造)
七、验证要点
- JADX 打开核心类确认真实代码(截图存证)
- 交付给
android-security-audit后,其快速开始 Step 2 的 grep 应能直接命中 - 壳 so 无法静态分析时,如实记录"该模块需动态追踪",不硬编
八、注意与边界
- 脱壳/反编译仅用于授权目标;在线脱壳上传前确认目标已授权
- 加固对抗在升级,新壳(VMP/混淆壳)可能需要针对性方案:先跑起来抓 dex 加载点,或结合
windows-reverse-engineering思路分析壳 so - 本技能产出的是审计输入,不直接定级漏洞——定级与成稿交给
android-security-audit+report
联动
- 下游挖洞:产物直接交给
android-security-audit(密钥追踪→未授权接口 / 组件安全) - 快筛前置:
asc-fast-hunt(大包秒级定位密钥/签名/接口,决定是否值得全量还原;脱壳后的 dex 打包成 zip 也能回它检索) - H5 内嵌小程序:
miniprogram-security - 提取的密钥/域名上报深挖:
recon-js-analysis - 正式报告:
report