Android Dex文件

2018-02-27 11:32:58来源:https://www.jianshu.com/p/7afbadd6099f作者:lbtrace人点击

分享



.dex(Dalvik Executable Format)文件用于保存类定义及其关联的辅助数据



文件格式



header文件头
数据结构
  // Raw header_item.
struct Header {
uint8_t magic_[8];
uint32_t checksum_; // See also location_checksum_
uint8_t signature_[kSha1DigestSize];
uint32_t file_size_; // size of entire file
uint32_t header_size_; // offset to start of next section
uint32_t endian_tag_;
uint32_t link_size_; // unused
uint32_t link_off_; // unused
uint32_t map_off_; // unused
uint32_t string_ids_size_; // number of StringIds
uint32_t string_ids_off_; // file offset of StringIds array
uint32_t type_ids_size_; // number of TypeIds, we don't support more than 65535
uint32_t type_ids_off_; // file offset of TypeIds array
uint32_t proto_ids_size_; // number of ProtoIds, we don't support more than 65535
uint32_t proto_ids_off_; // file offset of ProtoIds array
uint32_t field_ids_size_; // number of FieldIds
uint32_t field_ids_off_; // file offset of FieldIds array
uint32_t method_ids_size_; // number of MethodIds
uint32_t method_ids_off_; // file offset of MethodIds array
uint32_t class_defs_size_; // number of ClassDefs
uint32_t class_defs_off_; // file offset of ClassDef array
uint32_t data_size_; // unused
uint32_t data_off_; // unused
......
}

file_size_: 整个文件的大小,单位字节
header_size_: 文件头的大小,单位字节
string_ids_size_: 字符串列表中字符串的数量
string_ids_off_: 字符串列表在文件中的偏移
type_ids_size_: 类型列表中元素数量
type_ids_off_: 类型列表在文件中的偏移
proto_ids_size_: 方法原型列表元素数量
proto_ids_off_: 方法原型列表在文件中的偏移
field_ids_size_: 字段列表的元素数量
field_ids_off_: 字段列表在文件中的偏移
method_ids_size_: 方法列表元素的数量
method_ids_off_: 方法列表在文件中的偏移
class_defs_size_: 类定义列表中元素数量
class_defs_off_: 类定义列表在文件中的偏移
data_size_: data段的大小
data_off_: data段在文件中的偏移
例子
DEX file header:
magic : 'dex/n035/0'
checksum : 1181417f
signature : a022...4f7a
file_size : 2727984
header_size : 112
link_size : 0
link_off : 0 (0x000000)
string_ids_size : 19080
string_ids_off : 112 (0x000070)
type_ids_size : 2332
type_ids_off : 76432 (0x012a90)
proto_ids_size : 3091
proto_ids_off : 85760 (0x014f00)
field_ids_size : 8477
field_ids_off : 122852 (0x01dfe4)
method_ids_size : 17750
method_ids_off : 190668 (0x02e8cc)
class_defs_size : 1681
class_defs_off : 332668 (0x05137c)
data_size : 2325048
data_off : 402936 (0x0625f8) ===> 类定义列表与data段有间隙

string_ids字符串列表


文件使用的所有字符串的标识符,用于内部命名(例如类型描述符)或用作代码引用的常量对象。此列表必须使用 UTF-16 代码点值按字符串内容进行排序,且不得包含任何重复条目。



元素数据结构
  // Raw string_id_item.
struct StringId {
uint32_t string_data_off_; // offset in bytes from the base address
private:
DISALLOW_COPY_AND_ASSIGN(StringId);
};


string_data_off_: 字符串数据在文件中的偏移量,该偏移量应该在data段中,采用string_data_item指定的格式




















名称格式说明
utf16_sizeuleb128字符串的长度
dataubyte[]一系列MUTF-8代码单元


对于leb128以及MUTF-8请参考https://source.android.com/devices/tech/dalvik/dex-format?hl=zh-cn




例子
......
00000070 08 5d 18 00 0a 5d 18 00 0d 5d 18 00 22 5d 18 00 |.]...]...].."]..|
00000080 38 5d 18 00 3b 5d 18 00 3f 5d 18 00 44 5d 18 00 |8]..;]..?]..D]..|
......
string_ids => 0x70
第一个StringId(string_id_item)的string_data_off_为0x00185d08
第二个StringId(string_id_item)的string_data_off_为0x00185d0a
第三个StringId(string_id_item)的string_data_off_为0x00185d0d
第四个StringId(string_id_item)的string_data_off_为0x00185d22
......
data段的字符串信息:
00185d00 00 00 00 00 00 00 00 00 00 00 01 0a 00 13 0a 20 |............... |
00185d10 20 43 6c 69 65 6e 74 20 76 65 72 73 69 6f 6e 3a | Client version:|
00185d20 20 00 14 0a 20 20 53 65 72 76 69 63 65 20 76 65 | ... Service ve|
第一个字符串二进制: 00 00
第二个字符串二进制: 01 0a 00
第三个字符串二进制: 13 0a 20 20 43 6c 69 65 6e 74 20 76 65 72 73 69 6f 6e 3a 20 00

type_ids类型列表


类型标识符列表,包含文件引用的所有类型(类、数组或原始类型)的标识符,此列表必须按 string_id 索引进行排序,且不得包含任何重复条目。



元素数据结构
  // Raw type_id_item.
struct TypeId {
uint32_t descriptor_idx_; // index into string_ids
private:
DISALLOW_COPY_AND_ASSIGN(TypeId);
};

descriptor_idx_: 类描述符在string_ids列表中的索引
例子
type_ids位置:
00012a90 a5 04 00 00 9d 05 00 00 0b 07 00 00 6c 08 00 00 |............l...|
00012aa0 72 09 00 00 21 0a 00 00 06 0b 00 00 07 0b 00 00 |r...!...........|
第一个类描述符在string_ids列表中的索引: 0x000004a5 (位置: 0x70 + 0x4a5 * 4 = 0x1304)
对应的string_ids列表内容:
00001300 65 63 19 00 7a 63 19 00 7d 63 19 00 81 63 19 00 |ec..zc..}c...c..|
(0x0019637a)对应的data段的字符串信息:
00196370 65 44 65 6c 65 67 61 74 65 00 01 42 00 02 42 3a |eDelegate..B..B:|
对应的类型为B(基本类型byte)

proto_ids方法原型列表


方法原型标识符列表,包含文件引用的所有原型的标识符



元素数据结构
  // Raw proto_id_item.
struct ProtoId {
uint32_t shorty_idx_; // index into string_ids array for shorty descriptor
uint16_t return_type_idx_; // index into type_ids array for return type
uint16_t pad_; // padding = 0
uint32_t parameters_off_; // file offset to type_list for parameter types
private:
DISALLOW_COPY_AND_ASSIGN(ProtoId);
};

shorty_idx_: 方法原型的简短描述符字符串在string_ids列表中的索引


return_type_idx_: 方法原型的返回类型在type_ids列表中的索引


parameters_off_: 方法原型的参数类型列表在文件中的偏移量,如果没有参数,该值为0;该偏移量应该位于data段中,数据采用type_list指定的格式




















名称格式说明
sizeuint列表的大小
listtype_item[size]列表元素

type_item格式













名称格式说明
type_idxushort在type_ids列表中的索引


field_ids字段列表
元素数据结构
  // Raw field_id_item.
struct FieldId {
uint16_t class_idx_; // index into type_ids_ array for defining class
uint16_t type_idx_; // index into type_ids_ array for field type
uint32_t name_idx_; // index into string_ids_ array for field name
private:
DISALLOW_COPY_AND_ASSIGN(FieldId);
};

class_idx_: 定义该字段的类在type_ids中的索引
type_idx_: 字段的类型在type_ids中的索引
name_idx_: 字段的名称在string_ids中的索引
method_ids方法列表
元素数据结构
  // Raw method_id_item.
struct MethodId {
uint16_t class_idx_; // index into type_ids_ array for defining class
uint16_t proto_idx_; // index into proto_ids_ array for method prototype
uint32_t name_idx_; // index into string_ids_ array for method name
private:
DISALLOW_COPY_AND_ASSIGN(MethodId);
};

class_idx_: 定义该方法的类在type_ids中的索引
proto_idx_: 方法的原型在proto_ids中的索引,uint16_t表示(0~65535),最多有65536个方法原型
name_idx_: 方法的名字在string_ids中的索引
class_defs类定义列表
元素数据结构
  // Raw class_def_item.
struct ClassDef {
uint16_t class_idx_; // index into type_ids_ array for this class
uint16_t pad1_; // padding = 0
uint32_t access_flags_;
uint16_t superclass_idx_; // index into type_ids_ array for superclass
uint16_t pad2_; // padding = 0
uint32_t interfaces_off_; // file offset to TypeList
uint32_t source_file_idx_; // index into string_ids_ for source file name
uint32_t annotations_off_; // file offset to annotations_directory_item
uint32_t class_data_off_; // file offset to class_data_item
uint32_t static_values_off_; // file offset to EncodedArray
......
}

class_idx_: 类在type_ids中的索引


access_flags_: 类的访问标志


superclass_idx_: 超类在type_ids中的索引


interfaces_off_: 接口列表在文件中的偏移量,如果没有接口,该值为0,否则该偏移量应该位于data段,采用type_list制定的格式


source_file_idx_: 文件(包含该类)名称在string_ids列表中的索引;或者该值为NO_INDEX,表示缺少信息


annotations_off_: 类的注解在文件中的偏移量,如果没有注解,该值为0,否则该偏移量应该位于data段,采用annotations_directory_item指定的格式


















































名称格式说明
static_fields_sizeuleb128静态字段的数量
instance_fields_sizeuleb128实例字段的数量
direct_methods_sizeuleb128直接方法的数量
virtual_methods_sizeuleb128虚方法的数量
static_fieldsencoded_field[static_fields_size]静态字段,按在field_idx中的索引升序排序
instance_fieldsencoded_field[instance_fields_size]实例字段,按在field_idx中的索引升序排序
direct_methodsencoded_method[direct_methods_size]直接方法,按在method_ids中的索引升序排序
virtual_methodsencoded_method[virtual_methods_size]虚方法,按在method_ids中的索引升序排序

class_data_off_: 类数据在文件中的偏移量,如果没有没数据(标记接口),该值为0;否则该值应该位于data段,采用class_data_item指定格式


















































名称格式说明
static_fields_sizeuleb128静态字段的数量
instance_fields_sizeuleb128实例字段的数量
direct_methods_sizeuleb128直接方法的数量
virtual_methods_sizeuleb128虚方法的数量
static_fieldsencoded_field[static_fields_size]静态字段,按在field_idx中的索引升序排序
instance_fieldsencoded_field[instance_fields_size]实例字段,按在field_idx中的索引升序排序
direct_methodsencoded_method[direct_methods_size]直接方法,按在method_ids中的索引升序排序
virtual_methodsencoded_method[virtual_methods_size]虚方法,按在method_ids中的索引升序排序

encoded_field格式


















名称格式说明
field_idx_diffuleb128字段在field_ids列表中的索引;它的值为与列表中前一个元素的索引之差,第一个元素的值则直接表示
access_flagsuleb128字段的访问标志

encoded_method 格式























名称格式说明
method_idx_diffuleb128方法在method_ids列表中的索引;它的值为与列表中前一个元素的索引之差,第一个元素的值则直接表示
access_flagsuleb128方法的访问标志
code_offuleb128方法的代码数据在文件中的偏移量,如果方法是abstract或者native,该值为0;否则该偏移量应该在data段中,采用code_item指定格式

code_item
// Raw code_item.
struct CodeItem {
uint16_t registers_size_; // the number of registers used by this code
// (locals + parameters)
uint16_t ins_size_; // the number of words of incoming arguments to the method
// that this code is for
uint16_t outs_size_; // the number of words of outgoing argument space required
// by this code for method invocation
uint16_t tries_size_; // the number of try_items for this instance. If non-zero,
// then these appear as the tries array just after the
// insns in this instance.
uint32_t debug_info_off_; // file offset to debug info stream
uint32_t insns_size_in_code_units_; // size of the insns array, in 2 byte code units
uint16_t insns_[1]; // actual array of bytecode.
private:
DISALLOW_COPY_AND_ASSIGN(CodeItem);
};

registers_size_: 代码使用的寄存器数量(包括本地变量以及参数)
ins_size_: 方法入参字数
outs_size_: 方法出参字数
tries_size_: 该实例的try_items数目,如果非0,try_items出现在该实例的insns之后
debug_info_off_: 调试信息在文件中的偏移,如果非0,该偏移量应该在data段,采用debug_info_item指定格式
insns_size_in_code_units_: 指令列表的大小,以2个字节为单位
insns_: 字节码实际数组
try_item格式
// Raw try_item.
struct TryItem {
uint32_t start_addr_; // try代码块的起始地址
uint16_t insn_count_; // try代码块覆盖的16位代码单元的数量
uint16_t handler_off_; // 从关联的encoded_catch_hander_list开头部分到此条目的encoded_catch_handler的偏移量(以字节为单位)
private:
DISALLOW_COPY_AND_ASSIGN(TryItem);
};

encoded_catch_handler_list格式(在try_item列表之后)


















名称格式说明
sizeuleb128列表的大小
listencoded_catch_handler[handlers_size]异常处理程序列表

encoded_catch_handler格式























名称格式说明
sizesleb128捕获异常类型的数量,size为0表示捕获类型为“全部捕获”,而没有明确类型的捕获。size为2表示有两个明确类型的捕获,但没有“全部捕获”类型的捕获。size为-1表示有一个明确类型的捕获和一个“全部捕获”类型的捕获。
handlersencoded_type_addr_pair[abs(size)]明确类型异常处理列表
catch_all_addruleb128“全部捕获”处理程序的字节码地址。只有当 size 为非正数时,此元素才会存在。

encoded_type_addr_pair格式


















名称格式说明
type_idxuleb128要捕获的异常类型在type_ids列表中的索引
addruleb128对应的异常处理程序的字节码的地址

static_values_off_: 静态字段初始值列表在文件中的偏移量,如果没有该列表(或者静态字段使用0或者null进行初始化),该值为0;否则该值应该位于data段,采用encoded_array_item指定的格式


例子
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Example of a call to a native method
final TextView tv = (TextView) findViewById(R.id.sample_text);
tv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
tv.setText(stringFromJNI());

}
});
testClassLoader();
}
private void testClassLoader() {
try {
ClassLoader cl = getClassLoader();
if (cl == null)
Log.i("wlb", "classload null");
while (cl != null) {
Log.i("wlb", cl.toString());
cl = cl.getParent();
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* A native method that is implemented by the 'native-lib' native library,
* which is packaged with this application.
*/
public native String stringFromJNI();
// Used to load the 'native-lib' library on application startup.
static {
System.loadLibrary("native-lib");
}
}

MainActivity为例说明


MainActivity的class_def_item
class_defs_off      : 332668 (0x05137c)
MainActivity在class_ids中的索引:1665
MainActivity的class_def_item偏移:0x05137c + 1665 * 32 = 0x05E39C
==============================================================================
0005e390 50 10 29 00 78 65 08 00 00 00 00 00 36 08 00 00 |P.).xe......6...|
0005e3a0 01 00 00 00 2b 06 00 00 00 00 00 00 9f 15 00 00 |....+...........|
0005e3b0 00 00 00 00 92 65 08 00 00 00 00 00 1e 02 00 00 |.....e..........|
==============================================================================
类在type_ids中的索引:0x0836 (0x1be74d 'Lcom/tracemaker/nativedynamiclinkdemo/MainActivity;')
类的访问标志: 0x00000001
超类在type_ids中的索引: 0x062b
文件(包含该类)名称在string_ids列表中的索引: 0x0000159f (0x001c25fe 'MainActivity.java')
类数据在文件中的偏移量: 0x00086592

MainActivity的class_data




静态字段以及实例字段数量均为0
直接方法数为3 (<clinit>, <init>, testClassLoader)
虚方法数为2 (onCreate, stringFromJNI)

MainActivitytestClassLoader方法

testClassLoader对应的encoded_method十六进制数据06 02 8c 9a 55


方法在method_ids列表中的索引为: 0x439e (0x4397 + 0x1 + 0x6)
方法的访问标志: 0x0002
方法的代码数据在文件中的偏移: uleb128(8c 9a 55) = 0x154D0C

testClassLoader方法的代码数据




代码使用的寄存器数量: 0x0005
入参数量: 0x0001
出参数量: 0x0001
方法的try_items项数: 0x0001
方法的指令数: 0x00000025(37个16位代码单元,74个字节)
方法的字节码
154d0c:                                        |[154d0c] com.tracemaker.nativedynamiclinkdemo.MainActivity.testClassLoader:()V
154d1c: 6e10 9a43 0400 |0000: invoke-virtual {v4}, Lcom/tracemaker/nativedynamiclinkdemo/MainActivity;.getClassLoader:()Ljava/lang/ClassLoader; // method@439a
154d22: 0c00 |0003: move-result-object v0
154d24: 3900 0b00 |0004: if-nez v0, 000f // +000b
154d28: 1b02 244a 0000 |0006: const-string/jumbo v2, "wlb" // string@00004a24
154d2e: 1b03 e522 0000 |0009: const-string/jumbo v3, "classload null" // string@000022e5
154d34: 7120 113e 3200 |000c: invoke-static {v2, v3}, Landroid/util/Log;.i:(Ljava/lang/String;Ljava/lang/String;)I // method@3e11
154d3a: 3800 1500 |000f: if-eqz v0, 0024 // +0015
154d3e: 1b02 244a 0000 |0011: const-string/jumbo v2, "wlb" // string@00004a24
154d44: 6e10 4044 0000 |0014: invoke-virtual {v0}, Ljava/lang/Object;.toString:()Ljava/lang/String; // method@4440
154d4a: 0c03 |0017: move-result-object v3
154d4c: 7120 113e 3200 |0018: invoke-static {v2, v3}, Landroid/util/Log;.i:(Ljava/lang/String;Ljava/lang/String;)I // method@3e11
154d52: 6e10 0444 0000 |001b: invoke-virtual {v0}, Ljava/lang/ClassLoader;.getParent:()Ljava/lang/ClassLoader; // method@4404
154d58: 0c00 |001e: move-result-object v0
154d5a: 28f0 |001f: goto 000f // -0010
154d5c: 0d01 |0020: move-exception v1
154d5e: 6e10 0844 0100 |0021: invoke-virtual {v1}, Ljava/lang/Exception;.printStackTrace:()V // method@4408
154d64: 0e00 |0024: return-void

testClassLoader方法try_item




try代码块的起始地址: 0x0000(也就是0x154d1c)
try代码块覆盖的16位代码单元的数量: 0x001e
关联的encoded_catch_hander_list开头部分到此条目的encoded_catch_handler的偏移量: 0x0001(1字节)
encoded_catch_handler_list列表大小:0x01
捕获异常类型的数量:0x01
异常类型在type_ids列表中的索引: uleb128(e8 10)
异常类型字符串:
001bf410 15 4c 6a 61 76 61 2f 6c 61 6e 67 2f 45 78 63 65 |.Ljava/lang/Exce|
001bf420 70 74 69 6f 6e 3b 00 11 4c 6a 61 76 61 2f 6c 61 |ption;..Ljava/la|

异常处理程序的字节码的地址: 0x20(实际0x154d5c)
附录OAT文件格式



参考
https://source.android.com/devices/tech/dalvik/dex-format?hl=zh-cn
Hiding Behind ART







最新文章

123

最新摄影

闪念基因

微信扫一扫

第七城市微信公众平台