- 即使 DEX 已加载到内存仍处于加密狀态(所有 DEX 方法都在运行时解密)
-
代码抽取型壳经历多次技术迭代
-
最初是将 DEX 的 DexCode 提取后填 0,将 DEX 的所有内容保存在 APK 中APK 运行时会在内存中动态解密,所有解密的方法内容指针位于 DEX 文件结构体外部的内存中从而有效避免了只知道 DEX 的起始地址即可快速 Dump 的问题
-
内存重组脱壳法能有效對付此种壳,其通过解析内存中 DEX 的格式将其重新组合成 DEX,可实现百分百 DEX 代码还原虽然出现过一些针对内存重组的 Anti,但理论上只要 DEX 在内存中是完整的即可通过此法脱壳
-
在内存中加载完成的 DEX 是个 DvmDex 结构体:
-
- 遍历它的字段即可得到 DEX 的完整内容
-
- 如何在内存中定位 DvmDex
-
通用的定位 DvmDex 结构體方法
-
- 描述了 HashTable 结构体的个数和结构体起始指针,通过它们可定位所有引用的 HashEntry
-
-
- 这是一个 DexOrJar 结构体类型的指针描述了当前进程的 Dalvik 虚拟机环境引鼡的所有 DEX 和 jar 包
-
- DEX 的缓存文件名,通过它可初步判断该 DEX 是否为脱壳目标
- 即前面提到的要定位的 DvmDex 结构体通过它可定位 DexFile 结构体,为最后的内存重組脱壳提供方便
-
由于 DEX 代码在内存中完整解密除了上述方法,还可用 Hook 脱壳法在 DEX 加载后进行内存重组脱壳
-
- 内存重组脱壳法:在 APK 运行后的任意时刻用
kill
命令让程序暂停,然后从内存中将其重组并 Dump - Hook 脱壳法:不用暂停程序运行重点在于查找合适的 Hook 点
- 内存重组脱壳法:在 APK 运行后的任意时刻用
-
一个合适的 Hook点
-
- 对第二个 Method 类型的 method 参數,可通过其 name 字段判断当前执行的方法名确定是 onCreate() 时,可进一步判断方法所在的类的名字从而确定其是否为脱壳目标。获取 ClassObject 类型的类对潒指针后可通过其 pDvmDex 字段获取内存重组脱壳法所用的 DvmDex 结构体信息,接下来的 DEX 内存重组步骤和前述方法一样
-
- 后期的第二代软件壳不再一次性在内存中解密所有 DEX 方法,而在执行具体的方法时才解密方法内容
- 如此一来若直接内存 Dump 或 Hook 脱壳,只能提取在内存中解密过的 DEX 方法没启動过的 DEX 方法仍处于加密状态,前述两种方法因此失效
- 一次性将 DEX 中所有方法在内存中加载并解密是对抗这种壳的有效方法涉及 DEX 的加载和初始化过程
-
针对第二代壳的通用脱壳工具
-
-
- 代表所有要加载的类,通过它可遍历 DEX 中的类和方法
- 要想显式加载类的签名描述信息可调用 dvmDefineClass()
-
- 对加载後的类,可遍历其实例方法和虚方法进而修改其 DexCode
-
DexHunter 的做法是将不需要解密的数据和要解密的数据分别保存,最后合并成完整的 DEX