文章目录
- 1、class和dex文件的宏观区别
- 2、dex文件和class字节码的基本区别
- 3、dex文件实例分析
- 3.1配置dx环境变量
- 3.2Java源文件
- 3.3 编译成class二进制码
- 3.4 将二进制class文件优化成dex文件
- 4、分析dex文件
- 4.1头文件 header
- 4.1.1 解析头文件
- 4.2 索引区的分析
- 4.2.1 索引区的分析介绍
- 4.2.2 实例分析
前言:上一次分析了class字节码的文件,然后继续看书的过程中。需要自己制作分析一个dex的工具。制作工具之前,首先是得明白原理,然后再如说是没问题的,今天就此记录我的分析理解。如有不足之处希望大家指出!
1、class和dex文件的宏观区别
dex文件是从class字节码文件中优化出来的产物,class字节码是对于java虚拟机来说的。则dex文件则是对前者优化之后提供给android(安卓davlik虚拟机,应该是安卓4.4版本之前)来说。
2、dex文件和class字节码的基本区别
通过上面的说明,大致我们能明白,dex是建立在class基础之上优化过来的。那两者的区别有哪些呢?
区别 | class字节码 | dex文件 |
---|---|---|
endian(字符序) | 低位在右边,高位在左 | 低位在左,高位在后 |
文件区分不同 | 常量池/data | 头文件/索引区/数据区 |
LEB128 | 无 | 在int基础之上变种的数据类型,高位第7位数(索引为0)据表示是否结束,如果为1则表示后面还有字节。如果第7位表示0,则终止。当第二个或者第三个字节有存在的时候,第二个字节的第0索引,对应的是第一个字节的第七位!每个字节的第七位是判断位 |
3、dex文件实例分析
3.1配置dx环境变量
我相信能看到这里的人,java环境已经是配置好了的。
找到<SDK_HOME>/build-tools/<VERSION>
这里是你会看到一个dx.bat的批处理脚本。
打开我的电脑,环境变量中,找到环境变量中的path,将全路径放进入。
此时,按住win+r输入cmd,打出dx --version
dx --version
dx version 1.16
3.2Java源文件
package com.company.jvm;
public class Sample {public String m1;public String m2;public Object [] arr;public static void main(String[] args) {Sample sample = new Sample();sample.m1="22";sample.arr=new Object[12];System.out.println(sample.m1);}
}
3.3 编译成class二进制码
javac Sample
3.4 将二进制class文件优化成dex文件
dx --dex --output=Sample.dex com/comany/jvm/Sample.class
输入上面的指令之后,有可能会报错,出现的问题为:
class name (com/company/jvm/Sample) does not match path (Sample.class)
...while parsing Sample.class
大致意思就说,找不到路径。此时我们需要到包名的根目录下,进入cmd。
4、分析dex文件
上面已经说过,dex文件基本可以分为三个区域:1、头文件 2、索引区 3、数据区
4.1头文件 header
类型 | 字节长度 | 说明 |
---|---|---|
magic | U8 | 标识类型,列入class的文件是cafebabe,dex则是dex\n035\0 |
checksum | U4 | (除 magic 和此字段之外的所有内容)的 adler32 校验和 |
signature | byte[20] | 文件剩余内容(除 magic、checksum 和此字段之外的所有内容)的 SHA-1 签名(哈希);用于对文件进行唯一标识 |
file_size | U4 | 整个文件(包括标头)的大小,以字节为单位 |
header_size | U4 | 默认为0x70头文件(整个区段)的大小,以字节为单位。此项允许至少一定程度的向后/向前兼容性,而不会使格式失效。 |
endian_tag | U4 | 默认为低字符序,取值为0x12345678。如果是big endian则为0x78654321,说明进行过字节交换,这里具体可以查看android关于endian tag的说明 |
link_size | U4 | 链接区段的大小;如果此文件未进行静态链接,则该值为 0 |
link_off | U4 | 从文件开头到链接区段的偏移量,如果 link_size == 0,则该值为 0。该偏移量(如果为非零值)应该是到 |
map_off | U4 | 从文件开头到映射项的偏移量。该偏移量(必须为非零值)应该是到 data 区段的偏移量,而数据应采用下文中“map_list”指定的格式。 |
4.1.1 解析头文件
对应 | 偏移位 | 长度 | 字符节 | 说明 |
---|---|---|---|---|
magic | 0x0000 | U8 | 64 65 78 0a 30 33 35 00 | 长度为8的byte数组,转换成字符之后就是dex 035 |
checkSum | 0x0008 | U4 | 85 71 f0 2c | 其adler32校验和结果就是2CF07185的结果【高位字符序】 |
signature | 0x000C | byte[20] | ~0x10 | 其结果是sha-1的加密,我这里的结果为:6003000070000000785634120000000000000000 |
file_size | 0x20 | U4 | 60 03 00 00 | 计算得出为 864,说明当前有864个byte |
header_size | 0x24 | U4 | 70 00 00 00 | 一般情况下都为0x70 |
endian_tag | 0x28 | U4 | 78 56 34 12 | 这里说明是已经是进行过交换处理 |
link_size | 0x2c | U4 | 00 00 00 00 | 说明没有静态链接,数量为0【何为静态链接,后面的文章应该会梳理。今天的内容不在此】 |
link_off | 0x30 | U4 | 00 00 00 00 | 偏移量为0 |
map_off | 0x34 | U4 | C0 02 00 00 | 数据位置偏移为704 |
4.2 索引区的分析
4.2.1 索引区的分析介绍
类型 | 字节长度 | 说明 |
---|---|---|
string_ids_size | U4 | 字符串的数量 |
string_ids_off | U4 | 从文件开头到标识符的偏移量 |
type_ids_size | U4 | 类型标识符列表数量,最多为 65535 |
type_ids_off | U4 | 从文件开头到类型标识符列表的偏移量;如果 type_ids_size == 0(不可否认是一种奇怪的极端情况),则该值为 0。该偏移量(如果为非零值)应该是到 type_ids 区段开头的偏移量。 |
proto_ids_size | U4 | 原型标识符列表中的元素数量,最多为 65535 |
proto_ids_off | U4 | 原型从文件开头到标识符的偏移量 |
field_ids_size | U4 | 字段数量 |
field_ids_off | U4 | 从文件开头到字段标识符列表的偏移量 |
method_ids_size | U4 | 方法标识符列表中的元素数量 |
method_ids_off | U4 | 从文件开头到方法标识符列表的偏移量 |
class_defs_size | U4 | 类定义列表中的元素数量 |
class_defs_off | U4 | 方从文件开头到类定义列表的偏移量 |
data_size | U4 | data 区段的大小(以字节为单位)。该数值必须是 sizeof(uint) 的偶数倍 |
method_ids_off | U4 | 从文件开头到 data 区段开头的偏移量 |
4.2.2 实例分析
类型 | 偏移位 | 字节码 | 说明 |
---|---|---|---|
string_ids_size | 0x38 | 12 00 00 00 | 说明存在18个字符串 |
string_ids_off | 0x3c | 70 00 00 00 | 从文件开头到标识符的偏移量为112 |
type_ids_size | 0x40 | 08 00 00 00 | 类型标识符列表数量为:8 |
type_ids_off | 0x44 | b8 00 00 00 | 从文件开头到类型标识符列表的偏移量为:0xb8 |
proto_ids_size | 0x48 | 03 00 00 00 | 原型标识符列表中的元素数量为:3 |
proto_ids_off | 0x4c | d8 00 00 00 | 原型从文件开头到标识符的偏移量为:0xd8 |
field_ids_size | 0x50 | 04 00 00 00 | 字段数量为4 |
field_ids_off | 0x54 | fc 00 00 00 | 从文件开头到字段标识符列表的偏移量为0x54 |
method_ids_size | 0x58 | 04 00 00 00 | 方法标识符列表中的元素数量为:4 |
method_ids_off | 0x5c | 1c 01 00 00 | 从文件开头到方法标识符列表的偏移量为:0x011c |
class_defs_size | 0x60 | 01 00 00 00 | 类定义列表中的元素数量为:1 |
class_defs_off | 0x64 | 3c 01 00 00 | 方从文件开头到类定义列表的偏移量0x013c |
data_size | 0x70 | c2 01 00 00 | data 区段的大小(以字节为单位)。该数值必须是 sizeof(uint) 的偶数倍,为:450 |
data_off | 0x74 | c601 00 00 | 从文件开头到 data 区段开头的偏移量为0x01c6 |
~后文待续,坐了一个多小时了,休息会儿