首页 > 其他 > 详细

DEX文件格式

时间:2014-07-01 17:13:13      阅读:539      评论:0      收藏:0      [点我收藏+]

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

 

下面大家继续。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

 

 

 

DEX文件格式,布布扣,bubuko.com

DEX文件格式

原文:http://www.cnblogs.com/Cnforce/p/3817911.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!