2017年2月9日星期四

Android 逆向学习笔记 (八)- Classes.dex 结构简单介绍

Classes.dex 作为安卓 Dalvik 虚拟机的可执行文件有很多神奇的特性,部分特性也常被用来做加固方法,所以学习安卓逆向时,了解 Classes.dex 是很有必要的。

首先先来看一下 Classes.dex 的文件结构,如下图所示:


大概是这么样的一个结构,主要分成了七个部分:

Dex Header              # Dex 文件头部,记录整个 Dex 文件的相关属性
String Table            # 字符串数据索引,记录每个字符串在数据区的偏移量
Type Table              # 类型数据索引,记录每个类型的
Proto Table             # 原型数据索引,记录了方法生命的字符串、返回类型和参数列表
Field Table             # 字段数据索引,记录了所属类,类名及方法名
Method Table            # 类方法索引,记录方法所属类名,方法声明以及方法名称等信息
Class Def Table         # 类定义数据索引,记录指定类各类信息,包括借口,超类,类数据偏移量
Data Section            # 数据区,保存了各个类的真实数据

而这七个部分,在解析 Classes.dex 的安卓源码中,每个部分是由一个或多个结构体( struct ) 组成的,具体哪部分是由哪些结构体组成的不细说了,对现阶段安卓逆向来说用处不大,不过可以自己看看具体是什么样子的。

这里推荐一下一个二进制查看工具:010 Editor,可以打开二进制文件并且以多种格式显示,Classes.dex 也可以利用这个工具查看。值得一提的是,它支持解析很多种不同的二进制文件格式,非常方便。


Dalvik 虚拟机运行 Classes.dex 时有一个很奇妙的特性:不会马上生成所有 Class 里的 Method,而是在上文种提到的 Class Def Table 内有一个特殊的 DexMethod(struct) 里面有一个 codeOff(u4) 元素,保存的就是对应 Method 的 DexCode(struct) 的偏移地址。每次需要运行某个 Method 时,就根据各种索引查找到对应的 DexCode。在 DexCode 结构体里有很多个 insns[n](u2) 元素,而最终 Dalvik 执行的字节码就保存在 insns 里面。

现在有很多是通过 .so 通过 HOOK libdvm.so 里的 ClassResolver 实现代码还原,不过也有像娜迦这样,利用 static Method 提前初始化的特性进行代码还原。

因为上面这个特性,Classes.dex 变得非常灵活,可以通过 Native Library 在运行状态中对 insns 重新赋值,达到 Dalvik 执行过程中自篡改的目的;也可以通过修改 CodeOff 来切换指向的内存空间,达到重定义 DexCode 的目的;等等。

很多加固频繁使用的 Dex 类抽取其实就是用的这个特性,这种情况如果没有在调用后擦除 insns 那么则可以在加载成功后进行静态dump,否则,DexHunter 是个不错的选择。

Reference