Dalvik 字节码的读取

前言

想要读取 Dalvik 字节码,需要参考两篇说明文档,分别是:字节码格式Dalvik 可执行指令格式,下面以我的上篇博文 Android 虚拟机 — .dex 文件格式 中的例子为例,实战讲解一下 Dalvik 字节码怎么读取。

例一

1.1

70 10 03 00 00 00 0e 00

上面是例子的 .dex 文件中第一个 code_item 对应的字节码数组。
首先,第一个 8 位运算码为 70,因此在 字节码格式 中查找 70 所对应的格式、助记符、语法等,如下图所示:

这里写图片描述

由此可知,70 对应的指令格式为 35c,关于 35c 的含义,有下面一段解释:

  • 大多数格式 ID 包含三个字符:前两个是十进制数,最后一个是字母。第一个十进制数表示格式中 16 位代码单元的数量。第二个十进制数表示格式包含的最大寄存器数量(使用最大值是因为某些格式可容纳的寄存器数量为可变值),

类型代码字母的完整列表如下:

这里写图片描述

因此,35c 对应的含义为:格式的长度为 3 (48位),最多包含 5 个寄存器引用,另外还有一个常量池索引。
Dalvik 可执行指令格式 中去查找 35c,得到下图:

这里写图片描述

即 35c 对应的指令格式为 A|G|op BBBB F|E|D|C,由 35c 的含义可得,第一条指令对应的字节码数组应为:

70 10 03 00 00 00

由于 .dex 文件默认采用小端字节序,所以将其转换正常应为:

10 70 00 03 00 00

与指令格式对应得:

  • A = 1,即对应可执行指令为 [A=1] invoke-direct {vC}, meth@BBBB
  • G = 0,F|E|D|C 都为零,即上面的 vC 应为 v0
  • BBBB = 0003,即 BBBB 索引 method_ids 表(见 Android 虚拟机 — .dex 文件格式)的 index 为 3 的方法,即 Ljava/lang/Object;.< init >:()V

所以,第一条指令应解释为 invoke-direct {v0}, Ljava/lang/Object;.< init >:()V // method@0003

1.2

下面就只剩下:

0e 00

查看可得,0e 为 return-void,对应指令格式为 10x,即其格式长度为 1,不包含寄存器,无额外数据;因此,本条指令就为 return-void

例二

2.1

字节码数组为:

62 00 01 00 1a 01 02 00 6e 20 02 00 10 00 0e 00

第一个运算码为 62,对应的指令格式为 21c(AA|op BBBB,sget-object vAA, field@BBBB),即其格式长度为 2,包含一个寄存器, 有常量池索引,将此条指令转换为正常形式应为:

00 62 00 01

即:

  • AA = 00,即 vAA 为 v0
  • BBBB = 0001,即 BBBB 索引 field_ids 表(见 Android 虚拟机 — .dex 文件格式)的 index 为 1 的字段,即 Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0001

所以,此条指令为: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0001

2.2

剩余字节码数组为:

1a 01 02 00 6e 20 02 00 10 00 0e 00

第一个运算码为 1a,对应的指令格式为 21c (AA|op BBBB,const-string vAA, string@BBBB),将此条指令转换为正常形式应为:

01 1a 00 02

所以,此条指令为: const-string v1, “Hello! My Dex!\n” // string@0002

2.3

剩余字节码数组为:

6e 20 02 00 10 00 0e 00

第一个运算码为 6e,对应的指令格式为 35c (A|G|op BBBB F|E|D|C),将此条指令转换为正常形式应为:

20 6e 00 02 00 10
  • A = 2,即对应可执行指令为 [A=2] invoke-virtual {vC, vD}, meth@BBBB
  • G = 0,F|E|D|C = 0010,即 vC、vD 分别为 v0、v1
  • BBBB = 0002,即 BBBB 索引 method_ids 表(见 Android 虚拟机 — .dex 文件格式)的 index 为 2 的方法,即 Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0002

所以,此条指令为: invoke-virtual {v0, v1},Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0002

2.4

最后一条指令同 1.2 节,为 return-void
因此,例子的 .dex 文件中第二个 code_item 对应的指令为:

  • sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0001
  • const-string v1, “Hello! My Dex!\n” // string@0002
  • invoke-virtual {v0, v1},Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0002
  • return-void

总结

google 给的两篇文档,我理解的也不是很透彻,上面的结论很大程度上是我结合实践得出来的,如果有误欢迎大家指出。

猜你喜欢

转载自blog.csdn.net/u013989732/article/details/78326313