dex文件结构

dex 文件结构

什么是 dex 文件?

能够被 dvm 识别,加载并执行的文件格式

如何生成一个 dex 文件

  1. 手写调用 javac 生成 class 文件
javac -target 1.8 -source 1.8 Temp.java
  1. 调用 dx.bat 生成 dex 文件
dx --dex --output Temp.dex Temp.class
  1. 首先 push 要执行的 dex 文件到手机 sdcard
adb push Temp.dex /sdcard
  1. 调用 dalvikvm 命令执行
adb shell
dalvikvm -cp /sdcard/Temp.dex Temp

dalvikvm 命令:
dalvikvm -cp <dex文件路径> <执行的类>

dex 文件的作用

记录整个工程中所有类文件的信息

dex 文件格式详解

  1. 一种 8 位字节的二进制流文件
  2. 各个数据按顺序紧密的排列,无间隙
  3. 整个应用中所有 Java 源文件都放在一个 dex 文件中,注意 multidex

dex 文件可以分为 3 个模块,头文件 (header)、索引区 (xxxx_ids)、数据区 (data)。
ij2cq

一、文件头

dex 文件里的 header。除了描述整个.dex 文件的文件分布信息外,包括每一个索引区的大小跟偏移。

r5iw1
除了 magic、checksum、signature、file_size、endian_tag、map_off 其他元素都是成对出现的。以 _size_off 为后缀的描述:size 都是描述该区里元素的个数;off 描述相对与文件起始位置的偏移量。(data_size 是以 Byte 为单位描述 data 区的大小)。各项说明如下:

{ 0x64 0x65 0x78 0x0a 0x30 0x33 0x35 0x00 } = "dex\n035\0"

中间是一个换行,后面 035 是版本号。
ee5a7

完整的 header:
jwp4z

二、索引区

LEB128 ( little endian base 128 )
基于 1 个 Byte 的一种不定长度的编码方式;若第一个 Byte 的最高位为 1,则表示还需要下一个 Byte 来描述,直至最后一个 Byte 的最高位为 0;每个 Byte 的其余 bit 用来表示数据,如下表所示。实际中 LEB128 最大只能达到 32-bit,可以阅读 dalvik 中的 Leb128.h 源码看出来。
481yo

数据结构为:

ubyte    8-bit unsinged int
uint     32-bit unsigned int, little-endian
uleb128  unsigned LEB128, valriable length
struct string_ids_item
{
    uint string_data_off;
}
struct string_data_item
{
    uleb128 utf16_size;
    ubyte   data;
}

其中 data 保存的就是字符串的值。string_ids 是比较关键的,后续的区段很多都是直接指向 string_ids 的 index。

uint 32-bit unsigned int, little-endian

struct type_ids_item
{
    uint descriptor_idx;  //-->string_ids
}

type_ids_item 里面 descriptor_idx 的值的意思,是 string_ids 里的 index 序号,是用来描述此 type 的字符串。

uint 32-bit unsigned int, little-endian

struct proto_id_item
{
    uint shorty_idx;        //-->string_ids
    uint return_type_idx;    //-->type_ids
    uint parameters_off;
}
  1. shorty_idx: 跟 type_ids 一样,它的值是一个 string_ids 的 index 号 ,最终是一个简短的字符串描述,用来说明该 method 原型。
  2. return_type_idx: 它的值是一个 type_ids 的 index 号 ,表示该 method 原型的返回值类型。
  3. parameters_off: 指向 method 原型的参数列表 type_list,若 method 没有参数,值为 0。参数列表的格式是 type_list,下面会有描述。
ushort 16-bit unsigned int, little-endian 
uint   32-bit unsigned int, little-endian 

struct filed_id_item
{
    ushort class_idx;  //-->type_ids
    ushort type_idx;   //-->type_ids
    uint   name_idx;   //-->string_ids
}
  1. class_idx: 表示 field 所属的 class 类型,class_idx 的值是 type_ids 的一个 index,并且必须指向一个 class 类型。
  2. type_idx: 表示本 field 的类型,它的值也是 type_ids 的一个 index 。
  3. name_idx: 表示本 field 的名称,它的值是 string_ids 的一个 index 。
ushort 16-bit unsigned int, little-endian 
uint   32-bit unsigned int, little-endian 

struct filed_id_item
{
    ushort class_idx;  //-->type_ids
    ushort proto_idx;   //-->proto_ids
    uint   name_idx;   //-->string_ids
}
  1. class_idx: 表示 method 所属的 class 类型,class_idx 的值是 type_ids 的一个 index,并且必须指向一个 class 类型。ushort 类型也是为什么我们说一个 dex 只能有 65535 个方法的原因,多了必须分包。
  2. proto_idx: 表示 method 的类型,它的值也是 type_ids 的一个 index。
  3. name_idx: 表示 method 的名称,它的值是 string_ids 的一个 index。
    索引区:
    hvsrz

三、数据区

1、class_defs

class_defs 区段主要是对 class 的定义,它的结构很复杂,一层套一层。
a2op3

uint   32-bit unsigned int, little-endian

struct class_def_item
{
    uint class_idx;         //-->type_ids
    uint access_flags;
    uint superclass_idx;    //-->type_ids
    uint interface_off;     //-->type_list
    uint source_file_idx;    //-->string_ids
    uint annotations_off;    //-->annotation_directory_item
    uint class_data_off;    //-->class_data_item
    uint static_value_off;    //-->encoded_array_item
}
uint   32-bit unsigned int, little-endian

struct type_list
{
    uint       size;
    type_item  list [size]
}

struct type_item
{
    ushort type_idx   //-->type_ids
}
uleb128 unsigned little-endian base 128 

struct class_data_item
{
    uleb128 static_fields_size;
    uleb128 instance_fields_size;
    uleb128 direct_methods_size;
    uleb128 virtual_methods_size;

    encoded_field  static_fields[static_fields_size];
    encoded_field  instance_fields[instance_fields_size];
    encoded_method direct_methods[direct_methods_size];
    encoded_method virtual_methods[virtual_methods_size];
}

struct encoded_field
{
    uleb128 filed_idx_diff; 
    uleb128 access_flags;  
}

struct encoded_method
{
    uleb128 method_idx_diff; 
    uleb128 access_flags; 
    uleb128 code_off;
}

2、map_list

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

ushort 16-bit unsigned int, little-endian
uint   32-bit unsigned int, little-endian

struct map_list 
{
    uint     size;
    map_item list [size]; 
}
struct map_item 
{
    ushort type; 
    ushort unuse; 
    uint   size; 
    uint   offset;
}

map_list 里先用一个 uint 描述后面有 size 个 map_item,后续就是对应的 size 个 map_item 描述。
map_item 结构有 4 个元素: type 表示该 map_item 的类型,Dalvik Executable Format 里 Type Code 的定义; size 表示再细分此 item,该类型的个数;offset 是第一个元素的针对文件初始位置的偏移量; unuse 是用对齐字节的,无实际用处。
mwhos

class 和 dex 区别

vjf2k

JVM 的数据格式规范和 Class 文件

参考:http://www.importnew.com/16388.html
《Java 虚拟机规范》阅读(三):Class 文件格式
http://www.cnblogs.com/zhuYears/archive/2012/02/07/2340347.html

Reference