原文链接:https://github.com/JesusFreke/smali/wiki

一、概述

    smali和backsmali是dalvik虚拟机使用的dex文件的汇编器和反汇编器,它宽松的语法基于Jasmin/dedexer语法,并支持dex格式的所有功能。

二、寄存器

    dalvik字节码中,寄存器是32位的,可以存放任意类型的值。64位数据类型可以使用连个寄存器来存放。

(1)指定一个方法中寄存器的数量

    有两种方式可以指定一个方法中可用寄存器的数量。.register指令指定了方法中寄存器的总数,另一种方式,.locals指令指定了方法中非参数寄存器的数量。寄存器的数量包括存放方法参数的寄存器。

(2)方法的参数是如何传入一个方法

    当一个方法被执行,方法的参数将被存放到最后n个寄存器中。如果一个方法有两个参数,五个寄存器(v0-v4),则参数将被存放到最后两个寄存器v3和v4中。

    非静态方法的第一个参数总是执行该方法的对象。例如,非静态方法LMyObject;->callMe(II)V中有两个×××参数,但是它在两个×××参数之前隐含一个LMyObject;参数,所以该方法中总共有三个参数。

    我们可以指定这个方法中有五个寄存器,可以使用.registers 5指令或者.local 2指令(连个local寄存器和3个参数寄存器)。一旦这个方法被执行,执行该方法的对象将会被存放到v2中,第一个×××参数存放到v3中,第二个×××参数被存放到v4中。

    对于静态方法也是一样的,只是没有隐含该参数。

(3)寄存器命名

    存在两种寄存器命名方案,正常的v命名方案和针对参数的p命名方案。p命名方案中第一个寄存器是方法中第一个参数。上个例子中有三个参数和总共五个寄存器。下表分别是v命名方案和p命名方案:

LocalParam
v0
第一个local寄存器
v1
第二个local寄存器
v2p0第一参数寄存器
v3p1第二个参数寄存器
v4p2第三个参数寄存器

(4)引入参数寄存器的动机

    p命名方案的引进是很实用的事情,用来解决在编辑smali代码时一个普通的问题。

    例如存在一个有一定数量参数的方法,你将在该方法中添加一些代码并且你发现你需要一个额外的寄存器。此时你想:“没什么大不了的,我只需要修改.register指令就可以了”。不幸的的是,它并没有这么简单。注意,参数被存放在最后的寄存器中。如果你增加了寄存器的数量,也就是说你改变了参数get和put的位置。所以你不得不改变.register指令并且必须对每个参数寄存器进行重新编码。但是如果你使用p命名方案,你可以简单的改变寄存器的数量而不需要担心重新编码已经存在的寄存器。

    注意:默认情况下,baksmali使用p命名方法。如果你想强制baksmali使用v命名方案,你可以使用

-p/--no-parameter-register选项。

(5)Long、Double

    前面提到过,long和double是64位值,需要两个寄存器。这一点是非常重要的。例如,你有个一非静态方法:LMyObject;->MyMethod(IJZ)V.该方法的参数为:LMyObject;、int、long、bllo。所以该方法需要5个寄存器存放所有的参数:

RegisterType
p0this
p1I
p2,p3J
p4Z

三、Types、Methods、Fields

(1)Types

    dalvik的字节码有两大类型:primitive types和reference types。Reference types是对象和数组,其他的都是primitive type。

    primitives用一个字母表示。这些缩略词不是我提出来的,它们实际上以字符串形式存放在dex文件中。在dex-format.html文档中有详细的说明。

Vvoid-can only be used for return types
Zboolean
Bbyte
Sshort
Cchar
Iint
Jlong(64bits)
Ffloat
Ddouble(64bits)

    对象的形式:Lpackage/name/ObjectName;L表示它是一个对象类型,package/name/是对象的包,ObjectName是对象的名字,;表示对象名字的结尾。在java中等价于package.name.ObjectName。例如,Ljava/lang/String;等价于java.lang.String。数组的形式[I-表示为一维×××数组,例如:int[]。要表示多维数组只需要简单的增加‘[’的数量。[[I=int[][],[[[I=int[][][]等等。注意:数组的最大维度为255.也可以表示对象的数组,例如:[Ljava/lang/String;表示一个字符串数组。

(2)Methods

    methods总是被表示为一个非常冗长的形式包括:包含方法的类型、方法的名称、参数的类型和返回类型。虚拟机需要这些信息才能正确的定位方法,在字节码中执行静态方法。

Methods形式例子:

    Lpackage/name/ObjectName;->MethodName(III)Z

在这个例子中:Lpackage/name/ObjectName;为类型,MethodsName是方法的名称。(III)Z是方法的声明。III表示有三个int类型的参数,Z表示返回类型是boolean。

一个更加复杂的例子:

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

java中表示为:

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

(3)Fields

    同样的,fields总是表示为一个冗长的形式包括:包含field的类型、field的名称、field的类型。同样的,这是为了让虚拟机可以找到正确的field,同样的适用于字节码上的静态分析。

Fieldsde 形式:

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