您现在的位置是:首页 > 文章详情

Android 解析DEX文件

日期:2018-06-29点击:488

1. DEX文件简介

1). 基本格式
img_44dda2cbb40f28c643bd38c4a08894d6.png
Android DEX文件格式--非虫大神杰作.png
img_4a9cafdd0e27ebdd976b03e67347dca1.png
dex-file-general-structure.png
  • 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)); } } 
img_9a1f317c4bf53b37ba6b29498df56ec6.png
图1.png
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(); } } 

参考文章

Android逆向之旅---解析编译之后的Dex文件格式

代码下载

原文链接:https://yq.aliyun.com/articles/663287
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章