之前的笔记搬运
Dex文件格式
格式总览
Dex、Class文件格式区别:
一个Class文件对应一个Java源码文件,一个Dex文件对应多个Java源码文件;
字节序不一样-- Class文件采用 Big Endian(Java)、Dex文件采用 Little Endian(Android);
Dex新增 LEB128数据类型(Little Endian Based 128),唯一功能是表示32比特位长度的数据(可减少空间);
Dex的使用信息描述规则和Class的使用规则大体类似,只在某些具体细节上略有不同
数据类型描述(Type Descriptor):
用字符串表示不同的数据类型,格式同Class文件
简短描述(Shorty Descriptor):
Dex中用来描述函数的参数和返回值信息,类似Class文件的 MethodDescriptor
ShortyDescriptor的描述规则:(()表示一个Group,* 表示Group可有 0 到多个)
ShortyDescriptor -> ShortyReturnType (ShortyFieldType)*
ShortyReturnType的描述规则:
ShortyReturnType -> 'V' | ShortyFieldType
ShortyFieldType的描述规则:(引用类型统一用 L 表示)
ShortyFieldType -> Z | B | S | C | I | J | F | D | L
Dex格式概貌
如下图:
header:Dex文件头,类型为 header_item
string_ids:数组,元素类型为 string_id_item,存储字符串相关信息
type_ids:数组,元素类型为 type_id_item,存储类型相关信息(TypeDescriptor)
proto_ids:数组,元素类型为 proto_id_item,描述成员函数的参数、返回值类型(包含ShortyDescriptor信息)
field_ids:数组,元素类型为 field_id_item,存储成员变量信息,变量名、类型等
method_ids:数组,元素类型为 method_id_item,存储成员函数信息,函数名、参数、返回值类型等
class_defs:数组,元素类型为 class_def_item,存储类信息
data:Dex文件重要的数据内容都在data 区域里,一些数据结构等
link_data:预留区域,无特别作用
文件头 header对解析Dex文件至关重要
data 区域存储了绝大部分内容,data 的解析依赖于 header和相关数据项
header_item
伪代码:
1 | struct header_item { |
xxx_id_item
1 | id 区存储着字符串,type,prototype,field, method 资源的真正数据在文件中的偏移量,我们可以根据 id 区的偏移量去找到该 id 对应的真实数据。 |
string_id_item
名称 | 格式 | 说明 |
---|---|---|
string_data_off | uint | 从文件开头到此项的字符串数据的偏移量。该偏移量应该是到 data 区段中某个位置的偏移量,而数据应采用下文中“string_data_item ”指定的格式。没有偏移量对齐要求。 |
string_data_item
名称 | 格式 | 说明 |
---|---|---|
utf16_size | uleb128 | 此字符串的大小;以 UTF-16 代码单元(在许多系统中为“字符串长度”)为单位。也就是说,这是该字符串的解码长度(编码长度隐含在 0 字节的位置)。 |
data | ubyte[] | 一系列 MUTF-8 代码单元(又称八位字节),后跟一个值为 0 的字节。请参阅上文中的“MUTF-8(修改后的 UTF-8)编码”,了解有关该数据格式的详情和讨论。 注意:将 Unicode 常规地编码为 UTF-16 时,字符串可以包含(编码形式的)UTF-16 代理代码单元(即 U+d800 …U+dfff ),这些代码单元既可以单独出现,也可以乱序显示。在适用的情况下,可以进阶使用字符串来拒绝这类无效编码。 |
type_id_item
名称 | 格式 | 说明 |
---|---|---|
descriptor_idx | uint | 此类描述符字符串的 string_ids 列表中的索引。该字符串必须符合上文定义的 TypeDescriptor 的语法。 |
proto_id_item
名称 | 格式 | 说明 |
---|---|---|
shorty_idx | uint | 此原型的简短式描述符字符串的 string_ids 列表中的索引。该字符串必须符合上文定义的 ShortyDescriptor 的语法,而且必须与该项的返回类型和参数相对应。 |
return_type_idx | uint | 此原型的返回类型的 type_ids 列表中的索引。 |
parameters_off | uint | 从文件开头到此原型的参数类型列表的偏移量;如果此原型没有参数,则该值为 0 。该偏移量(如果为非零值)应该位于 data 区段中,且其中的数据应采用下文中“"type_list" ”指定的格式。此外,不得对列表中的类型 void 进行任何引用。 |
field_id_item
名称 | 格式 | 说明 |
---|---|---|
class_idx | ushort | 此字段的定义符的 type_ids 列表中的索引。此项必须是“类”类型,而不能是“数组”或“基元”类型。 |
type_idx | ushort | 此字段的类型的 type_ids 列表中的索引。 |
name_idx | uint | 此字段的名称的 string_ids 列表中的索引。该字符串必须符合上文定义的 MemberName 的语法。 |
method_id_item
名称 | 格式 | 说明 |
---|---|---|
class_idx | ushort | 此方法的定义符的 type_ids 列表中的索引。此项必须是“类”或“数组”类型,而不能是“基元”类型。 |
proto_idx | ushort | 此方法的原型的 proto_ids 列表中的索引。 |
name_idx | uint | 此方法名称的 string_ids 列表中的索引。该字符串必须符合上文定义的 MemberName 的语法。 |
class_def
类信息的伪代码:
1 | struct class_def { |
类的成员变量、成员函数等信息通过 class_data_off 域指向 class_data_item结构体描述
class_data_item结构体:
direct_methods数组和virtual_methods数组代表该类定义的方法以及它继承或实现的方法
direct_methods包含该类中所有 static、private函数以及构造函数
virtual_methods包含该类中除static、final以及构造函数之外的函数,不包括从父类继承的函数(若类中未重载)
static_fields和instance_fields代表静态成员以及非静态成员
encoded_field结构体:
field_idx_diff 索引值的偏移量,通过它能找到成员变量的变量名、数据类型、所在类的类名
encoded_method结构体:
method_idx_diff 索引值的偏移量,通过它能找到成员函数的函数名、函数签名、所在类的类名
code_off 指向该成员方法对应的dex指令码内容
code_item
数据结构伪代码:
1 | struct code_item { |