1.Dex文件结构
参考:http://newandroidbook.com/files/ArtOfDalvik.pdf
通过分析dalvik/libdex/DexFile.h和DexClass.h两个文件的源代码,我们可以提取出Dex的文件结构如下。
1.1 DexFile结构
位置:dalvik/libdex/DexFile.h
虚拟机通过将Dex文件和上面的DexFile结构体进行关联,使实际的类加载函数可以通过该DexFile数据结构对目标Dex文件的全部类数据进行索引并提前以完成类的实际加载工作。
成员变量类型 | 变量名 | 意义 |
const DexOptHeader* | pOptHeader | Odex文件头部(优化后) |
const DexHeader* | pHeader | Dex文件头部 |
const DexStringId* | pStringIds | 指向DexStringId索引区 |
const DexTypeId* | pTypesIds | 指向DexTypeId索引区 |
const DexFieldId* | pFieldIds | 指向DexFieldId索引区 |
const DexMethodId* | pMethodIds | 指向DexMethodId索引区 |
const DexProtoId* | pProtoIds | 指向DexProtoId索引区 |
const DexClassDef* | pClassDef | 指向类定义区 |
const DexLink* | pLinkData | 指向连接数据区 |
const DexClassLookup* | pClassLookup | 指向类索引 |
const u1* | baseAddr | 指向Dex文件在内存映射的首地址 |
未经优化过的Dex文件结构如下:
struct DexFile { DexHeader header; DexStringId StringIds[StringIdsSize]; DexTypeId TypeIds[typeIdsSize]; DexProtoId ProtoIds[protoIdsSize]; DexFieldId FieldIds[fieldIdsSize]; DexMethodId MethodIds[methodIdsSize]; DexClassDef Data[]; DexLink LinkData; }
1.2 DexHeader结构
位置:dalvik/libdex/DexFile.h
signature是20个byte。
1.3 DexMapList结构
位置:dalvik/libdex/DexFile.h
其中DexMapItem结构体中的type字段指向如下枚举常量:
1.4 DexStringId结构
位置:dalvik/libdex/DexFile.h
1.5 DexTypeId结构
位置:dalvik/libdex/DexFile.h
1.6 DexProtoId结构
位置:dalvik/libdex/DexFile.h
1.7 DexFieldId结构
位置:dalvik/libdex/DexFile.h
1.8 DexMethodId结构
位置:dalvik/libdex/DexFile.h
1.9 DexClassDef结构
位置:dalvik/libdex/DexFile.h
其中accessFlags参考如下:
1.10 DexClassData结构
位置:dalvik/libdex/DexClass.h
位置:dalvik/libdex/DexClass.h
位置:dalvik/libdex/DexClass.h
位置:dalvik/libdex/DexClass.h
位置:dalvik/libdex/DexFile.h
DexField结构体中,fieldIdx指向DexFieldId索引表;
DexMethod结构体中,methodIdx指向DexMethodId索引表;
备注:DexClass.h中由于引用了Leb128.h头文件,里面所有的u4类型都是uleb128类型,在计算的时候不能按4个字节计算,要按照uleb128的方法来计算。
计算方法如下:
TODO:补充内容
参考:http://newandroidbook.com/files/ArtOfDalvik.pdf
2 类、方法、成员原理机制
2.1查找class过程
2.2查找method过程
2.3查找field过程
3 实例分析
具体的分析dex文件实例的时候可以参考android源码里dalvik文件夹下的docs文档下的dalvik-bytecode.html和instruction-formats.html两个表来分析。
0x01 准备阶段
生成测试用的Test.dex文件
Test.java源码:
public class Test{ public int a; private long b; public static int c; public int add(int a, int b){ return a + b; } public float minus(float a, float b){ return a - b; } public void print(){ System.out.println("Hello World!"); } }
编译成.class字节码文件:
javac Test.java |
生成dex文件:
dx --dex --output=Test.dex Test.class |
Java源文件生成Dex文件的映射关系:
Dex文件结构如下:
用winhex打开Test.dex,16进制内容如下:
0x02 DexHeader分析
文件位置:/dalvik/libdex/DexFile.h。DexHeader结构如下:
对应的十六进制内容如下:
即:
0x03 DexMapList分析
文件位置:/dalvik/libdex/DexFile.h。结构如下:
根据0x02分析,知道DexMapList的offset为:0x304。跳转到dex文件的0x304位置,可知DexMapList的size=0x0D,即13个DexMapItem。
16进制内容如下:
其中,DexMapItem中的type字段类型参考如下:
文件位置:/dalvik/libdex/DexFile.h。
根据DexMapItem结构,分析出13个DexMapItem内容如下:
序号 |
u2 type |
u2 unused |
u4 size |
u4 offset |
#0 |
0x0000 (kDexTypeHeaderItem) |
0x0 |
0x01 |
0x0 |
#1 |
0x0001 (kDexTypeStringIdItem) |
0x0 |
0x17 |
0x70 |
#2 |
0x0002 (kDexTypeTypeIdItem) |
0x0 |
0x09 |
0xCC |
#3 |
0x0003 (kDexTypeProtoIdItem) |
0x0 |
0x04 |
0xF0 |
#4 |
0x0004 (kDexTypeFieldIdItem) |
0x0 |
0x04 |
0x0120 |
#5 |
0x0005 (kDexTypeMethodIdItem) |
0x0 |
0x06 |
0x0140 |
#6 |
0x0006 (kDexTypeClassDefItem) |
0x0 |
0x01 |
0x0170 |
#7 |
0x2001 (kDexTypeCodeItem) |
0x0 |
0x04 |
0x0190 |
#8 |
0x1001 (kDexTypeTypeList) |
0x0 |
0x03 |
0x01F8 |
#9 |
0x2002 (kDexTypeStringDataItem) |
0x0 |
0x17 |
0x020E |
#10 |
0x2003 (kDexTypeDebugInfoItem) |
0x0 |
0x04 |
0x02CE |
#11 |
0x2000 (kDexTypeClassDataItem) |
0x0 |
0x01 |
0x02E7 |
#12 |
0x1000 (kDexTypeMapList) |
0x0 |
0x01 |
0x0304 |
0x04 DexStringId分析
文件位置:/dalvik/libdex/DexFile.h。结构如下:
根据0x02分析,知道:
string_ids_size = 0x17 = 23个DexStringId结构体
string_ids_off = 0x70
跳转到dex文件的0x70偏移处:
可得到每个字符串的偏移位置:
序号 |
u4 stringDataOff |
#0 |
0x020E |
#1 |
0x0216 |
#2 |
0x0219 |
#3 |
0x021E |
#4 |
0x022C |
#5 |
0x022F |
#6 |
0x0234 |
#7 |
0x0237 |
#8 |
0x023F |
#9 |
0x0256 |
#10 |
0x026A |
#11 |
0x027E |
#12 |
0x0292 |
#13 |
0x029D |
#14 |
0x02A0 |
#15 |
0x02A4 |
#16 |
0x02A7 |
#17 |
0x02AC |
#18 |
0x02AF |
#19 |
0x02B2 |
#20 |
0x02B9 |
#21 |
0x02BE |
#22 |
0x02C5 |
可得DexStringId的stringDataOff字段为:0x020E。
跳转到dex文件的0x020E偏移处:
获取到第一个字符串:06 3C 69 6E 69 74 3E 00。其中,06表示6个字符,最后的00最为结束符不算入内。
由上表stringDataOff列表获得字符串列表如下:
序号 |
offset |
字符个数 |
字符串(十六进制) |
#0 |
0x020E |
0x06 |
3C 69 6E 69 74 3E 00 |
#1 |
0x0216 |
0x01 |
46 00 |
#2 |
0x0219 |
0x03 |
46 46 46 00 |
#3 |
0x021E |
0x0C |
48 65 6C 6C 6F 20 57 6F 72 6C 64 21 00 |
#4 |
0x022C |
0x01 |
49 00 |
#5 |
0x022F |
0x03 |
49 49 49 00 |
#6 |
0x0234 |
0x01 |
4A 00 |
#7 |
0x0237 |
0x06 |
4C 54 65 73 74 3B 00 |
#8 |
0x023F |
0x15 |
4C 6A 61 76 61 2F 69 6F 2F 50 72 69 6E 74 53 74 72 65 61 6D 3B 00 |
#9 |
0x0256 |
0x12 |
4C 6A 61 76 61 2F 6C 61 6E 67 2F 4F 62 6A 65 63 74 3B 00 |
#10 |
0x026A |
0x12 |
4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B 00 |
#11 |
0x027E |
0x12 |
4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 79 73 74 65 6D 3B 00 |
#12 |
0x0292 |
0x09 |
54 65 73 74 2E 6A 61 76 61 00 |
#13 |
0x029D |
0x01 |
56 00 |
#14 |
0x02A0 |
0x02 |
56 4C 00 |
#15 |
0x02A4 |
0x01 |
61 00 |
#16 |
0x02A7 |
0x03 |
61 64 64 00 |
#17 |
0x02AC |
0x01 |
62 00 |
#18 |
0x02AF |
0x01 |
63 00 |
#19 |
0x02B2 |
0x05 |
6D 69 6E 75 73 00 |
#20 |
0x02B9 |
0x03 |
6F 75 74 00 |
#21 |
0x02BE |
0x05 |
70 72 69 6E 74 00 |
#22 |
0x02C5 |
0x07 |
70 72 69 6E 74 6C 6E 00 |
然后对照ASCII表,翻译成字符串如下:
序号 |
u4 stringDataOff |
字符串 |
#0 |
0x020E |
<init> |
#1 |
0x0216 |
F |
#2 |
0x0219 |
FFF |
#3 |
0x021E |
Hello World! |
#4 |
0x022C |
I |
#5 |
0x022F |
III |
#6 |
0x0234 |
J |
#7 |
0x0237 |
LTest; |
#8 |
0x023F |
Ljava/io/PrintStream; |
#9 |
0x0256 |
Ljava/lang/Object; |
#10 |
0x026A |
Ljava/lang/String; |
#11 |
0x027E |
Ljava/lang/System; |
#12 |
0x0292 |
Test.java |
#13 |
0x029D |
V |
#14 |
0x02A0 |
VL |
#15 |
0x02A4 |
a |
#16 |
0x02A7 |
add |
#17 |
0x02AC |
b |
#18 |
0x02AF |
c |
#19 |
0x02B2 |
minus |
#20 |
0x02B9 |
out |
#21 |
0x02BE |
|
#22 |
0x02C5 |
println |
0x05 DexTypeId分析
文件位置:/dalvik/libdex/DexFile.h。结构如下:
DexTypeId结构体中的descriptorIdx字段指向DexStringId索引表。
由0x02分析知:
type_ids_size = 0x09 = 9个DexTypeId结构体
type_ids_off = 0xCC
跳转到dex文件的0xCC偏移处:
分析上图16进制内容可得DexTypeId索引表如下:
序号 |
u4 descriptorIdx |
字符串 |
#0 |
0x01 |
F |
#1 |
0x04 |
I |
#2 |
0x06 |
J |
#3 |
0x07 |
LTest; |
#4 |
0x08 |
Ljava/io/PrintStream; |
#5 |
0x09 |
Ljava/lang/Object; |
#6 |
0x0A |
Ljava/lang/String; |
#7 |
0x0B |
Ljava/lang/System; |
#8 |
0x0D |
V |
0x06 DexProtoId分析
文件位置:/dalvik/libdex/DexFile.h。结构如下:
DexProtoId结构体中,shortyIdx指向DexStringId索引表,returnTypeIdx指向DexTypeId索引表,parameterOff指向DexTypeList的偏移地址。其中,DexTypeList结构如下:
其中DexTypeItem结构体的typeIdx字段指向DexTypeId索引表。
由0x02分析知:
proto_ids_size = 0x4=4个DexProtoId
proto_ids_off = 0xF0
跳转到dex文件的0xF0偏移处:
分析上图16进制内容可得DexProtoId索引表如下:
序号 |
u4 shortyIdx |
u4 returnTypeIdx |
u4 parametersOff |
#0 |
0x02 |
0x00 |
0x01F8 |
#1 |
0x05 |
0x01 |
0x0200 |
#2 |
0x0D |
0x08 |
0x0000 |
#3 |
0x0E |
0x08 |
0x0208 |
接下来根据DexProtoId结构体中的parametersOff分析对应的DexTypeList结构体:
#0
parametersOff = 0x01F8
结合DexTypeList和DexTypeItem结构体和上图分析可知,DexProtoId[0]结构如下:
DexProtoId[0]->shortyIdx = 0x02 #FFF DexProtoId[0]->returnTypeIdx = 0x00 #F DexProtoId[0]->parametersOff = 0x01F8 DexTypeList->size = 0x02 DexTypeList->list[0] = 0x0 #F DexTypeList->list[1] = 0x0 #F |
#1
parametersOff = 0x0200
结合DexTypeList和DexTypeItem结构体和上图分析可知,DexProtoId[1]结构如下:
DexProtoId[1]->shortyIdx = 0x05 #III DexProtoId[1]->returnTypeIdx = 0x01 #I DexProtoId[1]->parametersOff = 0x0200 DexTypeList->size = 0x02 DexTypeList->list[0] = 0x01 #I DexTypeList->list[1] = 0x01 #I |
#2
parametersOff = 0x0000
即无DexTypeList,则DexProtoId[2]结构如下:
DexProtoId[1]->shortyIdx = 0x0D #V DexProtoId[1]->returnTypeIdx = 0x08 #Ljava/io/PrintStream; DexProtoId[1]->parametersOff = 0x0000 |
#3
parametersOff = 0x0208
结合DexTypeList和DexTypeItem结构体和上图分析可知,DexProtoId[3]结构如下:
DexProtoId[1]->shortyIdx = 0x0E #VL DexProtoId[1]->returnTypeIdx = 0x08 #V DexProtoId[1]->parametersOff = 0x0208 DexTypeList->size = 0x01 DexTypeList->list[0] = 0x06 #Ljava/lang/String; |
整理得,DexProtoId索引表如下:
#序号 |
方法声明 |
返回类型 |
参数列表 |
#0 |
FFF |
F |
2个参数,F和F |
#1 |
III |
I |
2个参数,I和I |
#2 |
V |
V |
无参数 |
#3 |
VL |
V |
1个参数,Ljava/lang/String; |
0x07 DexFieldId分析
文件位置:/dalvik/libdex/DexFile.h。结构如下:
DexFieldId结构体中,classIdx和typeIdx指向DexTypeId索引表,nameIdx指向DexStringId索引表。
由0x02分析知:
field_ids_size = 0x4=4个DexFieldId
field_ids_off = 0x120
跳转到dex文件的0x120偏移处:
结合DexFieldId结构体和上图分析得:
#0
DexFieldId[0]分析如下:
DexFieldId[0]->classIdx = 0x03 #Ltest;
DexFieldId[0]->typeIdx = 0x01 #I
DexFieldId[0]->nameIdx =0x0F #a
#1
DexFieldId[1]分析如下:
DexFieldId[1]->classIdx = 0x03 #LTest;
DexFieldId[1]->typeIdx = 0x02 #J
DexFieldId[1]->nameIdx = 0x11 #b
#2
DexFieldId[2]分析如下:
DexFieldId[2]->classIdx = 0x03 #LTest;
DexFieldId[2]->typeIdx = 0x01 #I
DexFieldId[2]->nameIdx = 0x12 #c
#3
DexFieldId[3]分析如下:
DexFieldId[3]->classIdx = 0x07 #Ljava/lang/System;
DexFieldId[3]->typeIdx = 0x04 #Ljava/io/PrintStream;
DexFieldId[3]->nameIdx = 0x14 #out
整理得,DexFieldId索引表如下:
序号 |
类类型 |
方法声明 |
字段名 |
#0 |
LTest; |
I |
a |
#1 |
LTest; |
J |
b |
#2 |
LTest; |
I |
c |
#3 |
Ljava/lang/System; |
Ljava/io/PrintStream; |
out |
0x08 DexMethodId分析
文件位置:/dalvik/libdex/DexFile.h。结构如下:
DexMethodId结构体中,classIdx指向DexTypeId索引表,protoIdx指向DexProtoid索引表,nameIdx指向DexStringId表。
由0x02分析知:
method_ids_size = 0x6=6个DexMethodId
method_ids_off = 0x140
跳转到dex文件的0x140偏移处:
结合DexMethoId结构体和上图分析得:
#0
DexMethoId[0]分析如下:
DexMethoId[0]->classIdx = 0x03 #LTest;
DexMethoId[0]->protoIdx = 0x02 #V, V, ()
DexMethoId[0]->nameIdx = 0x00 #<init>
#1
DexMethoId[1]分析如下:
DexMethoId[1]->classIdx = 0x03 #LTest;
DexMethoId[1]->protoIdx = 0x01 #III, I, (I,I)
DexMethoId[1]->nameIdx = 0x10 #add
#2
DexMethoId[2]分析如下:
DexMethoId[2]->classIdx = 0x03 #LTest;
DexMethoId[2]->protoIdx = 0x00 #FFF, F, (F,F)
DexMethoId[2]->nameIdx = 0x13 #minus
#3
DexMethoId[3]分析如下:
DexMethoId[3]->classIdx = 0x03 #LTest;
DexMethoId[3]->protoIdx = 0x02 #V, V, ()
DexMethoId[3]->nameIdx = 0x15 #print
#4
DexMethoId[4]分析如下:
DexMethoId[4]->classIdx = 0x04 #Ljava/io/PrintStream;
DexMethoId[4]->protoIdx = 0x03 #VL,V, (Ljava/lang/String;)
DexMethoId[4]->nameIdx = 0x16 #println
#5
DexMethoId[5]分析如下:
DexMethoId[5]->classIdx = 0x05 #Ljava/lang/Object;
DexMethoId[5]->protoIdx = 0x02 #V, V, ()
DexMethoId[5]->nameIdx = 0x00 #<init>
整理得,DexMethodId索引表如下:
序号 |
类类型 |
方法声明 |
方法名 |
#0 |
LTest; |
V |
<init> |
#1 |
LTest; |
III |
add |
#2 |
LTest; |
FFF |
minus |
#3 |
LTest; |
V |
|
#4 |
Ljava/io/PrintStream; |
VL |
println |
#5 |
Ljava/lang/Object; |
V |
<init> |
0x09 DexClassDef分析
#0 准备知识
文件位置:/dalvik/libdex/DexFile.h。结构如下:
DexClassDef结构体中,classIdx和superClassIdx指向DexTypeId列表,interfacesOff指向DexTypeList,sourceFileIdx指向DexStringId列表,classDataOff指向DexClassData结构体。
DexClassData结构体中DexClassDataHeader字段结构如下:
DexClassDef结构体中,accessFlags字段值参考下图:
由0x02分析知:
class_defs_size = 0x01=1个DexClassDef
class_defs_off = 0x170
跳转到dex文件的0x170偏移处:
结合DexClassDef结构体和上图分析得:
#0
DexClassDef[0]分析如下:
DexClassDef[0]->classIdx = 0x03 # LTest;
DexClassDef[0]->accessFlags = 0x01 # ACC_PUBLIC
DexClassDef[0]->superclasssIdx = 0x05 #Ljava/lang/Object;
DexClassDef[0]->interfaceOff = 0x00 #
DexClassDef[0]->sourceFileIdIdx = 0x0C #Test.java
DexClassDef[0]->annotationsOff = 0x00
DexClassDef[0]->classDataOff = 0x02E7
DexClassDef[0]->staticValueOff = 0x00
接下来跳转到dex文件的0x02CE偏移处,分析DexClassData结构体:
备注:DexClassData和DexClassDataHeader在/dalvik/libdex/DexClass.h,该文件下的u4类型都是uleb128类型。
uleb128计算方法如下:
所以,DexClassDataHeader解析如下:
staticFieldsSize=0x01
instanceFieldsSize = 0x02
directMethodsSize = 0x01
virtualMethodsSize = 0x03
即3个DexField和4个方法,跟我们最开始的Test.java源码吻合。
DexField和DexMethod结构如下:
DexField结构体中,fieldIdx指向DexFieldId索引表;
DexMethod结构体中,methodIdx指向DexMethodId索引表;
备注:DexField和DexMethod都在dalvik/lilbdex/DexClass.h文件内,其u4类型均属于uleb128类型,不能完全按4字节计算,要按uleb128类型计算。
在继续分析staticField、instanceField、directMethod和virtualMethod之前,需要通过查看源码说明下DexClassDef结构体中classDataOff字段指向的DexClassData结构体是如何解析的。
查看dalvik/dexdump/DexDump.c源码中的processDexFile函数的调用流程图(通过Doxygen生成):
可以知道,DexClassData的解析发生在dexReadAndVerifyClassData函数中。dexReadAndVerifyClassData函数在dalvik/libdex/DexClass.c文件中。通过下面代码的注释,我们知道这段函数代码作用是读取、验证并返回class_data_item,即DexClassData结构体,这部分代码有对DexClassData结构体做解析,所以我们可以看下它是怎么一个解析过程。
首先,通过DexClassData的结构体,我们知道staticFields、instanceFields、directMethods和virtualMethods三个字段是一个DexField指针变量,可能存在0个或多个DexField或DexMethod,其个数由DexClassDataHeader结构体中的成员属性决定。
/* Read, verify, and return an entire class_data_item. This updates * the given data pointer to point past the end of the read data. This * function allocates a single chunk of memory for the result, which * must subsequently be free()d. This function returns NULL if there * was trouble parsing the data. If this function is passed NULL, it * returns an initialized empty DexClassData structure. * * The verification done by this function is of the raw data format * only; it does not verify that access flags, indices, or offsets * are valid. */ DexClassData* dexReadAndVerifyClassData(const u1** pData, const u1* pLimit) { DexClassDataHeader header; u4 lastIndex; if (*pData == NULL) { DexClassData* result = malloc(sizeof(DexClassData)); memset(result, 0, sizeof(*result)); return result; } if (! dexReadAndVerifyClassDataHeader(pData, pLimit, &header)) { return NULL; } //这里的header是DexClassDataHeader结构体 size_t resultSize = sizeof(DexClassData) + (header.staticFieldsSize * sizeof(DexField)) + (header.instanceFieldsSize * sizeof(DexField)) + (header.directMethodsSize * sizeof(DexMethod)) + (header.virtualMethodsSize * sizeof(DexMethod)); DexClassData* result = malloc(resultSize); u1* ptr = ((u1*) result) + sizeof(DexClassData); bool okay = true; u4 i; if (result == NULL) { return NULL; } result->header = header; //获取DexClassDataHeader结构体中staticFieldsSize的值 if (header.staticFieldsSize != 0) { result->staticFields = (DexField*) ptr; ptr += header.staticFieldsSize * sizeof(DexField); } else { result->staticFields = NULL; } //获取DexClassDataHeader结构体中instanceFieldsSize的值 if (header.instanceFieldsSize != 0) { result->instanceFields = (DexField*) ptr; ptr += header.instanceFieldsSize * sizeof(DexField); } else { result->instanceFields = NULL; } //获取DexClassDataHeader结构体中directMethodsSize的值 if (header.directMethodsSize != 0) { result->directMethods = (DexMethod*) ptr; ptr += header.directMethodsSize * sizeof(DexMethod); } else { result->directMethods = NULL; } //获取DexClassDataHeader结构体中virtualMethodsSize的值 if (header.virtualMethodsSize != 0) { result->virtualMethods = (DexMethod*) ptr; } else { result->virtualMethods = NULL; } //从这里开始就是解析DexClassData结构体中staticFields、instanceFields、directMethods和 virtualMethods的DexField和DexMethod成员了。 lastIndex = 0; for (i = 0; okay && (i < header.staticFieldsSize); i++) { okay = dexReadAndVerifyClassDataField(pData, pLimit, &result->staticFields[i], &lastIndex); } lastIndex = 0; for (i = 0; okay && (i < header.instanceFieldsSize); i++) { okay = dexReadAndVerifyClassDataField(pData, pLimit, &result->instanceFields[i], &lastIndex); } lastIndex = 0; for (i = 0; okay && (i < header.directMethodsSize); i++) { okay = dexReadAndVerifyClassDataMethod(pData, pLimit, &result->directMethods[i], &lastIndex); } lastIndex = 0; for (i = 0; okay && (i < header.virtualMethodsSize); i++) { okay = dexReadAndVerifyClassDataMethod(pData, pLimit, &result->virtualMethods[i], &lastIndex); } if (! okay) { free(result); return NULL; } return result; }
根据上面的源码我们知道,DexField和DexMethod的解析分别由dexReadAndVerifyClassDataField和dexReadAndVerifyClassDataMethod完成。下面调到上述两个函数分析:
bool dexReadAndVerifyClassDataField(const u1** pData, const u1* pLimit, DexField* pField, u4* lastIndex) { if (! verifyUlebs(*pData, pLimit, 2)) { return false; } dexReadClassDataField(pData, pField, lastIndex); return true; }
由dexReadAndVerifyClassDataField函数知道,这里调用了dexReadClassDataField函数对DexField做解析。我们跳转到:dalvik/libdex/DexClass.h文件下的dexReadClassDataField函数:
/* Read an encoded_field without verification. This updates the * given data pointer to point past the end of the read data. * * The lastIndex value should be set to 0 before the first field in * a list is read. It is updated as fields are read and used in the * decode process. */ DEX_INLINE void dexReadClassDataField(const u1** pData, DexField* pField, u4* lastIndex) { u4 index = *lastIndex + readUnsignedLeb128(pData); pField->accessFlags = readUnsignedLeb128(pData); pField->fieldIdx = index; *lastIndex = index; }
通过上面的代码我们知道:DexField(存在多个staticField或instanceField时,见下图staticFields和instanceFields数组)的fieldIdx字段的索引值是在上一个staticField或instanceField的基础上叠加的。这个结论很重要,我们后面对DexField的解析会用到这个结论。
而且,从上面的代码知道,DexField结构体的两个字段都是uleb128类型。
下面分析dexReadAndVerifyClassDataMethod函数:
bool dexReadAndVerifyClassDataMethod(const u1** pData, const u1* pLimit, DexMethod* pMethod, u4* lastIndex) { if (! verifyUlebs(*pData, pLimit, 3)) { return false; } dexReadClassDataMethod(pData, pMethod, lastIndex); return true; }
由dexReadAndVerifyClassDataMethod函数知道,这里调用了dexReadClassDataMethod函数对DexMethod做解析。我们跳转到:dalvik/libdex/DexClass.h文件下的dexReadClassDataMethod函数:
/* Read an encoded_method without verification. This updates the * given data pointer to point past the end of the read data. * * The lastIndex value should be set to 0 before the first method in * a list is read. It is updated as fields are read and used in the * decode process. */ DEX_INLINE void dexReadClassDataMethod(const u1** pData, DexMethod* pMethod, u4* lastIndex) { u4 index = *lastIndex + readUnsignedLeb128(pData); pMethod->accessFlags = readUnsignedLeb128(pData); pMethod->codeOff = readUnsignedLeb128(pData); pMethod->methodIdx = index; *lastIndex = index; }
同样的,通过上面的代码我们知道:DexMethod(存在多个directMethods或virtualMethods时,见下图directMethods和virtualMethods数组)的methodIdx字段的索引值是在上一个directMethods或virtualMethods的基础上叠加的。这个结论同样很重要,我们后面对DexMethod的解析会用到这个结论。
好了,准备工作做完了,接下来进入正题,开始解析DexClassData结构体中的staticFields、instanceFields、directMethods和virtualMethods字段,从0x02EB(接着DexClassData结构体中的header字段后面)开始分析:
#1分析staticFields[0]
由于DexField结构体中的u4字段都是uleb128类型,所以:
staticFields[0]->fieldIdx = uleb128(02) = 0x02 #
序号 |
类类型 |
方法声明 |
字段名 |
#2 |
LTest; |
I |
c |
staticFields[0]->accessFlags = uleb128(09) = 0x09 = 0x01 + 0x08 = ACC_PUBLIC | ACC_STATIC
#2分析instanceFields[0]
同理:
instanceFields[0]->fieldIdx = uleb128(00) = 0x00 #
序号 |
类类型 |
方法声明 |
字段名 |
#0 |
LTest; |
I |
a |
instanceFields[0]->accessFlags = uleb128(01) = 0x01 # ACC_PUBLIC
#3分析instanceFields[1]
同理:
instanceFields[1]->fieldIdx = instanceFields[0]->fieldIdx + uleb128(01) = 0x00 + 0x01 = 0x01
备注:这里相加就是用到我们上面dexReadClassDataField函数中分析的结论,即:fieldIdx索引在上一个instanceFields的fieldIdx的基础上叠加。
序号 |
类类型 |
方法声明 |
字段名 |
#1 |
LTest; |
J |
b |
instanceFields[1]->accessFlags = 0x02 # ACC_PRIVATE
#4分析directMethods[0]
由于DexField(在DexClass.h文件中)结构体中的u4类型都是uleb128类型,参考上图readUnsignedLeb128函数计算,所以:
directMethods[0]->methodIdx =uleb128(00) = 0x0 #指向DexMethodId索引表
序号 |
类类型 |
方法声明 |
方法名 |
#0 |
LTest; |
V |
<init> |
directMethods[0]->accessFlags =uleb128( 81 80 04) = 0x10001 # ACC_PUBLIC|ACC_CONSTRUCTOR
directMethods[0]->codeOff = uleb128(90 03) = 0x190 #指向DexCode结构的偏移
其中,uleb128(81 80 04)计算过程如下:
第1个字节0x81 > 0x7f,表示需要用到第二字节。result = 0x81 & 0x7f = 0x01
第2个字节0x80 > 0x7f,表示需要用到第三个字节。result = result | ( (0x80 & 0x7f)<<7) = 0x01
第3个字节0x04 < 0x7f,表示到了结尾。result = result | ((0x04 & 0x7f)<<14) = 0x01 | 0x10000 = 0x10001
其中,uleb128(90 03)计算过程如下:
第1个字节0x90 > 0x7f,表示需要用到第二字节。result = 0x90 & 0x7f = 0x10
第2个字节0x03 < 0x7f,表示到了结尾。result = result | ((0x03 & 0x7f)<<7) = 0x10 | 0x180 = 0x190
DexCode结构体如下:
备注:DexCode结构体在dalvik/libdex/DexFile.h中,u4类型不是uleb128类型
跳转到dex文件0x190偏移处:
解析DexCode:
DexCode->registersSize = 0x01
DexCode->insSize = 0x01
DexCode->outsSize = 0x01
DexCode->triesSize = 0x00
DexCode->debugInfoOff = 0x02CE
DexCode->insnsSize = 0x04 = 4个insns指令
DexCode->insns = 7010 0500 0000 0E00
分析DexCode->insns字段:
查看dalvik-bytecode.html文件,70的Opcode为invoke-direct,格式为35C
查看instruction-formats.html文件,35c的指令格式为:
即:B|A|op CCCC G|F|E|D,由于B=1,A=0,所以采用代码:[B=1] op {vD}, kind@CCCC。所以:
B |
A |
CCCC |
G |
F |
E |
D |
1 |
0 |
0005 |
0 |
0 |
0 |
0 |
即:[B=1] op {v0}, method@0005,0e00查dalvik-bytecode表得到:return-void。
其中CCCC指向DexMethod的索引。
序号 |
类类型 |
方法声明 |
方法名 |
#5 |
Ljava/lang/Object; |
V |
<init> |
解码后得到如下代码段:
7010 0500 0000 invoke-redirect {v0}, method@0005 //Ljava/lang/Object;.<init>:()V 0e00 return-void |
#5分析virtualMethods[0]
接着上一个DexMethod后面0x2F7继续分析:
virtualMethods[0]->methodIdx =uleb128(01) = 0x1 #指向DexMethodId索引表
序号 |
类类型 |
方法声明 |
方法名 |
#1 |
LTest; |
III |
add |
virtualMethods[0]->accessFlags =uleb128( 01) = 0x1 # ACC_PUBLIC
virtualMethods[0]->codeOff = uleb128(A8 03) = 0x1A8 #指向DexCode结构的偏移,刚好是接着上一个DexCode
其中,uleb128(A8 03)计算过程如下:
第1个字节0xA8 > 0x7f,表示需要用到第二字节。result = 0xA8 & 0x7f = 0x28
第2个字节0x03 < 0x7f,表示到了结尾。result = result | ((0x03 & 0x7f)<<7) = 0x28 | 0x180 = 0x1A8
DexCode结构体如下:
跳转到dex文件0x1A8偏移处:
解析DexCode:
DexCode->registersSize = 0x04
DexCode->insSize = 0x03
DexCode->outsSize = 0x00
DexCode->triesSize = 0x00
DexCode->debugInfoOff = 0x02D3
DexCode->insnsSize = 0x03 = 3个insns指令
DexCode->insns = 9000 0203 0F00
分析DexCode->insns字段
查看dalvik-bytecode.html文件,90的Opcode为invoke-direct,格式为23x
查看instruction-formats.html文件,23x的指令格式为:
即:add-int vAA, vBB, vCC。
AA |
BB |
CC |
0x0 |
0x02 |
0x03 |
即:add-int v0, v2, v3。查dalvik-bytecode表,0F00指令翻译得:return vAA,即:return v0
解码后得到如下代码片段:
9000 0203 add-int v0, v2, v3 0F00 return v0 |
#6分析virtualMethods[1]
接着上一个DexMethod后面0x2FB继续分析:
virtualMethods[1]->methodIdx = virtualMethods[0]->methodIdx + uleb128(01) = 0x01 + 0x01 = 0x02
#指向DexMethodId索引表
备注:这里相加就是用到我们上面dexReadClassDataMethod函数中分析的结论,即:methodIdx索引在上一个virtualMethods的methodIdx的基础上叠加。
序号 |
类类型 |
方法声明 |
方法名 |
#2 |
LTest; |
FFF |
minus |
virtualMethods[1]->accessFlags =uleb128( 01) = 0x1 # ACC_PUBLIC
virtualMethods[1]->codeOff = uleb128(C0 03) = 0x01C0 #指向DexCode结构的偏移,刚好是接着上一个DexCode
其中,uleb128(C0 03)计算过程如下:
第1个字节0xC0 > 0x7f,表示需要用到第二字节。result = 0xC0 & 0x7f = 0x40
第2个字节0x03 < 0x7f,表示到了结尾。result = result | ((0x03 & 0x7f)<<7) = 0x40 | 0x180 = 0x1C0
DexCode结构体如下:
跳转到dex文件0x1C0偏移处:
备注:上一个DexMethod截止到0x01BD,剩下2字节用于填充padding,见DexCode结构体中的注释
解析DexCode:
DexCode->registersSize = 0x04
DexCode->insSize = 0x03
DexCode->outsSize = 0x00
DexCode->triesSize = 0x00
DexCode->debugInfoOff = 0x02DA
DexCode->insnsSize = 0x03 = 3个insns指令
DexCode->insns = A700 0203 0F00
分析DexCode->insns字段
查看dalvik-bytecode.html文件,A7的Opcode为invoke-direct,格式为23x
查看instruction-formats.html文件,23x的指令格式为:
即:sub-float vAA, vBB, vCC
AA |
BB |
CC |
0x00 |
0x02 |
0x03 |
即sub-float v0, v2, v3。
查dalvik-bytecode表,0F00指令翻译得:return vAA,即:return v0
解码后得到如下代码片段:
A700 0203 sub-float v0, v2, v3 0F00 return v0 |
#7分析virtualMethods[2]
接着上一个DexMethod后面0x2FF继续分析:
virtualMethods[2]->methodIdx = virtualMethods[1]->methodIdx + uleb128(01) = 0x02 + 0x01 = 0x03
#指向DexMethodId索引表
备注:这里相加就是用到我们上面dexReadClassDataMethod函数中分析的结论,即:methodIdx索引在上一个virtualMethods的methodIdx的基础上叠加。
序号 |
类类型 |
方法声明 |
方法名 |
#3 |
LTest; |
V |
|
virtualMethods[2]->accessFlags =uleb128( 01) = 0x1 # ACC_PUBLIC
virtualMethods[2]->codeOff = uleb128(D8 03) = 0x1D8 #指向DexCode结构的偏移
DexCode结构体如下:
跳转到dex文件0x1D8偏移处:
解析DexCode:
DexCode->registersSize = 0x03
DexCode->insSize = 0x01
DexCode->outsSize = 0x02
DexCode->triesSize = 0x00
DexCode->debugInfoOff = 0x02E1
DexCode->insnsSize = 0x08 = 8个insns指令
DexCode->insns = 6200 0300 1A01 0300 6E20 0400 1000 0E00
分析DexCode->insns字段
(1) 6200 0300
查看dalvik-bytecode.html文件,62的Opcode为sget-object,格式为21c
查看instruction-formats.html文件,21c的指令格式为:
即:sget-object vAA, field@BBBB
AA |
BBBB |
0x00 |
0x0003,指向DexField索引表,即:Ljava/lang/System;.out:Ljava/io/PrintStream; |
即:sget-object v0, field@0003
(2)1A01 0300
查看dalvik-bytecode.html文件,1A的Opcode为const-string,格式为21c
查看instruction-formats.html文件,21c的指令格式为:
即:const-string vAA, String@BBBB //”Hello World!”
AA |
BBBB |
0x01 |
0x0003 # Hello World! |
即:const-string v1, String@0003
(3)6E20 0400 1000
查看dalvik-bytecode.html文件,6E的Opcode为invoke-virtual,格式为35c
查看instruction-formats.html文件,35c的指令格式为:
即:invoke-virtual {vD, vE, vF, vG, vA}, meth@CCCC
B |
A |
CCCC |
G |
F |
E |
D |
2 |
0 |
0x0004,指向DexMethod索引表 |
0 |
0 |
1 |
0 |
B=2,即:[B=2] op {vD, vE}, kind@CCCC,即:[B=2] op {v0, v1}
即:invoke-virtual {v0, v1}, method@0004 //Ljava/io/PrintStream;.println: (Ljava/lang/String;)
(4)0E00
查看dalvik-bytecode表,得:return-void
综上,解码后得到如下代码片段:
6200 0300 sget-object v0, field@0003 //Ljava/lang/System;.out:Ljava/io/PrintStream; 1A01 0300 const-string vAA, String@BBBB //”Hello World!” 6E20 0400 1000 invoke-virtual {v0, v1}, method@0004 //Ljava/io/PrintStream;.println: (Ljava/lang/String;) 0E00 return-void标签:DEX,文件,04,DexCode,dalvik,0x01,如下,result,结构 From: https://www.cnblogs.com/domefy/p/16866484.html