smali指令详解

smali的数据类型

smali数据类型 数据类型
B byte
C char
D double
F float
I int
J long
S short
V void
Z boolean
[XXX array
Lxxx/yyy object

这里解析下最后两项,数组的表示方式是:在基本类型前加上前中括号“[”,例如int数组和float数组分别表示为:[I、[F;对象的表示则以L作为开头,格式是LpackageName/objectName;(注意必须有个分号跟在最后),例如String对象在smali中为:Ljava/lang/String;,其中java/lang对应java.lang包,String就是定义在该包中的一个对象。
内部类:LpackageName/objectNamesubObjectName;。也就是在内部类前加“”符号。

函数的定义

Func-Name (Para-Type1Para-Type2Para-Type3…)Return-Type
注意参数与参数之间没有任何分隔符,同样举几个例子就容易明白了:

 1. foo ()V

     void foo()。

 2. foo (III)Z

     boolean foo(int, int, int)。

 3. foo (Z[I[ILjava/lang/String;J)Ljava/lang/String;

     String foo (boolean, int[], int[], String, long) 

 4. foo ([Ljava/lang/String)Ljava/lang/String;

     String foo (String []) 

 5. foo(I[[IILjava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;

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

寄存器

对于一个使用m个寄存器(m=局部变量寄存器个数l+参数寄存器个数n)的方法而言,局部寄存器使用从v0开始的l个寄存器,而参数寄存器则使用最后的n个寄存器.举个例子说明假设实例方法test(String a,String b)一共使用了5个寄存器:0,1,2,3,4,那么参数寄存器是能使用2,3,4这三个寄存器,如图

寄存器的命名
寄存器有两种不同的命名方法:v字命名法和p字命 名法.这两种命名法仅仅是影响了字节码的可读性.

v字命名法

以小写字母v开头的方式表示方法中使用的局部变量和参数.
对于上面实例方法test(String a,String b)来说,v0,v1为局部变量能够使用的寄存器,v2,v3,v4为参数能够使用的寄存器:

p字命名法

以小写字母p开头的方式表示参数,参数名称从p0开始,依次增大.局部变量能够使用的寄存器仍然是以v开头.
总之不管是P还是V命名法,参数在后,局部变量在前。

指令:

数据定义指令

数据定义指令用于定义代码中使用的常量,类等数据,基础指令是const

指令 描述
const/4 vA,#+B 将数值符号扩展为32后赋值给寄存器vA
const-wide/16 vAA,#+BBBB 将数值符号扩展为64位后赋值个寄存器对vAA
const-string vAA,string@BBBB 通过字符串索引高走字符串赋值给寄存器vAA
const-class vAA,type@BBBB 通过类型索引获取一个类的引用赋值给寄存器vAA

数据操作指令

move指令用于数据操作,其表示move destination,source,即数据数据从source寄存器(源寄存器)移动到destionation寄存器(源寄存器),可以理解java中变量间的赋值操作.根据字节码和类型的不同,move指令后会跟上不同的后缀.

指令 描述
move vA,vB 将vB寄存器的值赋值给vA寄存器,vA和vB寄存器都是4位
move/from16 vAA,VBBBB 将vBBBB寄存器(16位)的值赋值给vAA寄存器(7位),from16表示源寄存器vBBBB是16位的
move/16 vAAAA,vBBBB 将寄存器vBBBB的值赋值给vAAAA寄存器,16表示源寄存器vBBBB和目标寄存器vAAAA都是16位
move-object vA,vB 将vB寄存器中的对象引用赋值给vA寄存器,vA寄存器和vB寄存器都是4位
move-result vAA 将上一个invoke指令(方法调用)操作的单字(32位)非对象结果赋值给vAA寄存器
move-result-wide vAA 将上一个invoke指令操作的双字(64位)非对象结果赋值给vAA寄存器
mvoe-result-object vAA 将上一个invoke指令操作的对象结果赋值给vAA寄存器
move-exception vAA 保存上一个运行时发生的异常到vAA寄存器

对象操作指令

与对象实例相关的操作,比如对象创建,对象检查等.

指令 描述
new-instance vAA,type@BBBB 构造一个指定类型的对象将器引用赋值给vAA寄存器.此处不包含数组对象
instance-of vA,vB,type@CCCC 判断vB寄存器中对象的引用是否是指定类型,如果是,将v1赋值为1,否则赋值为0
check-cast vAA,type@BBBB 将vAA寄存器中对象的引用转成指定类型,成功则将结果赋值给vAA,否则抛出ClassCastException异常.

数组操作指令

在实例操作指令中我们并没有发现创建对象的指令.Davilk中设置专门的指令用于数组操作.

指令 描述
new-array vA,vB,type@CCCC 创建指定类型与指定大小(vB寄存器指定)的数组,并将其赋值给vA寄存器
fill-array-data vAA,+BBBBBBBB 用指定的数据填充数组,vAA代表数组的引用(数组的第一个元素的地址)

数据运算指令

数据运算主要包括两种:算数运算和逻辑运算.

算术运算指令

指令 描述
add-type 加法指令
sub-type 减法指令
mul-type 乘法指令
div-type 除法指令
rem-type

逻辑元算指令

指令 描述
and-type 与运算指令
or-type 或运算指令
xor-type 异或元算指令

位移指令

指令 描述
shl-type 有符号左移指令
shr-type 有符号右移指令
ushr-type 无符号右移指令

上面的-type表示操作的寄存器中数据的类型,可以是-int,-float,-long,-double等.

比较指令

比较指令用于比较两个寄存器中值的大小,其基本格式格式是cmp+kind-type vAA,vBB,vCC,type表示比较数据的类型,如-long,-float等;kind则代表操作类型,因此有cmpl,cmpg,cmp三种比较指令.coml是compare less的缩写,cmpg是compare greater的缩写,因此cmpl表示vBB小于vCC中的值这个条件是否成立,是则返回1,否则返回-1,相等返回0;cmpg表示vBB大于vCC中的值这个条件是否成立,是则返回1,否则返回-1,相等返回0.
cmp和cmpg的语意一致,即表示vBB大于vCC寄存器中的值是否成立,成立则返回1,否则返回-1,相等返回0
来具体看看Davilk中的指令:

指令 描述
cmpl-float vAA,vBB,vCC 比较两个单精度的浮点数.如果vBB寄存器中的值大于vCC寄存器的值,则返回-1到vAA中,相等则返回0,小于返回1
cmpg-float vAA,vBB,vCC 比较两个单精度的浮点数,如果vBB寄存器中的值大于vCC的值,则返回1,相等返回0,小于返回-1
cmpl-double vAA,vBB,vCC 比较两个双精度浮点数,如果vBB寄存器中的值大于vCC的值,则返回-1,相等返回0,小于则返回1
cmpg-double vAA,vBB,vCC 比较双精度浮点数,和cmpl-float的语意一致
cmp-double vAA,vBB,vCC 等价与cmpg-double vAA,vBB,vCC指令

字段操作指令

字段操作指令表示对对象字段进行设值和取值操作,就像是你在代码中长些的set和get方法.基本指令是iput-type,iget-type,sput-type,sget-type.type表示数据类型普通字段读写操作
前缀是i的iput-type和iget-type指令用于字段的读写操作.

指令 描述
iget-byte vX,vY,filed_id 读取vY寄存器中的对象中的filed_id字段值赋值给vX寄存器
iput-byte vX,vY,filed_id 设置vY寄存器中的对象中filed_id字段的值为vX寄存器的值
iget-boolean vX,vY,filed_id
iput-boolean vX,vY,filed_id
iget-long vX,vY,filed_id
iput-long vX,vY,filed_id

静态字段读写操作

前缀是s的sput-type和sget-type指令用于静态字段的读写操作

指令 描述
sget-byte vX,vY,filed_id
sput-byte vX,vY,filed_id
sget-boolean vX,vY,filed_id
sput-boolean vX,vY,filed_id
sget-long vX,vY,filed_id
sput-long vX,vY,filed_id

方法调用指令

Davilk中的方法指令和JVM的中指令大部分非常类似.目前共有五条指令集:

指令 描述
invoke-direct{parameters},methodtocall 调用实例的直接方法,即private修饰的方法.此时需要注意{}中的第一个元素代表的是当前实例对象,即this,后面接下来的才是真正的参数.比如指令invoke-virtual {v3,v1,v4},Test2.method5:(II)V中,v3表示Test2当前实例对象,而v1,v4才是方法参数
invoke-static{parameters},methodtocall 调用实例的静态方法,此时{}中的都是方法参数
invoke-super{parameters},methodtocall 调用父类方法
invoke-virtual{parameters},methodtocall 调用实例的虚方法,即public和protected修饰修饰的方法
invoke-interface{parameters},methodtocall 调用接口方法

这五种指令是基本指令,除此之外,你也会遇到invoke-direct/range,invoke-static/range,invoke-super/range,invoke-virtual/range,invoke-interface/range指令,该类型指令和以上指令唯一的区别就是后者可以设置方法参数可以使用的寄存器的范围,在参数多于四个时候使用.

再此强调一遍对于非静态方法而言{}的结构是{当前实例对象,参数1,参数2,…参数n},而对于静态方法而言则是{参数1,参数2,…参数n}

需要注意,如果要获取方法执行有返回值,需要通过上面说道的move-result指令获取执行结果.

方法返回指令

在java中,很多情况下我们需要通过Return返回方法的执行结果,在Davilk中同样提供的return指令来返回运行结果:

指令 描述
return-void 什么也不返回
return vAA 返回一个32位非对象类型的值
return-wide vAA 返回一个64位非对象类型的值
return-object vAA 反会一个对象类型的引用

同步指令

  同步一段指令序列通常是由java中的synchronized语句块表示,则JVM中是通过monitorenter和monitorexit的指令来支持synchronized关键字的语义的,而在Davilk中同样提供了两条类似的指令来支持synchronized语义:

指令 描述
monitor-enter vAA 为指定对象获取锁操作
monitor-exit vAA 为指定对象释放锁操作

异常指令

很久以前,VM也是用过jsr和ret指令来实现异常的,但是现在的JVM中已经抛出原先的做法,转而采用异常表来实现异常.而Davilk仍然使用指令来实现:

指令 描述
throw vAA 抛出vAA寄存器中指定类型的异常

跳转指令

跳转指令用于从当前地址条状到指定的偏移处,在if,switch分支中使用的居多.Davilk中提供了goto,packed-switch,if-test指令用于实现跳转操作

指令 描述
goto +AA 无条件跳转到指定偏移处(AA即偏移量)
packed-switch vAA,+BBBBBBBB 分支跳转指令.vAA寄存器中的值是switch分支中需要判断的,BBBBBBBB则是偏移表(packed-switch-payload)中的索引值,
spare-switch vAA,+BBBBBBBB 分支跳转指令,和packed-switch类似,只不过BBBBBBBB偏移表(spare-switch-payload)中的索引值
if-test vA,vB,+CCCC 条件跳转指令,用于比较vA和vB寄存器中的值,如果条件满足则跳转到指定偏移处(CCCC即偏移量),test代表比较规则,可以是eq.lt等.

在条件比较中,if-test中的test表示比较规则.该指令用的非常多,因此我们简单的坐下说明:

指令 描述
if-eq vA,vB,target vA,vB寄存器中的相等,等价于java中的if(a==b),比如if-eq v3,v10,002c表示如果条件成立,则跳转到current position+002c处.其余的类似
if-ne vA,vB,target 等价与java中的if(a!=b)
if-lt vA,vB,target vA寄存器中的值小于vB,等价于if(a>=b)
if-gt vA,vB,target 等价于java中的if(a>b)
if-ge vA,vB,target 等价于java中的if(a>=b)
if-le vA,vB,target 等价于java中的if(a<=b)

除了以上指令之外,Davilk还提供可一个零值条件指令,该指令用于和0比较,可以理解为将上面指令中的vB寄存器的值固定为0.

指令 描述
if-eqz vAA,target 等价于java中的if(a==0)或者if(!a)
if-nez vAA,target 等价于java中的if(a!=0)或者if(a)
if-ltz vAA,target 等价于java中的if(a<0)
if-gtz vAA,target 等价于java中的if(a>0)
if-lez vAA,target 等价于java中的if(a<=0)
if-gtz vAA,target 等价于java中的if(a>=0)

上面我们说道两张偏移表packed-switch-payload和spare-switch-payload,两者唯一的区别就是表中的值是否有序,后面我们会在下文中进行详细的说明.

数据转换指令

数据类型转换对任何java开发者都是非常熟悉的,用于实现两种不同数据类型的相互转换.其基本指令格式是:unop vA,vB,表示对vB寄存器的中值进行操作,并将结果保存在vA寄存器中.

指令 描述
int-to-long 整形转为长整型
float-to-int 单精度浮点型转为整形
int-to-byte 整形转为字节类型
neg-int 求补指令,对整数求补
not-int 求反指令,对整数求反

结合下表的指令大全:

Opcode (hex) Opcode name Explanation Example
00 nop No operation 0000 - nop 
01 move vx,vy Moves the content of vy into vx. Both registers must be in the first 256 register range. 0110 - move v0, v1
Moves v1 into v0.
02 move/from16 vx,vy Moves the content of vy into vx. vy may be in the 64k register range while vx is one of the first 256 registers. 0200 1900 - move/from16 v0, v25
Moves v25 into v0.
03 move/16    
04 move-wide     
05 move-wide/from16 vx,vy Moves a long/double value from vy to vx. vy may be in the 64k register range while wx is one of the first 256 registers. 0516 0000 - move-wide/from16 v22, v0
Moves v0 into v22.
06 move-wide/16    
07 move-object vx,vy Moves the object reference from vy to vx. 0781 - move-object v1, v8
Moves the object reference in v8 to v1.
08 move-object/from16 vx,vy Moves the object reference from vy to vx, vy can address 64k registers and vx can address 256 registers. 0801 1500 - move-object/from16 v1, v21
Move the object reference in v21 to v1.
09 move-object/16    
0A move-result vx Move the result value of the previous method invocation into vx. 0A00 - move-result v0
Move the return value of a previous method invocation into v0.
0B move-result-wide vx Move the long/double result value of the previous method invocation into vx,vx+1. 0B02 - move-result-wide v2
Move the long/double result value of the previous method invocation into v2,v3.
0C move-result-object vx Move the result object reference of the previous method invocation into vx. 0C00 - move-result-object v0
0D move-exception vx Move the exception object reference thrown during a method invocation into vx.  0D19 - move-exception v25
0E return-void Return without a return value 0E00 - return-void
0F return vx Return with vx return value 0F00 - return v0
Returns with return value in v0.

10 return-wide vx Return with double/long result in vx,vx+1. 1000 - return-wide v0
Returns with a double/long value in v0,v1.
11 return-object vx Return with vx object reference value. 1100 - return-object v0
Returns with object reference value in v0
12 const/4 vx,lit4 Puts the 4 bit constant into vx 1221 - const/4 v1, #int2
Moves literal 2 into v1. The destination register is in the lower 4 bit in the second byte, the literal 2 is in the higher 4 bit.
13 const/16 vx,lit16 Puts the 16 bit constant into vx 1300 0A00 - const/16 v0, #int 10
Puts the literal constant of 10 into v0.
14
  • 5
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值