最近在搞ANDROID平台下的研究,ANDROID平台下最主要的文件格式就是DEX文件格式,这个格式相对于PE文件格式来说还是简单的多。一个DEX文件整体格式如下标所示。此表摘自dalvik虚拟机源代码目录的doc文档翻译而来。我翻译东西一直不怎么样。并且头一次研究Android上面的东西,加上我又不懂JAVA。难免有不正确的地方,本文只是我要学习Android开发的一些笔记,如果不小心有人看到此文的翻译,千万别喷。
以下这张表也是整个DEX文件的格式。首先是一个文件头,在文件头后随后是数据目录。有这样几项数据目录,字符串索引目录,类型索引目录,函数原型索引目录,区域索引目录,函数索引目录,类定义列表,数据段还有静态链接数据段。
名称 | 格式 | 描述 |
---|---|---|
header | header_item | 文件头 |
string_ids | string_id_item[] | 字符串标识符列表。这里标志了当前DEX文件所有字符串使用的标志,也有一些内部名称(例如.,类型描述)或者代码段引用的一些常量对象。 这个表按照字符串常量进行排序,字符串使用UTF-16进行编码(不在本地敏感方式),并且这个表不包含任何复制项。 |
type_ids | type_id_item[] | 类型标识项。这里是此文件中所有类型的标识(类,队列,或者主类型),无论在文件中是否定义。这个表按照字符串标识索引进行排序,它不包含任何复制项。 |
proto_ids | proto_id_item[] | 函数原型标识表。这里定义了此文件中所用引用的原型标识。这个表按照类型标识索引进行排序,并且参数也是通过类型标识索引进行排序。 此表不包含复制项。 |
field_ids | field_id_item[] | 区域标识符列表。这里定义了在此文件中的所有区域定义,此表按照类型标识索引进行主排序,并且按照区域名称作为中排序,按照类型作为子排序。并不包含复制项。 |
method_ids | method_id_item[] | 函数标识表。定义了此文件引用的方法标识。按照类型标识索引作为主排序,按照方法名作为辅排序,按照方法原型作为子排序。不包含复制项。 |
class_defs | class_def_item[] | 类标识表。引用这些类的父类以及接口前必须要经过排序。并且此列表中不能出现重复的类名。 |
data | ubyte[] | 以上表列出的数据,在此进行保存。 |
link_data | ubyte[] | 静态链接数据段。如果此节为空则没有静态链接文件。 |
以下是来自DexFile.h中的结构定义
/* * Direct-mapped "header_item" struct. * DEX文件头结构 */ 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; }; /* * Direct-mapped "string_id_item". * string identifiers list. These are identifiers for all the strings used by this file, * either for internal naming (e.g., type descriptors) or as constant objects referred to by code. * This list must be sorted by string contents, using UTF-16 code point values (not in a locale-sensitive manner), * and it must not contain any duplicate entries. * * 字符串标识符列表。这里标志了当前DEX文件所有字符串使用的标志,也有一些内部名称(例如.,类型描述)或者代码段引用的一些常量对象。 * 这个表按照字符串常量进行排序,字符串使用UTF-16进行编码(不在本地敏感方式),并且这个表不包含任何复制项。 */ struct DexStringId { /* 字符串数据的的文件偏移 */ u4 stringDataOff; /* file offset to string_data_item */ }; /* * Direct-mapped "type_id_item". * type identifiers list. These are identifiers for all types (classes, arrays, or primitive types) referred to by this file, * whether defined in the file or not. This list must be sorted * by string_id index, and it must not contain any duplicate entries. * * 类型标识项。这里是此文件中所有类型的标识(类,队列,或者主类型),无论在文件中是否定义。这个表按照字符串标识索引进行排序,它不包含任何复制项。 */ struct DexTypeId { /* 在字符串列表的类型描述的索引 */ u4 descriptorIdx; /* index into stringIds list for type descriptor */ }; /* * Direct-mapped "field_id_item". * field identifiers list. These are identifiers for all fields referred to by this file, whether defined in the file or not. * This list must be sorted, where the defining type (by type_id index) is the major order, field name (by string_id index) is the intermediate order, * and type (by type_id index) is the minor order. The list must not contain any duplicate entries. * * 区域标识符列表。这里定义了在此文件中的所有区域定义,此表按照类型标识索引进行主排序,并且按照区域名称作为中排序,按照类型作为子排序。并不包含复制项。 */ 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 */ }; /* * Direct-mapped "method_id_item". * method identifiers list. These are identifiers for all methods referred to by this file, whether defined in the file or not. * This list must be sorted, where the defining type (by type_id index) is the major order, method name (by string_id index) is the intermediate order, * and method prototype (by proto_id index) is the minor order. The list must not contain any duplicate entries. * * 函数标识表。定义了此文件引用的方法标识。按照类型标识索引作为主排序,按照方法名作为辅排序,按照方法原型作为子排序。不包含复制项。 */ 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 */ }; /* * Direct-mapped "proto_id_item". * method prototype identifiers list. These are identifiers for all prototypes referred to by this file. * This list must be sorted in return-type (by type_id index) major order, and * then by arguments (also by type_id index). * The list must not contain any duplicate entries. * * 函数原型标识表。这里定义了此文件中所用引用的原型标识。 * 这个表按照类型标识索引进行排序,并且参数也是通过类型标识索引进行排序。 * 此表不包含复制项。 * */ 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 */ }; /* * Direct-mapped "class_def_item". * class definitions list. The classes must be ordered such that a given class's superclass and implemented interfaces appear in the list earlier than the referring class. * Furthermore, it is invalid for a definition for the same-named class to appear more than once in the list. * * 类标识表。引用这些类的父类以及接口前必须要经过排序。并且此列表中不能出现重复的类名。 */ 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 */ };A
在DexFile.h中还有一个"映射目录项"结构,这个结构如果不出我意料的话,是dalvik将以上数据目录加载到内存时所用到的保存结构,现在只是分析文件信息而已,没必要去理会它。到后面分析DEX文件加载时在看它。
在android SDK中提供了一个名为dexdump的工具。可以打印DEX文件的信息。它的源代码在dalvik目录中。这里先熟悉一下它的使用。直接在命令行中输入它的名称会出现使用帮助
-c: 验证DEX文件的校验和
-d : 反汇编代码段
-f : 显示文件头摘要
-h : 显示文件头详细信息
-i : 忽略文件校验
-l : 输出格式,可以是'plain'或者'xml'格式
-m : 打印出寄存器图。(暂时还不知道这个什么玩意,等分析完源代码就清楚了)
-t : 临时文件名称。
随便新建一个android程序。其中工程目录中bin目录下的class.dex就是主dex文件了。
首先使用-c 参数 dexdump -c class.dex,屏幕显示如下:
Processing '/home/devilogic/workspace/tryme/bin/classes.dex'...
Checksum verified
这表示校验和验证通过。
使用 dexdump -d class.dex,这里输出一堆暂时我还看不懂的东西,如果没错的话就是传说中的smali代码。屏幕现实的太多。这里只截取了一部分
Class #0 -
Class descriptor : 'Landroid/support/v4/accessibilityservice/AccessibilitySer
viceInfoCompat$AccessibilityServiceInfoVersionImpl;'
Access flags : 0x0600 (INTERFACE ABSTRACT)
Superclass : 'Ljava/lang/Object;'
Interfaces -
Static fields -
Instance fields -
Direct methods -
Virtual methods -
#0 : (in Landroid/support/v4/accessibilityservice/Accessibility
ServiceInfoCompat$AccessibilityServiceInfoVersionImpl;)
name : 'getCanRetrieveWindowContent'
type : '(Landroid/accessibilityservice/AccessibilityServiceInfo;)
Z'
access : 0x0401 (PUBLIC ABSTRACT)
code : (none)
#1 : (in Landroid/support/v4/accessibilityservice/Accessibility
ServiceInfoCompat$AccessibilityServiceInfoVersionImpl;)
name : 'getDescription'
使用dexdump -f class.dex
显示文件头信息,说实话也非常多。这里仅打印一个文件头结构的内容。
Processing '/home/devilogic/workspace/tryme/bin/classes.dex'...
Opened '/home/devilogic/workspace/tryme/bin/classes.dex', DEX version '035'
DEX file header:
magic : 'dex\n035\0'
checksum : 7483dd9e
signature : ef45...c10f
file_size : 448704
header_size : 112
link_size : 0
link_off : 0 (0x000000)
string_ids_size : 4048
string_ids_off : 112 (0x000070)
type_ids_size : 572
type_ids_off : 16304 (0x003fb0)
field_ids_size : 837
field_ids_off : 27940 (0x006d24)
method_ids_size : 3561
method_ids_off : 34636 (0x00874c)
class_defs_size : 327
class_defs_off : 63124 (0x00f694)
data_size : 374848
data_off : 73856 (0x012080)
使用dexdump -h class.dex
显示部分如下:
Processing '/home/devilogic/workspace/tryme/bin/classes.dex'...
Opened '/home/devilogic/workspace/tryme/bin/classes.dex', DEX version '035'
Class #0 header:
class_idx : 64
access_flags : 1536 (0x0600)
superclass_idx : 508
interfaces_off : 0 (0x000000)
source_file_idx : 301
annotations_off : 442888 (0x06c208)
class_data_off : 82444 (0x01420c)
static_fields_size : 0
instance_fields_size: 0
direct_methods_size : 0
virtual_methods_size: 5
Class #0 -
Class descriptor : 'Landroid/support/v4/accessibilityservice/AccessibilitySer
viceInfoCompat$AccessibilityServiceInfoVersionImpl;'
Access flags : 0x0600 (INTERFACE ABSTRACT)
Superclass : 'Ljava/lang/Object;'
Interfaces -
Static fields -
Instance fields -
如果没错的话Class #0只的是其中的第一个类,当然还有很多
使用 dexdump -m class.dex,显示如下:
Processing '/home/devilogic/workspace/tryme/bin/classes.dex'...
Opened '/home/devilogic/workspace/tryme/bin/classes.dex', DEX version '035'
No register maps found
从结果看来输出没有找到寄存器图。这玩意慢慢再看是个什么。
其余的i,l,t三个参数都是辅助参数。可以忽略掉。
今天上午对dex就到此了。下午开始对照文档分析dexdump的代码。争取今天晚上把dex文件结构搞明白。