16、Android加固技术概览:DEX加固、SO加固、资源加固、VMP与Ollvm
各位同学,今天我们来聊聊Android加固。说实话,这行干久了你会发现,加固和脱壳就像猫鼠游戏,永远在互相追赶。我最早接触加固是在2016年,那时候一个简单的DEX整体加密就能拦住大部分逆向工具。现在?嗯,你想想看,连VMP和Ollvm都开始往移动端跑了。
这一章我不会讲得太深,主要是带大家建立一个全局认知。毕竟后面每一类加固我们都会单独开章节细讲。今天的目标很简单:搞懂市面上常见的加固手段,知道它们各自防什么、弱点在哪。
一、DEX加固:最基础的防线
DEX加固是入门最早、应用最广的一类。说白了,就是把你的classes.dex藏起来。我见过很多新手以为DEX加固就是整体加密,其实远不止这些。
常见的DEX加固手段有这几种:
- 整体加密:把整个DEX文件用AES或RC4加密,运行时由壳代码解密加载。优点是实现简单,缺点是内存中会暴露完整的DEX。
- 函数抽取:只加密每个方法的CodeItem,执行时才解密。我曾在项目中遇到过某加固厂商的抽取方案,结果发现它解密后没有及时清理内存,被我在/proc/self/maps里抓到了明文。
- 动态加载:把DEX拆成多个小dex,按需加载。这种方案对启动速度影响较大,但对抗静态分析效果不错。
二、SO加固:Native层的攻防
SO加固比DEX加固要复杂一些。为什么?因为Native层有更多的系统调用和内存操作空间。我个人习惯把SO加固分成两类:加壳和混淆。
| 类型 | 原理 | 常见对抗手段 |
|---|---|---|
| SO加壳 | 加密.so文件的代码段,运行时由stub解密 | 内存dump、断点绕过、手动修复ELF |
| SO混淆 | 对Native代码进行指令替换、控制流混淆 | 符号执行、动态跟踪、反混淆脚本 |
| 反调试 | 检测ptrace、/proc/self/status、时间差等 | patch掉检测点、frida绕过、内核模块 |
我记得有一次分析某直播App的SO加固,它用了UPX壳的变种。UPX本身是开源的,但厂商改了魔数和解密入口。我当时花了半天时间定位到解密函数,然后手动dump出了原始SO。嗯,这种经验多了之后,你会发现大部分SO加壳都有规律可循。
三、资源加固:别小看图片和XML
很多人觉得资源加固没什么技术含量,其实不然。你想想看,很多App的核心逻辑虽然写在代码里,但关键的配置、算法参数、甚至一些敏感字符串都藏在资源文件里。
常见的资源加固手段:
- 图片加密:把PNG或JPG文件头抹掉,或者整体AES加密,运行时解密到内存。
- XML对抗:修改AndroidManifest.xml或布局文件的二进制格式,让aapt和AXMLPrinter2解析失败。
- 资源名称混淆:把res/values/strings.xml里的key改成无意义字符串,增加静态分析的阅读成本。
我在项目中遇到过最狠的资源加固,是把所有图片都转成了自定义格式,然后通过Native代码解析。当时我为了提取一张启动图,不得不逆向它的图片解码算法。说实话,这种加固对普通逆向者来说挺恶心的,但如果你有耐心,还是能通过hook BitmapFactory或skia库来抓到原始数据。
四、VMP:虚拟机保护
VMP(Virtual Machine Protection)是加固领域的大杀器。它的思路是把Dalvik字节码或Native指令翻译成自定义的虚拟机指令,然后由解释器执行。这样一来,逆向工具看到的只是一堆虚拟机字节码,完全看不懂原始逻辑。
VMP的典型实现流程:
- 提取原始方法的指令序列
- 将每条指令映射到自定义的opcode
- 生成虚拟机字节码,并存储在so或dex中
- 运行时由解释器逐条执行这些opcode
我曾经分析过一个使用了VMP的金融App。它的核心加密算法被VMP保护了,我尝试用unicorn模拟执行,但发现解释器里有很多反模拟器检测。最后我用了frida的Stalker配合trace记录,硬是把opcode的执行序列录了下来,然后手动还原了算法逻辑。整个过程花了两周,但收获很大。
五、Ollvm:控制流平坦化
Ollvm(Obfuscator-LLVM)是LLVM的一个分支,专门用来做代码混淆。它最出名的是控制流平坦化(Control Flow Flattening)和指令替换。你想想看,原本一个简单的if-else,经过Ollvm处理后,会变成一堆switch-case和状态变量,阅读起来非常痛苦。
Ollvm的常见混淆方式:
- 控制流平坦化:把函数的所有基本块放到一个大的switch结构中,通过一个状态变量控制执行流。
- 指令替换:把简单的运算(如加法、异或)替换成等价的复杂指令序列。
- 虚假控制流:插入永远不会执行的分支,增加静态分析的复杂度。
六、各类加固的对比与选择
最后,我给大家整理了一个对比表格。在实际项目中,选择哪种加固方案,取决于你的威胁模型和性能要求。
| 加固类型 | 防护强度 | 性能影响 | 对抗难度 | 典型应用场景 |
|---|---|---|---|---|
| DEX整体加密 | 低 | 低 | 低 | 普通App防静态分析 |
| DEX函数抽取 | 中 | 中 | 中 | 核心算法保护 |
| SO加壳 | 中 | 低 | 中 | Native代码保护 |
| SO混淆 | 中高 | 中 | 高 | 安全SDK、加密库 |
| 资源加固 | 低 | 低 | 低 | 素材、配置文件保护 |
| VMP | 高 | 高 | 极高 | 金融、支付、核心算法 |
| Ollvm | 高 | 中高 | 高 | Native代码混淆、防逆向 |
好了,这一章的内容就到这里。记住,加固技术没有银弹。每种方案都有它的弱点,而我们逆向工程师要做的,就是找到那个最薄弱的环节。下一章我们会深入DEX加固的细节,到时候我会带大家手撕一个真实的加固壳。