Dalvik虚拟机为自己专门设计了一套指令集,并且指定了自己的指令格式与调用规范。我们将Dalvik指令集组成的代码称为Dalvik汇编代码,将这种代码表示的语言称为Dalvik汇编语言(Dalvik汇编语言并不是正式的语音,只是Dalvik指令代码的一种称呼)。本文主要介绍Dalvik汇编语言的相关基础知识。
Dalvik指令格式
一段Dalvik汇编代码由一系列Dalvik指令组成,指令语法由指令的位描述与指令格式标识来决定。位描述约定如下:
- 每16位的字采用空格分隔开来;
- 每个字母表示四位,每个字母按顺序从高字节开始,排列到低字节。每四位之间可能使用竖线“|”来表示不同的内容;
- 顺序采用A~Z的单个大写字母作为一个4位的操作码,op表示一个8位的操作码;
- “∅”来表示这字段所有位为0值;
以指令格式“A|G|op BBBB F|E|D|C”为例:
指令中间有两个空格,每个分开的部分大小为16位,所以这条指令由三个16为的字组成。第一个16为是“A|G|op”,高8位由A与G组成,低字节由操作码op组成。第二个16位由BBBB组成,他表示一个16位的偏移值。第三个16位分别由F、E、D、C共四个4字节组成,在这里它们表示寄存器参数。
单独使用位标识还无法确定一条指令,必须通过指令格式标识来指定的格式编码。它的约定如下:
- 指令格式标识大多由三个字符组成,前两个是数字,最后一个是字母;
- 第一个数字是表示指令有多少个16位字组成;
- 第二个数字是表示指令最多使用寄存器的个数。特殊标记“r”标识使用一定范围内的寄存器;
- 第三个字母位类型码,表示指令用到的额外数据的类型。取值如下表;
助记符 | 位大小 | 说明 |
b | 8 | 8位有符号立即数 |
c | 16,32 | 常量池索引 |
f | 16 | 接口常量(仅对静态链接格式有效) |
h | 16 | 有符号立即数(32位或64位数的高值位,低值位为0) |
i | 32 | 立即数,有符号整数或32为浮点数 |
l | 64 | 立即数,有符号整数或64位双精度浮点数 |
m | 16 | 方法常量(仅对静态链接格式有效) |
n | 4 | 4位的立即数 |
s | 16 | 短整数立即数 |
t | 8,16,32 | 跳转、分支 |
x | 0 | 无额外数据 |
还有一种特殊的情况是末尾可能会多出另一个字母,如果是字母s表示指令采用静态链接,如果是字母i表示指令应该被内联处理。
以指令格式标识22x为例:
第一个数字2表示指令有两个16为字组成,第二个数字2表示指令使用到2个寄存器,第三个字母x表示没有使用到额外的数据。
另外,Dalvik指令对语法做了一些说明,它约定如下:
- 每条指令从操作码开始,后面紧跟着参数,参数个数不定,每个参数之间采用逗号分开;
- 每条指令的参数丛指令第一部分开始,op位于低8位,高8位可以是一个8位的参数,也可以是两个4位的参数,还可以为空,如果指令超过16位,则后面部分依次作为参数;
- 如果参数采用“vX”的方式表示,表明它是一个寄存器,如v0、v1等。这里采用v而不用r是为了避免与基于该虚拟机架构本身的寄存器命名产生冲突,如ARM架构寄存器命名采用r开头;
- 如果参数采用“#+X”的方式表示,表明它是一个常量数字;
- 如果参数采用“+X”的方式表示,表明它是一个相对指令的地址偏移;
- 如果参数采用“kind@X”的方式表示,表明它是一个常量池索引值。其中kind表示常量池类型,它可以是“String”(字符串常量池索引)、“type”(类型常量池索引)、“field”(字段常量池索引)、或者“meth”(方法常量池索引);
以指令“op vAA,string@BBBB”为例:
指令用到了1个寄存器参数vAA,并且还附加了一个字符串常量池索引string@BBBB,其实这条指令格式代表着const-string指令。
DEX文件的反汇编工具
目前DEX可执行文件主流的反汇编工具有BakSmail与DeDexer。两者的反汇编效果都不错。在语法上也有着很多相似处。下面通过代码对比两者的语法差异,测试代码采用Hello.java,代码如下:
public class Hello {
public int foo(int a,int b){
return (a + b) * (a - b);
}
public static void main(String[] args) {
Hello hello = new Hello();
System.out.println(hello.foo(5,3));
}
}
首先使用dx工具生成Hell.dex文件然后在命令提示符下输入以下命令使用baksmali.jar反编译Hello.dex: