Android Dex文件解析

Dex文件解析 

Android5.0以上开始使用ART虚拟机,在此之前我们一直使用的Dalvik虚拟机,那么为什么Google突然换了Android运行的虚拟机呢?答案只有一个:ART虚拟机更优秀。

准备工作: 010editor  自己编写一个java文件 终端命令编译为classes.dex文件 安装java及android-SDK需要用到里面的dx 将class文件进一步编译为dex文件

1 编写简单的java文件

创建.java文件


创建文件

使用vim HelloWorld.java 编写HelloWorld.java文件

编写java文件

使用javac 将HelloWorld.java 编译成class


HelloWorld.class

在使用Android-SDK 中的dx将class文件编译为DEX

使用android-sdk中的dx  存放位置为 android-sdk/build-tools/dx 

命令为 ./dx --dex -output=HelloWorld.dex HelloWorld.class

     也可以使用 ./dx --dex --no-strict --output HelloWorld.dex HelloWorld.class 


编译dex文件


Dex文件格式


将生成的dex文件用010editor打开

解析Header_item文件的结构

1.Dex文件的标志头 dex.035  占了8个字节 00

DEX文件图

    2 checksum 校验和 用来校验文件是否被损坏 4个字节 

校验和

3 文件签名signature 占了20个字节 而且是数组 文件签名用来识别是否是盗版文件

Signature

4 File_Size 文件大小 代表当前的dex文件的大小是0x02EC 

File_Size

5 Header_size 头大小 默认0x70 代表header_item的大小

Header_Size

6 endian_tag 端序 如果是0x78563412 那么就是小端序的方式 左低右高 科普一波 小端序是大的在低地址 小的在高地址,而大端序则是正常 大的在大小的在小

0x12345678 小端序

7 Link_size 链接 大小 我们当前没有链接 所以是0x00

Link_Size

8 Link_off 链接 文件偏移量 

Link_off

9 map_off  Dex Map_list的偏移为0x24c  

Dex Map_list  
DexMapList 偏移的位置

10 String_ids_Size 字符ids大小为0x0E  也就是代表有14个保存着字符串的数组 从0开始

String_ids_Size 0x0E

   String_ids_off  字符ids偏移 通过这个索引可以找到String_id_list 

String_ids_off

 0070为偏移地址 也是String_id_list  的起始位置-00A7的位置结束 

下面的伪代码就是String_id_list的数据结构

String_id_item的伪代码

然后通过上面的索引找到下面的这个伪代码结构体就找到了真正的字符串内容位置

上面的索引找到的是这个结构体

  四个字节为一组 第一个字符串为 0x176  

String_ids[0]

通过String_id[0]的偏移 找到的第一个字符串  因为Dex新增了leb128数据类型 

Leb128数据类型为了减少文件空间,Dex文件定义了一种名为LEB128的数据类型,LEB128是Little Endian Based 128的缩写,其唯一功能就是用于表示32位比特长度的数据, 

我用红线圈住的第一个06代表了这段字符串的长度 而06后面的才是它真正的字符串数据 可以看到我第06后面的红线圈到的7位 包括00-字符串结尾符号  但是leb128 没有将它算进去,无伤大雅 

String_item String_data


String_id_list数据结构图

但是你会发现我们其实并没有使用init这个自动初始化的函数,那么也就是说在class文件中时就已经拥有了init的函数, 有兴趣的同学可以自行搜索

11 Type_ids_Size 类型ids大小  代表Type_id_list 有7个成员

Type_ids_Size 类型大小 0x07

   Type_ids_off 类型ids 偏移  Type_id_list 的文件偏移为A8 

Type_ids_off 0xA8
00A8 是Type_id_list的

通过偏移 00A8  找到Type_id_list  Type_id_list里面包含着Type_id_item 而经过我们学习了解 这个 descriptor_idx 是指向String_ids的索引 

Type_id_item的伪代码
Type_id[0]索引为0x03


用0x03这个索引去String_id_list寻找到的是指向到LHelloWorld字符串的索引

用0x03这个下表去String_id_list寻找到的是指向到LHelloWorld字符串的索引

现在也证实了 这一点

12 Proto_ids_Size 和 Proto_ids_off它们代表的是dex文件中方法原型的个数和位置偏移

Proto_ids_Size 03h
Proto_ids_off  C4偏移

proto_id_item 方法原型项伪代码


Proto_id_item数据结构

如果parameters_off不为0 则指向Type_list结构体

Type_list数据类型列表结构
Type_item 数据类型项

这里我们分析俩个 proto_id_item项的数据结构 一个parameters_off有值 一个为0

proto_id[0]

shorty_idx 指向string_id[0x8]

String_id[8]

return_type_idx 是指向一个type_ids[0x5]

去type_ids数组中寻找下表为5的就可以 因为刚刚找到type_ids的偏移为00A8 所以往后推五个uint下一个就是我们要找到的type_ids[0x5]

type_id[5]

所以返回值为V 也就是方法是 void 无类型方法也就无返回值

最后一个Parameters_off 因为这个是0 所以代表没有type_list数据结构

这里我们直接分析一个有Parameters_off的数据结构 

前面的俩个结构我们就不分析了 都是一样的

Parameters_off值为十进制的360十六进制的168

Type_list 的偏移显示为168 通过偏移找到了Type_list的位置

Type_list数据结构

一个为size 大小为1 代表这个Type_list中数组的数量为1 也就是只有Type_list[0]

通过type_list[0]数组中保存的Type_ids数据值为3 指向了Type_ids数据项的下表为3的数组


Type_ids[3]

而type_ids中的结构是一个descriptor_idx 指向String_ids 下标为6的内容

String_id[6]

找到下标为6的数组后 数据偏移为十进制的474十六进制的1DA我们通过偏移找到下面真正的字符串数据

Ljava/lang/String 

字段描述符为Ljava/lang/String的数据类型; 到这里 proto_id_item 分析完毕


13 field_ids_Size 和 field_ids_off  成员字段的大小/成员ids文件偏移 

成员字段大小为1 代表只有一个成员

field_ids_Size 0x01

field_ids_off 偏移为E8的位置就是field_id_list

field_ids_off 0x E8

现在我们看下 field_id_item的结构的伪代码数据结构 注意: 一个field_id_item占8个字节哦

field_id_item

从偏移E8的位置找到field_it_item

0x0004 为class_ids  0x0001为数据类型  0x0000000C 为名称 让我们分别找到看看

Field_id_item

class_ids 是指向type_ids的 所以找到type_ids偏移后找到它的索引为4的内容

class_id-type_ids

从type_id 上面的偏移0x0007 找到String_id 中保存data区字符串的偏移 找到真正的字符串

String_id

找到的字符串为 Ljava/lang/System   也就是class_id保存的是Ljava/lang/System

String_data

type_ids在上面我们找到的值为0x0001 它本身指向的是一个type_ids 所以去type_id_item 找到下标为1的数据 找到的descriptor_ids 值为4 指向String_ids的下标为4的数据

type_id_item

找到的String_ids 里面保存的是 指向data区字符串的偏移 值为0x000001AF

String_ids

从偏移01AF 找到data区字符串 Ljava/io/PrintStream  

String_data

接下来就是 Name_ids  指向的是String_ids 值为0x0c  通过String_ids找到的数据偏移0x00000224

String_data_off

通过上面的数据便宜找到数据 为out  也就是我们的System.out.println 中的out 

String_Data


14 Method_ids_Size和Method_ids_off 分别是方法的数量和指向Method_id_item的索引 下图已知 方法数量为4 开始的偏移为F0

Method_ids_Size  0x04
Method_ids_off 0xF0

先写出Method_id_item 的数据结构

分析方法就不写了 不会的可以查看上面的步骤 逐步分析

Method_id_item

15  class_ids_size和 class_ids_off 是类的数量和指向Class_def的偏移

Class_ids_Size 0x01
class_ids_off 0x0110

Class_def的数据结构伪代码

Class_def

access_flags 是一个枚举以ACC开头的权限标志 值为0x1时时PUBLIC  0x2是PRIVATE 0x4是PROTECT  详细请自行搜索 

superclass_idx 指向type_ids 代表的是基类基类 这里请自行寻找分析

interfaces_off 如果该值=0 代表没有参数,如果有值数据类型则是type_list 而每一个list中保存了每一个接口的type_ids 请自行手动分析

source_file_idx 指向了String_ids 保存的是源文件的名字

annotations_off 保存的是有存储和注解(注释)有关的信息的偏移

class_data_off 则指向了一个class_data_item的数据结构

Class_data_item

encode_field[] 和encode_method[]的数据结构伪代码

encoded_field和encoded_method

code_off 保存一个偏移值 指向一个code_item结构体  而code_item结构体类似于class文件的结构 看伪代码的结构图

Code_item

code_item和Class文件中的Code属性类似。注意,code_item里的padding、tries和handlers成员都是可选项。也就是只有在代码中确实有try语句块的时候,这几个成员才存在(padding则是需要对齐的时候才存在

static_values_off 是用来存储用来初始化类的静态变量值,静态变量如果没有显示初始值的话,默认是0或null,如果有初值的话,初值信息就存储在文件static_values_off的地方,对应的数据结构名为encode_array_item 

17 data_size 数据大小和data_off 数据偏移 

data_size 0x01BC
data_off 0x0130

上面的Header_item解析完成后 大家可以发现我截图的照片都是到0060h 因为默认的header_item 是大小0x70  

Header_Item  全部解析完成


Header_item

U 代表无符号 而java里面的数据类型都是有符号的 需要转换 转换方式自行上网查找

随后附 解析代码链接  https://bbs.pediy.com/thread-257722.htm

你可能感兴趣的:(Android Dex文件解析)