1. 程式人生 > >Android Dex檔案格式解析(第二篇)

Android 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 s