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文件
使用javac 将HelloWorld.java 编译成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文件用010editor打开
解析Header_item文件的结构
1.Dex文件的标志头 dex.035 占了8个字节 00
2 checksum 校验和 用来校验文件是否被损坏 4个字节
3 文件签名signature 占了20个字节 而且是数组 文件签名用来识别是否是盗版文件
4 File_Size 文件大小 代表当前的dex文件的大小是0x02EC
5 Header_size 头大小 默认0x70 代表header_item的大小
6 endian_tag 端序 如果是0x78563412 那么就是小端序的方式 左低右高 科普一波 小端序是大的在低地址 小的在高地址,而大端序则是正常 大的在大小的在小
7 Link_size 链接 大小 我们当前没有链接 所以是0x00
8 Link_off 链接 文件偏移量
9 map_off Dex Map_list的偏移为0x24c
10 String_ids_Size 字符ids大小为0x0E 也就是代表有14个保存着字符串的数组 从0开始
String_ids_off 字符ids偏移 通过这个索引可以找到String_id_list
0070为偏移地址 也是String_id_list 的起始位置-00A7的位置结束
下面的伪代码就是String_id_list的数据结构
然后通过上面的索引找到下面的这个伪代码结构体就找到了真正的字符串内容位置
四个字节为一组 第一个字符串为 0x176
通过String_id[0]的偏移 找到的第一个字符串 因为Dex新增了leb128数据类型
Leb128数据类型为了减少文件空间,Dex文件定义了一种名为LEB128的数据类型,LEB128是Little Endian Based 128的缩写,其唯一功能就是用于表示32位比特长度的数据,
我用红线圈住的第一个06代表了这段字符串的长度 而06后面的才是它真正的字符串数据 可以看到我第06后面的红线圈到的7位 包括00-字符串结尾符号 但是leb128 没有将它算进去,无伤大雅
但是你会发现我们其实并没有使用init这个自动初始化的函数,那么也就是说在class文件中时就已经拥有了init的函数, 有兴趣的同学可以自行搜索 😃
11 Type_ids_Size 类型ids大小 代表Type_id_list 有7个成员
Type_ids_off 类型ids 偏移 Type_id_list 的文件偏移为A8
通过偏移 00A8 找到Type_id_list Type_id_list里面包含着Type_id_item 而经过我们学习了解 这个 descriptor_idx 是指向String_ids的索引
用0x03这个索引去String_id_list寻找到的是指向到LHelloWorld字符串的索引
现在也证实了 这一点
12 Proto_ids_Size 和 Proto_ids_off它们代表的是dex文件中方法原型的个数和位置偏移
proto_id_item 方法原型项伪代码
如果parameters_off不为0 则指向Type_list结构体
这里我们分析俩个 proto_id_item项的数据结构 一个parameters_off有值 一个为0
shorty_idx 指向string_id[0x8]
return_type_idx 是指向一个type_ids[0x5]
去type_ids数组中寻找下表为5的就可以 因为刚刚找到type_ids的偏移为00A8 所以往后推五个uint下一个就是我们要找到的type_ids[0x5]
所以返回值为V 也就是方法是 void 无类型方法也就无返回值
最后一个Parameters_off 因为这个是0 所以代表没有type_list数据结构
这里我们直接分析一个有Parameters_off的数据结构
前面的俩个结构我们就不分析了 都是一样的
Type_list 的偏移显示为168 通过偏移找到了Type_list的位置
一个为size 大小为1 代表这个Type_list中数组的数量为1 也就是只有Type_list[0]
通过type_list[0]数组中保存的Type_ids数据值为3 指向了Type_ids数据项的下表为3的数组
而type_ids中的结构是一个descriptor_idx 指向String_ids 下标为6的内容
找到下标为6的数组后 数据偏移为十进制的474十六进制的1DA我们通过偏移找到下面真正的字符串数据
字段描述符为Ljava/lang/String的数据类型; 到这里 proto_id_item 分析完毕
13 field_ids_Size 和 field_ids_off 成员字段的大小/成员ids文件偏移
成员字段大小为1 代表只有一个成员
field_ids_off 偏移为E8的位置就是field_id_list
现在我们看下 field_id_item的结构的伪代码数据结构 注意: 一个field_id_item占8个字节哦
从偏移E8的位置找到field_it_item
0x0004 为class_ids 0x0001为数据类型 0x0000000C 为名称 让我们分别找到看看
class_ids 是指向type_ids的 所以找到type_ids偏移后找到它的索引为4的内容
从type_id 上面的偏移0x0007 找到String_id 中保存data区字符串的偏移 找到真正的字符串
找到的字符串为 Ljava/lang/System 也就是class_id保存的是Ljava/lang/System
type_ids在上面我们找到的值为0x0001 它本身指向的是一个type_ids 所以去type_id_item 找到下标为1的数据 找到的descriptor_ids 值为4 指向String_ids的下标为4的数据
找到的String_ids 里面保存的是 指向data区字符串的偏移 值为0x000001AF
从偏移01AF 找到data区字符串 Ljava/io/PrintStream
接下来就是 Name_ids 指向的是String_ids 值为0x0c 通过String_ids找到的数据偏移0x00000224
通过上面的数据便宜找到数据 为out 也就是我们的System.out.println 中的out
14 Method_ids_Size和Method_ids_off 分别是方法的数量和指向Method_id_item的索引 下图已知 方法数量为4 开始的偏移为F0
先写出Method_id_item 的数据结构
分析方法就不写了 不会的可以查看上面的步骤 逐步分析
15 class_ids_size和 class_ids_off 是类的数量和指向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的数据结构
encode_field[] 和encode_method[]的数据结构伪代码
code_off 保存一个偏移值 指向一个code_item结构体 而code_item结构体类似于class文件的结构 看伪代码的结构图
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 数据偏移
上面的Header_item解析完成后 大家可以发现我截图的照片都是到0060h 因为默认的header_item 是大小0x70
Header_Item 全部解析完成
U 代表无符号 而java里面的数据类型都是有符号的 需要转换 转换方式自行上网查找
随后附 解析代码链接 https://bbs.pediy.com/thread-257722.htm