Android APK 逆向实战:从 JADX、Apktool 到 Frida、Ghidra,理解 DEX、SO、脱壳与加固
在 Android 安全研究和 APP 开发过程中,APK 逆向是一个非常重要的技术方向。
很多人刚开始接触 APK 逆向时,通常只知道使用 JADX 打开 APK,然后查看 Java 代码。
但真正深入之后会发现,Android APK 背后涉及的内容远不止 Java 代码:
- APK 文件结构
- AndroidManifest.xml
- DEX
- Smali
- SO / ELF
- JNI
- ARM64
- ClassLoader
- ART
- Frida
- DEX 脱壳
- SO 分析
- VMP
- RASP
- Android 版本适配
如果想真正理解 Android 加固和脱壳,仅仅会使用一个反编译工具是不够的。
本文从一个普通 APK 出发,把 JADX、Apktool、Smali、Frida、Ghidra/IDA、DEX、SO、ART 串起来,建立一套比较完整的 Android APK 逆向分析思路。
一、APK 到底是什么?
APK 本质上是一个 ZIP 格式的 Android 应用程序包。
例如:
app.apk
修改扩展名或者直接使用解压工具打开,可以看到:
AndroidManifest.xml
classes.dex
classes2.dex
resources.arsc
res/
assets/
lib/
META-INF/
如果应用比较简单,可能只有一个:
classes.dex
大型 Android 应用则可能存在:
classes.dex
classes2.dex
classes3.dex
classes4.dex
Native 代码一般位于:
lib/
├── arm64-v8a/
│ ├── libxxx.so
│ └── libyyy.so
└── armeabi-v7a/
└── libxxx.so
因此可以简单理解:
APK
│
├── Java / Kotlin
│ ↓
│ DEX
│
├── Native
│ ↓
│ SO
│
├── Resources
│
└── Manifest / Signature
二、使用 JADX 进行第一轮静态分析
JADX 是 Android APK 逆向分析中非常常见的工具。
它可以直接加载 APK,并尝试将 DEX 反编译成接近 Java 的代码。
例如:
jadx app.apk
打开之后,可以看到类似:
com.example.app
├── MainActivity
├── LoginActivity
├── MainApplication
├── network
├── utils
└── ...
如果原始代码是:
fun checkLogin(token: String): Boolean {
return token.isNotEmpty()
}
编译以后会进入 DEX。
JADX 做的事情实际上是:
DEX
↓
反编译
↓
Java-like Code
所以需要注意:
JADX 显示出来的代码,并不等于原始 Java/Kotlin 源代码。
如果应用使用了:
- R8
- ProGuard
- 混淆
- Kotlin Coroutine
- Lambda
- Native
- 自定义 ClassLoader
- DEX 加密
- VMP
那么最终反编译出来的代码可能和原始源码存在非常大的差异。
三、为什么还需要 Apktool?
JADX 更适合查看代码逻辑。
Apktool 则更适合分析:
- APK 结构
- AndroidManifest.xml
- Resources
- Smali
- APK 修改与重新打包
例如:
apktool d app.apk -o app_out
解包之后通常可以看到:
app_out/
├── AndroidManifest.xml
├── smali/
├── smali_classes2/
├── res/
├── assets/
└── unknown/
其中非常重要的一部分就是:
smali/
这里保存的是 Smali 代码。
四、Smali 是什么?
理解 Android 逆向,Smali 是必须掌握的一部分。
可以简单理解成:
Java / Kotlin
↓
D8 / R8
↓
DEX
↓
Smali 表示
Smali 更接近 DEX 指令级别。
例如:
.method public test()I
.locals 1
const/4 v0, 0x1
return v0
.end method
逻辑非常简单:
v0 = 1
return v0
实际逆向过程中:
JADX 负责帮助我们快速理解整体代码逻辑,Smali 则用于进一步确认具体实现。
例如 JADX 中看到:
if (isLogin()) {
startActivity(...);
}
如果怀疑它存在某种校验逻辑,可以继续定位对应 Smali。
五、从 Java 层进入 Native 层
分析 APK 时,经常会遇到:
public native boolean verify(String data);
或者:
static {
System.loadLibrary("xxx");
}
这说明部分核心逻辑可能已经进入 Native 层。
对应的文件通常位于:
lib/arm64-v8a/libxxx.so
这时候继续使用 JADX 分析就不够了。
需要进入:
Java / Kotlin
↓
JNI
↓
C / C++
↓
SO
这个阶段通常会使用:
- Ghidra
- IDA
- Binary Ninja
等 Native 分析工具。
六、使用 Ghidra / IDA 分析 SO
Android 的 .so 文件通常属于 ELF 格式。
例如:
libxxx.so
使用 Ghidra 或 IDA 加载之后,可以重点关注:
ELF Header
Sections
Symbols
Strings
Imports
Exports
Functions
特别是 JNI 相关位置。
例如:
JNI_OnLoad
RegisterNatives
Java_xxx_xxx
这些内容可以帮助我们建立:
Java Method
↓
JNI
↓
Native Function
↓
SO
的调用关系。
七、JNI 是 DEX 和 SO 之间的重要桥梁
Android 应用经常存在这样的调用链:
Java / Kotlin
↓
JNI
↓
C / C++
↓
SO
例如:
public native String decrypt(String data);
实际运行过程中可能变成:
Java Method
↓
JNI Bridge
↓
Native Function
↓
libxxx.so
因此在分析 APK 时,如果发现大量:
native
System.loadLibrary()
RegisterNatives()
JNI_OnLoad()
就应该把分析范围扩展到 Native 层。
八、为什么加固 APK 很难直接用 JADX 分析?
普通 APK 的大致结构可以理解为:
classes.dex
↓
ClassLoader
↓
Class
↓
ART
↓
执行
但是经过加固之后,结构可能变成:
APK
│
├── 壳代码
├── 加密 DEX
└── Native Loader
↓
Runtime
↓
DEX 解密
↓
ClassLoader
↓
ART
因此静态分析工具拿到的:
classes.dex
可能并不是真正的业务 DEX。
例如一个实际拥有大量业务功能的 APP:
JADX
↓
几十个类
但实际运行时:
几千甚至更多业务类
这就是 Android 加固经常出现的情况。
九、什么是 DEX 脱壳?
很多人理解的脱壳是:
找到一个脚本,然后把 DEX 导出来。
但从技术原理来看,脱壳远不止如此。
一个典型的 DEX 加固流程可能是:
原始 DEX
↓
加密 / 转换 / 虚拟化
↓
保护后的 APK
运行的时候:
保护后的 APK
↓
壳代码
↓
运行时恢复
↓
真实 DEX / Code
↓
ClassLoader
↓
ART
↓
执行
所以从研究角度来看,真正值得理解的是:
代码什么时候被恢复、以什么形式存在,以及最终如何进入 ART 执行链路。
十、Frida 为什么重要?
JADX、Apktool、Ghidra、IDA 主要属于静态分析工具。
Frida 则属于动态分析工具。
两者最大的区别可以简单理解为:
静态分析
APK
↓
DEX / SO
↓
分析代码
动态分析:
APK
↓
实际运行
↓
观察函数
↓
观察参数
↓
观察返回值
↓
分析调用关系
例如:
Java Method
↓
参数
↓
执行
↓
返回值
动态分析能够帮助研究人员确认:
- 某个函数是否真的执行
- 函数什么时候执行
- 参数是什么
- 返回值是什么
- Java 和 Native 如何交互
对于复杂的加固应用,这些运行时信息往往比单纯查看反编译代码更加重要。
十一、静态分析 + 动态分析
实际 APK 分析过程中,通常不会只使用一个工具。
比较常见的组合是:
APK
│
┌──────────┴──────────┐
↓ ↓
JADX Apktool
│ │
Java/Kotlin Smali
│ │
└──────────┬──────────┘
↓
定位逻辑
│
┌──────┴──────┐
↓ ↓
Frida Ghidra / IDA
↓ ↓
动态分析 Native分析
│ │
└──────┬──────┘
↓
JNI / ART
↓
调用链
这样能够形成完整的分析闭环。
十二、ART 是理解 Android 加固的关键
如果只停留在 JADX 和 Smali 层面,很难真正理解现代 Android 加固。
进一步学习 Android 安全之后,就会遇到:
ART —— Android Runtime
可以简单理解为:
DEX
↓
ClassLoader
↓
DexFile
↓
Class
↓
Method
↓
ART Runtime
↓
解释执行 / JIT / AOT
这也是为什么研究 Android 加固时,经常需要阅读 AOSP 中 ART 相关源码。
重点关注的方向包括:
ClassLoader
DexFile
ClassLinker
JNI
JIT
AOT
Method
Class Loading
当一个加固方案深度介入这些运行时机制之后,就不能再简单地把它理解成:
DEX 加密
而应该理解成:
代码保护
+
运行时加载
+
执行环境保护
+
完整性保护
十三、为什么 Android 新版本会影响加固?
Android Runtime 并不是一成不变的。
随着 Android 版本升级:
Framework
ART
Class Loading
JNI
Hidden API
Memory Management
Native Interface
都会发生不同程度的变化。
因此,如果加固方案深度依赖 ART 内部实现,就需要持续进行 Android 版本适配。
可以把 Android 应用的运行环境简单理解为:
Application
↓
Java / Kotlin
↓
Android Framework
↓
ART
↓
Native
↓
Linux Kernel
越深入底层:
加固方案能够控制的运行时细节通常越多,同时也意味着更高的版本适配成本。
这也是为什么一个加固方案在 Android 10、Android 12、Android 14、Android 15、Android 16 上的表现不能简单认为完全一致。
十四、DEX 加固和 SO 加固有什么区别?
这是理解 Android 加固非常重要的一点。
1. DEX 加固
DEX 主要承载:
Java / Kotlin
业务逻辑
算法
接口逻辑
关键字符串
常见保护思路包括:
DEX Encryption
DEX Packing
Class Encryption
VMP
Runtime Loading
2. SO 加固
SO 主要承载:
Native 算法
核心计算
JNI 接口
底层逻辑
常见保护思路包括:
ELF Protection
Symbol Stripping
.text Protection
Integrity Check
Runtime Decryption
Anti-Debug
因此比较完整的 Android 加固方案通常不是只保护 DEX,而是多个层面共同保护:
DEX
+
SO
+
Resources
+
Runtime
+
Integrity
+
RASP
十五、VMP 是什么?
VMP 通常可以理解为:
Virtual Machine Protection,虚拟化保护。
普通代码执行过程可以简单理解成:
Java / Kotlin
↓
DEX
↓
ART
而虚拟化保护可能变成:
原始代码
↓
转换成虚拟指令
↓
Virtual Machine
↓
Interpreter
↓
执行
例如原始逻辑:
A + B
经过虚拟化后,不再直接保存成传统形式,而可能转换成一系列虚拟指令:
LOAD
LOAD
ADD
RETURN
然后由自定义解释器负责执行。
复杂的 VMP 可能包含:
Virtual Instruction
Dispatcher
Handler
Interpreter
Control Flow
Data Flow
因此 VMP 的分析难度通常明显高于普通的 DEX 混淆。
十六、Android APK 逆向的完整学习路线
如果从零开始学习 Android APK 逆向,可以按照下面的顺序进行。
第一阶段:APK 基础
先理解:
APK
Manifest
DEX
Resources
SO
Signature
工具:
JADX
Apktool
第二阶段:Smali
重点掌握:
register
invoke
move
const
if
goto
return
new-instance
iget
iput
目标不是机械背诵指令,而是能够:
看到 Smali 后快速还原程序逻辑。
第三阶段:Java / Kotlin → DEX
理解:
Java / Kotlin
↓
D8 / R8
↓
DEX
进一步研究:
classes.dex
classes2.dex
Multidex
R8
ProGuard
第四阶段:Native
学习:
C / C++
↓
ARM64
↓
ELF
↓
SO
工具:
Ghidra
IDA
重点掌握:
JNI
JNI_OnLoad
RegisterNatives
ARM64 Calling Convention
第五阶段:动态分析
学习:
Frida
重点理解:
Java Hook
Native Hook
参数观察
返回值观察
调用链跟踪
第六阶段:ART
最后深入:
ART
ClassLoader
DexFile
ClassLinker
JNI
JIT
AOT
然后再研究:
DEX Encryption
Runtime Loading
DEX Protection
VMP
SO Protection
RASP
Anti-Hook
Integrity
这样学习会比一开始直接研究所谓的“脱壳脚本”更加系统。
十七、从逆向角度理解 Android 加固
当把整个流程串起来之后,会发现:
Android 逆向和 Android 加固实际上是同一条技术链路上的两个方向。
逆向研究关注:
代码在哪里?
↓
什么时候加载?
↓
如何解密?
↓
谁调用?
↓
在哪里执行?
而加固则试图提高这些问题的分析成本:
代码不能直接看到
↓
代码不能轻易恢复
↓
降低运行时暴露
↓
增加动态分析成本
↓
检测异常运行环境
因此现代 Android 加固并不是简单地:
classes.dex 加密
而更接近:
APK
│
┌─────────────┼─────────────┐
↓ ↓ ↓
DEX SO Resources
│ │ │
加密/VMP ELF保护 加密
│ │ │
└─────────────┼─────────────┘
↓
Runtime
↓
ClassLoader / ART
↓
Integrity / RASP
十八、一套实用的 Android 逆向工具组合
如果主要用于学习和安全研究,不需要一开始安装几十个工具。
基础工具链可以使用:
| 分析目标 | 工具 |
|---|---|
| Java/Kotlin 代码分析 | JADX |
| APK 解包 | Apktool |
| Smali 分析 | Apktool |
| DEX 分析 | JADX / DEX 工具 |
| 动态分析 | Frida |
| Native 分析 | Ghidra / IDA |
| ARM64 分析 | Ghidra / IDA |
| Android Runtime | AOSP 源码 |
| 调试 | adb / LLDB |
最终可以形成:
APK
│
┌────────┴────────┐
↓ ↓
JADX Apktool
↓ ↓
Java/Kotlin Smali
│ │
└────────┬────────┘
↓
静态分析
│
┌────────┴────────┐
↓ ↓
Frida Ghidra / IDA
↓ ↓
动态分析 SO / ARM64
│ │
└────────┬────────┘
↓
JNI / ART
↓
DEX / SO
加载链
↓
加固与脱壳研究
十九、总结
Android APK 逆向真正值得学习的,并不是某一个工具或者某一个脱壳脚本,而是理解整个 Android Runtime 链路。
最核心的一条主线可以概括为:
APK
↓
DEX
↓
ClassLoader
↓
ART
↓
JNI
↓
SO
↓
Native
↓
Runtime
掌握 JADX + Apktool,可以完成基础静态分析;
掌握 Smali,可以进一步理解 DEX 指令;
加入 Frida,可以观察真实运行过程;
再结合 Ghidra / IDA,就可以进入 Native 和 ARM64 层;
继续研究 ART、DEX 加密、运行时加载、VMP、SO 加固和 RASP,才能真正理解现代 Android 加固的技术体系。
对于 Android 安全研究来说,最终需要建立的并不是“怎么脱壳”的单点知识,而是一套完整的认知:
代码在哪里 → 如何加载 → 什么时候恢复 → 如何执行 → 如何保护。
这条链路基本贯穿了 Android APK 逆向、脱壳以及加固技术的核心内容。
Top comments (0)