Dalvik虚拟机【2】——Dex文件格式
2016-07-16 17:25
666 查看
整个文件的布局
文件头
索引区
string_ids
type_ids
proto_ids
field_ids
method_ids
数据区
class_def
class_data_item
code_item
data
附录
测试用的Dex的java代码
参考资料
文件头
记录Dex文件的概览、包含文件大小、校验码以及其他字段的偏移和大小
索引区
记录字符串常量、类型、方法原形、域、方法的信息的索引
数据区
包含类的定义区class_defs,该部分记录类信息;以及数据区data,该部分包含实际信息(字符串、代码)等;link_data是链接数据区,主要和依赖库有关
使用 010 Editor 编辑器加上它官方的Dex模板可以方便、直观查看Dex文件内容.
该区域存储的是一个基址,也就是字符串实际内容在Dex文件的地址(在data区),它被Dalvik虚拟机读取后会转换成如下数据结构
根据偏移地址stringDataOff读取到字符串常量,该常量在内存的数据结构string_data_item为
用010 Editor查看一个测试用的Dex文件的string_ids区域如下
和string_ids不一样,descriptorIdx并不是偏移地址,而是string_ids里的index序号,如descriptorIdx=8,则代表string_ids[8]=”V”,则代表类型为Void。如下图,根据文件头找到typd_ids的基址为A8H,size为7,从A8H第一个4字节为0x0003,即是string_idsp[3],即是”LFoo”,代表class类型Foo
shorty_idx
跟 type_ids 一样,它的值是一个string_ids的index号,最终是一个简短的字符串描述 ,
returnTypeIdx
返回类型,值是type_ids的index序号
parametersOff
参数的基址
如下图
classIdx
该field所属的class,它的值是type_ids的index号
typeIdx
该field的类型,它的值也是type_ids的index号
nameIdx
该field的名称,它的值是string_ids的index号
如下图
classIdx
该方法所属的class,它的值是type_ids的index号
protoIdx
该方法的原形,它的值是proto_ids的index号
nameIdx
该方法的名称,它的值是string_ids的index号
如下图
classIdx
这个类的类型,值为type_ids的index
accessFlags
qccess标志,指示public、private等
superclassIdx
父类的类型,值为type_ids的index
interfacesOff
DexTypeList的偏移地址,该结构代表这个类所拥有的interface,若没有,值为0
sourceFileIdx
源文件名称,值为string_ids的index
annotationsOff
annotations_directory_item的偏移地址,在data区,代表注释,若没有注释,值为0
classDataOff
class_data_item的偏移地址,在data区,代表一个类的详细信息,包含field、method、method所执行的代码等,后面会详细介绍
staticValuesOff
值为偏移地址,指向类的static field
如下图
DexClassDataHeader、DexField、DexMethod数据结构如下
fieldIdx值为索引区的field_ids的index,methodIdx值为索引区的method_ids的index,重点看codeOff,值为偏移地址,指示code_item
insnsSize和insns[1]
insnsSize指示了指令个数,insns[1]就是实际的指令,大小并不是1,而是insnsSize
try_item 和 catch_handler_item
在insns[1]后面可能还存在try_item和catch_handler_item,这2个用于捕获Java的异常,常见的Java代码有try catch
如下图
文件头
索引区
string_ids
type_ids
proto_ids
field_ids
method_ids
数据区
class_def
class_data_item
code_item
data
附录
测试用的Dex的java代码
参考资料
整个文件的布局
整个Dex文件可以分成三部分,文件头、索引区、数据区,如下图所示。文件头
记录Dex文件的概览、包含文件大小、校验码以及其他字段的偏移和大小
索引区
记录字符串常量、类型、方法原形、域、方法的信息的索引
数据区
包含类的定义区class_defs,该部分记录类信息;以及数据区data,该部分包含实际信息(字符串、代码)等;link_data是链接数据区,主要和依赖库有关
使用 010 Editor 编辑器加上它官方的Dex模板可以方便、直观查看Dex文件内容.
文件头
偏移地址 | 字段名字 | 大小(byte) | 说明 |
---|---|---|---|
0 | Magic[8] | 8 | 魔数,用于识别Dex文件,内容为”dex\n035\0” |
8 | checksum | 4 | 文件校验码 |
C | Signature[20] | SHA-1签名 | |
20 | file_size | 4 | Dex文件总长度 |
24 | header_size | 4 | 文件头大小,一般固定为0x70 |
28 | endan_tag | 4 | 大小端标志,标志dex文件格式为小端,一般固定为0x12345678 |
2C | link_size | 4 | 链接段的大小 |
30 | link_off | 4 | 链接段的基址 |
34 | map_off | 4 | map item数据的基址 |
38 | string_ids_size | 4 | 字符串常量列表的个数 |
3C | string_ids_off | 4 | 字符串常量列表的基址 |
40 | type_ids_size | 4 | 类型的个数 |
44 | type_ids_off | 4 | 类型的基址 |
48 | proto_ids_size | 4 | 方法原形的个数 |
4C | proto_ids_off | 4 | 方法原形的基址 |
50 | field_ids_size | 4 | 域的个数 |
54 | field_ids_off | 4 | 域的基址 |
58 | method_ids_size | 4 | 方法的个数 |
5C | method_ids_off | 4 | 方法的基址 |
60 | class_defs_size | 4 | class_def的个数 |
64 | class_defs_off | 4 | class_def的基址 |
68 | data_size | 4 | 数据段的大小 |
6C | data_off | 4 | 数据段的基址 |
索引区
string_ids
这区域存储了字符串常量的索引信息。字符串常量并不仅仅是代码中定义的字符串,还包括所有的类名、方法名、类型名等信息.该区域存储的是一个基址,也就是字符串实际内容在Dex文件的地址(在data区),它被Dalvik虚拟机读取后会转换成如下数据结构
// /android4.0.4/dalvik/libdex/DexClass.h /* * Direct-mapped "string_id_item". */ struct DexStringId { u4 stringDataOff; /* file offset to string_data_item */ };
根据偏移地址stringDataOff读取到字符串常量,该常量在内存的数据结构string_data_item为
struct string_data_item { uleb128 utf16_size; //字符串长度 ubyte data; //字符串 }
用010 Editor查看一个测试用的Dex文件的string_ids区域如下
type_ids
这区域存储了dex 文件里的所有数据类型的索引信息,包括class类型,数组类型(array types)、基本类型(primitive types) 。其数据结构为/* * Direct-mapped "type_id_item". */ struct DexTypeId { u4 descriptorIdx; /* index into stringIds list for type descriptor */ };
和string_ids不一样,descriptorIdx并不是偏移地址,而是string_ids里的index序号,如descriptorIdx=8,则代表string_ids[8]=”V”,则代表类型为Void。如下图,根据文件头找到typd_ids的基址为A8H,size为7,从A8H第一个4字节为0x0003,即是string_idsp[3],即是”LFoo”,代表class类型Foo
proto_ids
proto是方法原形,包含方法的输入参数和输出参数,每个大小为12字节。其数据结构为/* * Direct-mapped "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 */ };
shorty_idx
跟 type_ids 一样,它的值是一个string_ids的index号,最终是一个简短的字符串描述 ,
returnTypeIdx
返回类型,值是type_ids的index序号
parametersOff
参数的基址
如下图
field_ids
这个区域里有dex文件引用的所有field(就是类的属性,包括静态的)索引,每个大小为12字节。其数据结构为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 */ };
classIdx
该field所属的class,它的值是type_ids的index号
typeIdx
该field的类型,它的值也是type_ids的index号
nameIdx
该field的名称,它的值是string_ids的index号
如下图
method_ids
这个区域有dex文件所有方法的索引,格式和field_ids类似,每个大小为12字节。其数据结构为/* * Direct-mapped "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 */ };
classIdx
该方法所属的class,它的值是type_ids的index号
protoIdx
该方法的原形,它的值是proto_ids的index号
nameIdx
该方法的名称,它的值是string_ids的index号
如下图
数据区
class_def
存储着Dex文件每一个类的相关信息,每个大小为32字节,其数据结构为/* * Direct-mapped "class_def_item". */ 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 */ };
classIdx
这个类的类型,值为type_ids的index
accessFlags
qccess标志,指示public、private等
superclassIdx
父类的类型,值为type_ids的index
interfacesOff
DexTypeList的偏移地址,该结构代表这个类所拥有的interface,若没有,值为0
sourceFileIdx
源文件名称,值为string_ids的index
annotationsOff
annotations_directory_item的偏移地址,在data区,代表注释,若没有注释,值为0
classDataOff
class_data_item的偏移地址,在data区,代表一个类的详细信息,包含field、method、method所执行的代码等,后面会详细介绍
staticValuesOff
值为偏移地址,指向类的static field
如下图
class_data_item
DexClassDef的classDataOff字段指示了class_data_item在Dex文件中的地址,读入内存后的数据结构为DexClassData,在DexClass.h文件中/* expanded form of class_data_item. Note: If a particular item is * absent (e.g., no static fields), then the corresponding pointer * is set to NULL. */ struct DexClassData { DexClassDataHeader header;//记录staticFields、instanceFields、directMethods、virtualMethods的size DexField* staticFields;//类的static域 DexField* instanceFields;//类的实例域 DexMethod* directMethods;//类的方法 DexMethod* virtualMethods;//类的virtual方法 };
DexClassDataHeader、DexField、DexMethod数据结构如下
/* expanded form of a class_data_item header */ struct DexClassDataHeader { u4 staticFieldsSize; u4 instanceFieldsSize; u4 directMethodsSize; u4 virtualMethodsSize; }; /* expanded form of encoded_field */ struct DexField { u4 fieldIdx; /* index to a field_id_item */ u4 accessFlags; }; /* expanded form of encoded_method */ struct DexMethod { u4 methodIdx; /* index to a method_id_item */ u4 accessFlags; u4 codeOff; /* file offset to a code_item */ };
fieldIdx值为索引区的field_ids的index,methodIdx值为索引区的method_ids的index,重点看codeOff,值为偏移地址,指示code_item
code_item
code_item记录类的方法的运行时相关信息,其数据结构在DexFile.h中声明:struct DexCode { u2 registersSize;//寄存器的个数 u2 insSize;//输入参数的个数 u2 outsSize;//本段代码调用其他方法需要的参数 u2 triesSize;//try item结构的个数, u4 debugInfoOff; /* file offset to debug info stream */ u4 insnsSize; /* 指令列表的大小,以16-bit为单位 */ u2 insns[1];//指令(就是字节码) /* followed by optional u2 padding */ /* followed by try_item[triesSize] */ /* followed by uleb128 handlersSize */ /* followed by catch_handler_item[handlersSize] */ };
insnsSize和insns[1]
insnsSize指示了指令个数,insns[1]就是实际的指令,大小并不是1,而是insnsSize
try_item 和 catch_handler_item
在insns[1]后面可能还存在try_item和catch_handler_item,这2个用于捕获Java的异常,常见的Java代码有try catch
如下图
data
存储着字符串常量、字节码、map_item等实际内容附录
测试用的Dex的java代码
Foo.java:class Foo { public static void main(String[] args) { System.out.println("Hello, world"); } }
参考资料
http://bbs.pediy.com/showthread.php?t=184761相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories