DEX文件格式


1 .DEX文件中使用的數據類型

u1,u2,u4,u8表示占某固定字節的無符號數

sleb128表示有符號的LEB128類型數據,uleb128表示無符號的LEB128,uleb128p1表示無符號的LEB128+1 

關於LEB128

LEB128是一種DEX文件中特有的用來存儲最大32位數的數據類型,他的特點是字節數可以1-5可變。每個字節的第一位用來表示是否用到下個字節,剩下的7位為有效位,所以第5個字節的收位一定不能為1。有符號LEB128SLEB128)的符號由最后字節的有效位的最高位決定。也就是最后字節的第二位(0為正 1為負)。

LEB128的編碼函數:

將一個整型值的二進制值通過在頭部添加零,將其擴展成7位的整數倍,然后每7位一組進行分組;從最低有效位到最高有效位方向,在每組的頭部添加一位構成一個字節,最高有效位所在的組的頭部一bit添加的是0;然后將這些組順序進行反轉,得到這一整數的LEB128編碼。

這里舉例:0x98765=10011000011101100101 把它補成7的倍數010011000011101100101然后按7位分組0100110  0001110  1100101然后在每組前加一位1,第一組加0得出 00100110 10001110 11100101 也就是0x268ee5 

 

DEX_INLINE u1* writeUnsignedLeb128(u1* ptr, u4 data){ 

while (true) { //循環

u1 out = data & 0x7f; ; 7F進行與運算 得出最后7

if (out != data) {

 *ptr++ = out | 0x80; 80就等於10000000 也就是跟前面補1

data >>= 7; 繼續下個7個字節

else { 

*ptr++ = out; break; 

return ptr; 

 

安卓源碼讀取LEB128的函數:

 

DEX_INLINE int readUnsignedLeb128(const u1** pStream) { 

    const u1* ptr = *pStream; 

    int result = *(ptr++); 

    if (result > 0x7f) { //判斷第一個字節的第一位是不是17F=二進制1111111

        int cur = *(ptr++); //指向第二個字節

        result = (result & 0x7f) | ((cur & 0x7f) << 7);  //通過邏輯運算符結合二進制

        if (cur > 0x7f) { 

            cur = *(ptr++); //指向第三個

            result |= (cur & 0x7f) << 14;//結合

            if (cur > 0x7f) {

                cur = *(ptr++);  

                result |= (cur & 0x7f) << 21;

                if (cur > 0x7f) {

                    cur = *(ptr++);  

                    result |= cur << 28;

                } 

            } 

        } 

    } 

    *pStream = ptr; 

    return result; 

 

2.DEX結構:

DEX結構圖:不會畫

 

 

文件頭:      header 文件頭

索引區:      string_ids 字符串的索引

                type_ids 類型的索引

                proto_ids 方法原型的索引

                field_ids 域的索引

                method_ids 方法的索引

Class_defs 類的定義區 (這一位有的資料說是數據區 無所謂)

數據區        data

Link_data

Header的內容:網上找了一張表

字段名稱

偏移值

長度

描述

magic

0x0

8

'Magic'值,即魔數字段,格式如”dex/n035/0”,其中的035表示結構的版本。

checksum

0x8

4

校驗碼。

signature

0xC

20

SHA-1簽名。

file_size

0x20

4

Dex文件的總長度。

header_size

0x24

4

文件頭長度,009版本=0x5C,035版本=0x70

endian_tag

0x28

4

標識字節順序的常量,根據這個常量可以判斷文件是否交換了字節順序,缺省情況下=0x78563412

link_size

0x2C

4

連接段的大小,如果為0就表示是靜態連接。

link_off

0x30

4

連接段的開始位置,從本文件頭開始算起。如果連接段的大小為0,這里也是0

map_off

0x34

4

map數據基地址。

string_ids_size

0x38

4

字符串列表的字符串個數。

string_ids_off

0x3C

4

字符串列表表基地址。

type_ids_size

0x40

4

類型列表里類型個數。

type_ids_off

0x44

4

類型列表基地址。

proto_ids_size

0x48

4

原型列表里原型個數。

proto_ids_off

0x4C

4

原型列表基地址。

field_ids_size

0x50

4

字段列表里字段個數。

field_ids_off

0x54

4

字段列表基地址。

method_ids_size

0x58

4

方法列表里方法個數。

method_ids_off

0x5C

4

方法列表基地址。

class_defs_size

0x60

4

類定義類表中類的個數。

class_defs_off

0x64

4

類定義列表基地址。

data_size

0x68

4

數據段的大小,必須以4字節對齊。

data_off

0x6C

4

數據段基地址

我們參照具體的例子分析:

這個是QQ的安卓版本頭文件如下 :

 

首先 模數字段為 64 65 78 0A 30 33 35 00也就是字符串dex.035.接下來是4個字節的checksum E2 21 E9 E8 。用來校驗文件是否被傳該。然后是20個字節的SHA-1簽名,判斷唯一性的。注意和上面的用途的區分,然后是文件大小 這里是0X929480,跟文件實際大小符合,文件頭大小header_size是0x70.cpu字節序little-endian0X012345678.link_size為00就表示是靜態連接。link_off也一樣。MAP_OFF制定dexmaplist的偏移。接下來就是各個表的基址和大小了 。

我們挨個分析 ,首先是map_off:

 

這個表示map item 的偏移地址 ,該 item 屬於 data 區里的內容 ,值要大於等於 data_off 的大小 。結構如

struct dexmaplist

{

u4 size;

dexmapitem list [size];

}

struct dexmapitem

{

u2 type;

u2 unuse;

u4 size;

u4 offset;

}

我們根據上面例子的偏移定位到圖中的位置

 

可以看到9293B0處的值是0x11 該值便為dexmaplist.size 

然后緊接着便是0x11dexmapitemDexmapitem的第一個元素為類型

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,

};

 

這里包含頭里面的那些,但是那里又更全面一些 ,又包括了 HEADER_ITEM , TYPE_LIST , STRING_DATA_ITEM 等 ,最后還有它自己 TYPE_MAP_LIST 。至此 , header 部分描述完畢 ,它包括描述 .dex 文件的信息 ,其余各索引區和 data 區的偏移信息 , 一個map_list 結構 。map_list 里除了對索引區和數據區的偏移地址又一次描述 ,也有其它諸如 HEAD_ITEM ,DEBUG_INFO_ITEM 等信息 。

 

 

看到這里才發現自己找的例子太大了。幾萬個字符串。。。。。。

 

                                           --------------------------睡覺 

換第三章最后的例子來解析,QQ的這是幾十。太大了。換老趙手寫的第三章的DEX文件來分析 

我們先用二進制編輯文件打開該DEX ,可以看到文件二進制信息如下 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

00000000   64 65 78 0A 30 33 35 00  A7 6C 26 11 EB EB E1 27   dex.035.&.?

00000016   9B 3D 51 47 7A 57 A3 7C  DB 6D 82 8D 1A 44 62 43   ?QGzW踡倣.DbC

00000032   20 03 00 00 70 00 00 00  78 56 34 12 00 00 00 00    ...p...xV4.....

00000048   00 00 00 00 74 02 00 00  0F 00 00 00 70 00 00 00   ....t.......p...

00000064   09 00 00 00 AC 00 00 00  03 00 00 00 D0 00 00 00   ....?......?..

00000080   01 00 00 00 F4 00 00 00  03 00 00 00 FC 00 00 00   ....?......?..

00000096   01 00 00 00 14 01 00 00  EC 01 00 00 34 01 00 00   ........?..4...

00000112   34 01 00 00 3C 01 00 00  49 01 00 00 57 01 00 00   4...<...I...W...

00000128   6E 01 00 00 82 01 00 00  9D 01 00 00 B1 01 00 00   n...?..?..?..

00000144   C5 01 00 00 C8 01 00 00  CC 01 00 00 D0 01 00 00   ?..?..?..?..

00000160   E5 01 00 00 EB 01 00 00  F0 01 00 00 02 00 00 00   ?..?..?......

00000176   03 00 00 00 04 00 00 00  05 00 00 00 06 00 00 00   ................

00000192   07 00 00 00 08 00 00 00  0A 00 00 00 0B 00 00 00   ................

00000208   08 00 00 00 06 00 00 00  00 00 00 00 09 00 00 00   ................

00000224   06 00 00 00 FC 01 00 00  09 00 00 00 06 00 00 00   ....?..........

00000240   04 02 00 00 04 00 01 00  0D 00 00 00 00 00 02 00   ................

00000256   0C 00 00 00 01 00 01 00  0E 00 00 00 03 00 00 00   ................

00000272   00 00 00 00 00 00 00 00  01 00 00 00 05 00 00 00   ................

00000288   00 00 00 00 FF FF FF FF  00 00 00 00 6C 02 00 00   ........l...

00000304   00 00 00 00 06 3C 69 6E  69 74 3E 00 0B 48 65 6C   .....<init>..Hel

00000320   6C 6F 20 57 6F 72 6C 64  00 0C 4C 48 65 6C 6C 6F   lo World..LHello

00000336   57 6F 72 6C 64 3B 00 15  4C 6A 61 76 61 2F 69 6F   World;..Ljava/io

00000352   2F 50 72 69 6E 74 53 74  72 65 61 6D 3B 00 12 4C   /PrintStream;..L

00000368   6A 61 76 61 2F 6C 61 6E  67 2F 53 74 72 69 6E 67   java/lang/String

00000384   3B 00 19 4C 6A 61 76 61  2F 6C 61 6E 67 2F 53 74   ;..Ljava/lang/St

00000400   72 69 6E 67 42 75 69 6C  64 65 72 3B 00 12 4C 6A   ringBuilder;..Lj

00000416   61 76 61 2F 6C 61 6E 67  2F 53 79 73 74 65 6D 3B   ava/lang/System;

00000432   00 12 4C 6A 61 76 65 2F  6C 61 6E 67 2F 4F 62 6A   ..Ljave/lang/Obj

00000448   65 63 74 3B 00 01 56 00  02 56 4C 00 02 5B 49 00   ect;..V..VL..[I.

00000464   13 5B 4C 6A 61 76 61 2F  6C 61 6E 67 2F 53 74 72   .[Ljava/lang/Str

00000480   69 6E 67 3B 00 04 6D 61  69 6E 00 03 6F 75 74 00   ing;..main..out.

00000496   07 70 72 69 6E 74 6C 6E  00 00 00 00 01 00 00 00   .println........

00000512   02 00 00 00 01 00 00 00  08 00 00 00 00 00 00 00   ................

00000528   00 01 00 07 00 00 00 00  04 00 01 00 02 00 00 00   ................

00000544   10 02 00 00 22 00 00 00  00 00 00 00 00 00 00 00   ...."...........

00000560   13 00 08 00 12 51 12 32  01 21 23 00 07 00 21 01   .....Q.2.!#...!.

00000576   22 01 03 00 70 10 02 00  01 00 39 00 03 00 28 0D   "...p.....9...(.

00000592   82 22 A6 02 02 02 2D 00  02 02 62 00 00 00 1A 01   ??..-...b.....

00000608   01 00 6E 20 01 00 10 00  0E 00 0E 00 00 00 01 00   ..n ............

00000624   00 09 98 04 0E 00 00 00  00 00 00 00 01 00 00 00   ..?............

00000640   00 00 00 00 01 00 00 00  0F 00 00 00 70 00 00 00   ............p...

00000656   02 00 00 00 09 00 00 00  AC 00 00 00 03 00 00 00   ........?......

00000672   03 00 00 00 D0 00 00 00  04 00 00 00 01 00 00 00   ....?..........

00000688   F4 00 00 00 05 00 00 00  03 00 00 00 FC 00 00 00   ?..........?..

00000704   06 00 00 00 01 00 00 00  14 01 00 00 02 20 00 00   ............. ..

00000720   0F 00 00 00 34 01 00 00  01 10 00 00 02 00 00 00   ....4...........

00000736   FC 01 00 00 03 10 00 00  01 00 00 00 0C 02 00 00   ?..............

00000752   03 20 00 00 01 00 00 00  10 02 00 00 01 20 00 00   . ........... ..

00000768   01 00 00 00 18 02 00 00  00 20 00 00 01 00 00 00   ......... ......

00000784   6C 02 00 00 00 10 00 00  01 00 00 00 74 02 00 00   l...........t...

 

下面具體分析各個索引 :

string_ids:

string_ids 區索引了文件所有的字符串 。 本區里的元素格式為 string_ids_item , 可以使用結構體如下描述 。

struct string_ids_item

{

uint string_data_off;

}

以 _ids 結尾的各個區段里放置的都是對應數據的偏移地址 ,只是一個索引 ,所以才會在 .dex文件布局里把這些區歸類為 “索引區” 。string_data_off 只是一個偏移地址 ,它指向的數據結構為 string_data_item

struct string_data_item

{

uleb128 utf16_size;  這里用到了前面的LEB128 可變字節類型!

ubyte data;

}

我們根據頭文件中提供的string_ids_size=0F和string_ids_off=70定位到字符串索引區

 

Offset      0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F

這里有0Fstring_ids_item結構的索引 :

00000070   34 01 00 00 3C 01 00 00  49 01 00 00 57 01 00 00   4...<...I...W...

00000080   6E 01 00 00 82 01 00 00  9D 01 00 00 B1 01 00 00   n...?..?..?..

00000090   C5 01 00 00 C8 01 00 00  CC 01 00 00 D0 01 00 00   ?..?..?..?..

000000A0   E5 01 00 00 EB 01 00 00  F0 01 00 00               ?..?..?..

 

 

我們再具體定位到string_data_item :

Offset      0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F

 

00000130   00 00 00 00 06 3C 69 6E  69 74 3E 00 0B 48 65 6C   .....<init>..Hel

00000140   6C 6F 20 57 6F 72 6C 64  00 0C 4C 48 65 6C 6C 6F   lo World..LHello

00000150   57 6F 72 6C 64 3B 00 15  4C 6A 61 76 61 2F 69 6F   World;..Ljava/io

00000160   2F 50 72 69 6E 74 53 74  72 65 61 6D 3B 00 12 4C   /PrintStream;..L

00000170   6A 61 76 61 2F 6C 61 6E  67 2F 53 74 72 69 6E 67   java/lang/String

00000180   3B 00 19 4C 6A 61 76 61  2F 6C 61 6E 67 2F 53 74   ;..Ljava/lang/St

00000190   72 69 6E 67 42 75 69 6C  64 65 72 3B 00 12 4C 6A   ringBuilder;..Lj

000001A0   61 76 61 2F 6C 61 6E 67  2F 53 79 73 74 65 6D 3B   ava/lang/System;

000001B0   00 12 4C 6A 61 76 65 2F  6C 61 6E 67 2F 4F 62 6A   ..Ljave/lang/Obj

000001C0   65 63 74 3B 00 01 56 00  02 56 4C 00 02 5B 49 00   ect;..V..VL..[I.

000001D0   13 5B 4C 6A 61 76 61 2F  6C 61 6E 67 2F 53 74 72   .[Ljava/lang/Str

000001E0   69 6E 67 3B 00 04 6D 61  69 6E 00 03 6F 75 74 00   ing;..main..out.

000001F0   07 70 72 69 6E 74 6C 6E  00 00 00 00 01 00         .println......

 

這樣我們解析到了所有的字符串。這里具體分析: 接下來的都大同小一 

索引

偏移

長度

內容

0

134

06

<init>

1

13c

0b

Hello World

2

149

0c

LHelloWorld;

3

157

15

Ljava/io/PrintStream;

4

16e

12

Ljava/lang/String;

5

182

19

Ljava/lang/StringBuilder;

6

19d

12

Ljava/lang/System;

7

1b1

12

Ljave/lang/Object;

8

1c5

01

V

9

1c8

02

VL

10

1cc

02

[I

11

1d0

13

[Ljava/lang/String;

12

1e5

04

main

13

1eb

03

out

14

1f0

07

println

 

 

type_ids:

 

type_ids 區索引了文件里的所有數據類型 ,包括 class 類型 ,數組類型和基本類型 。 本區域里的元素格式為 type_ids_item , 結構描述如下 :

struct type_ids_item

{

uint descriptor_idx;

}

type_ids_item 里面 descriptor_idx 的值的意思 ,是 string_ids 里的 index 序號 ,是用來描述此type 的字符串 。

根據 header 里 type_ids_size = 0x09 , type_ids_off = 0xaC , 找到對應的二進制描述區 。

Offset      0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F

 

000000A0                                        02 00 00 00               ....

000000B0   03 00 00 00 04 00 00 00  05 00 00 00 06 00 00 00   ................

000000C0   07 00 00 00 08 00 00 00  0A 00 00 00 0B 00 00 00   ................

 

這里有9個type_ids_item ,他的元素descriptor_idx存的是在string_ids的索引號 

我根據這些索引號去上面找出所有類型的名稱 這里具體分析:

 

索引

內容

0

02

LHelloWorld;

1

03

Ljava/io/PrintStream;

2

04

Ljava/lang/String;

3

05

Ljava/lang/StringBuilder;

4

06

Ljava/lang/System;

5

07

Ljave/lang/Object;

6

08

V

7

0a

[I

8

0b

[Ljava/lang/String;

 

proto_ids:

這里存的是方法method原型。機構如下 :

struct proto_id_item

{

uint shorty_idx; 指向字符串 索引

uint return_type_idx; 指向類型索引

uint parameters_off;  type_list的偏移

}

索引值如下 :

shorty_idx和上面的type_ids的descriptor_idx; 的元素一樣 存的是string_ids的索引 。得出一個字符串,用來說明method 原型

return_type_idx的值是依個type_ids的索引 。用來表示該method原型的返回值類型。

parameters_off用來指向method的參數列表的類型type_list結構的表。如果parameters_off為0 則表明該函數無參數。

parameters_off指向的type_list結構描述如下(也就是參數列表結構) :

struct type_list

{

uint size;

ushort type_idx[size];  也就是書上的 type_item  

}

Size是參數數量 type_idx 也就是對應的類型了 里面存的是type_ids的索引了 每個占兩個字節 我們根據頭文件中的proto_ids_size = 0x03 , proto_ids_off = 0xD0得到

 

Offset      0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F

 

000000D0   08 00 00 00 06 00 00 00  00 00 00 00 09 00 00 00   ................

000000E0   06 00 00 00 FC 01 00 00  09 00 00 00 06 00 00 00   ....?..........

000000F0   04 02 00 00                                        ....

得到type_liet如下 :

Offset      0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F

000001F0                            01 00 00 00               ....

00000200   02 00 00 00 01 00 00 00  08 00 00 00               ............

 

這里有點亂。等下一起研究。

索引

0

1

2

shorty_idx

08

09

09

return_type_idx

06

06

06

parameters_off

0

1fc

204

shorty string

V

VL

VL

return string

V

V

V

Param num

0

1

typeIDx

 

2(Ljava/lang/String;)

8([Ljava/lang/String;)

 

 

索引

偏移

長度

內容

0

134

06

<init>

1

13c

0b

Hello World

2

149

0c

LHelloWorld;

3

157

15

Ljava/io/PrintStream;

4

16e

12

Ljava/lang/String;

5

182

19

Ljava/lang/StringBuilder;

6

19d

12

Ljava/lang/System;

7

1b1

12

Ljave/lang/Object;

8

1c5

01

V

9

1c8

02

VL

10

1cc

02

[I

11

1d0

13

[Ljava/lang/String;

12

1e5

04

main

13

1eb

03

out

14

1f0

07

println

field_ids:

這個是存啥的 ?書上說是字段 本區的元素格式是 field_id_item ,結構如下

struct filed_id_item

{

ushort class_idx;

ushort type_idx;

uint name_idx;

}

class_idx , 表示本field所屬的class 類型,class_idx 的值是 type_ids 的索引 , 並且必須指向一個class 類型 。

type_idx , 表示本 field 的類型 ,它的值也是 type_ids 的一個索引 。

name_idx , 表示本 field 的名稱 ,它的值是 string_ids 的一個索引 。

header 里 field_ids_size = 1 , field_ids_off = 0xF4 。說明本 .dex 只有一個 field ,這部分的二進制描述如下 :

Offset      0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F

 

000000F0               04 00 01 00  0D 00 00 00                   ........

現在具體分析 :

。。。。。。。。。。。

索引

 

Class_idx

04

 Type_idx

01

name_idx

OD

class string

Ljava/lang/System;

Type string

Ljava/io/PrintStream;

name string

out

 

method_ids

最后一個了 1點16了 。。。。還沒整理完  。。method_ids 這個就是所有方法的條目索引 ,method_ids 的元素格式是 method_id_item。結構如下

struct filed_id_item

{

ushort class_idx;

ushort proto_idx;

uint name_idx;

}

class_idx , 和 name_idx 跟 fields_ids 是一樣的 。

class_idx , 表示本 method 所屬的 class 類型 , class_idx 的值是 type_ids 的一個索引 , 並且必須指向一個 class 類型 。

name_idx , 表示本 method 的名稱 ,它的值是 string_ids 的一個索引 。

proto_idx 描述該 method 的原型 ,指向 proto_ids 的一個索引

 

header 里 method_ids_size = 0x03 , method_ids_off = 0xfC 。本部分的二進制描述如下 :

 

Offset      0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F

 

000000F0                            0D 00 00 00 00 00 02 00           ........

00000100   0C 00 00 00 01 00 01 00  0E 00 00 00 03 00 00 00   ................

 

索引

0

1

2

class_idx

0

01

03

proto_idx

02

01

00

name_idx

0C

0E

00

class string

LHelloWorld;

Ljava/io/PrintStream;

Ljava/lang/StringBuilder;

protostring 

VL([Ljava/lang/String;)V

VL

V

name string

main

println

<init>

 

 

Class_defs

class_def_item

class 的定義 他的結構較前面的都比較復雜 

 

結構如下

 

struct class_def_item

{

uint class_idx;  類的類型

uint access_flags;  訪問標志

uint superclass_idx;  父類的類型

uint interfaces_off;  接口 ?

uint source_file_idx; 源文件名

uint annotations_off;  注解

uint class_data_off;  指向DexClassData      !!!!

uint static_value_off; 指向DexEncodedarray

}

 

摘抄:

(1) class_idx 描述具體的 class 類型 ,值是 type_ids 的一個索引 。值必須是一個 Class 類型 ,不能是數組類型或者基本類型 。

(2) access_flags 描述 class 的訪問類型 ,諸如 public , final , static 等 。在 dex-format.html 里 “access_flagsDefinitions” 有具體的描述 。

(3) superclass_idx , 描述 supperclass 的類型 ,值的形式跟 class_idx 一樣 。

(4) interfaces_off , 值為偏移地址 ,指向 class 的 interfaces , 被指向的數據結構為 type_list 。class 若沒有interfaces ,值為 0。

(5) source_file_idx , 表示源代碼文件的信息 ,值是 string_ids 的一個 index 。若此項信息缺失 ,此項值賦值為NO_INDEX=0xffff ffff 。

(6) annotions_off , 值是一個偏移地址 ,指向的內容是該 class 的注釋 ,位置在 data 區,格式為annotations_direcotry_item 。若沒有此項內容 ,值為 0 。(難道會把注釋也打包進去 ?????????????????)

(7) class_data_off , 值是一個偏移地址 ,指向的內容是該 class 的使用到的數據 ,位置在 data 區,格式為class_data_item 。若沒有此項內容 ,值為 0 。該結構里有很多內容 ,詳細描述該 class 的 field , method, method 里的執行代碼等信息.

(8) static_value_off , 值是一個偏移地址 ,指向 data 區里的一個列表 ( list ) ,格式為 encoded_array_iteM。若沒有此項內容 ,值為 0 。

 

header 里 class_defs_size = 0x01 , class_defs_off = 0x 0114 。則此段二進制描述為 :

Offset      0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F

 

00000110               00 00 00 00  01 00 00 00 05 00 00 00       ............

00000120    00 00 00 00 FF FF FF FF    00 00 00 00 6C 02 00 00   ........l...

00000130   00 00 00 00                                        ....

元素

內容

class_idx

0

LHelloWorld;

access_flags

1

 

superclass_idx

5

Ljave/lang/Object;

interfaces_off。。

0

 

source_file_idx

FFFFFFFF

無源文件信息

 annotions_off。。

0

 

class_data_off。。

26C

 

static_value_off。。

0

 

 

class_data_off 指向 data 區里的 class_data_item 結構 ,class_data_item 里存放着本 class 使用到的各種數據 ,下面是 class_data_item 的邏輯結構 :

 

struct class_data_item

{

U4 static_fields_size;

U4 instance_fields_size;

U4 direct_methods_size;

U4 virtual_methods_size;   不是U4 其實是LEB128

;上面為頭 

encoded_field static_fields [ static_fields_size ];

encoded_field instance_fields [ instance_fields_size ];

encoded_method direct_methods [ direct_method_size ];

encoded_method virtual_methods [ virtual_methods_size ];

}

 

 

 

struct encoded_field

{

uleb128 filed_idx_diff; // index into filed_ids for ID of this filed

uleb128 access_flags; // access flags like public, static etc.

}

 

 

struct encoded_method

{

uleb128 method_idx;

uleb128 access_flags;

uleb128 code_off;

}

 

 

(1)method_idx指向前面的方法那里 。

(2)access_flags , 訪問權限 , 比如 public、private、static、final 等 。

(3)code_off , 一個指向 data 區的偏移地址 ,目標是本 method 的代碼實現 。被指向的結構是code_item ,有近 10 項元素 .這里放着這個軟件的各種代碼的具體實現,也就是所謂的字節碼 

Offset      0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F

 

00000260                                        00 00 01 00               ....

00000270   00 09 98 04 0E 00 00 00  00 00 00 00 01 00 00 00   ..?............

00000280   00 00 00 00 01 00 00 00  0F 00 00 00 70 00 00 00   ............p...

00000290   02 00 00 00 09 00 00 00  AC 00 00 00 03            ........?...

 

 

 

 

Element

value

static_fields_size

0

instance_fields_size

0

directive_methods_size

1

vitual_methods_size

0

static_fields[]

 

instance_fields[]

 

directive_methods[]

00 09 98 04 0e

vitual_methods[]

 

 

得到上面的描述后從uleb128 轉換過來 

 

method

son-element

value

meaning

directive_method[0]

method_idx_diff

00

LHelloWorld;->main([Ljava/lang/String;)V

access_flags

09

quanxian

code_off

0X218

 

 

 

 

着重看+218處的 code_off吧  

struct code_item

{

ushort registers_size;

ushort ins_size;

ushort outs_size;

ushort tries_size;

uint debug_info_off;

uint insns_size;

ushort insns [ insns_size ];

ushort paddding; // optional

try_item tries [ tyies_size ]; // optional

encoded_catch_handler_list handlers; // optional  指令集

}

 

 

 

 

 

 

 

Offset      0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F

 

00000210                            04 00 01 00 02 00 00 00           ........

00000220   10 02 00 00 22 00 00 00  00 00 00 00 00 00 00 00   ...."...........

00000230   13 00 08 00 12 51 12 32  01 21 23 00 07 00 21 01   .....Q.2.!#...!.

00000240   22 01 03 00 70 10 02 00                            "...p...

 

 

 

我們粘貼下來22*2個字節 

Offset      0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F

 

00000220                            00 00 00 00 00 00 00 00           ........

00000230   13 00 08 00 12 51 12 32  01 21 23 00 07 00 21 01   .....Q.2.!#...!.

00000240   22 01 03 00 70 10 02 00  01 00 39 00 03 00 28 0D   "...p.....9...(.

00000250   82 22 A6 02 02 02 2D 00  02 02 62 00 00 00 1A 01   ??..-...b.....

00000260   01 00 6E 20 01 00 10 00  0E 00 0E 00               ..n ........

 

 

 

名稱 

值 

registers_size

04

ins_size

01

outs_size

02

tries_size

00

debug_info_off

0210

insns_size

22

Insnsdf[二進制字節碼 ]

上面那些

 

好亂 理下思路 

整個DEX文件被分為9個區 class_defs索引了DEX文件中用到的CLASS ,和對這個CLASS的描述,CLASS_DEFS里面實際是class_def_item結構。這個結構描述了類的各種信息 。例如名稱啥的 。class_def_item 里有一個元素 class_data_off , 指向data 區里的一個 class_data_item 結構 ,用來描述 class 使用到的各種數據 。 class_data_item 結構 ,里描述值着 class 里使用到的 static field , instance field , direct_method ,和 virtual_method 的數目和描述。encoded_method 結構 ,描述某個 method 的 method 類型 , access flags 和一個指向 code_item的偏移地址 ,里面存放的是該 method 的具體實現 。

 

 

好亂 

 

 

 

末尾的 3 項標志為 optional , 表示可能有 ,也可能沒有 ,根據具體的代碼來 。

(1) registers_size, 本段代碼使用到的寄存器數目。

(2) ins_size, method 傳入參數的數目 。

(3) outs_size, 本段代碼調用其它method 時需要的參數個數 。

(4) tries_size, try_item 結構的個數 。

(5) debug_off, 偏移地址 ,指向本段代碼的 debug 信息存放位置 ,是一個 debug_info_item 結構。

(6) insns_size, 指令列表的大小 ,以 16-bit 為單位 。 insns 是 instructions 的縮寫 。

(7) padding , 值為 0 ,用於對齊字節 。

(8) tries 和 handlers , 用於處理 java 中的 exception , 常見的語法有 try catch 。

 

 

 

;Dalvik VM Instruction Format

 

接下來我們翻譯這些指令 

 

我們粘貼下來22*2個字節 

Offset      0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F

 

00000220                            00 00 00 00 00 00 00 00           ........

00000230   13 00 08 00 12 51 12 32  01 21 23 00 07 00 21 01   .....Q.2.!#...!.

00000240   22 01 03 00 70 10 02 00  01 00 39 00 03 00 28 0D   "...p.....9...(.

00000250   82 22 A6 02 02 02 2D 00  02 02 62 00 00 00 1A 01   ??..-...b.....

00000260   01 00 6E 20 01 00 10 00  0E 00 0E 00               ..n ........

 

http://www.netmite.com/android/mydroid/dalvik/docs/instruction-formats.html   

 

http://www.netmite.com/android/mydroid/dalvik/docs/instruction-formats.html我們根據這個來解析部分代碼 

 

 

0000很容易 是4個NOP 

 

步驟 (1) 《Dalvik VM Instruction Format》 里操作符 op 都是位於首個 16bit 數據的低 8 bit ,起始的是 op =0x13。

 

(2) 在 《Bytecode for Dalvik VM》 里找到對應的 Syntax 和 format 。

syntax=const/16

Format=21S 

 

(3) 在《Dalvik VM Instruction Format》里查找 21s , 得知 op = 0x13 的指令占據 2 個 16 bit 數據 ,

格式是 AA|op BBBB ,解釋為 op vAA, #+BBBB 。因此這 8 組 16 bit 數據里 ,前 2 個是一組 。

對比數據得 AA=0x00, BBBB = 0x0008。

 

(4)返回《Bytecode for Dalvik VM》里查閱對 const/16 的解釋, AA 的值表示 Value Register ,即0 號寄存器; BBBB 表示 signed int  ,就是整形的一個立即數

A: destination register (8 bits)

B: signed int (16 bits)

 

所以13 00 08 00 就可以解釋為const/16 v0,8....是不是很容易 以此解釋就可以解釋完這條CLass的這個 method的全部代碼 

 

我們繼續5112 我們按第一條步驟 ,找出op=12 按照步驟2 找出該指令的描述 12 11n  const/4 vA, #+B  A: destination register (4 bits)   

B: signed int (4 bits) Format是11N 按步驟3繼續 該OP對應的格式是B|A|op

 

解釋為 op vA, #+B,該指令占16個字節 按照這個格式 所以前4個字節代表B 然后4個代表A 最后8個是OP  那么a=1,B=5 然后去查這個OP的解釋 A: destination register (4 bits)   

B: signed int (4 bits) Format  那么A是寄存器 B是值,大小都是4字節

該指令應該為 const/4,V1,5

 

接下來 又是個12 那么很容易推出 是 const/4,V2,3

 

 

接下來 是2101 我們查下01對應的OP 按照上面的方法推出是 move v1,v2 

 

下面是個0023 我們得出OP是

22C

new-array vA, vB, type@CCCC 

A: destination register (8 bits)

B: size register

C: type index

 

格式是B|A|op CCCC 那么它占了4個字節 我們再往后取兩個字節 就是0023 0007

A=0 b=0 C=7 這里有個問題 C的描述是type index ,我們要去上面的類型表里找 

 

得到 該指令的結果為 :

 

new-array v0, v0, 【l

 

下面看較難的一條指令+244處 1070 該指令的OP我們查找 是 70: invoke-direct 

描述如下 :

B: argument word count (4 bits)

C: method index (16 bits)

D..G, A: argument registers (4 bits each)

 

 

[B=5] op{vD, vE, vF, vG, vA}, meth@CCCC
[B=5] op{vD, vE, vF, vG, vA}, type@CCCC
[B=4] op{vD, vE, vF, vG}, kind@CCCC
[B=3] op{vD, vE, vF}, kind@CCCC
[B=2] op{vD, vE}, kind@CCCC
[B=1] op{vD}, kind@CCCC
[B=0] op{}, kind@CCCC

 

 

Format=35c 該指令的格式為 B|A|op CCCC G|F|E|D 我們看該指令的描述 B未描述了改指令的具體格式 從0到5 。B=1 A=0 那么該指令占6個字節 繼續取兩個字節整個為1070 0002 0001 C=2 D=1按格式整理 該指令為

 

invoke-direct {V1},Ljava/lang/StringBuilder;-> <init>()V

 

下面大家繼續。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM