Dex文件格式简解
一个简单的Dex文件分析文章,学习自http://www.blogfshare.com/dex-format.html,http://www.wjdiankong.cn/android%E9%80%86%E5%90%91%E4%B9%8B%E6%97%85-%E8%A7%A3%E6%9E%90%E7%BC%96%E8%AF%91%E4%B9%8B%E5%90%8E%E7%9A%84dex%E6%96%87%E4%BB%B6%E6%A0%BC%E5%BC%8F/。
先上图,文尾给出github地址。
从最后一张图可以看出,整个dex文件包括:
- 文件头,主要包含校验和其他结构的偏移地址和长度信息。
- 索引区,包含string,type,proto,field,method等信息。
- 数据区,包含class数据,data数据和链接数据。
Header
从中可以看到Header部分都包括哪些内容,简单介绍。
(1). magic value
这 8 个 字节一般是常量 ,为了使 .dex 文件能够被识别出来 ,它必须出现在 .dex 文件的最开头的
位置 。数组的值可以转换为一个字符串如下 :
{ 0x64 0x65 0x78 0x0a 0x30 0x33 0x35 0x00 } = “dex\n035\0”
中间是一个 ‘\n’ 符号 ,后面 035 是 Dex 文件格式的版本 。
(2). checksum 和 signature
文件校验码 ,使用alder32 算法校验文件除去 maigc ,checksum 外余下的所有文件区域 ,用于检查文件错误 。
signature , 使用 SHA-1 算法 hash 除去 magic ,checksum 和 signature 外余下的所有文件区域 ,
用于唯一识别本文件 。
(3). file_size
Dex 文件的大小 。
(4). header_size
header 区域的大小 ,单位 Byte ,一般固定为 0x70 常量 。
(5). endian_tag
大小端标签 ,标准 .dex 文件格式为 小端 ,此项一般固定为 0x1234 5678 常量 。
(6). link_size和link_off
这个两个字段是表示链接数据的大小和偏移值。
(7). map_off
map_item 的偏移地址 ,该 item 属于 data 区里的内容 ,值要大于等于 data_off 的大小 。
定义位置:data区。
引用位置:header 区 。
map_list 里先用一个 uint 描述后面有 size 个 map_item , 后续就是对应的 size 个 map_item 描述 。map_item 结构有 4 个元素 : type 表示该 map_item 的类型 ,本节能用到的描述如下 ,详细Dalvik Executable Format 里 Type Code 的定义 ;size 表示再细分此 item,该类型的个数;offset 是第一个元素的针对文件初始位置的偏移量 ; unuse 是用对齐字节的 ,无实际用处 。(这个部分略)
(8). string_ids_size和string_ids_off
这两个字段表示dex中用到的所有的字符串内容的大小和偏移值,我们需要解析完这部分,然后用一个字符串池存起来,后面有其他的数据结构会用索引值来访问字符串,这个池子也是非常重要的。
(9). type_ids_size和type_ids_off
这两个字段表示dex中的类型数据结构的大小和偏移值,比如类类型,基本类型等信息。
(10). proto_ids_size和type_ids_off
这两个字段表示dex中的元数据信息数据结构的大小和偏移值,描述方法的元数据信息,比如方法的返回类型,参数类型等信息,后面会详细介绍proto_ids的数据结构
(11). field_ids_size和field_ids_off
这两个字段表示dex中的字段信息数据结构的大小和偏移值,后面会详细介绍field_ids的数据结构
(12). method_ids_size和method_ids_off
这两个字段表示dex中的方法信息数据结构的大小和偏移值,后面会详细介绍method_ids的数据结构
(13). class_defs_size和class_defs_off
这两个字段表示dex中的类信息数据结构的大小和偏移值,这个数据结构是整个dex中最复杂的数据结构,他内部层次很深,包含了很多其他的数据结构,所以解析起来也很麻烦,所以后面会着重讲解这个数据结构
(14). data_size和data_off
这两个字段表示dex中数据区域的结构信息的大小和偏移值,这个结构中存放的是数据区域,比如我们定义的常量值等信息。
到这里我们就看完了dex的头部信息,头部包含的信息还是很多的,主要就两个个部分:
魔数+签名+文件大小等信息
后面的各个数据结构的大小和偏移值,都是成对出现的。
索引区
(1). string_ids数据结构
string_ids 区索引了 .dex 文件所有的字符串 。 本区里的元素格式为 string_ids_item。可以使用结
构体如下描述 。
|
|
public class TypeIdsItem {
/**
* struct type_ids_item
{
uint descriptor_idx;
}
*/
public int descriptor_idx;
public static int getSize(){
return 4;
}
@Override
public String toString(){
return Utils.bytesToHexString(Utils.int2Byte(descriptor_idx));
}
}
public class ProtoIdsItem {
/**
* struct proto_id_item
{
uint shorty_idx;
uint return_type_idx;
uint parameters_off;
}
*/
public int shorty_idx;
public int return_type_idx;
public int parameters_off;
//这个不是公共字段,而是为了存储方法原型中的参数类型名和参数个数
public List<String> parametersList = new ArrayList<String>();
public int parameterCount;
public static int getSize(){
return 4 + 4 + 4;
}
@Override
public String toString(){
return "shorty_idx:"+shorty_idx+",return_type_idx:"+return_type_idx+",parameters_off:"+parameters_off;
}
}
public class FieldIdsItem {
/**
* struct filed_id_item
{
ushort class_idx;
ushort type_idx;
uint name_idx;
}
*/
public short class_idx;
public short type_idx;
public int name_idx;
public static int getSize(){
return 2 + 2 + 4;
}
@Override
public String toString(){
return "class_idx:"+class_idx+",type_idx:"+type_idx+",name_idx:"+name_idx;
}
}
public class MethodIdsItem {
/**
* struct filed_id_item
{
ushort class_idx;
ushort proto_idx;
uint name_idx;
}
*/
public short class_idx;
public short proto_idx;
public int name_idx;
public static int getSize(){
return 2 + 2 + 4;
}
@Override
public String toString(){
return "class_idx:"+class_idx+",proto_idx:"+proto_idx+",name_idx:"+name_idx;
}
}
```
class_idx :表示本 method 所属的 class 类型 , class_idx 的值是 type_ids 的一个 index , 并且必须指向一个 class 类型 。
name_idx :表示本 method 的名称 ,它的值是 string_ids 的一个 index 。
proto_idx :描述该 method 的原型 ,指向 proto_ids 的一个 index 。
(6). class_defs数据结构 (这个太难了,略)
结束
后面都太难了,我也是心有余而力不足。就到此为止了。这篇文章主要的就是简单的header加索引区的分析,主要的还是得看上面的参考文章。