Dex文件格式学习

快速简记:

结构 单位结构体占字节 共计字节
DexHeader - 0x70h
String Table 4 -
Type Table 4 -
Proto Table 12 -
Field Table 8 -
Method Table 8 -
Class Def Table 32 -
Data Section(含Map Section) - -
## 练习用的dex文件链接
https://github.com/eternalsakura/ctf_pwn/blob/master/android%E9%80%86%E5%90%91/Hello.dex
## 文件布局
dex 文件可以分为3个模块,头文件(header)、索引区(xxxx_ids)、数据区(data)。
头文件概况的描述了整个 dex 文件的分布,包括每一个索引区的大小跟偏移。索引区的ids 是 identifiers 的缩写,表示每个数据的标识,索引区主要是指向数据区的偏移。
010Editor 中除了数据区(data)没有显示出来,其他区段都有显示,另外 link_data 在模板中被定为map_list
## header
header 描述了 dex 文件信息,和其他各个区的索引。010Editor中用结构体 struct header_item 来描述 header。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
typedef struct {
dex_magic magic <comment="Magic value">;
uint checksum <format=hex, comment="Alder32 checksum of rest of file">;
SHA1 signature <comment="SHA-1 signature of rest of file">;
uint file_size <comment="File size in bytes">;
uint header_size <comment="Header size in bytes">;
uint endian_tag <format=hex, comment="Endianness tag">;

if(endian_tag != ENDIAN_CONSTANT) {
// XXX we don't handle big endian files
Warning("Invalid endian_tag %.8X, should be %.8X", endian_tag, ENDIAN_CONSTANT);
}

uint link_size <comment="Size of link section">;
uint link_off <comment="File offset of link section">;
uint map_off <comment="File offset of map list">;
uint string_ids_size <comment="Count of strings in the string ID list">;
uint string_ids_off <comment="File offset of string ID list">;
uint type_ids_size <comment="Count of types in the type ID list">;
uint type_ids_off <comment="File offset of type ID list">;
uint proto_ids_size <comment="Count of items in the method prototype ID list">;
uint proto_ids_off <comment="File offset of method prototype ID list">;
uint field_ids_size <comment="Count of items in the field ID list">;
uint field_ids_off <comment="File offset of field ID list">;
uint method_ids_size <comment="Count of items in the method ID list">;
uint method_ids_off <comment="File offset of method ID list">;
uint class_defs_size <comment="Count of items in the class definitions list">;
uint class_defs_off <comment="File offset of class definitions list">;
uint data_size <comment="Size of data section in bytes">;
uint data_off <comment="File offset of data section">;
} header_item;

magic

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// utility for reading/checking the magic value
typedef struct {
char dex[3];
char newline;
char ver[3];
char zero;

// XXX not checking the version, but it should be 035
if((Strcmp(dex, "dex") && Strcmp(dex, "dey")) ||
newline != '\n' ||
zero != 0) {

Warning("Invalid DEX file");
return -1;
}
} dex_magic <read=DexMagicRead>;

‘\n’ 10(十进制) 换行(newline)

checksum:

文件校验码,使用 alder32 算法校验文件除去 maigc、checksum 外余下的所有文件区域,用于检查文件错误。

uint类型,大小为4个字节。

signature

使用 SHA-1 算法 hash 除去 magic、checksum 和 signature 外余下的所有文件区域, 用于唯一识别本文件,大小为20字节。

utility type to show the SHA1 hash in the value column
1
typedef ubyte SHA1[20] <read=SHA1Read, format=hex>;

file_size

dex 文件大小,4个字节。

header_size

header 区域的大小,目前是固定为 0x70

endian_tag

大小端标签,dex 文件格式为小端,固定值为 0x12345678 常量

link section的大小

link section在文件中的偏移

map_off

map_item 的偏移地址,该 item 属于 data 区里的内容,值要大于等于 data_off 的大小,处于 dex 文件的末端。

string_ids_sizestring_ids_off

这两个字段表示dex中用到的所有的字符串内容的大小和偏移值,我们需要解析完这部分,然后用一个字符串池存起来,后面有其他的数据结构会用索引值来访问字符串,这个池子也是非常重要的。后面会详细介绍string_ids的数据结构

type_ids_sizetype_ids_off

这两个字段表示dex中的类型数据结构的大小和偏移值,比如类类型,基本类型等信息,后面会详细介绍type_ids的数据结构

proto_ids_sizetype_ids_off

这两个字段表示dex中的元数据信息数据结构的大小和偏移值,描述方法的元数据信息,比如方法的返回类型,参数类型等信息,后面会详细介绍proto_ids的数据结构

field_ids_sizefield_ids_off

这两个字段表示dex中的字段信息数据结构的大小和偏移值,后面会详细介绍field_ids的数据结构

method_ids_sizemethod_ids_off

这两个字段表示dex中的方法信息数据结构的大小和偏移值,后面会详细介绍method_ids的数据结构

class_defs_sizeclass_defs_off

这两个字段表示dex中的类信息数据结构的大小和偏移值,这个数据结构是整个dex中最复杂的数据结构.

data_size和data_off`

这两个字段表示dex中数据区域的结构信息的大小和偏移值,这个结构中存放的是数据区域,比如我们定义的常量值等信息。


上述这些其实就是对应在下图中圈起来的部分的每部分的开头和大小。

string_ids

string_ids 区段描述了 dex 文件中所有的字符串。格式很简单只有一个偏移量,偏移量指向了 string_data 区段的一个字符串:

1
2
3
4
5
6
7
8
9
10
11
12
13
//////////////////////////////////////////////////
// strings
//////////////////////////////////////////////////

typedef struct {
uleb128 utf16_size <comment="Size of string in UTF-16 code units">;
string data <comment="A string in MUTF-8 format">;
} string_item;

typedef struct {
uint string_data_off <comment="File offset of string data">;
string_item string_data <comment="String item">;
} string_id_item <read=StringDataReader, optimize=false>;

一个string_id_item里有两个数据结构:

1
2
uint string_data_off 
string_item string_data <comment="String item">;


根据string_data_off可以找到字符串的数据位置。
string_data里存放字符串的大小和具体数据。

type_ids

type_ids 区索引了 dex 文件里的所有数据类型,包括 class 类型,数组类型(array types)和基本类型
(primitive types)。区段里的元素格式为 type_ids_item
type_ids_item 里面 descriptor_idx 的值的意思,是 string_ids 里的 index 序号,是用来描述此 type 的字符串。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94

//////////////////////////////////////////////////
// type IDs
//////////////////////////////////////////////////

typedef struct {
uint descriptor_idx <read=StringIdRead, comment="String ID for this type descriptor">;
} type_id_item <read=TypeIDRead, optimize=false>;

string TypeIDRead(type_id_item &i) {
return GetLongTypeDescriptor(GetStringById(i.descriptor_idx));
}

typedef struct (int size) {
local int s = size;
type_id_item type_id[size] <comment="Type ID">;
} type_id_list <read=TypeIDListRead>;

string TypeIDListRead(type_id_list &l) {
string s;
s = SPrintf(s, "%d types", l.s);
return s;
}

//////////////////////////////////////////////////
// type list
//////////////////////////////////////////////////
typedef struct {
ushort type_idx <comment="Index into type_ids list">;
} type_item;

typedef struct {
uint size <comment="Number of entries in type list">;
type_item list[size] <read=TypeItemRead, comment="Type entry">;
} type_item_list <read=TypeItemListRead, optimize=false>;

string TypeItemRead(type_item &t) {
return GetTypeById(t.type_idx);
}

string TypeItemListRead(type_item_list &l) {
string s = "";
string tmp;
int i;

for(i = 0; i < l.size; i++) {
s += GetTypeById(l.list[i].type_idx);
if(i+1 < l.size) {
s += ", ";
}
}
return s;
}

string GetLongTypeDescriptor(string descriptor) {
local string desc = "";
local string post = "";
local int i = 0;
local int len = Strlen(descriptor);

// array descriptors
while(descriptor[i] == '[') {
post += "[]";
i++;

if(i >= len) return "ERROR";
}

if(descriptor[i] == 'L') {
// fully qualified class descriptors
i++;
while(i < len) {
if(descriptor[i] == '/') desc += ".";
else if(descriptor[i] == ';') break;
else desc += descriptor[i];
i++;
}
} else {
// simple type descriptors
switch(descriptor[i]) {
case 'V': desc = "void"; break;
case 'Z': desc = "boolean"; break;
case 'B': desc = "byte"; break;
case 'S': desc = "short"; break;
case 'C': desc = "char"; break;
case 'I': desc = "int"; break;
case 'J': desc = "long"; break;
case 'F': desc = "float"; break;
case 'D': desc = "double"; break;
}
}

return desc + post;
}

proto_ids

proto 的意思是 method prototype 代表 java 语言里的一个 method 的原型 。proto_ids 里的元素为 proto_id_item,结构如下:

1
2
3
4
5
6
7
8
9
10
//////////////////////////////////////////////////
// protoypes
//////////////////////////////////////////////////

typedef struct {
uint shorty_idx <read=StringIdRead, comment="String ID of short-form descriptor">;
uint return_type_idx <read=TypeIdRead, comment="Type ID of the return type">;
uint parameters_off <comment="File offset of parameter type list">;
....
}
  • shorty_idx: 跟type_ids 一样,它的值是一个 string_ids 的 index 号 ,最终是一个简短的字符串描述,用来说明该 method 原型。
  • return_type_idx: 它的值是一个 type_ids 的 index 号 ,表示该 method 原型的返回值类型。
  • parameters_off: 指向 method 原型的参数列表 type_list,若 method 没有参数,值为0。
    参数列表的格式是 type_list,下面会有描述。

field_ids

filed_ids 区里面有 dex 文件引用的所有的 field。区段的元素格式是 field_id_item,结构如下:

1
2
3
4
5
6
7
8
9
//////////////////////////////////////////////////
// fields
//////////////////////////////////////////////////

typedef struct {
ushort class_idx <read=LongTypeIdRead, comment="Type ID of the class that defines this field">;
ushort type_idx <read=LongTypeIdRead, comment="Type ID for the type of this field">;
uint name_idx <read=StringIdRead, comment="String ID for the field's name">;
} field_id_item <read=FieldIdItemRead, optimize=false>;
  • class_idx: 表示 field 所属的 class 类型,class_idx 的值是 type_ids 的一个 index,并且必须指向一个 class 类型。

  • type_idx: 表示本 field 的类型,它的值也是 type_ids的一个 index 。

  • name_idx: 表示本 field 的名称,它的值是 string_ids 的一个 index 。

method_ids

method_ids 描述了 dex 文件里的所有的 method。method_ids 的元素格式是method_id_item,结构跟 fields_ids 很相似:

1
2
3
4
5
6
7
8
9
//////////////////////////////////////////////////
// methods
//////////////////////////////////////////////////

typedef struct {
ushort class_idx <read=LongTypeIdRead, comment="Type ID of the class that defines this method">;
ushort proto_idx <read=ProtoIdxRead, comment="Prototype ID for this method">;
uint name_idx <read=StringIdRead, comment="String ID for the method's name">;
} method_id_item <read=MethodIdItemRead, optimize=false>;
  • class_idx: 表示 method 所属的 class 类型,class_idx 的值是 type_ids 的一个 index,并且必须指向一个 class 类型。
  • proto_idx: 表示 method 的类型,它的值也是 type_ids 的一个 index。
  • name_idx: 表示 method 的名称,它的值是 string_ids 的一个 index。
    都是索引,分别指向类型池,函数原型池,字符串池

class_defs

概览

  • class_idx: 描述具体的 class 类型,值是 type_ids 的一个 index 。值必须是一个 class 类型,不能是数组类型或者基本类型。

  • access_flags: 描述 class 的访问类型,诸如 public , final , static 等。在 dex-format.html 里 “access_flags Definitions” 有具体的描述 。

  • superclass_idx: 描述 supperclass 的类型,值的形式跟 class_idx 一样 。

  • interfaces_off: 值为偏移地址,指向 class 的 interfaces,被指向的数据结构为 type_list 。class 若没有 interfaces,则值为 0。

  • source_file_idx: 表示源代码文件的信息,值是 string_ids 的一个 index。若此项信息缺失,此项值赋值为 NO_INDEX=0xffff ffff。

  • annotions_off: 值是一个偏移地址,指向的内容是该 class 的注释,位置在 data 区,格式为 annotations_direcotry_item。若没有此项内容,值为 0 。

  • class_data_off: 值是一个偏移地址,指向的内容是该 class 的使用到的数据,位置在 data 区,格式为 class_data_item。若没有此项内容值为 0。该结构里有很多内容,详细描述该 class 的 field、method, method 里的执行代码等信息,后面会介绍 class_data_item

  • static_value_off: 值是一个偏移地址 ,指向 data 区里的一个列表 (list),格式为 encoded_array_item。若没有此项内容值为 0。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    typedef struct {
    local int64 pos;

    uint class_idx <read=LongTypeIdRead, comment="Type ID for this class">;
    ACCESS_FLAGS access_flags <comment="Access flags">;
    uint superclass_idx <read=LongTypeIdRead, comment="Type ID for this class's superclass">;

    uint interfaces_off <comment="File offset to interface list">;
    if(interfaces_off != 0) {
    pos = FTell();
    FSeek(odexpad + interfaces_off);
    type_item_list interfaces <read=InterfacesRead, comment="Interface data">;
    FSeek(pos);
    }

    uint source_file_idx <read=StringIdRead, comment="String ID for the name of the file with this class defined">;

    uint annotations_off <comment="File offset to the annotation structure for this class">;
    if(annotations_off != 0) {
    pos = FTell();
    FSeek(odexpad + annotations_off);
    annotations_directory_item annotations <comment="Annotation data">;
    FSeek(pos);
    }

    uint class_data_off <comment="File offset to the class data for this class">;
    if(class_data_off != 0) {
    pos = FTell();
    FSeek(odexpad + class_data_off);
    class_data_item class_data <comment="Class data">;
    FSeek(pos);
    }

    uint static_values_off <comment="File offset to static field data">;
    if(static_values_off != 0) {
    pos = FTell();
    FSeek(odexpad + static_values_off);
    struct encoded_array_item static_values <comment="Static values">;
    FSeek(pos);
    }
    } class_def_item <read=ClassDefItemRead, optimize=false>;

    type_list

    type_list 在 data 区段,class_def_item->interface_off 就是指的这里的数据。数据结构如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    struct type_list
    {
    uint size;
    type_item list [size]
    }

    struct type_item
    {
    ushort type_idx //-->type_ids
    }
  • size: 表示类型个数

  • type_idx: 对应一个 type_ids 的 index

    annotations_directory_item

    class_def_item->annotations_off 指向的数据区段,定义了 annotation 相关的数据描述,数据结构如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    struct annotation_directory_item
    {
    uint class_annotations_off; //-->annotation_set_item
    uint fields_size;
    uint annotated_methods_size;
    uint annotated_parameters_size;

    field_annotation field_annotations[fields_size];
    method_annotation method_annotations[annotated_methods_size];
    parameter_annotation parameter_annotations[annotated_parameters_size];
    }

    struct field_annotation
    {
    uint field_idx;
    uint annotations_off; //-->annotation_set_item
    }

    struct method_annotation
    {
    uint method_idx;
    uint annotations_off; //-->annotation_set_item
    }

    struct parameter_annotation
    {
    uint method_idx;
    uint annotations_off; //-->annotation_set_ref_list
    }
  • class_annotations_off: 这个偏移指向了annotation_set_item

  • fields_size: 表示属性的个数

  • annotated_methods_size: 表示方法的个数

  • annotated_parameters_size: 表示参数的个数

class_data_item

class_data_off 指向 data 区里的 class_data_item 结构,class_data_item 里存放着本 class 使用到的各种数据,下面是 class_data_item 的结构 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
typedef struct {
uleb128 static_fields_size <comment="The number of static fields">;
uleb128 instance_fields_size <comment="The number of instance fields">;
uleb128 direct_methods_size <comment="The number of direct methods">;
uleb128 virtual_methods_size <comment="The number of virtual methods">;

if(uleb128_value(static_fields_size) > 0) {
encoded_field_list static_fields(uleb128_value(static_fields_size)) <comment="Encoded sequence of static fields">;
}

if(uleb128_value(instance_fields_size) > 0) {
encoded_field_list instance_fields(uleb128_value(instance_fields_size)) <comment="Encoded sequence of instance fields">;
}

if(uleb128_value(direct_methods_size) > 0) {
encoded_method_list direct_methods(uleb128_value(direct_methods_size)) <comment="Encoded sequence of direct methods">;
}

if(uleb128_value(virtual_methods_size) > 0) {
encoded_method_list virtual_methods(uleb128_value(virtual_methods_size)) <comment="Encoded sequence of virtual methods">;
}
} class_data_item <read=ClassDataItemRead>;

// encoded fields
typedef struct (int previd) {
local int p = previd;

uleb128 field_idx_diff <comment="Field ID for this field, represented as the difference from the previous index">;
uleb128 access_flags <read=AccessFlagsReadUleb, comment="Access flags">;
} encoded_field <read=EncodedFieldRead, optimize=false>;

// encoded methods
typedef struct (int previd) {
local int p = previd;

uleb128 method_idx_diff <comment="Method ID for this method, represented as the difference from the previous index">;
uleb128 access_flags <read=AccessFlagsReadUleb, comment="Access flags">;
uleb128 code_off <comment="File offset to the code for this method">;

if(uleb128_value(code_off) != 0) {
local int64 pos = FTell();
FSeek(odexpad + uleb128_value(code_off));
struct code_item code <comment="Code structure for this method">;
FSeek(pos);
}
} encoded_method <read=EncodedMethodRead, optimize=false>;
  • static_fields_size: 静态成员变量的个数
  • instance_fields_size: 实例成员变量个数
  • direct_methods_size: 直接函数个数
  • virtual_methods_size: 虚函数个数

    encoded_methods

  • access_flags: 访问权限,比如 public、private、static、final 等。
  • code_off: 一个指向 data 区的偏移地址,目标是本 method 的代码实现。被指向的结构是code_item,有近 10 项元素。

    code_item

    code_item 结构里描述着某个 method 的具体实现.

map_list

map_list 中大部分 item 跟 header 中的相应描述相同,都是介绍了各个区的偏移和大小,但是 map_list 中描述的更加全面,包括了 HEADER_ITEM 、TYPE_LIST、STRING_DATA_ITEM、DEBUG_INFO_ITEM 等信息。