1 .DEX文件中使用的數據類型
u1,u2,u4,u8表示占某固定字節的無符號數
sleb128表示有符號的LEB128類型數據,uleb128表示無符號的LEB128,uleb128p1表示無符號的LEB128+1 ,
關於LEB128:
LEB128是一種DEX文件中特有的用來存儲最大32位數的數據類型,他的特點是字節數可以1-5可變。每個字節的第一位用來表示是否用到下個字節,剩下的7位為有效位,所以第5個字節的收位一定不能為1。有符號LEB128(SLEB128)的符號由最后字節的有效位的最高位決定。也就是最后字節的第二位(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) { //判斷第一個字節的第一位是不是1,7F=二進制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-endian為0X012345678.link_size為0為0就表示是靜態連接。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
然后緊接着便是0x11個dexmapitem。Dexmapitem的第一個元素為類型
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的這是幾十M 。太大了。換老趙手寫的第三章的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
這里有0F個string_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 |
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 |
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
下面大家繼續。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。