1. DEX文件简介
1). 基本格式
- Dex Header: header文件头
- String Table: 字符串的索引
- Type Table: 类型的索引
- Proto Table: 方法原型的索引
- Field Table: 域的索引
- Method Table: 方法索引
- Class Def Table: 类的定义区
- Data Section: 数据区
1). Dex Header
dex文件里的header,描述.dex文件的文件信息,及其它各个区域的索引。
/**
* DEX 头部信息类型
* 主要分为两部分:
* 1). 魔数 + 签名 + 文件大小等信息
* 2). 后面的各个数据结构的大小和偏移值,成对出现
*
* struct DexHeader {
u1 magic[8]; // includes version number
u4 checksum; // adler32 checksum
u1 signature[kSHA1DigestLen]; // SHA-1 hash
u4 fileSize; // length of entire file
u4 headerSize; // offset to start of next section
u4 endianTag;
u4 linkSize;
u4 linkOff;
u4 mapOff;
u4 stringIdsSize;
u4 stringIdsOff;
u4 typeIdsSize;
u4 typeIdsOff;
u4 protoIdsSize;
u4 protoIdsOff;
u4 fieldIdsSize;
u4 fieldIdsOff;
u4 methodIdsSize;
u4 methodIdsOff;
u4 classDefsSize;
u4 classDefsOff;
u4 dataSize;
u4 dataOff;
};
*
* @author mazaiting
*/
public class HeaderType {
/**8个字节,一般是常量,为使.dex文件能够被识别出来,必须出现在.dex文件的最开头位置
* 数组的值一般可以转换为一个字符串: { 0x64 0x65 0x78 0x0a 0x30 0x33 0x35 0x00 } = "dex\n035\0"
* 中间是一个 ‘\n' 符号 ,后面 035 是 Dex 文件格式的版本 。
*/
public byte[] magic = new byte[8];
/**文件校验码,使用alder32算法校验文件出去magic,checknum外余下所有文件区域用于检查文件错误*/
public int checkSum;
/**采用SHA-1算法hash出去magic,checknum和signature外余下所有的文件区域,用于唯一识别本文件*/
public byte[] signAture = new byte[20];
/**DEX文件的大小*/
public int fileSize;
/**header区域的大小,单位Byte,一般固定为0x70常量*/
public int headerSize;
/**大小端标签,标准.dex文件为小端,此项一般固定为0x12345678常量*/
public int endianTag;
/**链接数据的大小*/
public int linkSize;
/**链接数据的偏移值*/
public int linkOff;
/**map item的偏移地址,该item属于data区里的内容,值要大于等于dataOff的大小*/
public int mapOff;
/**DEX中用到的所有字符串内容的大小*/
public int stringIdsSize;
/**DEX中用到的所有字符串内容的偏移量*/
public int stringIdsOff;
/**DEX中类型数据结构的大小*/
public int typeIdsSize;
/**DEX中类型数据结构的偏移值*/
public int typeIdsOff;
/**DEX中的元数据信息数据结构的大小*/
public int protoIdsSize;
/**DEX中的元数据信息数据结构的偏移值*/
public int protoIdsOff;
/**DEX中字段信息数据结构的大小*/
public int fieldIdsSize;
/**DEX中字段信息数据结构的偏移值*/
public int fieldIdsOff;
/**DEX中方法信息数据结构的大小*/
public int methodIdsSize;
/**DEX中方法信息数据结构的偏移值*/
public int methodIdsOff;
/**DEX中的类信息数据结构的大小*/
public int classDefsSize;
/**DEX中的类信息数据结构的偏移值*/
public int classDefsOff;
/**DEX中数据区域的结构信息的大小*/
public int dataSize;
/**DEX中数据区域的结构信息的偏移值*/
public int dataOff;
@Override
public String toString(){
return "magic:"+Util.bytesToHexString(magic)+"\n"
+ "checksum:"+checkSum + "\n"
+ "siganature:"+Util.bytesToHexString(signAture) + "\n"
+ "file_size:"+fileSize + "\n"
+ "header_size:"+headerSize + "\n"
+ "endian_tag:"+endianTag + "\n"
+ "link_size:"+linkSize + "\n"
+ "link_off:"+Util.bytesToHexString(Util.int2Byte(linkOff)) + "\n"
+ "map_off:"+Util.bytesToHexString(Util.int2Byte(mapOff)) + "\n"
+ "string_ids_size:"+stringIdsSize + "\n"
+ "string_ids_off:"+Util.bytesToHexString(Util.int2Byte(stringIdsOff)) + "\n"
+ "type_ids_size:"+typeIdsSize + "\n"
+ "type_ids_off:"+Util.bytesToHexString(Util.int2Byte(typeIdsOff)) + "\n"
+ "proto_ids_size:"+protoIdsSize + "\n"
+ "proto_ids_off:"+Util.bytesToHexString(Util.int2Byte(protoIdsOff)) + "\n"
+ "field_ids_size:"+fieldIdsSize + "\n"
+ "field_ids_off:"+Util.bytesToHexString(Util.int2Byte(fieldIdsOff)) + "\n"
+ "method_ids_size:"+methodIdsSize + "\n"
+ "method_ids_off:"+Util.bytesToHexString(Util.int2Byte(methodIdsOff)) + "\n"
+ "class_defs_size:"+classDefsSize + "\n"
+ "class_defs_off:"+Util.bytesToHexString(Util.int2Byte(classDefsOff)) + "\n"
+ "data_size:"+dataSize + "\n"
+ "data_off:"+Util.bytesToHexString(Util.int2Byte(dataOff));
}
}
2). String Table
string_ids区索引.dex文件所有的字符串
/**
* 索引.dex文件所有的字符串
*
* struct DexStringId {
u4 stringDataOff; // file offset to string_data_item
};
* @author mazaiting
*/
public class StringIdsItem {
/**字符串数据的偏移地址*/
public int stringDataOff;
/**
* 获取当前类属性所占字节大小
* @return
*/
public static int getSize() {
return 4;
}
@Override
public String toString() {
return Util.bytesToHexString(Util.int2Byte(stringDataOff));
}
}
string_data_off是一个偏移地址,指向的数据结构为string_data_item.
/**
* StringIdsItem中stringDataOff指向的数据结构
* struct string_data_item {
uleb128 utf16_size;
ubyte data;
}
* @author mazaiting
*/
public class StringDataItem {
/**LEB128 ( little endian base 128 ) 格式 ,是基于 1 个 Byte 的一种不定长度的
编码方式 。若第一个 Byte 的最高位为 1 ,则表示还需要下一个 Byte 来描述 ,直至最后一个 Byte 的最高
位为 0 。每个 Byte 的其余 Bit 用来表示数据*/
public byte data;
public List<Byte> utf16Size = new ArrayList<>();
}
3). Type Table
主要描述dex中所有的类型,如类类型,基本类型等信息,type_ids区索引了dex文件里的所有数据类型,包括class类型,数据类型(array types) 和基本类型(primitive types).
/**
* DEX所有类型,基本类型等信息
* type_ids 区索引了 dex 文件里的所有数据类型 ,包括 class 类型 ,数组类型(array types)和基本类型(primitive types)
*
* struct DexTypeId {
u4 descriptorIdx; // index into stringIds list for type descriptor
};
* @author mazaiting
*/
public class TypeIdsItem {
/**
* ID索引
*/
public int descriptorIdx;
/**
* 获取所属
* @return
*/
public static int getSize() {
return 4;
}
}
4). Proto Table
proto是method prototype代表java语言里的一个method的原型
/**
* proto 的意思是 method prototype 代表 java 语言里的一个 method 的原型 。proto_ids 里的元素为 proto_id_item ,
*
* struct DexProtoId {
u4 shortyIdx; // index into stringIds for shorty descriptor
u4 returnTypeIdx; // index into typeIds list for return type
u4 parametersOff; // file offset to type_list for parameter types
};
*
* @author mazaiting
*/
public class ProtoIdsItem {
/**值为一个string_ids的index号,用来说明该method原型*/
public int shortyIdx;
/**值为一个type_ids的index,表示该method原型的返回值类型*/
public int returnTypeIdx;
/**指定method原型的参数列表type_list,若method没有参数,则值为0. 参数的格式是type_list*/
public int parametersOff;
// 这个不是公共字段,而是为了存储方法原型中的参数类型名和参数个数
public List<String> paramtersList = new ArrayList<>();
public int paramterCount;
/**
* 获取当前类字节数
* @return
*/
public static int getSize() {
return 4 + 4 + 4;
}
@Override
public String toString() {
return "shortyIdx:"+shortyIdx+",returnTypeIdx:"+returnTypeIdx+",parametersOff:"+parametersOff;
}
}
5). Field Table
field_ids区里面存放的是dex文件引用的所有field
/**
* field_ids区里存放的是dex文件引用的所有的field,本区元素格式是field_id_item
*
* struct DexFieldId {
u2 classIdx; // index into typeIds list for defining class
u2 typeIdx; // index into typeIds for field type
u4 nameIdx; // index into stringIds for field name
};
*
* @author mazaiting
*/
public class FieldIdsItem {
/**
* field所属的class类型,class_idx的值时type_ids的一个index,指向所属的类
*/
public short classIdx;
/**
* field的类型,值是type_ids的一个index
*/
public short typeIdx;
/**
* field的名称,它的值是string_ids的一个index
*/
public int nameIdx;
/**
* 当前区域所占字节大小
* @return
*/
public static int getSize() {
return 2 + 2 + 4;
}
@Override
public String toString() {
return "classIdx: " + classIdx + ",typeIdx: " + typeIdx + ",nameIdx: " + nameIdx;
}
}
6). Method Table
method_ids是索引区的最后一个条目,它索引了dex文件里的所有的method.
/**
* method_ids是索引区的最后一个条目,它索引了dex文件里的所有method.
* method_ids的元素格式是method_id_item
*
* struct DexMethodId {
u2 classIdx; // index into typeIds list for defining class
u2 protoIdx; // index into protoIds for method prototype
u4 nameIdx; // index into stringIds for method name
};
*
* @author mazaiting
*/
public class MethodIdsItem {
/**
* method所属的class类型,class_idx的值是type_ids的一个index,必须指向一个class类型
*/
public short classIdx;
/**
* method的原型,指向proto_ids的一个index
*/
public short protoIdx;
/**
* method的名称,值为string_ids的一个index
*/
public int nameIdx;
public static int getSize() {
return 2 + 2 + 4;
}
@Override
public String toString(){
return "classIdx: " + classIdx + ",protoIdx: " + protoIdx + ",nameIdx: " + nameIdx;
}
}
7). Class Def Table
class_defs区域里存放着class definitions,class的定义。它的结构较dex区要复杂,有的数据直接指向data区
/**
* class_defs区域里存放着class definitions,有些数据直接指向了data区
*
* struct DexClassDef {
u4 classIdx; /* index into typeIds for this class
u4 accessFlags;
u4 superclassIdx; /* index into typeIds for superclass
u4 interfacesOff; /* file offset to DexTypeList
u4 sourceFileIdx; /* index into stringIds for source file name
u4 annotationsOff; /* file offset to annotations_directory_item
u4 classDataOff; /* file offset to class_data_item
u4 staticValuesOff; /* file offset to DexEncodedArray
};
*
* @author mazaiting
*/
public class ClassDefItem {
/**
* 描述具体的class类型,值是type_ids的一个index,值必须是一个class类型,不能是数组雷兴国或者基本类型
*/
public int classIdx;
/**
* 描述class的访问类型,如public,final,static等
*/
public int accessFlags;
/**
* 描述父类的类型,值必须是一个class类型,不能是数组雷兴国或者基本类型
*/
public int superClassIdx;
/**
* 值为偏移地址,被指向的数据结构为type_list,class若没有interfaces,值为0
*/
public int interfacesOff;
/**
* 表示源代码文件的信息,值为string_ids的一个index。若此项信息丢失,此项赋值为NO_INDEX=0xFFFFFFFF
*/
public int sourceFileIdx;
/**
* 值为偏移地址,指向的内容是该class的注解,位置在data区,格式为annotations_directory_item,若没有此项,值为0
*/
public int annotationsOff;
/**
* 值为偏移地址,指向的内容是该class的使用到的数据,位置在data区,格式为class_data_item。无偶没有此项,则值为0
*/
public int classDataOff;
/**
* 值为偏移地址,指向data区里的一个列表,格式为encoded_array_item。若没有此项,值为0.
*/
public int staticValueOff;
/**
* enum {
ACC_PUBLIC = 0x00000001, // class, field, method, ic
ACC_PRIVATE = 0x00000002, // field, method, ic
ACC_PROTECTED = 0x00000004, // field, method, ic
ACC_STATIC = 0x00000008, // field, method, ic
ACC_FINAL = 0x00000010, // class, field, method, ic
ACC_SYNCHRONIZED = 0x00000020, // method (only allowed on natives)
ACC_SUPER = 0x00000020, // class (not used in Dalvik)
ACC_VOLATILE = 0x00000040, // field
ACC_BRIDGE = 0x00000040, // method (1.5)
ACC_TRANSIENT = 0x00000080, // field
ACC_VARARGS = 0x00000080, // method (1.5)
ACC_NATIVE = 0x00000100, // method
ACC_INTERFACE = 0x00000200, // class, ic
ACC_ABSTRACT = 0x00000400, // class, method, ic
ACC_STRICT = 0x00000800, // method
ACC_SYNTHETIC = 0x00001000, // field, method, ic
ACC_ANNOTATION = 0x00002000, // class, ic (1.5)
ACC_ENUM = 0x00004000, // class, field, ic (1.5)
ACC_CONSTRUCTOR = 0x00010000, // method (Dalvik only)
ACC_DECLARED_SYNCHRONIZED =
0x00020000, // method (Dalvik only)
ACC_CLASS_MASK =
(ACC_PUBLIC | ACC_FINAL | ACC_INTERFACE | ACC_ABSTRACT
| ACC_SYNTHETIC | ACC_ANNOTATION | ACC_ENUM),
ACC_INNER_CLASS_MASK =
(ACC_CLASS_MASK | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC),
ACC_FIELD_MASK =
(ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL
| ACC_VOLATILE | ACC_TRANSIENT | ACC_SYNTHETIC | ACC_ENUM),
ACC_METHOD_MASK =
(ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL
| ACC_SYNCHRONIZED | ACC_BRIDGE | ACC_VARARGS | ACC_NATIVE
| ACC_ABSTRACT | ACC_STRICT | ACC_SYNTHETIC | ACC_CONSTRUCTOR
| ACC_DECLARED_SYNCHRONIZED),
};
*/
/**
* 访问修饰符
*/
public static final int
ACC_PUBLIC = 0x00000001, // class, field, method, ic
ACC_PRIVATE = 0x00000002, // field, method, ic
ACC_PROTECTED = 0x00000004, // field, method, ic
ACC_STATIC = 0x00000008, // field, method, ic
ACC_FINAL = 0x00000010, // class, field, method, ic
ACC_SYNCHRONIZED = 0x00000020, // method (only allowed on natives)
ACC_SUPER = 0x00000020, // class (not used in Dalvik)
ACC_VOLATILE = 0x00000040, // field
ACC_BRIDGE = 0x00000040, // method (1.5)
ACC_TRANSIENT = 0x00000080, // field
ACC_VARARGS = 0x00000080, // method (1.5)
ACC_NATIVE = 0x00000100, // method
ACC_INTERFACE = 0x00000200, // class, ic
ACC_ABSTRACT = 0x00000400, // class, method, ic
ACC_STRICT = 0x00000800, // method
ACC_SYNTHETIC = 0x00001000, // field, method, ic
ACC_ANNOTATION = 0x00002000, // class, ic (1.5)
ACC_ENUM = 0x00004000, // class, field, ic (1.5)
ACC_CONSTRUCTOR = 0x00010000, // method (Dalvik only)
ACC_DECLARED_SYNCHRONIZED =
0x00020000, // method (Dalvik only)
ACC_CLASS_MASK =
(ACC_PUBLIC | ACC_FINAL | ACC_INTERFACE | ACC_ABSTRACT
| ACC_SYNTHETIC | ACC_ANNOTATION | ACC_ENUM),
ACC_INNER_CLASS_MASK =
(ACC_CLASS_MASK | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC),
ACC_FIELD_MASK =
(ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL
| ACC_VOLATILE | ACC_TRANSIENT | ACC_SYNTHETIC | ACC_ENUM),
ACC_METHOD_MASK =
(ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL
| ACC_SYNCHRONIZED | ACC_BRIDGE | ACC_VARARGS | ACC_NATIVE
| ACC_ABSTRACT | ACC_STRICT | ACC_SYNTHETIC | ACC_CONSTRUCTOR
| ACC_DECLARED_SYNCHRONIZED);
/**
* 获取当前区域所占字节数
* @return
*/
public static int getSize() {
return 4 * 8;
}
@Override
public String toString(){
return "classIdx: " + classIdx + ",accessFlags: " + accessFlags + ",superClassIdx: " + superClassIdx +
",iterfacesOff: " + interfacesOff + ",sourceFileIdx: " + sourceFileIdx + ",annotationsOff: " +
annotationsOff + ",classDataOff: " + classDataOff + ",staticValueOff: " + staticValueOff;
}
}
class_data_off指向data区里的class_data_itetm结构,class_data_item里存放着本class使用到的各种数据.
/**
* class_data_off指向data区里的class_data_item结构,class_data_item里存放着本class使用到的各种数据
*
* struct class_data_item {
uleb128 static_fields_size;
uleb128 instance_fields_size;
uleb128 direct_methods_size;
uleb128 virtual_methods_size;
encoded_field static_fields [ static_fields_size ];
encoded_field instance_fields [ instance_fields_size ];
encoded_method direct_methods [ direct_method_size ];
encoded_method virtual_methods [ virtual_methods_size ];
};
*
* @author mazaiting
*/
public class ClassDataItem {
/**
* 静态字段的大小
*/
public int staticFieldsSize;
/**
* 实例化字段的大小
*/
public int instanceFieldsSize;
/**
* 实现方法的大小
*/
public int directMethodsSize;
/**
* 虚拟方法的大小
*/
public int virtualMethodsSize;
/**
* 静态字段数组
*/
public EncodeField[] staticFields;
/**
* 实例化字段数组
*/
public EncodeField[] instanceFields;
/**
* 实现方法数组
*/
public EncodeMethod[] directMethods;
/**
* 虚拟方法的数组
*/
public EncodeMethod[] virtualMethods;
@Override
public String toString(){
return "staticFieldsSize: " + staticFieldsSize + ",instanceFieldsSize: " + instanceFieldsSize
+ ",directMethodsSize:" + directMethodsSize + ",virtualMethodsSize: " + virtualMethodsSize
+ "\n" + getFieldsAndMethods();
}
private String getFieldsAndMethods() {
StringBuilder sb = new StringBuilder();
sb.append("staticFields:\n");
for(int i=0;i<staticFields.length;i++){
sb.append(staticFields[i]+"\n");
}
sb.append("instanceFields:\n");
for(int i=0;i<instanceFields.length;i++){
sb.append(instanceFields[i]+"\n");
}
sb.append("directMethods:\n");
for(int i=0;i<directMethods.length;i++){
sb.append(directMethods[i]+"\n");
}
sb.append("virtualMethods:\n");
for(int i=0;i<virtualMethods.length;i++){
sb.append(virtualMethods[i]+"\n");
}
return sb.toString();
}
}
8). Data Section
/**
* 代码条目
*
* (1) 一个 .dex 文件被分成了 9 个区 ,其中有一个索引区叫做class_defs , 索引了 .dex 里面用到的 class ,以及对这个 class 的描述 。
* (2) class_defs 区 , 里面其实是class_def_item 结构 。这个结构里描述了 LHello; 的各种信息 ,诸如名称 ,superclass , access flag,
* interface 等 。class_def_item 里有一个元素 class_data_off , 指向data 区里的一个 class_data_item 结构 ,
* 用来描述 class 使用到的各种数据 。自此以后的结构都归于 data区了 。
* (3) class_data_item 结构 ,里描述值着 class 里使用到的 static field , instance field , direct_method ,和 virtual_method
* 的数目和描述 。例子 Hello.dex 里 ,只有 2 个 direct_method , 其余的 field 和method 的数目都为 0 。描述 direct_method 的结构叫
* 做 encoded_method ,是用来详细描述某个 method的 。
* (4) encoded_method 结构 ,描述某个 method 的 method 类型 , access flags 和一个指向 code_item的偏移地址 ,里面存放的是该 method
* 的具体实现 。
* (5) code_item ,结构里描述着某个 method 的具体实现 。
*
* struct DexCode {
u2 registersSize;
u2 insSize;
u2 outsSize;
u2 triesSize;
u4 debugInfoOff; /* file offset to debug info stream
u4 insnsSize; /* size of the insns array, in u2 units
u2 insns[1];
/* followed by optional u2 padding
/* followed by try_item[triesSize]
/* followed by uleb128 handlersSize
/* followed by catch_handler_item[handlersSize]
};
*
* @author mazaiting
*
*/
public class CodeItem {
/**
* 本段代码使用到的寄存器数目
*/
public short registersSize;
/**
* method传入参数的数目
*/
public short insSize;
/**
* 本段代码调用其他方法时需要的参数个数
*/
public short outsSize;
/**
* try_item结构的个数
*/
public short triesSize;
/**
* 偏移地址,指向本段代码的debug信息存放位置,是一个debug_info_item结构
*/
public int debugInfoOff;
/**
* 指令列表的大小,以16-bit为单位。insns是instructions的缩写
*/
public int insnsSize;
/**
* insns数组
*/
public short[] insns;
/**tries和handlers用于处理java中的exception*/
@Override
public String toString(){
return "registersSize: " + registersSize + ",insSize: " + insSize + ",outsSize: " + outsSize +
",triesSize: " + triesSize + ",debugInfoOff: " + debugInfoOff + ",insnsSize: " + insnsSize
+ "\ninsns: " + getInsnsStr();
}
private String getInsnsStr(){
StringBuilder sb = new StringBuilder();
if (insns != null && insns.length > 0) {
for(int i=0;i<insns.length;i++){
sb.append(Util.bytesToHexString(Util.short2Byte(insns[i]))+",");
}
}
return sb.toString();
}
}