通过前两篇介绍,你应该知道如下几点:
1..虚拟机分类和机制
2..字节码和寄存器
3..反编译dex文件之.class -> .smali 变换流程
4..class -> .smali过程中涉及到的相关工具和下载说明
基础知识很枯燥,也很重要。本片文章呢,主要介绍操作指令,通过不同指令完成各式各样行为动作。
Tip(规定):
A/B/C/D/E/F/G/H 表示4位数值
wide指定操作的数据宽度为64位(2个32位,至于为什么看前面的文章)
from/16表示源寄存器16位
举个芝麻
move-wide/from16 VA VBBBB
move:基础字节码 wide:操作的数据宽度为 64 位
from16:源数据为 16 位
VA:目标寄存器,在源寄存器前面,取值为v0 - v255
VBBBB:源寄存器,取值范围为v0-v65535(这个数字熟悉不→_→)
这里对各种常用的指令做一下归总,大致分类如下:
一.空指令
nop无实际操作,操作码 00 eg:0000 - nop,主要用作代码对齐
二.数据操作指令(move)
1.赋值操作指令
基本格式:基础字节码 - 名称后缀/字节码后缀 目标寄存器 源寄存器
move vA vB :将寄存器vB的值赋值给vA,两个寄存器都是4位
move/from16 vA vBBBB :将寄存器vBBBB的值赋值给vA,源寄存器是16位,目标寄存器4位
move/16 vAAAA vBBBB:将寄存器vBBBB的值赋值给vAAAA,源寄存器和目标寄存器都是16位
move-wide/from16 vAAAA vBBBB:将寄存器vBBBB的值赋值给vAAAA,寄存器16位,操作字符是64位
move-object vA vB:将寄存器vB的值赋值给vA,赋值的类型是对象object,寄存器都是4位
move-object/from16 vAA vBBBB :将寄存器vBBBB的值赋值给vAA,源寄存器16位,目标寄存器8位
move-object/16 vAA vBBBB:将寄存器vBBBB的值赋值给vAA,寄存器都是16位
move-result vAA:将invoke方法返回单值(非对象)赋值给寄存器vAA
move-result-wide v1:将invoke方法返回的long/double赋值给v1,v2(long和double型的值占用两个寄存器)
move-result-object v1:将invoke方法返回的对象赋值给v1寄存器
move-exception v2:将invoke抛出的异常对象赋值给v2寄存器
2.返回值指令
return-void :没有返回值
return v1:返回寄存器v1里面的值
return-wide v1:返回v1,v2中的long/double值
return-object v0:返回v0寄存器中的对象
3.数据定义
const/4 v2,#+B :将数值符号扩展到32位给v2寄存器 (eg:const/4 v0, 0x6)
const/16 v2,#+B :将数值符号扩展到32位赋值给v2寄存器(eg:const/4 v0, 0x6)
const v2,#+B :将数值赋值给 v2 寄存器(eg:const v0, 0x6)
const/hight16 v2,#+B:将数值符号扩展到32位赋值给v2寄存器,最高支持16位浮点数,符号不足右边补 0
const-wide/16 v2,#+B:将数值符号扩展到64位赋值给 v2,v3寄存器
const-wide/32 v2,#+B:将数值符号扩展到64位赋值给 v2,v3寄存器
const-wide/hight16 v2,#+B:将数值符号扩展到64位赋值给 v2,v3寄存器,不足右边补 0
const-string v2,string@0000:将字符串索引条目赋值给v2寄存器(eg:const-string v2 ,”支付失败”)
const-class v1,type@0001:通过索引查找到类的引用赋值给v1(eg:const-class v1,Test.class)
4.实例操作
check-cast v1,type@0001 将v1中的对象转换成指定类型,不可转换抛出异常ClassCastException(eg: check-cast v1,Test.class)
instance-of v1,v2,type@0001 v2中的对象是否可以转换成指定类型,是:v1赋值 1,否:v1 赋值0(eg: instance-of v1,v2,Test.class)
new-instance v1,type@001 根据索引创建一个对象引用存入v1(eg:new-instance v1,Test.class)
5.数组操作
arrry-length v2,v3 将v2中的数组长度赋值给v2
new-array v1,v2,type@001 根据索引创建一个长度为v2的数组赋值给v1
filled-new-array {v0,v1},type@cc 根据索引创建一个长度为2的数组,填充内容是v0,v1
filled-array-data v1,+B 用指定的数据来填充v1(filled-array-data v1,:const-a)
6.异常指令
throw v0:抛出异常,异常引用为v0
7.跳转指令
goto:无条件跳转到指定地方
packed-switch:有规律的跳转 eg:packed-switch v1,:pswitch_data_f4
sparse-switch:无规律跳转 eg:sparse-switch v1,00c->:goto 00d->return v2
8.条件、比较指令
if-eq等于 eg:if-eq v9, v10, :cond_82
if-ne不等于 eg:if-ne v9,v10,:cond_82
if-lt小于 if-ltz小于 0
if-gt大于 if-gtz大于 0
if-le小于等于 if-lez小于等于 0
if-ge大于等于 if-gez大于等于 0
cmpg,cmpl比较浮点型和double
cmpg-float 大于(1)等于(0)小于(-1)
cmpl-float 大于(-1)等于(0)小于(1) eg:cmpl-float v0, v3, v0 v3大于v0返回-1..
cmpg-double eg:cmpg-double v2 v5 比较v2,v3和v5,v6的值,大于返回1..
9.字段操作指令
iget v1,v2,La/a;->a:I 读取int类型字段到v1,v2存储的是对象实例 a的引用
iput v1,v2,La/a;->a:I 将v1寄存器的值存取int类型的字段a,v2是对象实例的引用
eg:iget-object v1,v2,La/a;->Ljava/lang/object
eg:iput-object v1,v2,La/a;->Ljava/lang/object
sget v1,La/a;->a:I 读取a中的静态int类型的字段a,赋值给v1
sput v1,La/a;->a:I 将v1的值赋值给a中的静态int型字段a
10.调用方法
invoke-virtual{参数},方法名
eg:invoke-virtual{v0,v1},Ljava/lang/String;->length()I v0表示引用this,v1表示参数
invoke-super{参数},方法名
eg:invoke-super{p0},Ljava/io/IOException;->toString()Ljava/lang/String;
invoke-direct{参数},方法名
eg:invoke-direct{v5,v0,v2},Ljava/util/StringTokenizer;-><init>(Ljava/lang/String;Ljava/lang/Stiring)V
invoke-static{参数},方法名
eg:invoke-static{v1},Ljava/lang/Integer;->parseInt(Ljava/lang/String)I
invoke-interface{参数},方法名
eg:invoke-interface{p2,v1},Ljava/util/Map;->get(Ljava/lang/Object)Ljava/lang/Object
接口调用示例对应 java代码如图:
11.数据转换指令
neg-int v1,v2 备注:一元二进制补码 eg:neg-int v1,v2 计算-v2结果存入v1
not-int v1,v2 备注:一元反码 eg:not-int v1,v2 计算~v2 结果存入v1
12.运算指令
add/sub/mul/div-int 加/减/乘/除
and/or/xor 且/或/异或
shl/shr/nshr 有符号左移/有符号右移/无符号右移
以上就是常用的一些操作指令,需要强记,一些不常用的,可以翻阅 Dalvik字节码表表或直接下载.doc