第十四节 Android 代码混淆与解混淆
(第2章 安卓逆向基础)
学习目标
学完本节,希望你能够:
- 说出 Android 代码混淆 在干啥:把类名、方法名、变量名等替换成短无意义名(如 a、b、c),减小体积、增加逆向难度。
- 知道常见混淆工具:ProGuard 、R8 (默认启用)、DexGuard 、以及国内加固(如 360)等;混淆和加壳不是一回事。
- 能判断 APK 是否被混淆:反编译后看类名/方法名是否大量为 a、b、c 等;若有 mapping.txt 可用来还原符号。
- 了解解混淆思路:用 mapping.txt 还原(ProGuard retrace)、用 jadx/Bytecode Viewer 看逻辑、手动根据字符串/调用关系推测原名(仅限授权环境)。
阅读提示 :解混淆、脱壳等仅限授权环境、学习与安全研究;不得对他人应用做未授权逆向或传播脱壳包。
常见疑问:混淆和加壳有啥区别?混淆是「改名字、删未用代码」;加壳是「把 DEX/so 加密或隐藏,运行时再解密」,两者可同时存在。
一、Android 代码混淆是啥?
代码混淆 (Obfuscation)说白了就是:在编译时把类名、方法名、变量名 换成短且无意义的符号 (如 a、b、c),并可能删除未使用代码 ,达到减小 APK 体积 和提高逆向难度的目的。不会改逻辑,只是名字难读。
为啥要混淆?
- 防逆向:名字没语义,读代码费劲。
- 减体积:删未用代码、短名字。
- 护核心:算法、校验逻辑不易一眼看出。
常见混淆工具一句话
| 工具 | 在干啥 | 是否默认 |
|---|---|---|
| ProGuard | 缩减、重命名、优化 | 早期默认,现多被 R8 替代 |
| R8 | 替代 ProGuard,支持 D8 | 当前默认 |
| DexGuard | 商用,加密、动态混淆等 | 需单独购买 |
| 360 加固等 | 加固 + 混淆,DEX 可能被加密 | 需单独配置 |
记一句:混淆 = 改名字 + 删未用代码;和加壳(加密 DEX/so)是两回事。
二、怎么判断 APK 是否被混淆?
在干啥:反编译后看类名、方法名是否大量为 a、b、c 等
用 jadx 反编译 APK,看 com/xxx/a.java 、b.java ,方法名 a() 、b(),若大量这种短名,基本可认为被混淆。
bash
jadx -d output/ app.apk
ls output/sources/com/example/
若目录里多是 a.java 、b.java,且方法名也是 a、b、c,说明已混淆。
三、解混淆一般怎么干?
在干啥:把混淆后的名字还原成可读的类名/方法名,或至少理清逻辑
有 mapping.txt 时 :ProGuard/R8 会生成 mapping.txt ,记录「原名 → 混淆名」的映射,用 proguard-retrace 或 jadx 的 mapping 功能可还原堆栈或符号。
bash
# 还原堆栈示例
proguard-retrace mapping.txt stacktrace.txt
没有 mapping.txt 时 :只能靠逻辑推断 ------看字符串、看调用关系、看参数类型,手动给类/方法起「推测名」;或用 Bytecode Viewer 、jadx 多换几种反编译器看,逻辑不变,名字可读性会不同。
手动还原示例(Smali)
混淆后可能是:
smali
.method public b()V
.locals 1
const-string v0, "Login Success"
invoke-static {v0}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)V
return-void
.end method
根据字符串 "Login Success" 可推测该方法可能是 checkLogin 或 onLoginSuccess ,在注释或重命名时写成 checkLogin()V 方便后续分析。工具上可用 jadx 的「重命名」功能做标注。
四、加固(如 360)和混淆的区别
在干啥:加固会加密/隐藏 DEX 或 so,运行时再解密;混淆只改名字
混淆 :不加密,只是名字变短、未用代码删除;反编译后仍能看到字节码,只是难读。
加固(加壳) :DEX 或 so 被加密或抽走,直接反编译可能失败或只有壳代码;需要脱壳才能拿到真实 DEX(脱壳见后续「加固与脱壳」相关节)。
若遇到无法直接反编译、或反编译后只有壳类,多半是加固;若反编译正常但满屏 a、b、c,多半是混淆。
记一句:混淆 = 名字难读;加固 = 代码被藏/加密,要脱壳。
本节小结
你只要记住这几条就行:
- 混淆 = 类/方法/变量名替换成短符号(a、b、c),减体积、增逆向难度;ProGuard/R8 常用,会生成 mapping.txt。
- 判断是否混淆:反编译后看类名、方法名是否大量为单字母或短名。
- 解混淆 :有 mapping 用 retrace 或 jadx mapping;没有就靠字符串、调用关系手动推断 ;混淆和加壳别混。
- 仅限授权、合规环境下分析与解混淆。
本节术语速查
| 术语 | 一句话解释 |
|---|---|
| 混淆(Obfuscation) | 编译时把类/方法/变量名换成短无意义名,减体积、增逆向难度。 |
| ProGuard / R8 | 常用混淆与缩减工具,R8 为当前默认。 |
| mapping.txt | 记录原名与混淆名映射,用于还原堆栈或符号。 |
| 加壳/加固 | 对 DEX/so 加密或隐藏,与混淆不同,需脱壳才能拿到真实代码。 |
本节思考与练习
- 概念:混淆和加壳有啥区别?分别会对反编译结果造成什么影响?
- 应用 :若反编译后看到大量 a() 、b() ,但有一个方法里调用了 Log.d("Login", ...),你会怎么推测这个方法的作用?
- 动手 :找一个已混淆的 APK(或自己打一个带 ProGuard 的 release 包),用 jadx 反编译,看类名和方法名,再尝试用 mapping.txt(若有)做一次 retrace 或导入 jadx。
- 动手:在同一 APK 里选一个混淆后的类,根据字符串和调用关系,在笔记里给它和主要方法起「推测原名」,体会手动解混淆的过程。
下一节预告 :下一节讲逆向加密算法(MD5、AES、RSA),会介绍这些算法在应用里的用途、如何用 jadx 找加解密逻辑、以及如何用 Frida Hook 拿密钥和明文(仅限授权环境)。