第 10 章 Android APK 逆向
Android 逆向是逆向入门最友好的平台:APK 里 Java 层可反编译成可读源码,DEX 结构规范,工具链成熟。但核心逻辑常藏在 native .so 里,本章教会你完整的"解包 → Java 层 → smali → .so → 重打包 → Hook"全链路。
📍 知识点地图 | 主题:Android APK逆向 | 前置:第3-6章 | 后续:第47章 | 核心概念:jadx、apktool、smali、Frida、重打包
10.1 APK 结构
app.apk (ZIP)
├── classes.dex # Java 字节码(核心逻辑)
├── AndroidManifest.xml # 二进制 XML(组件/权限/入口)
├── res/ # 资源(布局、字符串、图片)
├── resources.arsc # 资源索引表
├── assets/ # 原始资源
├── lib/
│ ├── arm64-v8a/*.so # 原生库(64 位)
│ └── armeabi-v7a/*.so # 原生库(32 位)
└── META-INF/ # 签名
10.2 完整工作流
Step 1: 解包(Triage)
# Java 反编译(可读源码)
jadx -d jadx_out app.apk
jadx --single-class com.example.LoginActivity -d jadx_out app.apk
jadx --deobf -d jadx_out app.apk # 去混淆命名
# 解包 smali + 资源
apktool d app.apk -o apktool_out
# 设备/进程侦察
adb devices
frida-ps -U
先看三样东西:
1. AndroidManifest.xml:包名、Application/MainActivity、权限、导出组件
2. lib/ 是否有 .so(有 → 核心逻辑可能 native)
3. 主 package 结构
Step 2: Java 层逻辑
在 jadx_out 里优先看:
MainActivity/Application- 登录、网络、加密、风控相关类
- 第三方 SDK 初始化(加固壳的 SDK 通常在此)
搜索关键词:login sign encrypt cipher token root certificate trust okhttp retrofit webview
Step 3: smali 与资源层
当 jadx 结果不完整、混淆重、或需要实际 patch 时切 smali:
smali*/目录(DEX 的汇编形式)res/values/strings.xml(字符串常量)
优先 patch 点:
- android:exported(组件导出)
- 调试标记
- root 检测返回值(const/4 v0, 0x1 → 0x0)
- 登录验证分支(if-ne ↔ if-eq)
- 证书校验分支
Step 4: 重打包(改完必做)
apktool b apktool_out -o rebuilt.apk
# 然后签名安装(见下)
仓库自带一条龙脚本:
pwsh -File "C:\Users\1\src\reverse-skill\skills\apk-reverse\scripts\rebuild-sign-install.ps1" -ProjectDir "apktool_out" -Install -Reinstall -DeviceSerial "127.0.0.1:7555"
Step 5: 动态 Hook(静态不足时)
# Hook 登录/加密函数
frida -U -f com.example.app -l hook.js
# 自动 trace 证书相关
frida-trace -U -f com.example.app -j '*!*certificate*'
Hook 优先级:Java 层 → smali patch → native Hook,先打印参数返回值,再决定是否改。
Step 6: Native .so 分流(关键决策!)
出现这些信号 → 立刻切 native 分析:
□ Java 层只是 JNI 包装(System.loadLibrary 后逻辑消失)
□ 核心签名逻辑不在 Java
□ 证书校验/风控在 .so 中
□ 加固壳:classes.dex 加密,真实 dex 运行时才有
- 快速侦察:radare2(
rabin2 -z/-i) - 深度分析:IDA / Ghidra 反编译
10.3 工具分工速查
| 工具 | 用途 | 关键命令 |
|---|---|---|
| jadx | Java 反编译 | jadx -d out app.apk |
| apktool | smali/资源/重打包 | apktool d/b |
| frida | 动态 Hook | frida -U -f pkg -l s.js |
| adb | 设备/日志/安装 | adb devices/logcat/install |
| apksigner | 签名 | 重建脚本内置 |
| zipalign | 对齐 | 重建脚本内置 |
| MobSF | 自动化安全分析 | web 界面 |
10.4 重打包签名闭环
# 仓库脚本全自动: 重建 → zipalign → apksigner → adb install
pwsh -File "...\apk-reverse\scripts\rebuild-sign-install.ps1" -ProjectDir "C:\work\apktool_out" -Install -Reinstall
手动链路:
apktool b apktool_out -o rebuilt.apk
zipalign -v 4 rebuilt.apk aligned.apk
apksigner sign --ks debug.keystore --ks-pass pass:android aligned.apk
adb install -r aligned.apk
10.5 常见 APK 逆向目标清单
□ 登录/签名算法(MD5/AES/RSA DIY)
□ SSL Pinning 绕过
□ Root/模拟器检测绕过
□ 风控参数(device_id、指纹)
□ WebView 注入点
□ 硬编码密钥/API Key(APKLeaks 扫描)
□ 加固壳脱壳
10.6 加固(加壳)处理
| 壳 | 特征 | 脱壳思路 |
|---|---|---|
| 360/腾讯/梆梆/爱加密 | 自定义 loader | Frida dump dex(内存中完整 dex) |
| 免费壳(简单加密) | dex 头部被改 | 修复 dex magic + FART/Youpk |
| 商业壳 | 双 dex + 自校验 | 动态 dump + 修复 |
通用思路:在 dex 解密完成后、使用前 dump 内存——Frida 脚本找到 DexClassLoader/ClassLoader.loadClass 时机,读取 BaseDexClassLoader 里的 dex 数组 dump 出来,再用 jadx 分析。
10.7 APK 安全测试清单(OWASP MASTG 视角)
□ Manifest: exported 组件、backup 标志、cleartext 流量
□ 权限: 是否过度申请
□ 存储: SharedPreferences/数据库明文敏感数据
□ 网络: 证书校验、TLS 配置
□ 硬编码: 密钥/Token(APKLeaks)
□ 加固: 是否加壳、能否被反编译
□ 动态: SSL Pinning、Root 检测、防调试
动手练习
- 找一个开源/测试 APK(frida 官方测试 App 或 GitHub 示例),完整走一遍:解包 → 看 Manifest → 看 MainActivity → 找登录逻辑。
- 练习 smali patch:把某个
const/4 v0, 0x1改成0x0改变分支,重打包签名安装验证。 - 用 Frida Hook
Cipher.init,打印某 App 的加密密钥。 - 判断一个 APK 是否加壳:
jadx解出的是壳代码还是业务代码?
深入阅读
- 仓库:
skills/apk-reverse/SKILL.md(CLI 作业规范 + 脚本用法) - 仓库:
skills/apk-reverse/references/apk-security-checklist.md(MASTG 安全清单) - 仓库:
skills/apk-reverse/references/frida-cookbook.md/frida-bypass-kit.md(Hook 脚本库) - 仓库:
skills/mobile-reverse/SKILL.md(Android+iOS 统一方法论) - 仓库:
skills/reverse-engineering/languages-platforms.md(JNI RegisterNatives 混淆、DEX 运行时 patch、.so 加载绕过) - 实战案例:
skills/field-journal/seed-008_apk-okhttp-ssl-pin-bypass.md