04-DEX文件结构分析

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

print

#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

print

#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

print

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
posted @ 2023-01-30 18:48  Domefy  阅读(142)  评论(0编辑  收藏  举报