Dalvik指令集代码格式及调用规范

1、Dalvik指令格式

指令语法由指令的位描述与指令格式标识来决定。

位描述

约定如下:

  • 每16位的字采用空格分隔开来。
  • 每个字母表示四位,每个字母按顺序从高字节开始,排列到低字节。每四位之间可能使用竖线“|”来表示不同的内容。
  • 顺序采用A-Z的单个大写字母作为一个4位的操作码,op表示一个8位的操作码。
  • “Ø”来表示这字段的所有位为0值。

例:A|G|op BBBB F|E|D|C
此条指令由三个16位的字组成。

  • 第一个16位:A|G|op,高8位为A和G,低字节为操作码op。
  • 第二个16位:BBBB,表示一个16位的偏移值。
  • 第三个16位:F、E、D、C,这里表示寄存器参数。

指令格式标识

约定如下:

  • 指令格式标识大多由三个字符组成,前两个是数字,最后一个是字母。
  • 第一个数字表示指令由多少个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指令。

ps:在Android4.0源码Dalvik/docs目录下提供了一份文档instruction-formats.html,里面详细列举了Dalvik指令的所有格式。

2、DEX文件反汇编工具

主流的有BakSmali和Dedexer。
采用上一节得到的Hello.dex。
Hello.java内容如下

public class Hello {
    public int foo(int a, int b) {
        return (a + b) * (a - b);
    }

    public static void main(String[] argc) {
        Hello hello = new Hello();
        System.out.println(hello.foo(5, 3));
    }
}

执行命令行:java -jar baksmali.jar -o baksmaliout Hello.dex
生成baksmaliout目录及目录下的Hello.smali文件,使用sublime打开它,foo函数部分如下:

# virtual methods
.method public foo(II)I
    .registers 5
    .parameter
    .parameter

    .prologue
    .line 3
    add-int v0, p1, p2

    sub-int v1, p1, p2

    mul-int/2addr v0, v1

    return v0
.end method

注:BakSmali生成的方法代码以.method指令开始,以.end method指令结束,根据方法类型的不同,在方法指令开始前可能会用井号“#”加以注释。如# virtual methods表示这是一个虚方法,# direct methods表示这是一个直接方法。

ps:BakSmali生成的字段代码以.field指令开头,根据字段类型的不同,在字段指令的开始可能会用到井号“#”加以注释,如#instance fields表示这是一个实例字段,#static fields表示这是一个静态字段。

执行命令行:java -jar ddx.jar -d ddxout Hello.dex
生成ddxout目录及目录下的Hello.ddx文件,使用sublime打开它,foo函数部分如下:

.method public foo(II)I
.limit registers 5
; this: v2 (LHello;)
; parameter[0] : v3 (I)
; parameter[1] : v4 (I)
.line 3
    add-int v0,v3,v4
    sub-int v1,v3,v4
    mul-int/2addr   v0,v1
    return  v0
.end method

两者在语法细节上有如下不同点:

语法 BakSmali Dedexer
寄存器数目表示 .registers .limit registers
this引用表示 寄存器p0 寄存器v2
函数参数指定 一条.parameter指令指定一个参数 parameter数组指明参数寄存器
指明代码起始处 .prologue
寄存器表示法 p命名法 v命名法

BakSmali功能:反汇编功能、支持使用Smali工具打包反汇编代码重新生成dex文件(可用于apk文件的修改、补丁、破解等场合)。

3、Dalvik寄存器

Dalvik中用到的寄存器都是32位的,支持任何类型,64位类型用2个相邻寄存器表示。

ØØ|op AAAA BBBB的指令的语法为op vAAAA vBBBB,每个大写字母代表4位,AAAA最大值为2的16次方,寄存器采用v0作起始值,因此其取值范围为v0-v65535。

Dalvik虚拟机为每个进程维护一个调用栈,这个调用栈的一个作用就是用来“虚拟”寄存器。虚拟机通过处理字节码,对寄存器进入读与写的操作,其实都是在写栈空间。

4、寄存器命令法——v命名法和p命名法

Dalvik虚拟机参数传递方式中的规定:
假设一个函数使用到M个寄存器,并且该函数有N个参数,参数使用最后的N个寄存器,局部变量使用从v0开始的前M-N个寄存器。

  • v命名规则:
    所有寄存器从v0开始命名,依次递增。
    对于foo()函数,v0,v1表示函数的局部变量寄存器,v2表示被传入的Hello对象的引用,v3和v4分别表示两个传入的整形参数。

  • p命名规则:
    引入的参数命名从p0开始,依次递增。对于foo()函数,v0,v1表示函数的局部变量寄存器,p0表示被传入的Hello对象的引用,p1和p2分别表示两个传入的整形参数。

p命名法更容易让人判断寄存器是局部变量寄存器还是参数寄存器。

5、Dalvik字节码的类型、方法和字段表示方法

类型

Dalvik字节码类型分为:基本类型与引用类型。除了对象与数组属于引用类型外,其它的Java类型都是基本类型。

语法      含义
V       void,只用于返回类型
Z       boolean
B       byte
S       short
C       char
I       int
J       long
F       float
D       double
L       java类类型
[       数组类型

L类型可以表示java类型中的任何类。这些类在java代码中以package.name.ObjectName方式引用,到了Dalvik汇编代码中,它们以Lpackage/name/ObjectName;形式表示,注意最后有个分号,L表示后面跟着一个java类,package/name表示对象所在的包,Objectname表示对象的名称,最后的分号表示对象名结束,例如:Ljava/lang/String;相当于java.lang.String

[类型可以表示所有基本类型数组。[后面紧跟基本类型描述符,如[I表示一个整型一维数组,相当与java中的int[],多个[在一起时可以表示多维数组。记住多维数组最大为255个。

方法

描述方法:方法名、类型参数和返回值
格式如下:

Lpackage/name/ObjectName;->MethodName(III)Z
  • Lpackage/name/ObjectName;:表示一个类型
  • MethodName:具体的方法名
  • (III)Z:方法的签名部分,其中括号内的III为方法的参数,在此为三个整型参数,Z表示方法的返回类型为boolean类型。

例:

method(I[[IILjava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;

method:方法名
括号里边的为方法参数,即
Iint
[[Iint[][]
Ljava/lang/String;String
[Ljava/lang/Object;Object[]
返回值:
Ljava/lang/String;String
还原来就是:

String method(int ,int[][],int ,String,Object[])

字段

字段的格式:

Lpackage/name/ObjectName;->FieldName:Ljava/lang/String;

Lpackage/name/ObjectName;:类型
FieldName:字段名
Ljava/lang/String;:字段类型
字段名与字段类型中间用冒号“:”隔开

参考书籍

《Android软件安全与逆向分析》

你可能感兴趣的:(读书笔记)