dex文件格式
dex文件中的數(shù)據(jù)結(jié)構(gòu)
類(lèi)型 | 含義 |
---|---|
u1 | 等同uint8_t,表示1字節(jié)的無(wú)符號(hào)數(shù) |
u2 | 等同于uint16_t,表示2字節(jié)的無(wú)符號(hào)數(shù) |
u4 | 等同于uint32_t,表示4字節(jié)的無(wú)符號(hào)數(shù) |
u8 | 等同于uint64_t黎棠,表示8字節(jié)的無(wú)符號(hào)數(shù) |
sleb128 | 有符號(hào)LEB128,可變長(zhǎng)度1~5字節(jié) |
uleb128 | 無(wú)符號(hào)LEB128镰绎,可變長(zhǎng)度1~5字節(jié) |
uleb128p1 | 無(wú)符號(hào)LEB128值加1脓斩,可變長(zhǎng)度1~5字節(jié) |
每個(gè)LB125由1~5個(gè)字節(jié)組成,所有的字節(jié)組合在一起表示一個(gè)32位的數(shù)據(jù)畴栖。每個(gè)字節(jié)只有7位有效位随静,如果第一個(gè)字節(jié)的最高位為1,表示LEB128需要使用到第2個(gè)字節(jié),如果LEB第二個(gè)字節(jié)的最高位為1燎猛,表示會(huì)使用到第3個(gè)字節(jié)恋捆,以此類(lèi)推。LEB128最多使用5個(gè)字節(jié)重绷,如果讀取5個(gè)字節(jié)后下一個(gè)字節(jié)最高位仍為1沸停,則表示該dex文件無(wú)效。
以字符序列“c0 83 92 25”為例论寨,計(jì)算它的uleb128的值:
第1個(gè)字節(jié)0xc0 > 0x7c 星立,表示會(huì)用到第2個(gè)字節(jié)。result = 0x0c & 0x7f
第2個(gè)字節(jié)0x83 > 0x7c葬凳,表示會(huì)用到第3個(gè)字節(jié)。result = result + 0x83 & 0x7f << 7
第3個(gè)字節(jié)0x92 > 0x7c室奏,表示會(huì)用到第4個(gè)字節(jié)火焰。result = result + 0x92 & 0x7c << 14
第4個(gè)字節(jié)0x25 < 0x7c,表示到了結(jié)尾胧沫。result = result + 0x25 & 0x7c << 21
計(jì)算結(jié)果為0x40 + 0x180 + 0x48000 +0x4a00000 = 0x4a481c0
以字符序列“d1 c2 b3 40”為例昌简,計(jì)算它的sleb128值。
result = 0xd1 & 0x7f
result=result + 0xc2 & 0x7f << 7
result=result + b3 & 0x7f << 14
result=(result + 40 & 0x7f <<21)<<4)>>4
dex文件整體結(jié)構(gòu)
dex文件由7個(gè)部分組成绒怨。dex header為dex文件頭纯赎,它指定了dex文件的一些屬性,并記錄了其他6部分?jǐn)?shù)據(jù)結(jié)構(gòu)在dex文件中的物理偏移南蹂。string_ids
和 class_def
結(jié)構(gòu)可以裂解為索引結(jié)構(gòu)區(qū)犬金,真實(shí)的數(shù)據(jù)存放在data中。最后的link_data
為靜態(tài)鏈接數(shù)據(jù)區(qū)六剥,對(duì)于目前生成的dex文件而言晚顷,它始終為空。
dex head |
---|
string_ids |
type_ids |
proto_ids |
field_ids |
method_ids |
class_def |
data |
link_data |
未經(jīng)過(guò)優(yōu)化的dex文件結(jié)構(gòu)表示如下:
struct DexFile {
DexHeader header;
DexStringId StringIds[StringIdsSize];
DexTypeId TypeIds[typeIdsSize];
DexProtoId ProtoIds[protoIdsSize];
DexFieldId FieldIds[fieldIdsSize];
DexMethodId MethodIds[methodIdsSize];
DexClassDef Data[];
DexLink LinkData;
}
DexFile結(jié)構(gòu)的聲明在Android系統(tǒng)源碼dalvik\libdex\DexFile.h文件中疗疟。
dex文件頭
字段名稱(chēng) | 偏移量字節(jié) | 長(zhǎng)度(byte) | 字段描述 |
---|---|---|---|
magic[8] | 0x0 | 0x8 | dex版本標(biāo)識(shí) |
checksum | 0x8 | 0x4 | alder32算法, 去除了magic和checksum 字段之外的所有內(nèi)容的校驗(yàn)碼 |
signature | 0xc | 0x14 | sha-1簽名, 去除了magic该默、checksum和 signature字段之外的所有內(nèi)容的簽名 |
fileSize | 0x20 | 0x4 | 整個(gè)dex的文件大小 |
headerSize | 0x24 | 0x4 | 整個(gè)dex文件頭的大小 (固定大小為0x70) |
endianTag | 0x28 | 0x4 | 字節(jié)序 (大尾方式、小尾方式) 默認(rèn)為小尾方式 <--> 0x12345678 |
linkSize | 0x2c | 0x4 | 鏈接段的大小, 默認(rèn)為0表示靜態(tài)鏈接 |
linkOff | 0x30 | 0x4 | 鏈接段開(kāi)始偏移 |
mapOff | 0x34 | 0x4 | DexMapList的文件偏移 |
stringIdsSize | 0x38 | 0x4 | DexStringId個(gè)數(shù) |
stringIdsOff | 0x3c | 0x4 | DexStringId偏移 |
typeIdsSize | 0x40 | 0x4 | DexTypeId個(gè)數(shù) |
typeIdsOff | 0x44 | 0x4 | DexTypeId偏移 |
protoIdsSize | 0x48 | 0x4 | DexProtoId個(gè)數(shù) |
protoIdsOff | 0x4c | 0x4 | DexProtoId偏移 |
fieldIdsSize | 0x50 | 0x4 | DexFieldId個(gè)數(shù) |
fieldIdsOff | 0x54 | 0x4 | DexFieldId偏移 |
methodIdsSize | 0x58 | 0x4 | DexMethodId個(gè)數(shù) |
methodIdsOff | 0x5c | 0x4 | DexMethodId偏移 |
classDefsSize | 0x60 | 0x4 | DexClassDef個(gè)數(shù) |
classDefsOff | 0x64 | 0x4 | DexClassDef偏移 |
dataSize | 0x68 | 0x4 | 數(shù)據(jù)段大小 |
dataOff | 0x6c | 0x4 | 數(shù)據(jù)段偏移 |
DexHeader結(jié)構(gòu)下面的數(shù)據(jù)為“索引結(jié)構(gòu)區(qū)”與“數(shù)據(jù)區(qū)”策彤。
“索引結(jié)構(gòu)區(qū)”中各數(shù)據(jù)結(jié)構(gòu)的偏移地址都是從DexHeader結(jié)構(gòu)的stringIdsOff~classDefsOff字段的值指定的栓袖,它們并非真正的類(lèi)數(shù)據(jù),而是指向dex文件的data數(shù)據(jù)區(qū)的偏移或數(shù)據(jù)結(jié)構(gòu)索引店诗。
DexHeader結(jié)構(gòu)聲明如下:
struct DexHeader {
u1 magic[8];
u4 checksum;
u1 signature[kSHA1DigestLen];
u4 fileSize;
u4 headerSize;
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;
};
dex文件結(jié)構(gòu)分析
DexMapItem
Dalvik虛擬機(jī)解析dex文件的內(nèi)容裹刮,最終將其映射成DexMapList數(shù)據(jù)結(jié)構(gòu),DexHeader結(jié)構(gòu)的mapOff字段指明了DexMapList結(jié)構(gòu)在dex文件中的偏移必搞,它的聲明如下必指。
struct DexMapList {
u4 size; /* DexMapItem的個(gè)數(shù) */
DexMapItem list[1]; /* DexMapItem的結(jié)構(gòu) */
};
size字段表示接下來(lái)有多少個(gè)DexMapItem結(jié)構(gòu),DexMapItem的結(jié)構(gòu)聲明如下恕洲。
struct DexMapItem {
u2 type; /* kDexType開(kāi)頭的類(lèi)型 */
u2 unused; /*未使用塔橡,用于字節(jié)對(duì)齊 */
u4 size; /* 指定類(lèi)型的個(gè)數(shù) */
u4 offset; /* 指定類(lèi)型的文件偏移 */
};
type字段為一個(gè)枚舉常量梅割,如下所示,通過(guò)類(lèi)型名稱(chēng)很容易判斷它的具體類(lèi)型葛家。
enum {
kDexTypeHeaderItem = 0x0000,
kDexTypeStringIdItem = 0x0001,
kDexTypeTypeIdItem = 0x0002,
kDexTypeProtoIdItem = 0x0003,
kDexTypeFieldIdItem = 0x0004,
kDexTypeMethodIdItem = 0x0005,
kDexTypeClassDefItem = 0x0006,
kDexTypeMapList = 0x1000,
kDexTypeTypeList = 0x1001,
kDexTypeAnnotationSetRefList = 0x1002,
kDexTypeAnnotationSetItem = 0x1003,
kDexTypeClassDataItem = 0x2000,
kDexTypeCodeItem = 0x2001,
kDexTypeStringDataItem = 0x2002,
kDexTypeDebugInfoItem = 0x2003,
kDexTypeAnnotationItem = 0x2004,
kDexTypeEncodedArrayItem = 0x2005,
kDexTypeAnnotationsDirectoryItem = 0x2006,
};
以hello.dex為例户辞,用winhex打開(kāi)文件。
在偏移0x34處找到mapOff癞谒,即MapList的偏移:0x0290
讀取0x290處的一個(gè)雙字值為0x0d底燎,表明接下來(lái)會(huì)有13個(gè)DexMapItem。(此處占四個(gè)字節(jié)弹砚?)
根據(jù)前文中提到的DexMapItem結(jié)構(gòu)双仍,一個(gè)DexMapItem占12個(gè)字節(jié),1-2個(gè)字節(jié)表示類(lèi)別桌吃,5-8個(gè)字節(jié)表示個(gè)數(shù)朱沃,9-12個(gè)字節(jié)表示偏移量。
所有DexMapItem如下表:
類(lèi)型 | 個(gè)數(shù) | 偏移 |
---|---|---|
kDexTypeHeaderItem | 0x1 | 0x0 |
kDexTypeStringIdItem | 0x10 | 0x70 |
kDexTypeTypeIdItem | 0x7 | 0xb0 |
kDexTypeProtoIdItem | 0x4 | 0xcc |
kDexTypeFieldIdItem | 0x1 | 0xfc |
kDexTypeMethodIdItem | 0x5 | 0x104 |
kDexTypeClassDefItem | 0x1 | 0x12c |
kDexTypeCodeItem | 0x3 | 0x1b4 |
kDexTypeTypeList | 0x3 | 0x1ca |
kDexTypeStringDataItem | 0x10 | 0x16c |
kDexTypeDebugInfoItem | 0x3 | 0x267 |
kDexTypeClassDataItem | 0x1 | 0x27b |
kDexTypeMapList | 0x1 | 0x290 |
kDexTypeHeaderItem描述了DexHeader部分茅诱,它占用了文件0x70個(gè)字節(jié)的空間逗物。而接下來(lái)的kDexTypeStringIdItem~kDexTypeClassDataItem與DexHeader當(dāng)中對(duì)應(yīng)的類(lèi)型個(gè)數(shù)字段的值是相同的。
kDexTypeStringIdItem
比如kDexTypeStringIdItem對(duì)應(yīng)了DexHeader的stringIdsSize與stringIdsOff字段瑟俭,表明了在0
x70偏移處翎卓,有0x10個(gè)DexStringId對(duì)象。DexStringId結(jié)構(gòu)的聲明如下摆寄。
struct DexStringId {
u4 stringDataOff; /* 字符串?dāng)?shù)據(jù)偏移 */
};
DexStringId結(jié)構(gòu)只有一個(gè)stringDataOff字段失暴,直接指向字符串?dāng)?shù)據(jù),從0x70開(kāi)始椭迎,有0x10個(gè)DexStringId對(duì)象锐帜,一個(gè)對(duì)象是4個(gè)字節(jié)代表字符串偏移。一共16個(gè)字段如下:
序號(hào) | 偏移 | 字符串 |
---|---|---|
0x0 | 0x1ca | <init> |
0x1 | 0x1d2 | Hello.java |
0x2 | 0x1de | I |
0x3 | 0x1e1 | III |
0x4 | 0x1e6 | LHello; |
0x5 | 0x1ef | Ljava/io/PrintStream; |
0x6 | 0x206 | Ljava/lang/Object; |
0x7 | 0x21a | Ljava/lang/System; |
0x8 | 0x22e | V |
0x9 | 0x231 | VI |
0xa | 0x235 | VL |
0xb | 0x392 | [Ljava/lang/String; |
0xc | 0x24e | foo |
0xd | 0x253 | main |
0xe | 0x259 | out |
0xf | 0x25e | println |
上表中的字符串并非普通的ascii字符串畜号,他們是有MUTF-8編碼表示的缴阎。MUTF-8含義為Modified UTF-8。
kDexTypeTypeIdItem
他對(duì)應(yīng)DexHeader中的typeIdsSize和typeIdsOff字段简软,指向的結(jié)構(gòu)體為:
struct DexTypeId {
u4 descriptorIdx; /* 指向DexStringId列表的索引 */
};
descriptorIdx
是指向DexStringId列表的索引 蛮拔,他對(duì)應(yīng)的字符串代表具體類(lèi)的類(lèi)型,我們根據(jù)上面字段可知:從0xb0起有0x7個(gè)DexTypeId結(jié)構(gòu):
類(lèi)型索引 | 字符串索引 | 字符串 |
---|---|---|
0 | 0x2 | I |
1 | 0x4 | LHello; |
2 | 0x5 | Ljava/io/PrintStream; |
3 | 0x6 | Ljava/lang/Object; |
4 | 0x7 | Ljava/lang/System; |
5 | 0x8 | V |
6 | 0xb | [Ljava/lang/String; |
kDexTypeProtoIdItem
對(duì)應(yīng)DexHeader中的protoIdsSize與protoIdsOff字段痹升,聲明如下:
struct DexProtoId {
u4 shortyIdx; /* 指向DexStringId列表的索引 */
u4 returnTypeIdx; /* 指向DexTypeId列表的索引 */
u4 parametersOff; /* 指向DexTypeList的偏移 */
};
他是一個(gè)方法的聲明結(jié)構(gòu)體建炫,shortyIdx為方法聲明字符串,returnTypeIdx為方法返回類(lèi)型字符串疼蛾,parametersOff指向一個(gè)DexTypeList的結(jié)構(gòu)體存放了方法的參數(shù)列表 肛跌。DexTypeList聲明如下:
struct DexTypeList{
u4 size; /* 接下來(lái)DexTypeItem的個(gè)數(shù) */
DexTypeItem list[1]; /* DexTypeItem結(jié)構(gòu) */
};
DexTypeItem聲明如下:
struct DexTypeItem {
u2 typeIdx; /* 指向DexTypeId列表的索引 */
};
從0xcc開(kāi)始,有4個(gè)DexProtoId,如表:
DexProtoId結(jié)構(gòu)表
索引 | 方法聲明 | 返回類(lèi)型 | 參數(shù)列表 |
---|---|---|---|
0 | III |
I |
2個(gè)參數(shù)I 衍慎、I
|
1 | V |
V |
無(wú)參數(shù) |
2 | VI |
V |
1個(gè)參數(shù)I
|
3 | VL |
V |
1個(gè)參數(shù)[Ljava/lang/String;
|
通過(guò)上表可以發(fā)現(xiàn)转唉,方法聲明由返回類(lèi)型與參數(shù)列表組成,并且返回類(lèi)型位于參數(shù)列表的前面稳捆。
kDexTypeFieldIdItem
對(duì)應(yīng)DexHeader中的fieldIdsSize和fieldIdsOff字段赠法,指向DexFieldId結(jié)構(gòu)體 :
struct DexFieldId {
u2 classIdx; /* 類(lèi)的類(lèi)型,指向DexTypeId列表索引 */
u2 typeIdx; /* 字段類(lèi)型乔夯,指向DexTypeId列表索引 */
u4 nameIdx; /* 字段名砖织,指向DexStringId列表 */
};
DexFieldId結(jié)構(gòu)體中的數(shù)據(jù)全部是索引值,指明了字段所在的類(lèi)末荐,字段的類(lèi)型以及字段名侧纯,從0xfc開(kāi)始共有1個(gè)DexFieldId結(jié)構(gòu)。結(jié)果如圖:
類(lèi)類(lèi)型 | 字段類(lèi)型 | 字段名 |
---|---|---|
Ljava/lang/System; |
Ljava/io/PrintStream; |
out |
kDexTypeMethodIdItem
它對(duì)應(yīng)DexHeader中的methodIdsSize與methodIdsOff字段鞠评,指向的結(jié)構(gòu)體DexMethodId
struct DexMethodId {
u2 classIdx; /* 類(lèi)的類(lèi)型茂蚓,指向DexTypeId列表的索引 */
u2 protoIdx; /* 聲明類(lèi)型,指向DexProtoId列表索引 */
u4 nameIdx; /* 方法名剃幌,指向DexStringId列表的索引 */
};
數(shù)據(jù)也是索引,指明了方法所在的類(lèi)晾浴,方法聲明和方法名负乡。從0x104有0x5個(gè)kDexTypeMethodIdItem
類(lèi)類(lèi)型 | 方法聲明 | 方法名 |
---|---|---|
LHello |
V |
<init> |
LHello |
III |
foo |
LHello |
VL |
main |
Ljava/io/PrintStream; |
VI |
println |
Ljava/lang/Object; |
V |
<init> |
kDexTypeClassDefItem
對(duì)應(yīng)DexHeader中的classDefsSize和classDefsOff字段,指向結(jié)構(gòu)體DexClassDef
struct DexClassDef {
u4 classIdx; /* 類(lèi)的類(lèi)型脊凰,指向DexTypeId列表索引 */
u4 accessFlags; /* 訪問(wèn)標(biāo)志 */
u4 superclassIdx; /* 父類(lèi)的類(lèi)型抖棘,指向DexTypeId列表的索引 */
u4 interfacesOff; /* 實(shí)現(xiàn)了哪些接口,指向DexTypeList結(jié)構(gòu)的偏移 */
u4 sourceFileIdx; /* 源文件名狸涌,指向DexStringId列表的索引 */
u4 annotationsOff; /* 注解切省,指向DexAnnotationsDirectoryItem結(jié)構(gòu)的偏移 */
u4 classDataOff; /* 指向DexClassData結(jié)構(gòu)的偏移 */
u4 staticValuesOff; /* 指向DexEncodedArray結(jié)構(gòu)的偏移 */
};
-
classIdx
:索引值,表明類(lèi)的類(lèi)型 -
accessFlags
:類(lèi)的訪問(wèn)標(biāo)志帕胆,它是以ACC_
開(kāi)頭的一個(gè)枚舉值 -
superclassIdx
:父類(lèi)類(lèi)型索引值朝捆, -
interfacesOff
:如果類(lèi)中含有接口聲明或?qū)崿F(xiàn),字段會(huì)指向1個(gè)DexTypeList結(jié)構(gòu)懒豹,否則這里為0 -
sourceFileIdx
:字符串索引值芙盘,表示類(lèi)所在的源文件名稱(chēng) -
annotationsOff
:指向注解目錄結(jié)構(gòu) -
classDataOff
:指向DexClassData結(jié)構(gòu),這是數(shù)據(jù)部分 -
staticValuesOff
:指向DexEncodedArray結(jié)構(gòu)脸秽,記錄了類(lèi)中的靜態(tài)數(shù)據(jù)
DexClassData
struct DexClassData {
DexClassDataHeader header; //指向DexClassDataHeader儒老,字段和方法個(gè)數(shù)
DexField* staticFields; //靜態(tài)字段
DexField* instanceFields; //實(shí)例字段
DexMethod* directMethods; //直接方法
DexMethod* virtualMethods;//虛方法
};
DexClassDataHeader
記錄了當(dāng)前類(lèi)中的字段和方法的數(shù)目,聲明如下:
struct DexClassDataHeader {
u4 staticFieldsSize; //靜態(tài)字段個(gè)數(shù)
u4 instanceFieldsSize;//實(shí)例字段個(gè)數(shù)
u4 directMethodsSize;//直接方法個(gè)數(shù)
u4 virtualMethodsSize;//虛方法個(gè)數(shù)
};
DexField
描述了字段類(lèi)型與訪問(wèn)標(biāo)志记餐,聲明如下:
struct DexField {
u4 fieldIdx; /* 指向DexFieldId列表的索引 */
u4 accessFlags; /* 訪問(wèn)標(biāo)志 */
};
DexMethod
struct DexMethod {
u4 methodIdx; /* 指向DexMethodId列表的索引 */
u4 accessFlags; /* 訪問(wèn)標(biāo)志 */
u4 codeOff; /* 指向DexCode結(jié)構(gòu)的偏移 */
};
codeOff字段指向一個(gè)DexCode結(jié)構(gòu)體驮樊,聲明如下:
/dalvik/libdex/DexFile.h
struct DexCode {
u2 registersSize;//使用寄存器個(gè)數(shù)
u2 insSize;//參數(shù)個(gè)數(shù)
u2 outsSize;//調(diào)用其他方法時(shí)使用的寄存器個(gè)數(shù)
u2 triesSize;//try/catch個(gè)數(shù)
u4 debugInfoOff;//指向調(diào)試信息的偏移
u4 insnsSize;//指令集個(gè)數(shù),以2字節(jié)為單位
u2 insns[1];//指令集
/* followed by optional u2 padding */
/* followed by try_item[triesSize] */
/* followed by uleb handlersSize */
/* followed by catch_handler_item[handlersSize] */
};