安全圈的朋友们,对于DEX文件应该是比较了解的。我们这次就简单介绍一下吧:
DEX文件(Dalvik Executable)是一种专为 Android 操作系统设计的可执行文件格式。DEX文件包含了由 Java 语言编写的程序的字节码,这些程序在运行时被 Dalvik 虚拟机(DVM)解释执行。
在 Android 应用程序开发中,Java 代码经过编译器编译生成Java字节码文件(.class文件),然后通过工具将字节码文件转换为DEX格式,最后打包成APK文件供安装和运行。由于 Android 设备的处理器架构和Java虚拟机的差异,所以需要将Java字节码转换为DEX格式,以便在Dalvik虚拟机上运行。
DEX文件的优点:
1.能够高效地使用内存和处理器资源,这是因为它采用了基于寄存器的架构,相对于传统的基于堆栈的Java虚拟机,在执行Java程序时,能够更快地加载和执行代码。
2.DEX文件支持在应用程序运行时动态加载类和方法,提供了更高的灵活性。
所以我们如果想要了解加固解决方案如何帮助Android 应用对抗逆向和破解,首先需要了解DEX文件到底是什么样的。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LCz6I734-1681365804280)(/img/bVc7k1C)]
DEX文件在010中的表现
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Dw8leHkG-1681365804281)(/img/bVc7k2Y)]
struct Header {
uint8_t magic[8]; // dex版本标识
uint32_t checksum; // adler32校验和
uint8_t signature[kShalDigestSize]; // SHA-1哈希值
uint32_t file_size; // 文件大小
uint32_t header_size; //Header结构大小
uint32_t endian_tag; //字节序标记
uint32_t link_size; // 链接段大小
uint32_t link_off; // 链接段偏移
uint32_t map_off; //映射项偏移
uint32_t string_ids_size; //字符串标识符列表个数
uint32_t string_ids_off; //字符串标识符列表偏移
uint32_t type_ids_size; //类型标识符列表个数
uint32_t type ids_off; //类型标识符列表偏移
uint32_t proto_ids_size; //方法原型标识符列表个数
uint32_t proto_ids_off; //方法原型标识符列表偏移
uint32 t field ids size; //字段标识符列表个数
uint32_t field_ids_off; //字段标识符列表偏移
uint32 t method_ids_size; //方法标识符列表个数
uint32_t method_ids_off; //方法标识符列表偏移
uint32_t class_defs_size; //类定义列表个数
uint32_t class_defs_off; //类定义列表偏移
uint32_t data_size; //数据区大小
uint32_t data_off; //数据区偏移
};
文件头
magic[8]:dex版本标识。这类字节必须出现在 .dex 文件的开头,以便系统将其原样识别。该值会特意包含一个换行符("\n" 或 0x0a)和空字节("\0" 或 0x00),以便协助检测某些形式的损坏问题。该值还可以将格式版本号编码为 3 个十进制数字;随着格式的演变,预计该值会单调递增。
checksum:可以用于文件剩余内容(除 magic 和此字段之外的所有内容)的 adler32 校验和。另外,还可以用于检测文件损坏情况。
signature[kSha1DigestSize]:文件剩余内容(除 magic、checksum 和此字段之外的所有内容)的 SHA-1 签名(哈希);用于对文件进行唯一标识。
file_size:整个文件(包括标头)的大小,以字节为单位。
header_size:标头(整个区段)的大小,以字节为单位。这一项允许至少一定程度的向后/向前兼容性,而不必让格式失效。
endian_tag:字节序标记。ENDIAN_CONSTANT ,表示小端字节序。
REVERSE_ENDIAN_CONSTANT 表示大端字节序,默认值为 ENDIAN_CONSTANT 。
link_size 与 link_off :链接区段的大小与文件偏移。如果此文件未进行静态链接,则两个值都为0。
map_off:从文件开头到映射项列表的文件偏移量。
string_ids_size 与 string_ids_off:字符串标识符列表中的字符串数量与文件偏移。
type_ids_size 与 type_ids_off:类型标识符列表中的元素数量及文件偏移。元素数量上限为65535。
proto_ids_size 与 proto_ids_off:原型标识符列表中的元素数量及文件偏移。元素数量上限为65535。
field_ids_size 与 field_ids_off:字段标识符列表中的元素数量及文件偏移。
method_ids_size 与 method_ids_off:方法标识符列表中的元素数量及文件偏移。
class_defs_size 与 class_defs_off:类定义列表中的元素数量及文件偏移。
data_size 与 data_off :data区段的大小及文件偏移。
字符串标识符列表( dex_string_ids )在DEX文件以 DexStringId[] 的形式存在,其结构如下。
struct DexStringId {
u4 stringDataOff; //字符串数据的文件偏移
};
类型标识符列表( dex_type_ids )在DEX文件以 DexTypeId[] 的形式存在,其结构如下。
struct DexType {
u4 descriptorIdx; //类型描述符对应在字符串标识符列表中的索引
};
方法原型标识符列表( dex_proto_ids )在DEX文件以 DexProtoId[] 的形式存在,其中 DexProtoId 结构如下。
struct DexProtoId {
u4 shortyIdx; // 方法原型的简写形式对应在字符串标识符列表中的索引
u4 returnTypeIds; // 返回值类型对应在类型标识符列表中的索引
u4 parametersOff; // 参数类型在DEX文件中的偏移,结构为DexTypeList
};
DexTypeList 结构如下。
struct DexTypeItem {
u2 typeIdx; // 类型在类型标识符表中的索引
};
struct DexTypeList {
u4 size; // 返回值类型对应在类型标识符列表中的索引
DexTypeItem List[1]; // DexTypeItem数组
};
方法标识符列表( dex_method_ids ) 在DEX文件以 DexMethodId[] 的形式存在,其中 DexMethodId 结构如下
struct DexMethodId {
u2 classIdx; // 类定义在类型标识符列表中的索引
u2 protoIdx; // 方法原型类型在方法原型标识符列表中的索引
u2 nameIdx; // 方法名称在字符串列表中的索引
};
类定义列列表( dex_class_defs )在DEX文件以 DexClassDef[] 的形式存在,其中 DexClassDef 结构如下
struct DexClassId {
u4 classIdx; // 类定义在类型标识符列表中的索引
u4 accessFlags; // 类访问标识
u4 superclassIdx; // 超类在类型标识符列表中的索引
u4 interfaceOff; // 接口,DexTypeList结构的文件偏移
u4 sourceFileIdx; // 源文件名在字符串表示列表中的索引
u4 annotationsOff; // 注解,DexAnnotationsDirectoryItem结构的文件偏移
u4 classDataOff; // 类数据,DexClassData结构的文件偏移
u4 staticValuesOff; // 静态值,DexEncodeArray结构的文件偏移
};
映射项列表( dex_map_list ) 在DEX文件以 DexMapItem[] 的形式存在,其中 DexMapItem 结构如下。
struct DexMapItem {
u2 type; //类型代码
u2 unused; //未使用
u4 size; //在指定偏移量处找到的项数量
u4 offset; //相关项的文件偏移量
};
类型代码表如图所示。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AFsRhOuH-1681365804281)(/img/bVc7k85)]
今天就先讲到这里啦,下次将在第二篇文章中继续解析DEX文件的DexAnnotationsDirectoryItem 、 DexClassData 与 DexEncodeArray 结构,请继续关注~