版权声明:本文为CSDN博主「Jankin_W」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Kelaker/article/details/84403369
————————————————
语法简介
Dalvik是基于寄存器结构的,在Dalvik字节码中,寄存器都是32位的,能够支持任何类型,64位类型(Long/Double)用2个寄存器表示;Dalvik字节码有两种类型:原始类型;引用类型(包括对象和数组)。

原始类型

简写 含义
B byte
C char
D double (64 bits)
F float
I int
J long (64 bits)
S short
V void 只能用于返回值类型
Z boolean

对象类型

形式:Lxxx/yyy/zzz;
L 表示这是一个对象类型
xxx/yyy是该对象所在的包
zzz是对象名称
;标识对象名称的结束
如:Ljava/lang/String;

数组类型

形式:[XXX
[I表示一个int型的一维数组,相当于int[]
增加一个维度增加一个[,如[[I表示int[][]
数组每一个维度最多255个;
对象数组表示也是类似

如:String数组的表示是[Ljava/lang/String

方法

形式:Lxxx/yyy/zzz;->methodName(Lxxx/yyy/zzz;Lxxx/yyy/zzz;I)Z
Lxxx/yyy/zzz 表示对象的类型
methodName 表示方法名
Lxxx/yyy/zzz;Lxxx/yyy/zzz;I 表示三个参数,两个对象类型,一个整型(方法的参数是一个接一个的,中间没有隔开)
Z返回值类型,这里是boolean类型

如:Log.i(“xxx”, msg); 转换成smali语句之后是
Landroid/util/Log;->i(Ljava/lang/String;Ljava/lang/String;)I

字段

形式:Lxxx/yyy/zzz;->FieldName:Lxxx/yyy/zzz;
Lxxx/yyy/zzz; 表示对象的类型
FieldName表示字段名称
Lxxx/yyy/zzz表示字段类型
如:ff = “aa”; 转换后是 Lcom/example/reforceapk/MyLog;->ff:Ljava/lang/String

寄存器和变量

Dalvik变量都是存放在寄存器中的,寄存器的命名方法有两种,v命名法和p命名法,一般我们都用p命名法。
p命名法:寄存器采用v和p来命名,v表示本地寄存器,p表示参数寄存器。
如果一个非静态方法有两个本地变量,有三个参数,需要的寄存器关系如下:

v0 第一个本地寄存器
v1 第二个本地寄存器
p0 // 指代调用这个方法的this对象。
p1 第一个参数
p2 第二个参数
p3 第三个参数

如果是静态方法,那么就不需要this对象了,需要的寄存器是v0, v1, p0, p1, p2。

.registers使用这个指令指定方法中寄存器的总数
.locals使用这个指定表明方法中非参寄存器的总数,放在方法的第一行。

常用指令

.field private isFlag:z — 定义变量
.method — 方法
.prologue — 方法开始
.end method — 方法结束
.parameter — 方法参数
.line 12 — 此方法位于第12行
invoke-super — 调用父函数
invoke-direct — 调用函数
invoke-static — 调用静态函数
const/high16 v0, 0x7f03 — 把0x7f03赋值给v0
return-void — 函数返回void
new-instance — 创建实例
move-result v0 — 将上一个invoke类型的指令操作的非对象结果赋值给v0寄存器
move-result-object v0 — 将上一个invoke类型指令操作的对象赋值给v0寄存器

字段操作指令

对于实例字段和静态字段有两类指令:
iget, iput 对实例字段进行读,写
sget, sput 对静态字段进行读,写

会根据不同类型添加不同的后缀:

iget,iget-object,iget-boolean,iget-byte,iget-char,iget-short
iput,iput-object,iput-boolean,iput-byte,iput-char,iput-short

获取static fields的指令示例:

sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;

上句中sget-object指令把out这个变量获取并放到v0寄存器中。

获取instance fields的指令与static fields的类似,需要指明对象所属的实例。示例:

iget-object v0, p0, Lcom/example/reforceapk/MyLog;->ff:Ljava/lang/String;

上句iget-object指令比sget-object多了一个参数p0,就是该变量所在类的实例,在这里就是p0即“this”。

条件跳转指令

“if-eq vA, vB, :cond_x” — 如果vA等于vB则跳转到:cond_x
“if-ne vA, vB, :cond_x” — 如果vA不等于vB则跳转到:cond_x
“if-lt vA, vB, :cond_x” — 如果vA小于vB则跳转到:cond_x
“if-ge vA, vB, :cond_x” — 如果vA大于等于vB则跳转到:cond_x
“if-gt vA, vB, :cond_x” — 如果vA大于vB则跳转到:cond_x
“if-le vA, vB, :cond_x” — 如果vA小于等于vB则跳转到:cond_x
“if-eqz vA, :cond_x” — 如果vA等于0则跳转到:cond_x
“if-nez vA, :cond_x” — 如果vA不等于0则跳转到:cond_x
“if-ltz vA, :cond_x” — 如果vA小于0则跳转到:cond_x
“if-gez vA, :cond_x” — 如果vA大于等于0则跳转到:cond_x
“if-gtz vA, :cond_x” — 如果vA大于0则跳转到:cond_x
“if-lez vA, :cond_x” — 如果vA小于等于0则跳转到:cond_x

条件判断例子:

源代码:
public void a() {
int i2 = 22;
if (i2 >= 0) {
System.out.println(“1111111”);
}
if (i2 <= 0) {
System.out.println(“222222”);
}
}
smail代码:
# virtual methods
.method public a()V
.registers 4
.prologue
.line 49
const/16 v0, 0x16
.line 50
.local v0, “i2”:I
if-ltz v0, :cond_b
.line 51
sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
const-string v2, “111111”
invoke-virtual {v1, v2}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
.line 53
:cond_b
if-gtz v0, :cond_14
.line 54
sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
const-string v2, “222222”
invoke-virtual {v1, v2}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
.line 56
:cond_14
return-void
.end method
Tips:
这里if-gtz v0之后会跟一个:cond_14,后面也有:cond_14,
可以把他们看成类似大括号的标志开始和结束标识