Android 虚拟机 — .dex 文件格式

前言

  关于 .dex 文件格式,网址 https://source.android.com/devices/tech/dalvik/dex-format 中有极为详尽的描述,讲述的非常清楚,还可以把它当作工具来参考。但只是大体略一遍,印象不会太深刻,因此,我自己写了一个例子拿来分析一下,并给出一些自己的整理和总结。

示例

public class Hello {
    private static final String Hello_DEX = "Hello! My Dex!";

    public static void main(String[] args) {
        System.out.println(Hello_DEX + "\n");
    }
}
  • 编译:javac Hello.java
  • 执行:java Hello
  • 生成 dex 文件:dx –dex –output=Hello.dex Hello.class
  • 运行 push 进手机的 dex 文件:
    • adb shell
    • dalvikvm -cp /storage/emulated/0/Hello.dex Hello
  • 查看二进制 dex 文件:hexdump -C Hello.dex
00000000  64 65 78 0a 30 33 35 00  d5 68 ac d8 fd 4c 28 8c  |dex.035..h...L(.|
00000010  61 91 7d 47 00 f8 c2 d7  e6 d4 f2 f2 fa 3d f3 58  |a.}G.........=.X|
00000020  20 03 00 00 70 00 00 00  78 56 34 12 00 00 00 00  | ...p...xV4.....|
00000030  00 00 00 00 74 02 00 00  10 00 00 00 70 00 00 00  |....t.......p...|
00000040  07 00 00 00 b0 00 00 00  03 00 00 00 cc 00 00 00  |................|
00000050  02 00 00 00 f0 00 00 00  04 00 00 00 00 01 00 00  |................|
00000060  01 00 00 00 20 01 00 00  e0 01 00 00 40 01 00 00  |.... .......@...|
00000070  86 01 00 00 8e 01 00 00  9e 01 00 00 af 01 00 00  |................|
00000080  bb 01 00 00 c6 01 00 00  cf 01 00 00 e6 01 00 00  |................|
00000090  fa 01 00 00 0e 02 00 00  22 02 00 00 25 02 00 00  |........"...%...|
000000a0  29 02 00 00 3e 02 00 00  44 02 00 00 49 02 00 00  |)...>...D...I...|
000000b0  05 00 00 00 06 00 00 00  07 00 00 00 08 00 00 00  |................|
000000c0  09 00 00 00 0a 00 00 00  0c 00 00 00 0a 00 00 00  |................|
000000d0  05 00 00 00 00 00 00 00  0b 00 00 00 05 00 00 00  |................|
000000e0  78 01 00 00 0b 00 00 00  05 00 00 00 80 01 00 00  |x...............|
000000f0  00 00 03 00 04 00 00 00  04 00 01 00 0e 00 00 00  |................|
00000100  00 00 00 00 00 00 00 00  00 00 02 00 0d 00 00 00  |................|
00000110  01 00 01 00 0f 00 00 00  02 00 00 00 00 00 00 00  |................|
00000120  00 00 00 00 01 00 00 00  02 00 00 00 00 00 00 00  |................|
00000130  03 00 00 00 00 00 00 00  61 02 00 00 5e 02 00 00  |........a...^...|
00000140  01 00 01 00 01 00 00 00  52 02 00 00 04 00 00 00  |........R.......|
00000150  70 10 03 00 00 00 0e 00  03 00 01 00 02 00 00 00  |p...............|
00000160  57 02 00 00 08 00 00 00  62 00 01 00 1a 01 02 00  |W.......b.......|
00000170  6e 20 02 00 10 00 0e 00  01 00 00 00 03 00 00 00  |n ..............|
00000180  01 00 00 00 06 00 06 3c  69 6e 69 74 3e 00 0e 48  |.......<init>..H|
00000190  65 6c 6c 6f 21 20 4d 79  20 44 65 78 21 00 0f 48  |ello! My Dex!..H|
000001a0  65 6c 6c 6f 21 20 4d 79  20 44 65 78 21 0a 00 0a  |ello! My Dex!...|
000001b0  48 65 6c 6c 6f 2e 6a 61  76 61 00 09 48 65 6c 6c  |Hello.java..Hell|
000001c0  6f 5f 44 45 58 00 07 4c  48 65 6c 6c 6f 3b 00 15  |o_DEX..LHello;..|
000001d0  4c 6a 61 76 61 2f 69 6f  2f 50 72 69 6e 74 53 74  |Ljava/io/PrintSt|
000001e0  72 65 61 6d 3b 00 12 4c  6a 61 76 61 2f 6c 61 6e  |ream;..Ljava/lan|
000001f0  67 2f 4f 62 6a 65 63 74  3b 00 12 4c 6a 61 76 61  |g/Object;..Ljava|
00000200  2f 6c 61 6e 67 2f 53 74  72 69 6e 67 3b 00 12 4c  |/lang/String;..L|
00000210  6a 61 76 61 2f 6c 61 6e  67 2f 53 79 73 74 65 6d  |java/lang/System|
00000220  3b 00 01 56 00 02 56 4c  00 13 5b 4c 6a 61 76 61  |;..V..VL..[Ljava|
00000230  2f 6c 61 6e 67 2f 53 74  72 69 6e 67 3b 00 04 6d  |/lang/String;..m|
00000240  61 69 6e 00 03 6f 75 74  00 07 70 72 69 6e 74 6c  |ain..out..printl|
00000250  6e 00 01 00 07 0e 00 05  01 00 07 0e 78 00 01 17  |n...........x...|
00000260  01 01 00 02 00 00 1a 00  81 80 04 c0 02 01 09 d8  |................|
00000270  02 00 00 00 0e 00 00 00  00 00 00 00 01 00 00 00  |................|
00000280  00 00 00 00 01 00 00 00  10 00 00 00 70 00 00 00  |............p...|
00000290  02 00 00 00 07 00 00 00  b0 00 00 00 03 00 00 00  |................|
000002a0  03 00 00 00 cc 00 00 00  04 00 00 00 02 00 00 00  |................|
000002b0  f0 00 00 00 05 00 00 00  04 00 00 00 00 01 00 00  |................|
000002c0  06 00 00 00 01 00 00 00  20 01 00 00 01 20 00 00  |........ .... ..|
000002d0  02 00 00 00 40 01 00 00  01 10 00 00 02 00 00 00  |....@...........|
000002e0  78 01 00 00 02 20 00 00  10 00 00 00 86 01 00 00  |x.... ..........|
000002f0  03 20 00 00 02 00 00 00  52 02 00 00 05 20 00 00  |. ......R.... ..|
00000300  01 00 00 00 5e 02 00 00  00 20 00 00 01 00 00 00  |....^.... ......|
00000310  61 02 00 00 00 10 00 00  01 00 00 00 74 02 00 00  |a...........t...|
00000320

一、.dex 文件结构

1.1 DexHeader (header_item)

Type 偏移 size 数据 value 说明
magic 0 8 64 65 78 0a 30 33 35 00 “dex\n035\0” 魔法值。
checksum 8 4 d5 68 ac d8 文件剩余内容(除 magic 和此字段之外的所有内容)的 adler32 校验和;用于检测文件损坏情况
siganature 12 20 文件剩余内容(除 magic、checksum 和此字段之外的所有内容)的 SHA-1 签名(哈希);用于对文件进行唯一标识
file_size 32 4 20 03 00 00 0x0320 整个文件(包括标头)的大小,以字节为单位
header_size 36 4 70 00 00 00 0x70 标头(整个区段)的大小,以字节为单位。
endian_tag 40 4 78 56 34 12 0x12345678 字节序标记。
link_size 44 8 00 00 00 00 0x00 链接区段的大小;如果此文件未进行静态链接,则该值为 0
link_off 48 4 00 00 00 00 0x00 该偏移量(如果为非零值)应该是到 link_data 区段的偏移量。
map_off 52 4 74 02 00 00 0x0274
string_ids_size 56 4 10 00 00 00 0x10 字符串标识符列表中的字符串数量
string_ids_off 60 4 70 00 00 00 0X70 从文件开头到字符串标识符列表的偏移量
type_ids_size 64 4 07 00 00 00 0x07 类型标识符列表中的元素数量,最多为 65535
type_ids_off 68 4 b0 00 00 00 0xb0 从文件开头到类型标识符列表的偏移量
proto_ids_size 72 4 03 00 00 00 0x03 原型标识符列表中的元素数量,最多为 65535
proto_ids_off 76 4 cc 00 00 00 0xcc 从文件开头到原型标识符列表的偏移量
field_ids_size 80 4 02 00 00 00 0x02 字段标识符列表中的元素数量
field_ids_off 84 4 f0 00 00 00 0xf0 从文件开头到字段标识符列表的偏移量
method_ids_size 88 4 04 00 00 00 0x04 方法标识符列表中的元素数量
method_ids_off 92 4 00 01 00 00 0x0100 从文件开头到方法标识符列表的偏移量
class_defs_size 96 4 01 00 00 00 0x01 类定义列表中的元素数量
class_defs_off 100 4 20 01 00 00 0x0120 从文件开头到类定义列表的偏移量
data_size 104 4 e0 01 00 00 0x01e0 data 区段的大小(以字节为单位)。该数值必须是 sizeof(uint) 的偶数倍
data_off 108 4 40 01 00 00 0x0140 从文件开头到 data 区段开头的偏移量


DexHeader 的结构如下所示:

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;
};

1.1.1 DEX_FILE_MAGIC (magic)

主要作用是版本标识,这里:

ubyte[8] DEX_FILE_MAGIC = { 0x64 0x65 0x78 0x0a 0x30 0x33 0x35 0x00 }
                        = "dex\n035\0"
  • Android 7.0 版本中新增了对 037 版格式的支持。在 037 版本之前,大多数 Android 版本都使用过 035 版格式。035 版与 037 版之间的唯一区别是,是否添加默认方法以及是否调整 invoke。
  • Android 8.0 版本中新增了对 038 版格式的支持。038 版本中添加了新字节码(invoke-polymorphic 和 invoke-custom)和用于方法句柄的数据。

1.1.2 ENDIAN_CONSTANT (endianTag)

标准的 .dex 格式采用小端字节序,即 endian_tag 为 ENDIAN_CONSTANT。如果实现遇到其 endian_tag 为 REVERSE_ENDIAN_CONSTANT(而非 ENDIAN_CONSTANT)的标头,则会识别该文件已从预期格式进行过字节交换。

uint ENDIAN_CONSTANT = 0x12345678;
uint REVERSE_ENDIAN_CONSTANT = 0x78563412;

1.2 .dex 索引区

1.2.1 string_ids

字符串标识符列表,格式:string_id_item[],列表中存储的就是一个个如下的数据结构,占用 4 bytes,数据 stringDataOff 指示的是每个 string_data_item 结构的偏移

/*
 * Direct-mapped "string_id_item".
 */
struct DexStringId {
    u4 stringDataOff;      /* file offset to string_data_item */
};

由 00000030 00 00 00 00 74 02 00 00 10 00 00 00 70 00 00 00 可知:
本 dex 文件共有 16 个 string_id_item,第一个的偏移是 0x70,数据如下:
00000070 86 01 00 00 8e 01 00 00 9e 01 00 00 af 01 00 00 |…………….|
00000080 bb 01 00 00 c6 01 00 00 cf 01 00 00 e6 01 00 00 |…………….|
00000090 fa 01 00 00 0e 02 00 00 22 02 00 00 25 02 00 00 |……..”…%…|
000000a0 29 02 00 00 3e 02 00 00 44 02 00 00 49 02 00 00 |)…>…D…I…|
string_data_item 的结构:(对齐:无字节对齐)

struct string_data_item 
{ 
    uleb128 utf16_size; 
    ubyte data; 
} 

string_data_item 的数据如下所示:
00000180 01 00 00 00 06 00 06 3c 69 6e 69 74 3e 00 0e 48 |……...H|
00000190 65 6c 6c 6f 21 20 4d 79 20 44 65 78 21 00 0f 48 |ello! My Dex!..H|
000001a0 65 6c 6c 6f 21 20 4d 79 20 44 65 78 21 0a 00 0a |ello! My Dex!…|
000001b0 48 65 6c 6c 6f 2e 6a 61 76 61 00 09 48 65 6c 6c |Hello.java..Hell|
000001c0 6f 5f 44 45 58 00 07 4c 48 65 6c 6c 6f 3b 00 15 |o_DEX..LHello;..|
000001d0 4c 6a 61 76 61 2f 69 6f 2f 50 72 69 6e 74 53 74 |Ljava/io/PrintSt|
000001e0 72 65 61 6d 3b 00 12 4c 6a 61 76 61 2f 6c 61 6e |ream;..Ljava/lan|
000001f0 67 2f 4f 62 6a 65 63 74 3b 00 12 4c 6a 61 76 61 |g/Object;..Ljava|
00000200 2f 6c 61 6e 67 2f 53 74 72 69 6e 67 3b 00 12 4c |/lang/String;..L|
00000210 6a 61 76 61 2f 6c 61 6e 67 2f 53 79 73 74 65 6d |java/lang/System|
00000220 3b 00 01 56 00 02 56 4c 00 13 5b 4c 6a 61 76 61 |;..V..VL..[Ljava|
00000230 2f 6c 61 6e 67 2f 53 74 72 69 6e 67 3b 00 04 6d |/lang/String;..m|
00000240 61 69 6e 00 03 6f 75 74 00 07 70 72 69 6e 74 6c |ain..out..printl|
00000250 6e 00 01 00 07 0e 00 05 01 00 07 0e 78 00 01 17 |n………..x…|

Index 偏移 utf16_size (uleb128 / int) data String
0 0x0186 0x06/6 3c 69 6e 69 74 3e 00 < init >
1 0x018e 0x0e/14 48 65 6c 6c 6f 21 20 4d 79 20 44 65 78 21 00 Hello! My Dex!
2 0x019e 0x0f/15 48 65 6c 6c 6f 21 20 4d 79 20 44 65 78 21 0a 00 Hello! My Dex!\n
3 0x01af 0x0a/10 48 65 6c 6c 6f 2e 6a 61 76 61 00 Hello.java
4 0x01bb 0x09/9 48 65 6c 6c 6f 5f 44 45 58 00 Hello_DEX
5 0x01c6 0x07/7 4c 48 65 6c 6c 6f 3b 00 LHello;
6 0x01cf 0x15/21 4c 6a 61 76 61 2f 69 6f 2f 50 72 69 6e 74 53 74 72 65 61 6d 3b 00 Ljava/io/PrintStream;
7 0x01e6 0x12/18 4c 6a 61 76 61 2f 6c 61 6e 67 2f 4f 62 6a 65 63 74 3b 00 Ljava/lang/Object;
8 0x01fa 0x12/18 4c 6a 61 76 61 2f 6c 61 6e 67 2f 53 74 72 69 6e 67 3b 00 Ljava/lang/String;
9 0x020e 0x12/18 6a 61 76 61 2f 6c 61 6e 67 2f 53 79 73 74 65 6d 3b 00 Ljava/lang/System;
10 0x0222 0x01/1 56 00 V
11 0x0225 0x02/2 56 4c 00 VL
12 0x0229 0x13/19 2f 6c 61 6e 67 2f 53 74 72 69 6e 67 3b 00 [Ljava/lang/String;
13 0x023e 0x04/4 6d 61 69 6e 00 main
14 0x0244 0x03/3 6f 75 74 00 out
15 0x0249 0x07/7 70 72 69 6e 74 6c 6e 00 println

1.2.2 type_ids

类型标识符列表,格式:type_id_item[]

/*
 * Direct-mapped "type_id_item".
 */
struct DexTypeId {
    u4  descriptorIdx;      /* index into stringIds list for type descriptor */
};

descriptorIdx 为对应 type 描述字符串的 stringId 的 index
00000040 07 00 00 00 b0 00 00 00 03 00 00 00 cc 00 00 00 |…………….|
由 dexheader 的数据可知,type_id_item 共有 7 个,从 0xb0 开始
000000b0 05 00 00 00 06 00 00 00 07 00 00 00 08 00 00 00 |…………….|
000000c0 09 00 00 00 0a 00 00 00 0c 00 00 00 0a 00 00 00 |…………….|

Index String
0 0x05 LHello;
1 0x06 Ljava/io/PrintStream;
2 0x07 Ljava/lang/Object;
3 0x08 Ljava/lang/String;
4 0x09 Ljava/lang/System;
5 0x0a V
6 0x0c [Ljava/lang/String;

1.2.3 proto_ids

方法原型标识符列表,格式:proto_id_item[],这些是此文件引用的所有原型的标识符。

/*
 * Direct-mapped "proto_id_item".
 */
struct DexProtoId {
    u4  shortyIdx;          /* index into stringIds for shorty descriptor */
    u4  returnTypeIdx;      /* index into typeIds list for return type */
    u4  parametersOff;      /* file offset to type_list for parameter types */
};
  • shortyIdx 为对应 shorty 描述字符串的 stringId 的 index
  • returnTypeIdx 为对应 return type 的 typeId 的 index
  • parametersOff 是 offset , 指向 method 原型的参数列表 type_list ; 若 method 没有参数 ,值为0 。参数列表的格式是 type_list ,结构从逻辑上如下描述 。size 表示参数的个数 ;type_idx 是对应参数的类型 ,它的值是一个 typeId 的 index 号 ,跟 returnTypeIdx 含义类似。
struct type_list 
{ 
    uint size; 
    ushort type_idx[size]; 
} 

由上面可知每个 proto_id_item 占 12 bytes ,其共有 3 个,起始偏移为 0xcc
000000c0 09 00 00 00 0a 00 00 00 0c 00 00 00 0a 00 00 00 |…………….|
000000d0 05 00 00 00 00 00 00 00 0b 00 00 00 05 00 00 00 |…………….|
000000e0 78 01 00 00 0b 00 00 00 05 00 00 00 80 01 00 00 |x……………|
查找 0x0178 对应的参数 list:
00000170 6e 20 02 00 10 00 0e 00 01 00 00 00 03 00 00 00 |n …………..|
注意:因为 type_list 是 4 字节对齐的,所以这里这用了 8 个字节
由此可知其共有一个参数,为 Ljava/lang/String;
查找 0x0180 对应的参数 list:
00000180 01 00 00 00 06 00 06 3c 69 6e 69 74 3e 00 0e 48 |……...H|
由此可知其共有一个参数,为 [Ljava/lang/String;

Index shortyIdx returnTypeIdx parametersOff
0 0x0a 0x05 0x00 V V
1 0x0b 0x05 0x0178 VL V Ljava/lang/String;
2 0x0b 0x05 0x0180 VL V [Ljava/lang/String;

由此可知,其分别描述了 < init >、System.out.println() 和 public static void main()

1.2.4 field_ids

字段标识符列表,格式:field_id_item[],这些是此文件引用的所有字段(相当于成员变量?)的标识符(无论文件中是否已定义)。

/*
 * Direct-mapped "field_id_item".
 */
struct DexFieldId {
    u2  classIdx;           /* index into typeIds list for defining class */
    u2  typeIdx;            /* index into typeIds for field type */
    u4  nameIdx;            /* index into stringIds for field name */
};
  • classIdx: defining class 对应的 typeIds 的 index
  • typeIdx: field type 对应的 typeIds 的 index
  • nameIdx: field name 对应的 stringIds 的 index

由此可知,每个 field_id_item 占用 8 bytes,其共有 2 个,起始偏移为 0xf0
000000f0 00 00 03 00 04 00 00 00 04 00 01 00 0e 00 00 00 |…………….|

Index classIdx typeIdx nameIdx class_string type_string name_string
0 0x00 0x00 0x0004 LHello; Ljava/lang/String; Hello_DEX
1 0x04 0x01 0x000e Ljava/lang/System; Ljava/io/PrintStream; out

1.2.5 method_ids

方法标识符列表,格式:method_id_item[],这些是此文件引用的所有方法的标识符(无论文件中是否已定义)。

/*
 * Direct-mapped "method_id_item".
 */
struct DexMethodId {
    u2  classIdx;           /* index into typeIds list for defining class */
    u2  protoIdx;           /* index into protoIds for method prototype */
    u4  nameIdx;            /* index into stringIds for method name */
};

由此可知,其结构基本同上,每个占用 8 bytes,共有 4 个,起始偏移为 0x0100
00000100 00 00 00 00 00 00 00 00 00 00 02 00 0d 00 00 00 |…………….|
00000110 01 00 01 00 0f 00 00 00 02 00 00 00 00 00 00 00 |…………….|

Index classIdx protoIdx nameIdx class_string proto_string name_string
0 0x00 0x00 0x0000 LHello; V()V < init >
1 0x00 0x02 0x000d LHello; VL([Ljava/lang/String;)V main
2 0x01 0x01 0x000f Ljava/io/PrintStream; VL(Ljava/lang/String;)V println
3 0x02 0x00 0x0000 Ljava/lang/Object; V()V < init >

1.2.6 class_defs

类定义列表,格式:class_def_item[],这些类必须进行排序,以便所指定类的超类和已实现的接口比引用类更早出现在该列表中。

/*
 * Direct-mapped "class_def_item".
 */
struct DexClassDef {
    u4  classIdx;           /* index into typeIds for this class */
    u4  accessFlags;
    u4  superclassIdx;      /* index into typeIds for superclass */
    u4  interfacesOff;      /* file offset to DexTypeList */
    u4  sourceFileIdx;      /* index into stringIds for source file name */
    u4  annotationsOff;     /* file offset to annotations_directory_item */
    u4  classDataOff;       /* file offset to class_data_item */
    u4  staticValuesOff;    /* file offset to DexEncodedArray */
};

由上可知每个 class_def_item 占用 32 bytes,其共有 1 个,起始偏移是 0x0120
00000120 00 00 00 00 01 00 00 00 02 00 00 00 00 00 00 00 |…………….|
00000130 03 00 00 00 00 00 00 00 61 02 00 00 5e 02 00 00 |……..a…^…|

名称 value 说明
classIdx 0x00 是 class type 在 typeIds 区中的 index,即 LHello;
accessFlags 0x00000001 是类的访问标记(public、final 等)。
superclassIdx 0x02 是 superclass type 在 typeIds 区中的 index,即 LHello; 的父类为 Ljavag/Object;
interfacesOff 0x00 从文件开头到接口列表的偏移量;如果没有接口,则该值为 0。该偏移量应该位于 data 区段,且其中的数据应采用 “type_list” 指定的格式。
sourceFileIdx 0x03 是 source file name 在 stringIds 区中的 index,即 Hello.java
annotationsOff 0x00 从文件开头到此类的注释结构的偏移量;如果此类没有注释,则该值为 0。该偏移量(如果为非零值)应该位于 data 区段,且其中的数据应采用 “annotations_directory_item” 指定的格式
classDataOff 0x0261 从文件开头到此项的关联类数据的偏移量;如果此类没有类数据,则该值为 0。 该偏移量(如果为非零值)应该位于 data 区段,且其中的数据应采用 “class_data_item” 指定的格式
staticValuesOff 0x025e 从文件开头到 static 字段的初始值列表的偏移量;如果没有该列表(并且所有 static 字段都将使用 0 或 null 进行初始化),则该值为 0。该偏移量应该位于 data 区段,且其中的数据应采用 “encoded_array_item” 指定的格式。

1.3 data 区

1.3.1 class_data_item

00000260 01 01 00 02 00 00 1a 00 81 80 04 c0 02 01 09 d8 |…………….|
00000270 02 00 00 00 0e 00 00 00 00 00 00 00 01 00 00 00 |…………….|

class_data_item 被 1.2.6 中的 classDataOff 指引,其结构如下所示:

名称 格式 说明 对应值 含义/value
static_fields_size uleb128 此项中定义的静态字段的数量 0x01 1
instance_fields_size uleb128 此项中定义的实例字段的数量 0x00 0
direct_methods_size uleb128 此项中定义的实例字段的数量 0x02 2
virtual_methods_size uleb128 此项中定义的实例字段的数量 0x00 0
static_fields encoded_field[static_fields_size] 定义的静态字段;以一系列编码元素的形式表示。这些字段必须按 field_idx 以升序进行排序。 0x00 0x1a private static final Hello_DEX (得出方式见后面)
instance_fields encoded_field[instance_fields_size] 定义的实例字段;以一系列编码元素的形式表示。这些字段必须按 field_idx 以升序进行排序。
direct_methods encoded_method[direct_methods_size] 定义的直接(static、private 或构造函数的任何一个)方法;以一系列编码元素的形式表示。 1.
0x00 0x818004 0xc002
2.
0x01 0x09 0xd802
1.
LHello; 的 public 的构造函数方法 < init >,并且 code_off 为 0x140
2.
LHello; 的 public static main() 方法,并且 code_off 为 0x158
virtual_methods encoded_method[virtual_methods_size] 定义的虚拟(非 static、private 或构造函数)方法;以一系列编码元素的形式表示。


看一下 encoded_field 和 encoded_method 的格式:

  • encoded_field 格式
名称 格式 说明
field_idx_diff uleb128 此字段标识(包括名称和描述符)的 field_ids 列表中的索引;它会表示为与列表中前一个元素的索引之间的差值。列表中第一个元素的索引则直接表示出来。
access_flags uleb128 字段的访问标记(public、final 等)。


所以可知:
static_fields 对应的值为 0x00 0x1a,即其对应 private static final Hello_DEX

  • encoded_method 格式
名称 格式 说明
method_idx_diff uleb128 此方法标识(包括名称和描述符)的 method_ids 列表中的索引;它会表示为与列表中前一个元素的索引之间的差值。列表中第一个元素的索引则直接表示出来。
access_flags uleb128 方法的访问标记(public、final 等)。
code_off uleb128 从文件开头到此方法代码结构的偏移量;如果此方法是 abstract 或 native,则该值为 0。该偏移量应该是到 data 区段中某个位置的偏移量。数据格式由下文的“code_item”指定。


所以可知:

  • direct_methods 中第一个元素对应的值为 0x00 0x818004 0xc002,即其对应 LHello; 的 public 的构造函数方法 < init >,并且 code_off 为 0x140
  • direct_methods 中第二个元素对应的值为 0x01 0x09 0xd802,即其对应 LHello; 的 public static main() 方法,并且 code_off 为 0x158

1.3.2 code_item

引用自上面 encoded_method,以 4 个字节对齐。

名称 格式 说明
registers_size ushort 此代码使用的寄存器数量
ins_size ushort 此代码所用方法的传入参数的字数
outs_size ushort 此代码进行方法调用所需的传出参数空间的字数
tries_size ushort 此实例的 try_item 数量。如果此值为非零值,则这些项会显示为 tries 数组(正好位于此实例中 insns 的后面)。
debug_info_off uint 从文件开头到此代码的调试信息(行号 + 局部变量信息)序列的偏移量;如果没有任何信息,则该值为 0。该偏移量(如果为非零值)应该是到 data 区段中某个位置的偏移量。数据格式由下文的“debug_info_item”指定。
insns_size uint 指令列表的大小(以 16 位代码单元为单位)
insns ushort[insns_size] 字节码的实际数组。
padding ushort(可选)= 0 使 tries 实现四字节对齐的两字节填充。只有 tries_size 为非零值且 insns_size 是奇数时,此元素才会存在。
tries try_item[tries_size](可选) 用于表示在代码中捕获异常的位置以及如何对异常进行处理的数组。该数组的元素在范围内不得重叠,且数值地址按照从低到高的顺序排列。只有 tries_size 为非零值时,此元素才会存在。
handlers encoded_catch_handler_list(可选) 用于表示“捕获类型列表和关联处理程序地址”的列表的字节。每个 try_item 都具有到此结构的分组偏移量。只有 tries_size 为非零值时,此元素才会存在。


下面看一下相关的数据:
00000140 01 00 01 00 01 00 00 00 52 02 00 00 04 00 00 00 |……..R…….|
00000150 70 10 03 00 00 00 0e 00 03 00 01 00 02 00 00 00 |p……………|
00000160 57 02 00 00 08 00 00 00 62 00 01 00 1a 01 02 00 |W…….b…….|
00000170 6e 20 02 00 10 00 0e 00 01 00 00 00 03 00 00 00 |n …………..|

  • < init > 方法对应的 code_off 为 0x140,所以分析其数据应为:
名称 数据 含义
registers_size 0x01 此代码使用的寄存器数量为 1
ins_size 0x01 此代码所用方法的传入参数的字数为 1
outs_size 0x01 此代码进行方法调用所需的传出参数空间的字数为 1
tries_size 0x00 此实例的 try_item 数量为 0
debug_info_off 0x0252 从文件开头到此代码的调试信息(行号 + 局部变量信息)序列的偏移量为 0x0252
insns_size 0x04 指令列表的大小(以 16 位代码单元为单位)
insns 0x1070 0x0003 0x0000 0x000e invoke-direct {v0}, Ljava/lang/Object;.< init >:()V // method@0003
return-void
padding
tries
handlers


  • public static main() 方法对应的 code_off 为 0x158,所以分析其数据应为:
名称 数据 含义
registers_size 0x03 此代码使用的寄存器数量为 3
ins_size 0x01 此代码所用方法的传入参数的字数为 1
outs_size 0x02 此代码进行方法调用所需的传出参数空间的字数为 2
tries_size 0x00 此实例的 try_item 数量为 0
debug_info_off 0x0257 从文件开头到此代码的调试信息(行号 + 局部变量信息)序列的偏移量为 0x0257
insns_size 0x08 指令列表的大小(以 16 位代码单元为单位)
insns 0x0062 0x0001 0x011a 0x0002
0x206e 0x0002 0x0010 0x000e
sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0001
const-string v1, “Hello! My Dex!\n” // string@0002
invoke-virtual {v0, v1},Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0002
return-void
padding
tries
handlers


如何读取翻译 dalvik 字节码,请参考我的另一篇博文 Dalvik 字节码的读取
我们通过命令:

  • dexdump -d Hello.dex

来解析一下 Hello.dex 文件,如下图所示,可以发现我们分析的与解析结果一般无二

这里写图片描述

点击查看大图

1.3.3 map_list

引用自 header_item,即 header_item 中的 map_off 为 0x0274,其以 4 个字节对齐。

这是文件全部内容(依序显示)的列表。该列表包含关于 header_item 的一些冗余信息,但这些冗余信息存在的目的是提供一种用于遍历整个文件的简单方式。指定类型在映射中最多只能出现一次,但除了格式其余部分所隐含的限制条件(例如,header 区段必须先显示,随后是 string_ids 区段等等)之外,对于类型可以什么顺序显示,则没有任何限制。此外,映射条目必须按初始偏移量进行排序,不得重叠。(每个 map_item 占用 12 字节)

名称 格式 说明
size unit 列表的大小(以条目数表示)
list map_item[size] 列表的元素


由下面可知,size 为 0x0e 即 14 个,所以 map_list 总共占用空间大小为 4 + 14 × 12 = 172 字节,即 0xac,0x274 + 0xac = 0x320。所以看出从 map_off 处一直到文件末尾的数据都属于 map_list

00000270 02 00 00 00 0e 00 00 00 00 00 00 00 01 00 00 00 |…………….|
00000280 00 00 00 00 01 00 00 00 10 00 00 00 70 00 00 00 |…………p…|
00000290 02 00 00 00 07 00 00 00 b0 00 00 00 03 00 00 00 |…………….|
000002a0 03 00 00 00 cc 00 00 00 04 00 00 00 02 00 00 00 |…………….|
000002b0 f0 00 00 00 05 00 00 00 04 00 00 00 00 01 00 00 |…………….|
000002c0 06 00 00 00 01 00 00 00 20 01 00 00 01 20 00 00 |…….. …. ..|
000002d0 02 00 00 00 40 01 00 00 01 10 00 00 02 00 00 00 |….@………..|
000002e0 78 01 00 00 02 20 00 00 10 00 00 00 86 01 00 00 |x…. ……….|
000002f0 03 20 00 00 02 00 00 00 52 02 00 00 05 20 00 00 |. ……R…. ..|
00000300 01 00 00 00 5e 02 00 00 00 20 00 00 01 00 00 00 |….^…. ……|
00000310 61 02 00 00 00 10 00 00 01 00 00 00 74 02 00 00 |a………..t…|
00000320

总结

Dex 文件的结构图如下所示:

这里写图片描述

猜你喜欢

转载自blog.csdn.net/u013989732/article/details/78286476