Java源文件通过Java编译器生成CLASS文件,再通过dx工具转换为classes.dex文件。
DEX文件从整体上来看是一个索引的结构,类名、方法名、字段名等信息都存储在常量池中,这样能够充分减少存储空间,相较于Java字节码文件更适合手机设备。
DEX文件的相关结构声明定义在/dalvik/libdex/DexFile.h文件中,下面我们先来看一下DEX文件中使用到的数据结构。
表1 dex文件使用到的数据结构
类型 | 含义 |
---|---|
u1 | 等同于uint8_t,表示1字节的无符号数 |
u2 | 等同于uint16_t,表示2字节的无符号数 |
u4 | 等同于uint32_t,表示4字节的无符号数 |
u8 | 等同于uint64_t,表示8字节的无符号数 |
sleb128 | 有符号LEB128,可变长度1~5字节 |
uleb128 | 无符号LEB128,可变长度1~5字节 |
uleb128p1 | 无符号LEB128加1,可变长度1~5字节 |
DEX文件的基本结构如下图所示:
header |
---|
string_ids |
type_ids |
proto_ids |
field_ids |
method_ids |
class_def |
data |
link_data |
图1 DEX文件结构
1. header(基本信息)
header是DEX文件头,包含magic字段、alder32校验值、SHA-1哈希值、string_ids的个数以及偏移地址等。DEX文件的头结构很固定,占用0x70个字节,具体定义代码如下所示(摘自DexFile.h):
/*
* Direct-mapped "header_item" struct.
*/
struct DexHeader {
u1 magic[8]; /* includes version number */
u4 checksum; /* adler32 checksum */
u1 signature[kSHA1DigestLen]; /* SHA-1 hash */
u4 fileSize; /* length of entire file */
u4 headerSize; /* offset to start of next section */
u4 endianTag;
u4 linkSize;
u4 linkOff;
u4 mapOff;
u4 stringIdsSize;
u4 stringIdsOff;
u4 typeIdsSize;
u4 typeIdsOff;
u4 protoIdsSize;
u4 protoIdsOff;
u4 fieldIdsSize;
u4 fieldIdsOff;
u4 methodIdsSize;
u4 methodIdsOff;
u4 classDefsSize;
u4 classDefsOff;
u4 dataSize;
u4 dataOff;
};
magic[8]:共8个字节。目前为固定值dex\n035。
checksum:文件校验码,使用alder32算法校验文件除去magic、checksum外余下的所有文件区域,用于检查文件错误。
signature:使用 SHA-1算法hash除去magic,checksum和signature外余下的所有文件区域 ,用于唯一识别本文件 。
fileSize:DEX文件的长度。
headerSize:header大小,一般固定为0x70字节。
endianTag:指定了DEX运行环境的cpu字节序,预设值ENDIAN_CONSTANT等于0x12345678,表示默认采用Little-Endian字节序。
linkSize和linkOff:指定链接段的大小与文件偏移,大多数情况下它们的值都为0。link_size:LinkSection大小,如果为0则表示该DEX文件不是静态链接。link_off用来表示LinkSection距离DEX头的偏移地址,如果LinkSize为0,此值也会为0。
mapOff:DexMapList结构的文件偏移。
stringIdsSize和stringIdsOff:DexStringId结构的数据段大小与文件偏移。
typeIdsSize和typeIdsOff:DexTypeId结构的数据段大小与文件偏移。
protoIdsSize和protoIdsSize:DexProtoId结构的数据段大小与文件偏移。
fieldIdsSize和fieldIdsSize:DexFieldId结构的数据段大小与文件偏移。
methodIdsSize和methodIdsSize:DexMethodId结构的数据段大小与文件偏移。
classDefsSize和classDefsOff:DexClassDef结构的数据段大小与文件偏移。
dataSize和dataOff:数据段的大小与文件偏移。
下面我们来看某apk中classes.dex的解析结果,确实与上面的结构一致: