第十四节 Android 代码混淆与解混淆
(第2章 安卓逆向基础)
学习目标
学完本节,希望你能够:
阅读提示:解混淆、脱壳等仅限授权环境、学习与安全研究;不得对他人应用做未授权逆向或传播脱壳包。
常见疑问:混淆和加壳有啥区别?混淆是「改名字、删未用代码」;加壳是「把 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(),若大量这种短名,基本可认为被混淆。
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 功能可还原堆栈或符号。
# 还原堆栈示例
proguard-retrace mapping.txt stacktrace.txt
没有 mapping.txt 时:只能靠逻辑推断——看字符串、看调用关系、看参数类型,手动给类/方法起「推测名」;或用 Bytecode Viewer、jadx 多换几种反编译器看,逻辑不变,名字可读性会不同。
手动还原示例(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,多半是混淆。
记一句:混淆 = 名字难读;加固 = 代码被藏/加密,要脱壳。
本节小结
你只要记住这几条就行:
本节术语速查
| 混淆(Obfuscation) | 编译时把类/方法/变量名换成短无意义名,减体积、增逆向难度。 |
| ProGuard / R8 | 常用混淆与缩减工具,R8 为当前默认。 |
| mapping.txt | 记录原名与混淆名映射,用于还原堆栈或符号。 |
| 加壳/加固 | 对 DEX/so 加密或隐藏,与混淆不同,需脱壳才能拿到真实代码。 |
本节思考与练习
下一节预告:下一节讲逆向加密算法(MD5、AES、RSA),会介绍这些算法在应用里的用途、如何用 jadx 找加解密逻辑、以及如何用 Frida Hook 拿密钥和明文(仅限授权环境)。