这里的dalvik汇编代码值得是针对dalvik虚拟机设计的指令集,与一般的汇编代码不同
1.dalvik指令的格式
Dalvik汇编代码由一系列的dalvik指令组成,指令语法由指令位描述和格式标识来决定,这部分感觉没用先不看了,在Android4.0及之前的安卓源码Dalvik/docs目录下的instruction-formats.html可以看到具体介绍
2.相关工具
smali.jar用来将smali文件汇编为dex文件,而baksmali.jar则是用来将dex文件反汇编为smali文件,ddx.jar将dex文件反汇编为ddx文件,其语法规则与smali类似
使用方法分别为:
java -jar smali.jar -o xxx.dex xxx.smali
java -jar baksmali.jar -o ouput/ XXX.dex
java -jar baksmali.jar -o ouput/ XXX.dex
3.dalvik寄存器
dalvik虚拟机采用ARM架构,将一部分寄存器映射到ARM寄存器上,另一部分使用调用栈进行模拟。Dalvik的寄存器都是32位的,64位需要用两个相邻寄存器表示。寄存器编号取值范围为v0-v65535
Android SDK中有名为dalvik.bytecode.Opcodes的接口,处理字节码的函数为一个宏HANDLE_OPCODE(),每个字节码对应一个cpp文件,在其中对这个宏进行重写。
寄存器的命名有两种,v命名和p命名。一个函数有m个寄存器用于储存局部变量和隐式传入的函数引用对象,n个用于储存参数,v命名法将寄存器命名为v0~vm+n-1,而p命名法前m个寄存器仍然为v0~vm-1,储存参数的寄存器则被命名为p0~pn-1。
4.Dalvik字节码的类型、方法与字段表示方法
可以参考官网https://source.android.com/devices/tech/dalvik/dalvik-bytecode的介绍,百度了好多还是官网说的比较清楚
(1)类型:
Dalvik字节码只有基本类型和引用类型两种,这两种类型可以表示Java语言的全部类型,除了对象和数组属于引用对象以外,其它的Java类型都是基本类型
(2)方法
方法的格式为Lpackage/name/ObjectName;->MethodName(III)Z III表示该方法有三个int型参数,Z表示返回值为boolean型,转换为java方法应该为Boolean MethodName(int,int, int)
(3)字段
格式为Lpackage/name/ObjectName;->FildName:Ljava/lang/String;
5.Dalvik指令集
(1)特点
参数顺序先是目的操作数,后是源操作数
名称后缀:64位字节码添加-wide后缀,特殊类型字节码根据具体类型添加后缀,例如-boolean -byte -class -void。
字节码后缀:根据布局和选项的不同,添加后缀用“/”分隔开例如/from16表示源为16位的寄存器变量
(2)空操作指令
助记符为nop,被用来对齐代码
(3)数据操作指令
move vA, vB 将vB的值赋给vA ; move-wide vA, vB 赋值为64位
寄存器默认为4位,/16表示源寄存器和目的寄存器都为16位,/from16表示源为16位 -object表示为对象赋值,
move-result vA表示将上一个invoke类型操作的单字非对象结果赋值给vA寄存器,这里只有destination
move-exception vA 保存一个异常到vA寄存器
(4)返回指令
return-void 从一个void方法返回
return-vAA 表示返回一个32位非对象类型的值,返回值寄存器为8位的寄存器
return-wide vAA 表示函数返回64位非对象类型的值,返回值寄存器为8位的寄存器
return-object vAA 表示函数返回对象类型的值,返回值寄存器为8位的寄存器
(5)数据定义指令
const-wide/16 vAA, #+BBBB 将该常量值符号拓展至64位后赋值给vAA
......
(6)锁指令
monitor-enter vAA 为指定的对象获取锁
monitor-exit vAA 释放指定对象的锁
(7)实例操作指令
包括实例的类型转换、检查以及新建等
check-cast vAA,type@BBBB 将vAA寄存器中的对象引用转换为指定类型,失败会抛出异常
instance-of vA , vB ,type@CCCC 判断vB寄存器中的对象引用是否可以转换为指定类型,可以则给vA赋值为1,否则为0
new-instance vAA,type@BBBB 构造一个制定类型对象的新实例,并将对象引用赋值给vAA寄存器,type不能是数组类型
这三种指令加上/jumbo之后功能一样,但是寄存器值与指令索引取值范围更大
(8)数组操作指令
array-length vA, vB 获取vB寄存器数组的长度并将值赋给vA寄存器,数组长度指的是数组条目个数
new-array vA, vB, type@CCCC 构造指定类型的与大小的数组,将索引赋给vA,大小由vB决定
filled-new-array {vC,vD,vE,vF,vG} , type@BBBB 构造指定类型与大小的数组并填充数组内容,大小由vA隐含提供,并将值赋给vA
fill-array-data vAA,+BBBBBBBB 用指定的数据填充数组,vAA寄存器为数组引用,引用必须为基础类型的数组
(9)异常指令
throw vAA 抛出vAA寄存器中制定的异常
(10)跳转指令
分为无条件跳转goto、分支跳转switch与条件跳转if
goto +AA 跳转AA的偏移量
packed-switch vAA, +BBBBBBBB分支跳转指令,vAA寄存器为switch分支需要判断的值。+BBBBBBB指向一个packed-switch-payload格式的偏移表
sparse-switch vAA, +BBBBBBBB分支跳转指令,vAA寄存器为switch分支需要判断的值。+BBBBBBB指向一个sparse-switch-payload格式的偏移表
if-xx vA,vB, +CCCC 比较vA和vB,满足xx条件跳转到CCCC的偏移处,条件包括eq(等于)、ne(不等于)、lt(小于)、ge(大于等于)等等
if-xxx vA, +CCCC 把vA和0比较,满足xxx条件跳转到CCCC的偏移处,条件包括eqz(等于)、nez(不等于)、ltz(小于)、gez(大于等于)等等
(11)比较指令
cmpl-xxx vAA,vBB,vCC 如果vBB小于vCC,则结果为1,相等为0,否则-1,xxx指的是float、double
cmpg-xxx vAA,vBB,vCC 如果vBB大于vCC,则结果为1,相等为0,否则-1,xxx指的是float、double
cmp-long vAA,vBB,vCC 如果vBB大于vCC,则结果为1,相等为0,否则-1
(12)字段操作指令
这些指令用来对对象实例的字段进行读写操作,按照普通字段和静态字段分为两类:
iop vA,vB, field@CCCC 和 sop vAA, field@BBBB
iop代表iget iget-wide iget-object iget-boolean iget-byte iput iput-wide......
sop代表sget sget-wide sget-object sget-boolean sget-byte sput sput-wide......
其中get表示读操作,put表示写操作
(13)方法调用指令
invoke-kind {vC,vD,vE,vF,vG},meth@BBBB和 invoke-kind/range {vCCCC..vNNN},meth@BBBB ,二者相似,只是后者使用range指定寄存器范围。按照调用方法的kind具体分为以下几种:
invoke-virtual 或 invoke-virtual/range调用实例的虚方法
invoke-super 或 invoke-super/range调用实例的父类方法
invoke-direct 或 invoke-direct/range调用实例的直接方法
invoke-static 或 invoke-static/range调用实例的静态方法invoke-interface 或 invoke-interface/ragne调用实例的接口方法
(14)数据转换指令
op vA,vB 表示将VB寄存器的数据进行转换然后储存到vA寄存器.op分为以下几种
neg-int/long/float/double 求补
not-int/long/float/double 求补
int-to-long int转换为long
float-to-int
.......
(15)数据运算指令
数据运算指令包括算术运算指令(加减乘除移位等)和逻辑运算指令(与或非异或得等)
指令格式为以下几种:
op vAA,vBB,vCC 将vBB,vCC 进行运算,保存到vAA
op/2addr vA,vB 将 vA,vB进行运算,保存到vA
op/lit16 vA,vB,#+CCCC 将vB与常量CCCC运算,保存到vA
op/lit8 vAA,vBB,#+CC 将vBB与常量CC运算,保存到vAA
op包含add-type sub-type mul-type div-type rem-type and-type or-type xor-type shl-type shr-type ushr-type
6.smali代码实例
.class public LHelloWorld; #定义类名
.super Ljava/lang/Object; #定义父类
.method public static main([Ljava/lang/String;)V
.registers 4 #程序中使用4个寄存器,v0,v1,v2和一个参数寄存器
.parameter #一个参数,有n个参数则有n行
.prologue #代码起始的指令
#空指令
nop
nop
nop
nop
#数据定义指令
const/16 v0, 0x8
const/4 v1, 0x5
const/4 v2, 0x3
#数据操作指令
move v1, v2
#数组操作指令
new-array v0, v0, [I
array-length v1, v0
#实例操作指令
new-instance v1, Ljava/lang/StringBuilder;
#方法调用指令
invoke-direct {v1}, Ljava/lang/StringBuilder;->()V
#跳转指令
if-nez v0, :cond_0
goto :goto_0
:cond_0
#数据转换指令
int-to-float v2, v2
#数据运算指令
add-float v2, v2, v2
#比较指令
cmpl-float v0, v2, v2
#字段操作指令
sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
const-string v1, "Hello World" #构造字符串
#方法调用指令
invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
#返回指令
:goto_0
return-void
.end method