深入理解Dalvik字节码指令及Smali文件

今天来介绍有关Dalvik虚拟机相关的知识,首先便是介绍我们最关心的Dalvik字节码相关知识,进而深入到Android逆向领域.之所以写这篇文章,是因为有姑娘要学习这,再加上网上的许多资料太过零散和片面,当然,更重要的是为以前做个总结.


Dalvik寄存器

在开始之前,首先来了解寄存器相关的知识.Dalvik中用的寄存器都是32位,64位类型数据则用两个相邻的32位寄存器表示,也就是对于double这种64位类型的数据,需要用到两个32位寄存器来存储.

虚拟机寄存器

我们知道Dalvik最多支持65536个寄存器(编号从0~65535),但是在ARM架构的cpu中只存在37个寄存器,那么这种不对称是怎么解决的呢?
Dalvik中的寄存器是虚拟寄存器, 通过映射真实的寄存器来实现.我们知道每个Dalvik维护了一个调用栈,该调用栈就是用来支持虚拟寄存器和真实寄存器相互映射的.在执行具体函数时,Dalvik会根据.registers指令来确定该函数要用到的寄存器数目.具体的原理,可以自行参考Davilk的实现.

下面我们谈到的寄存器都是虚拟寄存器.

寄存器的使用规则

对于一个使用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开头.

对于上面实例方法test(String a,String b)来说,v0,v1为局部变量能够使用的寄存器,p0,p1,p2为参数能够使用的寄存器:
这里写图片描述


Dalvik描述符

与JVM相类似,Davilk字节码中同样有一套用于描述类型,方法,字段的方法,这些方法结合Davilk的指令便形成了完整的汇编代码.

字节码和数据类型

Davilk字节码只有两种类型:基本类型和引用类型.对象和数组都是引用类型,Davilk中对字节码类型的描述和JVM中的描述符规则一致:对于基本类型和无返回值的void类型都是用一个大写字母表示,对象类型则用字母L加对象的全限定名来表示.数组则用[来表示,具体规则如下所示:

全限定名是什么
以String为例,其完整名称是java.lang.String,那么其全限定名就是java/lang/String;,即java.lang.String的”.”用”/”代替,并在末尾添加分号”;”做结束符.

java类型 类型描述符
boolean Z
byte B
short S
char C
int I
long J
float F
double D
void V
对象类型 L
数组类型 [

这里我们重点解释对象类型和数组类型:

对象类型

L可以表示java类型中的任何类.在java代码中以package.name.ObjectName的方式引用,而在Davilk中其描述则是以Lpackage/name/ObjectName;的形式表示.L即上面定义的java类类型,表示后面跟着的是累的全限定名.比如java中的java.lang.String对应的描述是Ljava/lang/String;.

数组类型

[类型用来表示所有基本类型的数组,[后跟着是基本类型的描述符.每一维度使用一个前置的[.
比如java中的int[] 用汇编码表示便是[I;.二维数组int[][]为[[I;,三维数组则用[[[I;表示.

对于对象数组来说,[后跟着对应类的全限定符.比如java当中的String[]对应的是[java/lang/String;.

字段的描述

Davilk中对字段的描述分为两种,对基本类型字段的描述和对引用类型的描述,但两者的描述格式一样:
对象类型描述符->字段名:类型描述符;
比如com.sbbic.Test类中存在String类型的name字段及int类型的age字段,那么其描述为:

Lcom/sbbic/Test;->name:Ljava/lang/String;
Lcom/sbbic/test;->age:I

方法的描述

java中方法的签名包括方法名,参数及返回值,在Davilk相应的描述规则为:
对象类型描述符->方法名(参数类型描述符)返回值类型描述符

下面我们通过几个例子来说明,以java.lang.String为例:

java方法:public char charAt(int index){
  ...}
Davilk描述:Ljava/lang/String;->charAt(I)C

java方法:public void getChars(int srcBegin,int srcEnd,char dst[],int dstBegin){
  ...}
Davilk描述:Ljava/lang/String;->getChars(II[CI)V

java方法:public boolean equals(Object anObject){
  ...}
Davilk描述:Ljava/lang/String;->equals(Ljava/lang/Object)Z

Dalvik指令集

掌握以上的字段和方法的描述,只能说我们懂了如何描述一个字段和方法,而关于方法中具体的逻辑则需要了解Dalvik中的指令集.因为Dalvik是基于寄存器的架构的,因此指令集和JVM中的指令集区别较大,反而更类似x86的中的汇编指令.

数据定义指令

数据定义指令用于定义代码中使用的常量,类等数据,基础指令是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代表数组的引用(数组的第一个元素的地址)

数据运算指令

  • 39
    点赞
  • 161
    收藏
    觉得还不错? 一键收藏
  • 16
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值