应用安全 --- 应知应会 之 壳
核心概念:什么是加壳?
“壳”是一种保护技术。开发商将原始程序(Dex/So,即“源程序”)进行加密、压缩、混淆,然后包裹在一个独立的“外壳”程序中。原始程序对外不可见。
-
运行过程:App启动时,先执行“外壳”程序。外壳在内存中完成解密、解压操作,并将原始Dex文件加载到内存中,最后将执行权交还给原始程序。
-
目的:防止静态分析。逆向者直接反编译安装包(APK)只能看到外壳代码,而无法看到核心业务逻辑,从而保护知识产权、增加破解难度。
由于不同厂商(腾讯、360、爱加密、梆梆等)的“壳”每一代技术思路都不同,因此不存在一种通用的脱壳方案。脱壳的本质就是在正确的时机,从内存中抓到解密后的原始Dex文件。
1. 现象分析:为什么反编译后代码很少?
你把“酒仙App”的APK文件拖入Jadx后,发现看到的代码非常少,就像是看到了一个空壳子的“包装盒”,而不是里面的“商品”。
这是因为:
这个APK已经被加壳了。你看到的少量代码,并不是App真正的业务逻辑代码,而是“壳程序”的代码。
-
壳的特征:你提到的腾讯、梆梆、360,正是国内主流的几家提供加壳服务的厂商。它们的壳程序都有自己独特的特征(比如特定的包名、so库名、初始化方法),有经验的分析者一眼就能看出来用的是哪家的壳。
一个简单的比喻:
-
未加壳的App:像一个透明的玻璃瓶,里面的饮料(代码)一览无余。
-
加壳的App:像一个不透明的、包装精美的易拉罐。你只能看到罐子的外观(壳程序),看不到里面装的是什么饮料(真实代码)。
2. 加壳原理:到底发生了什么?
我们一步步来看一个App是如何从“裸奔”变成“穿盔甲”的。
第1步:开发一个正常的App(“裸奔”状态)
一个正常的安卓App开发出来之后,它的核心组成部分是:
-
classes.dex
文件:这里面存放了你写的所有Java/Kotlin代码编译后的字节码。这是代码的核心。 -
lib/
目录下的.so
文件:这是你用C/C++写的 native 代码(JNI代码)编译后的动态链接库。 -
资源文件:图片、布局文件(
.xml
)、字符串等。
此时的APK结构清晰,classes.dex
可以直接被Jadx这类工具反编译。
第2步:使用加壳工具进行加密(“穿上盔甲”)
开发者把这个正常的APK提交给加壳平台(比如梆梆加固)。加壳平台会做以下几件事:
-
抽取:将原始APK里最重要的
classes.dex
文件加密。加密后的数据看起来就是一串乱码,毫无意义。 -
隐藏:把这串加密后的“乱码”(即原始的dex),藏匿到一个原本的
.so
库文件中,或者单独放入assets文件夹。这就是为什么你感觉 “so文件很大” ,因为它里面可能就“藏私货”了。 -
包裹:加壳平台会生成一个新的、很小的
classes.dex
文件。这个dex不包含任何业务逻辑,它唯一的作用就是“壳” —— 负责在App运行时解密和加载那个被隐藏起来的原始dex。 -
重组:最后,加壳平台会用这个新的“壳dex”和被修改过的、藏有原始代码的so文件,重新打包成一个新的、加固后的APK。
所以,你用Jadx打开这个新APK,反编译的只是那个壳dex,自然看不到多少有价值的代码。
3. 运行时:壳是如何工作的?
App安装到手机后,它的启动和执行过程也发生了变化。
未加壳的App启动:
系统直接加载 classes.dex
,然后正常执行里面的代码。简单粗暴。
加壳的App启动(“金蝉脱壳”):
这是一个“套娃”过程,如下图解所示:
flowchart TD
A[启动加壳App] --> B[系统加载“壳dex”<br>(Jadx所见即此)]
B --> C[“壳dex”开始执行]
C --> D[加载藏有密文的.so文件]
D --> E[执行.so文件中的解密程序]
E --> F[在内存中解密出<br>原始的“真实dex”]
F --> G[动态加载内存中的“真实dex”]
G --> H[执行原始业务逻辑<br>用户正常使用App]
关键点在于:
-
原始代码从未出现在磁盘上:解密后的、完整的原始dex只存在于内存中。APK安装包里永远只是加密状态。这有效地防止了静态分析。
-
牺牲速度换安全:这一系列的解密操作,必然会导致App启动速度变慢。
4. 大厂 vs 小公司的选择
你最后提到的点非常准确:
-
大厂(如淘宝、微信):他们一般不自研加壳,或者用很轻的壳。
-
为什么? 因为加壳影响启动速度和用户体验。对他们来说,用户体验是重中之重。
-
那他们怎么安全? 他们采用 “强加密”+“自研安全方案”。
-
核心算法放在so库:把最关键的业务逻辑、加密算法、风控规则用C++实现,编译成so库。so库的反编译和逆向难度远大于dex。
-
运行时防护:在App内植入大量反调试、反注入、环境检测的代码,动态防御。
-
服务器端校验:把核心逻辑放在服务器,App只是一个客户端,即使被破解了也拿不到核心数据。
-
-
这种方案技术门槛极高,需要养一个强大的安全团队,但效果好,对用户体验影响小。
-
-
小公司/创业公司:非常依赖第三方加壳服务。
-
为什么? 因为他们没有足够的技术力量和资金去自研一套强大的安全体系。使用第三方加壳是成本最低、见效最快的安全方案。
-
虽然会牺牲一些启动速度,但能有效防止 apk 被直接反编译、破解、二次打包,抵御绝大部分普通黑产玩家。
-
脱壳方案详解
1. 手动脱壳(难度大)
原理:通过动态调试(如使用 IDA Pro、GDB),跟踪分析外壳的解密逻辑,精确找到解密后的 Dex 文件在内存中的起始地址和大小,然后将其 dump(导出)到本地文件。
-
技术要点:
-
定位关键点:需要在外壳代码中定位到调用
dalvik.system.DexClassLoader
或dexFileParse
等底层函数的地方。 -
计算偏移:通过分析寄存器(如 ARM 架构的 R0、R1)和内存指针,计算出解密后 Dex 文件的内存映射地址和长度。
-
内存操作:使用调试器或命令行工具(如
dd
、gdb
)将指定内存区域的数据拷贝出来。
-
-
为什么难度大?
-
反调试:强壳会植入大量反调试技术,如检测调试器连接、检查进程状态、代码混淆等,一旦发现被调试立即退出或执行错误逻辑。
-
代码混淆:外壳代码本身被高度混淆,增加静态分析和动态跟踪的难度。
-
完整性校验:外壳会校验自身和脱壳后的 Dex 完整性,防止内存被修改或 dump。
-
需要深厚的功底:要求分析者熟悉 ARM 汇编、Android 运行时机制和各种反调试技巧。
-
2. 工具脱壳(主流方案)
这类方案基于 Hook 技术,通过拦截系统关键函数或扫描内存特征,自动化的发现和 dump 内存中的 Dex 文件。
a. 基于 Xposed 的脱壳工具
-
代表工具:
-
Fdex2:其经典思路是 Hook
ClassLoader.loadClass
方法。当外壳解密 Dex 并加载第一个类时,必然要调用此方法。此时通过堆栈回溯,可以找到DexFile
对象,进而获取其对应的内存地址,完成 dump。这是一种“时机”抓取。 -
dumpDex:原理类似,但通常更先进。它可能 Hook 更底层的函数(如
dvmDexFileOpenPartial
)或结合内存扫描,兼容性和成功率更高。
-
-
优点:通常一键操作,简单易用。
-
缺点:需要安装 Xposed 框架,环境修改明显,容易被壳检测到 Xposed 环境而导致脱壳失败或 App 闪退。
b. 基于 Frida 的脱壳工具(当前主流)
-
代表工具:frida-dexdump (
https://github.com/hluwa/FRIDA-DEXDump
) -
原理:它不依赖特定加载时机,而是采用 “主动搜索” 策略。
-
内存扫描:遍历应用程序的所有内存区域。
-
特征匹配:寻找符合 Dex 文件格式 的魔数(
dex\n035\0
或dex\n036\0
等)。 -
头部校验:找到魔数后,进一步解析 Dex 头部结构,检查其合法性(如
checksum
校验和、sha1
签名等)。 -
导出文件:将找到的、合法的 Dex 内存块 dump 到本地。
-
-
优点:
-
无需特定时机:只要 Dex 文件被映射到内存中,任何时候都可以扫描。
-
对抗性强:Frida 的隐蔽性优于 Xposed,搭配 strongR-frida 等对抗工具后,成功率更高。
-
通用性强:基于格式特征搜索,理论上能应对各种不同的壳。
-
-
使用方法(命令行):
# 启动App adb shell am start com.example.target.app # 在电脑上运行frida-dexdump frida-dexdump -U -p <进程名或PID> -d <输出目录>
3. 定制化脱壳机(高阶方案)
原理:直接修改 Android 操作系统(AOSP)的源码,在系统底层主动记录所有 Dex 文件的加载行为。
-
实现方式:
-
下载 AOSP 源码。
-
找到
dalvik
或art
虚拟机中加载 Dex 文件的关键函数(如DexFile::Open
、OpenCommon
)。 -
在这些函数中加入日志代码,或者更直接地,加入将传入的
Dex
数据buffer自动保存到文件的功能。 -
编译整个系统,烧录到手机中。
-
-
优点:
-
降维打击:在系统层面实现,完全无法被App层面的壳检测和对抗。只要壳要运行,就必须通过系统接口加载 Dex,一加载就会被捕获。
-
效果极佳:堪称“万能脱壳机”,尤其是对付那些运行时才解密、且反调试极强的第三代、第四代壳。
-
-
缺点:
-
技术门槛极高:需要深厚的 Android 系统编译和移植经验。
-
过程繁琐:需要为特定手机型号编译和刷机,耗时极长。
-
4. 商业脱壳工具(付费方案)
-
代表工具:ArmPro 等。
-
原理:通常是上述多种技术的集大成者。它们可能是:
-
一个高度定制化的、已经刷好脱壳系统的手机(即“脱壳机”)。
-
一个强大的、持续更新的软件工具,集成了虚拟机、内存搜索、反反调试等多种功能。
-
-
优点:
-
省心省力:用户无需研究技术细节,交钱即可使用。
-
成功率最高:商业工具会持续更新以对抗最新的壳,并提供技术支持。
-
-
缺点:
-
费用高昂:通常按次数或时间收费,价格不菲。
-
黑盒操作:你不知道它具体如何工作,也无法自定义。
-
总结与选择建议
方案 | 适用场景 | 优点 | 缺点 | 推荐度 |
---|---|---|---|---|
手动脱壳 | 学习研究、对付未知新壳 | 理解原理,锻炼能力 | 难度极大,耗时极长 | ⭐⭐ |
Xposed工具 | 初学者、对付普通壳 | 简单易用 | 易被检测,环境明显 | ⭐⭐⭐ |
Frida-dexdump | 当前主流,对付大部分常见壳 | 通用性强,隐蔽性好,免费 | 对某些强壳可能失效 | ⭐⭐⭐⭐⭐ |
定制脱壳机 | 专业分析、对付最强壳 | 理论上万能,无法防御 | 技术门槛高,需刷机 | ⭐⭐⭐⭐ |
商业工具 | 有预算的企业、无技术背景 | 省心,成功率最高 | 费用高,黑盒 | ⭐⭐⭐ |
给你的建议:
-
从
frida-dexdump
开始:这是目前免费方案中最有效、最通用的选择。遇到大多数情况都应该先尝试它。 -
如果
frida-dexdump
失效,检查是否是Frida环境被检测,尝试使用 strongR-frida 等对抗工具后再试。 -
如果依然无效,说明壳很强,可以考虑寻找定制好的脱壳手机(市面上有人出售)或求助商业脱壳服务。
-
手动脱壳和自建脱壳机是安全研究人员的终极道路,适合希望深入理解系统原理的人。