Dex文件格式詳解

什么是dex文件

他是Android系統(tǒng)的可執(zhí)行文件,包含應(yīng)用程序的全部操作指令以及運(yùn)行時(shí)數(shù)據(jù)。

由于dalvik是一種針對(duì)嵌入式設(shè)備而特殊設(shè)計(jì)的java虛擬機(jī),所以dex文件與標(biāo)準(zhǔn)的class文件在結(jié)構(gòu)設(shè)計(jì)上有著本質(zhì)的區(qū)別

當(dāng)java程序編譯成class后,還需要使用dx工具將所有的class文件整合到一個(gè)dex文件,目的是其中各個(gè)類(lèi)能夠共享數(shù)據(jù),在一定程度上降低了冗余,同時(shí)也是文件結(jié)構(gòu)更加經(jīng)湊,實(shí)驗(yàn)表明,dex文件是傳統(tǒng)jar文件大小的50%左右

可以看見(jiàn):
dex將原來(lái)class每個(gè)文件都有的共有信息合成一體,這樣減少了class的冗余

數(shù)據(jù)結(jié)構(gòu)

類(lèi)型 含義
u1 unit8_t,1字節(jié)無(wú)符號(hào)數(shù)
u2 unit16_t,2字節(jié)無(wú)符號(hào)數(shù)
u4 unit32_t,4字節(jié)無(wú)符號(hào)數(shù)
u8 unit64_t,8字節(jié)無(wú)符號(hào)數(shù)
sleb128 有符號(hào)LEB128,可變長(zhǎng)度1~5
uleb128 無(wú)符號(hào)LEB128,
uleb128p1 無(wú)符號(hào)LEB128值加1,

其中u1~u8很好理解,不理解的可以參考這里,表示1到8個(gè)字節(jié)的無(wú)符號(hào)數(shù),后面三個(gè)是dex特有的數(shù)據(jù)類(lèi)型,更詳細(xì)的參考:(深入到源碼解析leb128數(shù)據(jù)類(lèi)型)[http://i.woblog.cn/2016/07/23/leb128-format/]

dex文件結(jié)構(gòu)

首先從宏觀上來(lái)說(shuō)dex的文件結(jié)果很簡(jiǎn)單,實(shí)際上是由多個(gè)不同結(jié)構(gòu)的數(shù)據(jù)體以首尾相接的方式拼接而成。如下圖:

數(shù)據(jù)名稱(chēng) 解釋
header dex文件頭部,記錄整個(gè)dex文件的相關(guān)屬性
string_ids 字符串?dāng)?shù)據(jù)索引,記錄了每個(gè)字符串在數(shù)據(jù)區(qū)的偏移量
type_ids 類(lèi)似數(shù)據(jù)索引,記錄了每個(gè)類(lèi)型的字符串索引
proto_ids 原型數(shù)據(jù)索引,記錄了方法聲明的字符串,返回類(lèi)型字符串,參數(shù)列表
field_ids 字段數(shù)據(jù)索引,記錄了所屬類(lèi),類(lèi)型以及方法名
method_ids 類(lèi)方法索引,記錄方法所屬類(lèi)名,方法聲明以及方法名等信息
class_defs 類(lèi)定義數(shù)據(jù)索引,記錄指定類(lèi)各類(lèi)信息,包括接口,超類(lèi),類(lèi)數(shù)據(jù)偏移量
data 數(shù)據(jù)區(qū),保存了各個(gè)類(lèi)的真是數(shù)據(jù)
link_data 連接數(shù)據(jù)區(qū)

/dalvik/libdex/DexFile.h

定義如下:

struct DexFile {
    const DexHeader*    pHeader;
    const DexStringId*  pStringIds;
    const DexTypeId*    pTypeIds;
    const DexFieldId*   pFieldIds;
    const DexMethodId*  pMethodIds;
    const DexProtoId*   pProtoIds;
    const DexClassDef*  pClassDefs;
    const DexLink*      pLinkData;
}

注意:其中一些定義的字段是在內(nèi)存中并沒(méi)有存到真是的dex文件中

header

簡(jiǎn)單記錄了dex文件的一些基本信息,以及大致的數(shù)據(jù)分布。長(zhǎng)度固定為0x70,其中每一項(xiàng)信息所占用的內(nèi)存空間也是固定的,好處是虛擬機(jī)在處理dex時(shí)不用考慮dex文件的多樣性

字段名稱(chēng) 偏移值 長(zhǎng)度 說(shuō)明
magic 0x0 8 魔數(shù)字段,值為"dex\n035\0"
checksum 0x8 4 校驗(yàn)碼
signature 0xc 20 sha-1簽名
file_size 0x20 4 dex文件總長(zhǎng)度
header_size 0x24 4 文件頭長(zhǎng)度,009版本=0x5c,035版本=0x70
endian_tag 0x28 4 標(biāo)示字節(jié)順序的常量
link_size 0x2c 4 鏈接段的大小,如果為0就是靜態(tài)鏈接
link_off 0x30 4 鏈接段的開(kāi)始位置
map_off 0x34 4 map數(shù)據(jù)基址
string_ids_size 0x38 4 字符串列表中字符串個(gè)數(shù)
string_ids_off 0x3c 4 字符串列表基址
type_ids_size 0x40 4 類(lèi)列表里的類(lèi)型個(gè)數(shù)
type_ids_off 0x44 4 類(lèi)列表基址
proto_ids_size 0x48 4 原型列表里面的原型個(gè)數(shù)
proto_ids_off 0x4c 4 原型列表基址
field_ids_size 0x50 4 字段個(gè)數(shù)
field_ids_off 0x54 4 字段列表基址
method_ids_size 0x58 4 方法個(gè)數(shù)
method_ids_off 0x5c 4 方法列表基址
class_defs_size 0x60 4 類(lèi)定義標(biāo)中類(lèi)的個(gè)數(shù)
class_defs_off 0x64 4 類(lèi)定義列表基址
data_size 0x68 4 數(shù)據(jù)段的大小,必須4k對(duì)齊
data_off 0x6c 4 數(shù)據(jù)段基址

/dalvik/libdex/DexFile.h

定義如下:

struct DexHeader {
    u1  magic[8];           /* includes version number */
    u4  checksum;           /* adler32 checksum */
    u1  signature[kSHA1DigestLen]; /* SHA-1 hash */
    u4  fileSize;           /* length of entire file */
    u4  headerSize;         /* offset to start of next section */
    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;
};

我們可以用:hexdump -c classes.dex查看dex單字節(jié)顯示的結(jié)果,如下:

0000000   d   e   x  \n   0   3   5  \0 022 217   ?   w   z   ? 031 221
0000010   ?  \f   ?   ?   ?   ?   ?   ? 217 235 200   z   ? 030   I   ?
0000020   ? 003  \0  \0   p  \0  \0  \0   x   V   4 022  \0  \0  \0  \0
0000030  \0  \0  \0  \0   ? 002  \0  \0 024  \0  \0  \0   p  \0  \0  \0
0000040  \b  \0  \0  \0   ?  \0  \0  \0 005  \0  \0  \0   ?  \0  \0  \0
0000050 001  \0  \0  \0 034 001  \0  \0 005  \0  \0  \0   $ 001  \0  \0
0000060 001  \0  \0  \0   L 001  \0  \0   8 002  \0  \0   l 001  \0  \0
0000070   l 001  \0  \0   t 001  \0  \0 201 001  \0  \0 204 001  \0  \0
0000080 222 001  \0  \0 226 001  \0  \0   ? 001  \0  \0   ? 001  \0  \0
0000090   ? 001  \0  \0   ? 001  \0  \0 004 002  \0  \0  \a 002  \0  \0
00000a0  \v 002  \0  \0     002  \0  \0   ( 002  \0  \0   . 002  \0  \0
00000b0   4 002  \0  \0   9 002  \0  \0   B 002  \0  \0   L 002  \0  \0
00000c0 003  \0  \0  \0 005  \0  \0  \0 006  \0  \0  \0  \a  \0  \0  \0
00000d0  \b  \0  \0  \0  \t  \0  \0  \0  \n  \0  \0  \0  \f  \0  \0  \0
00000e0 002  \0  \0  \0 003  \0  \0  \0  \0  \0  \0  \0 004  \0  \0  \0
00000f0 004  \0  \0  \0   x 002  \0  \0  \n  \0  \0  \0 006  \0  \0  \0
0000100  \0  \0  \0  \0  \v  \0  \0  \0 006  \0  \0  \0   x 002  \0  \0
0000110  \v  \0  \0  \0 006  \0  \0  \0   p 002  \0  \0 005  \0 001  \0
0000120 020  \0  \0  \0  \0  \0 004  \0 017  \0  \0  \0 001  \0 003  \0
0000130 021  \0  \0  \0 004  \0 002  \0  \0  \0  \0  \0 004  \0 001  \0
0000140  \r  \0  \0  \0 004  \0  \0  \0 022  \0  \0  \0  \0  \0  \0  \0
0000150 001  \0  \0  \0 002  \0  \0  \0  \0  \0  \0  \0   ?   ?   ?   ?
0000160  \0  \0  \0  \0   ? 002  \0  \0  \0  \0  \0  \0 006   <   i   n
0000170   i   t   >  \0  \v   H   e   l   l   o       W   o   r   l   d
0000180  \0 001   L  \0  \f   L   H   e   l   l   o   W   o   r   l   d
0000190   ;  \0 002   L   L  \0 025   L   j   a   v   a   /   i   o   /
00001a0   P   r   i   n   t   S   t   r   e   a   m   ;  \0 022   L   j
00001b0   a   v   a   /   l   a   n   g   /   O   b   j   e   c   t   ;
00001c0  \0 022   L   j   a   v   a   /   l   a   n   g   /   S   t   r
00001d0   i   n   g   ;  \0 031   L   j   a   v   a   /   l   a   n   g
00001e0   /   S   t   r   i   n   g   B   u   i   l   d   e   r   ;  \0
00001f0 022   L   j   a   v   a   /   l   a   n   g   /   S   y   s   t
0000200   e   m   ;  \0 001   V  \0 002   V   L  \0 023   [   L   j   a
0000210   v   a   /   l   a   n   g   /   S   t   r   i   n   g   ;  \0
0000220 006   a   p   p   e   n   d  \0 004   a   r   g   s  \0 004   m
0000230   a   i   n  \0 003   o   u   t  \0  \a   p   r   i   n   t   l
0000240   n  \0  \b   t   o   S   t   r   i   n   g  \0 016   ?   ? 231
0000250   ? 230   ?   ?   ? 200   ?   ?   ?   ? 211 213   ? 206 231   ?
0000260 232 204   s   m   a   l   i   ?   ? 236   ?   ? 213  \0  \0  \0
0000270 001  \0  \0  \0  \a  \0  \0  \0 001  \0  \0  \0 003  \0  \0  \0
0000280  \0  \0  \0  \0  \0  \0  \0  \0  \0 001 017  \a  \0  \0  \0  \0
0000290  \v  \0 001  \0 002  \0  \0  \0 210 002  \0  \0   (  \0  \0  \0
00002a0   b  \0  \0  \0  \0  \0  \0  \0  \0  \0 022   2 023 003   ?   ?
00002b0 030 004  \0  \0 001  \0  \0  \0  \0  \0 034 005 003  \0 001   &
00002c0   "  \a 004  \0   p 020 002  \0  \a  \0 032  \b 023  \0   n    
00002d0 003  \0 207  \0  \f  \a   n 020 004  \0  \a  \0  \f  \t   n    
00002e0 001  \0 220  \0 032 001 001  \0   n     001  \0 020  \0 016  \0
00002f0  \0  \0 001  \0  \0  \t 220 005 016  \0  \0  \0  \0  \0  \0  \0
0000300 001  \0  \0  \0  \0  \0  \0  \0 001  \0  \0  \0 024  \0  \0  \0
0000310   p  \0  \0  \0 002  \0  \0  \0  \b  \0  \0  \0   ?  \0  \0  \0
0000320 003  \0  \0  \0 005  \0  \0  \0   ?  \0  \0  \0 004  \0  \0  \0
0000330 001  \0  \0  \0 034 001  \0  \0 005  \0  \0  \0 005  \0  \0  \0
0000340   $ 001  \0  \0 006  \0  \0  \0 001  \0  \0  \0   L 001  \0  \0
0000350 002      \0  \0 024  \0  \0  \0   l 001  \0  \0 001 020  \0  \0
0000360 002  \0  \0  \0   p 002  \0  \0 003 020  \0  \0 002  \0  \0  \0
0000370 200 002  \0  \0 003      \0  \0 001  \0  \0  \0 210 002  \0  \0
0000380 001      \0  \0 001  \0  \0  \0 220 002  \0  \0  \0      \0  \0
0000390 001  \0  \0  \0   ? 002  \0  \0  \0 020  \0  \0 001  \0  \0  \0
00003a0   ? 002  \0  \0                                                
00003a4

我們還可以用-C顯示16進(jìn)制和ASCII碼

hexdump -C classes.dex

00000000  64 65 78 0a 30 33 35 00  12 8f b1 77 7a e9 19 91  |dex.035....wz...|
00000010  f2 0c ff ce a0 ce aa cd  8f 9d 80 7a ac 18 49 bf  |...........z..I.|
00000020  a4 03 00 00 70 00 00 00  78 56 34 12 00 00 00 00  |....p...xV4.....|
00000030  00 00 00 00 f8 02 00 00  14 00 00 00 70 00 00 00  |............p...|
00000040  08 00 00 00 c0 00 00 00  05 00 00 00 e0 00 00 00  |................|
00000050  01 00 00 00 1c 01 00 00  05 00 00 00 24 01 00 00  |............$...|
00000060  01 00 00 00 4c 01 00 00  38 02 00 00 6c 01 00 00  |....L...8...l...|
00000070  6c 01 00 00 74 01 00 00  81 01 00 00 84 01 00 00  |l...t...........|
00000080  92 01 00 00 96 01 00 00  ad 01 00 00 c1 01 00 00  |................|
00000090  d5 01 00 00 f0 01 00 00  04 02 00 00 07 02 00 00  |................|
000000a0  0b 02 00 00 20 02 00 00  28 02 00 00 2e 02 00 00  |.... ...(.......|
000000b0  34 02 00 00 39 02 00 00  42 02 00 00 4c 02 00 00  |4...9...B...L...|
000000c0  03 00 00 00 05 00 00 00  06 00 00 00 07 00 00 00  |................|
000000d0  08 00 00 00 09 00 00 00  0a 00 00 00 0c 00 00 00  |................|
000000e0  02 00 00 00 03 00 00 00  00 00 00 00 04 00 00 00  |................|
000000f0  04 00 00 00 78 02 00 00  0a 00 00 00 06 00 00 00  |....x...........|
00000100  00 00 00 00 0b 00 00 00  06 00 00 00 78 02 00 00  |............x...|
00000110  0b 00 00 00 06 00 00 00  70 02 00 00 05 00 01 00  |........p.......|
00000120  10 00 00 00 00 00 04 00  0f 00 00 00 01 00 03 00  |................|
00000130  11 00 00 00 04 00 02 00  00 00 00 00 04 00 01 00  |................|
00000140  0d 00 00 00 04 00 00 00  12 00 00 00 00 00 00 00  |................|
00000150  01 00 00 00 02 00 00 00  00 00 00 00 ff ff ff ff  |................|
00000160  00 00 00 00 f0 02 00 00  00 00 00 00 06 3c 69 6e  |.............<in|
00000170  69 74 3e 00 0b 48 65 6c  6c 6f 20 57 6f 72 6c 64  |it>..Hello World|
00000180  00 01 4c 00 0c 4c 48 65  6c 6c 6f 57 6f 72 6c 64  |..L..LHelloWorld|
00000190  3b 00 02 4c 4c 00 15 4c  6a 61 76 61 2f 69 6f 2f  |;..LL..Ljava/io/|
000001a0  50 72 69 6e 74 53 74 72  65 61 6d 3b 00 12 4c 6a  |PrintStream;..Lj|
000001b0  61 76 61 2f 6c 61 6e 67  2f 4f 62 6a 65 63 74 3b  |ava/lang/Object;|
000001c0  00 12 4c 6a 61 76 61 2f  6c 61 6e 67 2f 53 74 72  |..Ljava/lang/Str|
000001d0  69 6e 67 3b 00 19 4c 6a  61 76 61 2f 6c 61 6e 67  |ing;..Ljava/lang|
000001e0  2f 53 74 72 69 6e 67 42  75 69 6c 64 65 72 3b 00  |/StringBuilder;.|
000001f0  12 4c 6a 61 76 61 2f 6c  61 6e 67 2f 53 79 73 74  |.Ljava/lang/Syst|
00000200  65 6d 3b 00 01 56 00 02  56 4c 00 13 5b 4c 6a 61  |em;..V..VL..[Lja|
00000210  76 61 2f 6c 61 6e 67 2f  53 74 72 69 6e 67 3b 00  |va/lang/String;.|
00000220  06 61 70 70 65 6e 64 00  04 61 72 67 73 00 04 6d  |.append..args..m|
00000230  61 69 6e 00 03 6f 75 74  00 07 70 72 69 6e 74 6c  |ain..out..printl|
00000240  6e 00 08 74 6f 53 74 72  69 6e 67 00 0e e8 bf 99  |n..toString.....|
00000250  e6 98 af e4 b8 80 e4 b8  aa e6 89 8b e5 86 99 e7  |................|
00000260  9a 84 73 6d 61 6c 69 e5  ae 9e e4 be 8b 00 00 00  |..smali.........|
00000270  01 00 00 00 07 00 00 00  01 00 00 00 03 00 00 00  |................|
00000280  00 00 00 00 00 00 00 00  00 01 0f 07 00 00 00 00  |................|
00000290  0b 00 01 00 02 00 00 00  88 02 00 00 28 00 00 00  |............(...|
000002a0  62 00 00 00 00 00 00 00  00 00 12 32 13 03 ff ff  |b..........2....|
000002b0  18 04 00 00 01 00 00 00  00 00 1c 05 03 00 01 26  |...............&|
000002c0  22 07 04 00 70 10 02 00  07 00 1a 08 13 00 6e 20  |"...p.........n |
000002d0  03 00 87 00 0c 07 6e 10  04 00 07 00 0c 09 6e 20  |......n.......n |
000002e0  01 00 90 00 1a 01 01 00  6e 20 01 00 10 00 0e 00  |........n ......|
000002f0  00 00 01 00 00 09 90 05  0e 00 00 00 00 00 00 00  |................|
00000300  01 00 00 00 00 00 00 00  01 00 00 00 14 00 00 00  |................|
00000310  70 00 00 00 02 00 00 00  08 00 00 00 c0 00 00 00  |p...............|
00000320  03 00 00 00 05 00 00 00  e0 00 00 00 04 00 00 00  |................|
00000330  01 00 00 00 1c 01 00 00  05 00 00 00 05 00 00 00  |................|
00000340  24 01 00 00 06 00 00 00  01 00 00 00 4c 01 00 00  |$...........L...|
00000350  02 20 00 00 14 00 00 00  6c 01 00 00 01 10 00 00  |. ......l.......|
00000360  02 00 00 00 70 02 00 00  03 10 00 00 02 00 00 00  |....p...........|
00000370  80 02 00 00 03 20 00 00  01 00 00 00 88 02 00 00  |..... ..........|
00000380  01 20 00 00 01 00 00 00  90 02 00 00 00 20 00 00  |. ........... ..|
00000390  01 00 00 00 f0 02 00 00  00 10 00 00 01 00 00 00  |................|
000003a0  f8 02 00 00                                       |....|
000003a4

magic

標(biāo)識(shí)一個(gè)有效的dex文件,他的固定值為:64 65 78 0a 30 33 35 00,轉(zhuǎn)換為字符串為dex.035.
在電子取證中也稱(chēng)“文件簽名”

checksum

他是整個(gè)頭部的校驗(yàn)和。它被用來(lái)校驗(yàn)頭部是否損壞

signature

file_size

記錄包括dexHeader在內(nèi)的整個(gè)dex文件大小,用來(lái)計(jì)算偏移和方便定位某區(qū)段(section),他也有諸如唯一的標(biāo)識(shí)dex,因?yàn)樗莇ex文件中計(jì)算sha-1區(qū)段的一個(gè)組成部分

header_size

存放整個(gè)DexHeadeer結(jié)構(gòu)體的長(zhǎng)度,它也可用來(lái)計(jì)算下一個(gè)區(qū)段在文件中的起始位置,目前值為0x70

endian_tag

指定dex運(yùn)行環(huán)境的CPU字節(jié)序,存放的是一個(gè)固定值,所有dex文件都一樣的,值為:78 56 34 12,0x12345678,表示默認(rèn)采用little-endian字節(jié)序

link_size

link_off

當(dāng)多個(gè)class文件被編譯到一個(gè)dex文件是,他們會(huì)用到link_size和link_off,通常為0

可以看到上面的,link_off:為00 00 00 00

map_off

他指定了dexMapList結(jié)構(gòu)的文件偏移量

string_ids_size

是指string存放區(qū)段的大小,用來(lái)計(jì)算string區(qū)段起始位置-相對(duì)于dex文件加載基地址的偏移量

string_ids_off存放string區(qū)段的實(shí)際偏移量,單位字節(jié)。他可以幫助編譯器和虛擬機(jī)直接跳到這個(gè)區(qū)段,而不必從前讀到后,一直讀取到該位置。
type,prototype,method,class,data id的大小(size)和偏移量(offset)和string的作用一樣

每個(gè)字符串都對(duì)應(yīng)一個(gè)DexStringId數(shù)據(jù)結(jié)構(gòu),大小為4B,同時(shí)虛擬機(jī)可以通過(guò)頭文件中的string_ids_size知道當(dāng)前dex文件中字符串的總數(shù),也就是string_ids區(qū)域中DexStringId數(shù)據(jù)結(jié)構(gòu)的總數(shù),所以虛擬機(jī)可以通過(guò)簡(jiǎn)單的乘法運(yùn)算即可實(shí)現(xiàn)對(duì)字符串資源的索引,也可以根據(jù)kDexTypeStringIdItem獲取字符串

我們舉個(gè)例子來(lái)根據(jù)header里面的字符串信息索引字符串,還是以上面的classes.dex文件來(lái)分析:

根據(jù)stringIdsSize找到有多少個(gè)DexStringId(也就是有多少個(gè)字符串):

0x38:0x14,說(shuō)明有20個(gè)字符串

根據(jù)stringIdsOff查看DexStringId的偏移量:

0x3c:0x70,說(shuō)明DexStringId的開(kāi)始位置在0x70

讀取4個(gè)字節(jié):6c 01 00 00,轉(zhuǎn)為地址為0x16c,這就是第一個(gè)字符串的位置
在讀取4個(gè)字節(jié):74 01 00 00,0x174

分別獲取這個(gè)兩個(gè)位置的字符串:

06 3c 69 6e 69 74 3e 00:值為<init>\0,其中06表示后面有6個(gè)字符(不包括\0)
0b 48 65 6c 6c 6f 20 57 6f 72 6c 64:值為Hello World\0,0b表示有11個(gè)字符

我們發(fā)現(xiàn)每個(gè)字符串是使用“\0”分割的

首先他的開(kāi)始位置0x70,我們根據(jù)stringIdsSize的值得知接下來(lái)有20個(gè)字符,首先我們計(jì)算DexStringId的地址截止到:0x70+0x14(20)*4=0xc0(不包括0xc0)

先獲取DexStringId,然后在獲取地址位置的值

DexStringId偏移 String偏移
0x70 0x16c <init>
74 174 Hello World
78 181 L
7c 184 LHelloWorld;
80 192 LL
84 196 Ljava/io/PrintStream;
88 1ad Ljava/lang/Object;
8c 1c1 Ljava/lang/String;
90 1d5 Ljava/lang/StringBuilder;
94 1f0 Ljava/lang/System;
98 204 V
9c 207 VL
a0 20b [Ljava/lang/String;
a4 220 append
a8 228 args
ac 22e main
b0 234 out
b4 239 println
b8 242 toString
bc 24c

上面的字符串并非普通的ASCII字符串,他們是由MUTF-8編碼來(lái)表示的,更詳細(xì)的介紹參考這篇文章

dex文件結(jié)構(gòu)分析

我們采用前面的classes.dex文件作為演示對(duì)象

dalvik虛擬機(jī)解析dex文件的內(nèi)容,最終將其映射成DexMapList數(shù)據(jù)結(jié)構(gòu),DexHeader中的mapOff字段指定了DexMapList結(jié)構(gòu)在dex在文件中的偏移,他的申明如下:

/dalvik/libdex/DexFile.h

struct DexMapList {
    u4  size;               /* 個(gè)數(shù) */
    DexMapItem list[1];     /* DexMapItem的結(jié)構(gòu) */
};

其中size字段表示dex接來(lái)下有多少個(gè)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               = 0x0,
    kDexTypeStringIdItem             = 0x1,
    kDexTypeTypeIdItem               = 0x2,
    kDexTypeProtoIdItem              = 0x3,
    kDexTypeFieldIdItem              = 0x4,
    kDexTypeMethodIdItem             = 0x5,
    kDexTypeClassDefItem             = 0x6,
    kDexTypeMapList                  = 0x0,
    kDexTypeTypeList                 = 0x1,
    kDexTypeAnnotationSetRefList     = 0x2,
    kDexTypeAnnotationSetItem        = 0x3,
    kDexTypeClassDataItem            = 0x0,
    kDexTypeCodeItem                 = 0x1,
    kDexTypeStringDataItem           = 0x2,
    kDexTypeDebugInfoItem            = 0x3,
    kDexTypeAnnotationItem           = 0x4,
    kDexTypeEncodedArrayItem         = 0x5,
    kDexTypeAnnotationsDirectoryItem = 0x6,
};

這里我們以上面的clsses.dex來(lái)分析,DexHeader結(jié)構(gòu)的mapOff字段為f8 02 00 00,根據(jù)小端序,他的值為0x2f8,讀取出的雙字值為0e 00 00 00(0x0e),表示接下來(lái)有14個(gè)DexMapItem結(jié)構(gòu),接著在讀取0x2fc值為:0x00表示這個(gè)DexMapItem類(lèi)型是kDexTypeHeaderItem,在讀取0x2fe值為:0x00這個(gè)字段沒(méi)有使用。在讀取0x300值為:01 00 00 00表示有一個(gè),在0x304讀取:00 00 00 00表示偏移為0x0

根據(jù)上面的規(guī)則我們整理除了14個(gè)Item

類(lèi)型 個(gè)數(shù) 偏移
kDexTypeHeaderItem 0x1 0x0
kDexTypeStringIdItem 0x14 0x70
kDexTypeTypeIdItem 0x8 0xc0
kDexTypeProtoIdItem 0x5 0xe0
kDexTypeFieldIdItem 0x1 0x11c
kDexTypeMethodIdItem 0x5 0x124
kDexTypeClassDefItem 0x1 0x14c
kDexTypeStringDataItem 0x14 0x16c
kDexTypeTypeList 0x2 0x270
kDexTypeAnnotationSetItem 0x2 0x280
kDexTypeDebugsssInfoItem 0x1 0x288
kDexTypeCodeItem 0x1 0x290
kDexTypeClassDataItem 0x1 0x2f0
kDexTypeMapList 0x1 0x2f8

對(duì)比文件我們發(fā)下DexHeader就是kDexTypeHeaderItem描述的結(jié)構(gòu),他占用了文件前0x70個(gè)字節(jié)空間,接下來(lái)的kDexTypeStringIdItem~kDexTypeClassDefItem與DexHeader中對(duì)應(yīng)字段值是一樣的

kDexTypeStringIdItem

對(duì)應(yīng)DexHeader中的stringIdsSize與stringIdsOff字段,表示從0x70位置起有連續(xù)0x14個(gè)DexStringId:

struct DexStringId {
    u4 stringDataOff;      /* file offset to string_data_item */
};

他只有一個(gè)stringDataOff字段,指向字符串?dāng)?shù)據(jù)的偏移位置,開(kāi)始地址為0x70+(14*4)=0xc0,所以我們最后一個(gè)dexStringId的偏移為:0xbc,我們根據(jù)此信息整理了所以字符串:

dexStringId偏移 真實(shí)字符串偏移 字符串 索引
0x70 0x16c <init> 0x0
74 174 Hello World 1
78 181 L 2
7c 184 LHelloWorld; 3
80 192 LL 4
84 196 Ljava/io/PrintStream; 5
88 1ad Ljava/lang/Object; 6
8c 1c1 Ljava/lang/String; 7
90 1d5 Ljava/lang/StringBuilder; 8
94 1f0 Ljava/lang/System; 9
98 204 V a
9c 207 VL b
a0 20b [Ljava/lang/String; c
a4 220 append d
a8 282 args e
ac 22e main f
b0 234 out 10
b4 239 println 11
b8 242 toString 12
bc 24c 這是一個(gè)手寫(xiě)的smali實(shí)例 13

kDexTypeTypeIdItem

他對(duì)應(yīng)DexHeader中的typeIdsSize和typeIdsOff字段,指向的結(jié)構(gòu)體為:

struct DexTypeId {
    u4  descriptorIdx;      /* 指向DexStringId列表的索引 */
};

對(duì)應(yīng)的字符串代表具體的類(lèi)型,我們根據(jù)上面字段可知:從0xc0起有0x8個(gè)DexTypeId結(jié)構(gòu):

類(lèi)型索引 字符串索引 字符串 DexTypeId偏移
0 0x3 LHelloWorld; 0xc0
1 0x5 Ljava/io/PrintStream; 0xc4
2 0x6 Ljava/lang/Object; 0xc8
3 0x7 Ljava/lang/String; 0xcc
4 0x8 Ljava/lang/StringBuilder; 0xd0
5 0x9 Ljava/lang/System; 0xd4
6 0xa L 0xd8
7 0xc [Ljava/lang/String; 0xdc

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ù)列表

struct  {
    u4  size;               /* 接下來(lái)DexTypeItem的個(gè)數(shù) */
    DexTypeItem list[1];    /* DexTypeItem結(jié)構(gòu) */
};

DexTypeItem聲明:

struct DexTypeItem {
    u2  typeIdx;            /* 指向DexTypeId列表的索引 */
};

根據(jù)上面的信息我們得知從0xe0開(kāi)始有0x5個(gè)DexProtoId對(duì)象

索引 方法聲明 返回類(lèi)型 參數(shù)列表 paramOff偏移
0 L Ljava/lang/String; 無(wú)參數(shù) 0
1 LL Ljava/lang/StringBuilder; Ljava/lang/String; 0x278
2 V L 無(wú)參數(shù)
3 VL L Ljava/lang/String; 0x278
4 VL L [Ljava/lang/String; 0x270

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列表 */
};

可以看見(jiàn)這個(gè)結(jié)構(gòu)的數(shù)據(jù)全部是索引信息,指明了字段所在的類(lèi),字段類(lèi)型,字段名,通過(guò)上面的信息我們發(fā)現(xiàn)從0x11c有一個(gè)kDexTypeFieldIdItem

類(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),方法聲明和方法名。從0x124有0x5個(gè)kDexTypeMethodIdItem

類(lèi)類(lèi)型 方法聲明 方法名
LHelloWorld; VL main
Ljava/io/PrintStream; VL println
Ljava/lang/StringBuilder; V <init>
Ljava/lang/StringBuilder; LL append
Ljava/lang/StringBuilder; L toString

kDexTypeClassDefItem

對(duì)應(yīng)DexHeader中的classDefsSize和classDefsOff字段,指向結(jié)構(gòu)體DexClassDef

struct DexClassDef {
    u4  classIdx;           /* 類(lèi)的類(lèi)型,指向DexTypeId列表索引 */
    u4  accessFlags;        /* 訪(fǎng)問(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是一個(gè)索引值,表示類(lèi)的類(lèi)型

accessFlags是類(lèi)的訪(fǎng)問(wèn)標(biāo)志,他是以ACC_開(kāi)頭的枚舉值

superclassIdx是父類(lèi)的類(lèi)型

interfacesOff如果類(lèi)含有接口聲明或?qū)崿F(xiàn),他就會(huì)指向一個(gè)DexTypeList結(jié)構(gòu),否則為0

sourceFileIdx是類(lèi)所在源文件名稱(chēng)

annotationsOff字段指向注解目錄結(jié)構(gòu),根據(jù)類(lèi)型不同有注解類(lèi),注解方法,注解字段,注解參數(shù)。如果沒(méi)有注解,值為0

classDataOff指向DexClassData結(jié)構(gòu),是類(lèi)的數(shù)據(jù)部分

staticValuesOff記錄了類(lèi)中的靜態(tài)數(shù)據(jù)

DexClassData

聲明在DexClass.h中

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)型與訪(fǎng)問(wèn)標(biāo)志,聲明如下:

struct DexField {
    u4 fieldIdx;    /* 指向DexFieldId列表的索引 */
    u4 accessFlags; //訪(fǎng)問(wèn)標(biāo)志
};

DexMethod

描述了方法的原型,名稱(chēng),訪(fǎng)問(wèn)標(biāo)志和代碼數(shù)據(jù)塊,聲明如下:

struct DexMethod {
    u4 methodIdx;    /* 指向DexMethodId列表的索引 */
    u4 accessFlags;
    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] */
};

registersSize指令了方法使用的寄存器個(gè)數(shù),對(duì)應(yīng)smali中的.locals 2

insSize指定了方法的參數(shù)個(gè)數(shù),對(duì)應(yīng).param p1, "noteId" # Ljava/lang/String;

如果一個(gè)方法使用5個(gè)寄存器,其中2有兩個(gè)參數(shù)寄存器,而該方法調(diào)用另一個(gè)方法使用了20個(gè)寄存器,那么虛擬機(jī)在分配該方法寄存器是會(huì)在分配20個(gè)寄存器

triesSize指定了方法中Try/Catch格式

debugInfoOff指向調(diào)試信息偏移,如果有,解析函數(shù)為dexDecodoDebugInfo在DexDebugInfo.cpp文件

insnsSize接下來(lái)指令個(gè)數(shù)

insns真正的代碼部分

根據(jù)上面的信息,我們發(fā)現(xiàn)從0x14c有0x1個(gè)kDexTypeClassDefItem:

第一個(gè)字段值為0x0,表示對(duì)應(yīng)DexType中的索引為0,值為L(zhǎng)HelloWorld;,表示類(lèi)名為HelloWorld

第二個(gè)字段值為0x1表示訪(fǎng)問(wèn)標(biāo)識(shí)符為ACC_PUBLIC

第三個(gè)字段值0x2表示父類(lèi),表示指向DexType的索引,值為L(zhǎng)java/lang/Object;,表示父類(lèi)?為java/lang/Object;

第四個(gè)字段值0x0,表示沒(méi)有接口

第五個(gè)字段值

第六個(gè)字段值0x0表示沒(méi)有注解

第七個(gè)字段值0x2f0表示DexClassData的偏移

第八個(gè)字段值0x0表示沒(méi)有靜態(tài)值

我們繼續(xù)分析DexClassData

DexClassDataHeader為4個(gè)uleb128數(shù)據(jù)類(lèi)型,從0x2f0開(kāi)始值為:0,0,1,0,表示靜態(tài)字段為0個(gè),實(shí)例字段為0個(gè),1個(gè)直接方法,0個(gè)虛方法

由于沒(méi)有靜態(tài)字段,實(shí)例字段,虛方法,所以我們直接分析DexMethod

從0x2f4開(kāi)始為一個(gè)直接方法相關(guān)數(shù)據(jù),第一個(gè)字段為0,指向的DexMethod列表第0個(gè),也就是main方法。第二個(gè)值09,表示public+static,具體的看下面表:

現(xiàn)在來(lái)說(shuō)下accessFlags在字節(jié)碼中的計(jì)算方式:
access_flags的計(jì)算公式為:access_flags = flagA | flagB | flagB ...

標(biāo)志名稱(chēng) 標(biāo)志值 含義
ACC_PUBLIC 0x0001 public
ACC_PRIVATE 0x0002 private
ACC_PROTECTED 0x0004 protected
ACC_STATIC 0x0008 static
ACC_FINAL 0x0010 final
ACC_VOLATILE 0x0040 volatile
ACC_TRANSIENT 0x0080 transient
ACC_SYNTHETIC 0x1000 是否是編譯器自動(dòng)生成的
ACC_ENUM 0x4000 enum

第三個(gè)字段為90 05,值為0x290,我們從0x290開(kāi)始分析DexCode,值為:

0b 00=11

01 00=1

02 00=2

00 00=0

表示使用了11個(gè)寄存器,1個(gè)參數(shù)寄存器,調(diào)用其他方法使用了2個(gè)寄存器,沒(méi)有Try/Catch

88 02 00 00=0x288,debugInfoOff

28 00 00 00=0x28,insnsSize,指令個(gè)數(shù),以2字節(jié)為單位

我們先讀取一個(gè)兩個(gè)字節(jié)6200,查看dalvik bytecode發(fā)現(xiàn)為sget-object,指令格式為21c,查詢(xún)instruction formats可以看到指令格式為:

AA|op BBBB

可以看出他需要兩個(gè)16位,21C對(duì)應(yīng)有以下幾種格式:

op vAA, type@BBBB check-cast
op vAA, field@BBBB const-class
op vAA, string@BBBB const-string

由于我們的指令是sget,所有這條指令格式為op vAA, field@BBBB

在讀取兩個(gè)字節(jié)0000,表示在字段索引0,所以這條指令為

sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;

繼續(xù)從0x2a4分析:

00 00,查看bytecode為10x,在查詢(xún)指令格式為:

??|op 10x op,可以看出只需要兩個(gè)字節(jié),所以這條指令為:nop

00 00:nop

00 00:nop

12 32:op=12,A=2,B=3查詢(xún)字節(jié)碼代碼格式,const/4 vA, #+B,格式為11n,最終翻譯為:const/4 v2, 0x3

13 03:op=13,const/16 vAA, #+BBBB,格式21s,格式為AA|op BBBB,所以需要兩個(gè)字節(jié),在讀取兩個(gè)字節(jié),ff ff,最終指令格式為:const/16 v3, -0x1

剩下的指令可以按照上面的步驟翻譯完

18 04:

const-wide vAA, #+BBBBBBBBBBBBBBBB

AA|op
BBBBlo BBBB BBBB BBBBhi

0000 0100 0000 0000
每?jī)晌徽{(diào)換位置:
0000 0010 0000 0000
從低位到高位排列
0000 0000 0010 0000

最后這條指令為:

const-wide v4, 0x100000

如果我的文章對(duì)來(lái)帶來(lái)的幫助或者有不明白的地方,可加QQ群:129961195,大家一起交流,下一篇文章我們講解odex文件格式

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 國(guó)家電網(wǎng)公司企業(yè)標(biāo)準(zhǔn)(Q/GDW)- 面向?qū)ο蟮挠秒娦畔?shù)據(jù)交換協(xié)議 - 報(bào)批稿:20170802 前言: 排版 ...
    庭說(shuō)閱讀 12,525評(píng)論 6 13
  • 什么是dex文件 他是Android系統(tǒng)的可執(zhí)行文件,包含應(yīng)用程序的全部操作指令以及運(yùn)行時(shí)數(shù)據(jù)。 由于dalvik...
    _楓_閱讀 3,036評(píng)論 0 2
  • 點(diǎn)擊查看原文 Web SDK 開(kāi)發(fā)手冊(cè) SDK 概述 網(wǎng)易云信 SDK 為 Web 應(yīng)用提供一個(gè)完善的 IM 系統(tǒng)...
    layjoy閱讀 14,416評(píng)論 0 15
  • Ubuntu的發(fā)音 Ubuntu,源于非洲祖魯人和科薩人的語(yǔ)言,發(fā)作 oo-boon-too 的音。了解發(fā)音是有意...
    螢火蟲(chóng)de夢(mèng)閱讀 100,818評(píng)論 9 468
  • 一個(gè)人走得更快,一群人走得更穩(wěn)!無(wú)論什么時(shí)候我們都相信一個(gè)優(yōu)秀的團(tuán)隊(duì)是克服一切困難的基礎(chǔ)。優(yōu)秀團(tuán)隊(duì)的打造不是說(shuō)出來(lái)...
    蒼葭閱讀 1,088評(píng)論 0 0

友情鏈接更多精彩內(nèi)容