最近在搞ANDROID平台下的研究,ANDROID平台下最主要的文件格式就是DEX文件格式,这个格式相对于PE文件格式来说还是简单的多。一个DEX文件整体格式如下标所示。此表摘自dalvik虚拟机源代码目录的doc文档翻译而来。我翻译东西一直不怎么样。并且头一次研究Android上面的东西,加上我又不懂JAVA。难免有不正确的地方,本文只是我要学习Android开发的一些笔记,如果不小心有人看到此文的翻译,千万别喷。

以下这张表也是整个DEX文件的格式。首先是一个文件头,在文件头后随后是数据目录。有这样几项数据目录,字符串索引目录,类型索引目录,函数原型索引目录,区域索引目录,函数索引目录,类定义列表,数据段还有静态链接数据段。

名称
格式描述
headerheader_item文件头
string_idsstring_id_item[]字符串标识符列表。这里标志了当前DEX文件所有字符串使用的标志,也有一些内部名称(例如.,类型描述)或者代码段引用的一些常量对象。
这个表按照字符串常量进行排序,字符串使用UTF-16进行编码(不在本地敏感方式),并且这个表不包含任何复制项。
type_idstype_id_item[]类型标识项。这里是此文件中所有类型的标识(类,队列,或者主类型),无论在文件中是否定义。这个表按照字符串标识索引进行排序,它不包含任何复制项。
proto_idsproto_id_item[]函数原型标识表。这里定义了此文件中所用引用的原型标识。这个表按照类型标识索引进行排序,并且参数也是通过类型标识索引进行排序。
此表不包含复制项。
field_idsfield_id_item[]区域标识符列表。这里定义了在此文件中的所有区域定义,此表按照类型标识索引进行主排序,并且按照区域名称作为中排序,按照类型作为子排序。并不包含复制项。
method_idsmethod_id_item[]函数标识表。定义了此文件引用的方法标识。按照类型标识索引作为主排序,按照方法名作为辅排序,按照方法原型作为子排序。不包含复制项。
class_defsclass_def_item[]类标识表。引用这些类的父类以及接口前必须要经过排序。并且此列表中不能出现重复的类名。
dataubyte[]以上表列出的数据,在此进行保存。
link_dataubyte[]静态链接数据段。如果此节为空则没有静态链接文件。

以下是来自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文件结构搞明白。