Android逆向 - Dalvik 汇编语言

(转载)

Dalvik虚拟机为自己专门设计了一套指令集,并且制定了自己的指令格式与调用规范。

一 Dalvik 指令格式

1.1 位描述

Dalvik汇编代码由Dalvik指令组成,指令语法由指令的位描述与指令格式辨识来决定。位描述的约定如下所示:

  • 每16位的字采用空格分隔开来;
  • 每个字母表示四位,每个字符顺序从高字节开始,排列到低字节,每四位之间可能使用“|”来表示不同的内容。
  • 顺序采用A~Z的单个大写字母作为一个4位操作码,op表示一个8位的操作码;
  • “Ø”表示这字段所有位为0值。

举例,如以下指令:

A|G|op BBBB F|E|D|C
  • 两个空格:每个被分开的部分有16位,共有3组;
  • A|G|op:高8位有A和G组成,低字节由op操作码组成;
  • BBBB:一个16位的偏移值;
  • F|E|D|C:4个字节组成,表示寄存器参数。

1.2 指令格式标识

单独使用位标识还无法确定一条指令,必须通过指令格式标识来指定格式编码,它的约定如下所示:

  • 指令格式大多由三个字符组成,前两个是数字,最后 一个是字母;
  • 第一个数字表示指令由多少个16位的字组成;
  • 第二个数字表示指令最多使用寄存器的个数,特殊标记“r”标识使用一定范围内的寄存器。
  • 第三个字母是类型码,表示指令用到的额外数据的类型。该类型码的具体含义如下表所示:
    这里写图片描述

1.3 语法约定

  • 每条指令从操作码开始,后面紧跟参数,参数个数不定,每个参数采用逗号隔开;
  • 每条指令的参数从指令的第一部分开始,op位于低8位,高8位可以是一个8位的参数,也可以是两个4位的参数,还可以为空,如果指令超过16位,则后面部分依次作为参数;
  • 如果参数采用“vX”的方式表示,表明它是一个寄存器,如v0、v1等,ARM架构的寄存器名采用“r”开头;
  • 如果参数采用“#+X”的方式表示,表明它是一个常量数字;
  • 如果参数采用“+X”的方式表示,表明她是一个相对指令的地址偏移;
  • 如果参数采用“kind@X”的方式表示,表示它是一个常量池索引值,其中kind表示常量池类型:string(字符串常量池索引)、type(类型常量池索引)、field(字段常量池索引)和meth(方法常量池索引)。

举例,如以下指令

op vAA, string@BBBB

1个寄存器vAA,1个字符串常量池索引。

二 Dalvik指令集

Dalvik指令的语法与助记符有如下特点:

  • 参数采用从目标到源的方式;
  • 根据字节码大小和类型不同,一些字节码添加了名称后缀以消除歧义,32位常规类型字节码未添加任何后缀,64位类型的字节码添加-wide后缀,特殊类型字节码根据具体的类型添加后缀,它们可以是-boolean、-byte、-char、-short、-int、-long、-float、-double、-object、-string、-class和-void;
  • 根据字节码的布局和选项不同,一些字节码添加了字节码后缀以消除歧义,这些后缀通过在字节码主名称后添加“/”来分隔开;
  • 在指令集的描述中,宽度值中的每个字母表示宽度为4位。

举例,如以下指令:

move-wide/from16 vAA,vBBBB
  • move:基础字节码,标识这是基础操作
  • -wide:名称后缀,标识指令操作的数据宽度(64位)
  • from:字节码后缀,标识源为一个16位的寄存器引用变量
  • vAAA:目的寄存器,它始终在源的前面,取值范围v0~v255
  • vBBBB:源寄存器,取值范围v0~v65535

2.1 空操作指令

助记符:nop

指令:对齐代码,无实际操作。

2.2 数据操作指令

助记符:move

指令:move指令会根据字节码的大小和类型不同,后面跟不同的后缀

move destination,source

举例

move/from16 vAA,vBBBB;将vBBBB寄存器的值赋给vAA寄存器,源寄存器和目的寄存器都为16位 
move-wide vA,vB;4位寄存器对赋值,源寄存器与目的寄存器都为4位
move-result vAA;将上一个invoke类型指令操作的双字非对象结果赋值给vAA寄存器
move-object vA,vB;为对象赋值,源寄存器与目的寄存器都为4位
move-exception vAA;保存一个运行时发生的异常到vAA寄存器

2.3 返回指令

助记符:return

指令:它有4中类型

return-void;表示函数从一个void方法返回
return-vAA;表示函数返回一个32位非对象类型的值,返回寄存器为6位jicunqivAA
return-wide vAA;表示函数返回一个62位非对象类型的值,返回值为8位的寄存器vAA
return-object vAA;表示函数返回一个对象类型的值,返回值为8位的寄存器vAA

2.4 数据定义指令

助记符:const

指令:数据定义指令用来定义程序中用到的常量、字符串和类等数据。

const/4 vA,#+B;将数值符号扩展为32位后赋值给寄存器vA
const/16 vAA,#+BBBB;将数值符号扩展为32位后赋给寄存器vAA
const/high16 vAA,#+BBBB0000;将数值右边零扩展为32位后赋给寄存器vAA
const-wide/16 vAA,#+BBBB;将数值符号扩展为64位后赋值给寄存器vAA
const-string vAA,string@BBBB;通过字符串索引构造一个字符串并赋值给寄存器vAA
const-class vAA,type@BBBB;通过类型索引获取一个类引用并赋给寄存器vAA
const-class/jumbo vAAAA,type@BBBBBBBB;通过给定类型的索引获取一个类引用并赋给寄存器vAAAA,这条指令占用两个字节,值为0x00ff

2.5 锁指令

助记符:monitor

指令:锁指令多用在多线程程序中对同一对象的操作。

monitor-enter vAA;为指定对象获取锁
monitor-exit vAA;释放指定对象的锁

2.6 实例操作指令

助记符:check-cast、instace-of、new-instance

指令:实例的类型转换、检查和更新。

check-cast vAA,type@BBBB;将寄存器vAA中的对象引用转换成指定的类型,如果失败则抛出ClassCastException,如果类型B指定的是基本类型,对于非基本类型A来说,运行时始终会失败
instance-of vA,vB,type@CCCC;判断vB寄存器中的对象引用是否可以转换成指定的类型,如果可以vA的寄存器赋值为1,否则赋值为0
new-instance vAA,type@BBBB;构造一个指定类型对象的新实例,并将对象引用赋值给vAA寄存器,类型符type指定的类型不能是数组类

2.7 数组操作指令

助记符:array-length、new-array、filed-new-array、fill-array-data、arrayop

指令:取数组长度、新建数组、数组赋值、数组元素取值与赋值。

array-length vA,vB;获取给定vB寄存器中数组的长度并赋值给vA寄存器,数组长度指的是数组的条目个数
new-array vA,vB,type@CCCC;构造指定类型(type@CCCC)与大小(vB)的数组,并将值赋值给vA寄存器
filled-new-array {vC,vD,vE,vF,vG},type@BBBB;构造指定类型(type@BBBB)与大小(vA)的数组并填充数组内容,vA寄存器是隐含使用的,除了指定数组的大小外还指定了参数的个数,vC~vG是使用到的参数寄存器序列

2.8 异常指令

助记符:throw

指令:抛出异常

throw vA;抛出vA寄存器中指定类型的异常

2.9 跳转指令

助记符:goto、packed-switch、sparse-switch、if-test、if-testz

指令:从当前地址跳转到指定的偏移处。

goto+AA;无条件跳转到指定偏移处,偏移量AA不能为0
packed-switch vAA,+BBBBBBBB;分支跳转指令,vAA寄存器为switch分支需要判断的值,BBBBBBBBB指向一个packed-swithc-payload格式的偏移表,表中的值是按规律递增的
sparse-switch vAA,+BBBBBBBB;分支跳转指令,vAA寄存器为switch分支需要判断的值,BBBBBBBBB指向一个sparse-swithc-payload格式的偏移表,表中的值无规律的偏移量 
if-test vA,vB,+CCCC;条件跳转指令,比较vA寄存器和vB寄存器的值,如果比较结果满足就跳转到CCCC指定的偏移处,偏移量CCCC不能为0
if-eq vA,vB,+CCCC;if(vA==vB)则跳转
if-ne vA,vB,+CCCC;if(vA!=vB)则跳转
if-lt vA,vB,+CCCC;if(vA<vB)则跳转
if-ge vA,vB,+CCCC;if(vA>=vB)则跳转
if-gt vA,vB,+CCCC;if(vA>vB)则跳转
if-le vA,vB,+CCCC;if(vA<=vB)则跳转 
if-testz vA,+CCCC;条件跳转指令,比较vA寄存器和0比较,如果比较结果满足或值为0就跳转到CCCC指定的偏移处,偏移量CCCC不能为0
if-eqz vA,+CCCC;if(!vA)则跳转
if-nez vA,+CCCC;if(vA)则跳转
if-ltz vA,+CCCC;if(vA<0)则跳转
if-gez vA,+CCCC;if(vA>=0)则跳转
if-gtz vA,+CCCC;if(vA>0)则跳转
if-lez vA,+CCCC;if(vA<=0)则跳转

2.10 比较指令

助记符:cmpl-float、cmpg-float、cmpl-double、cmpg-double和cmp-log

指令:比较两个寄存器的值

cmpl-float vAA,vBB,vCC;比较两个单精度浮点数,vBB>vCC,则结果为-1,vBB==vCC,则结果为0,vBB<vCC,则结果为1,最终结果存放到vAA中
cmpg-float vAA,vBB,vCC;比较两个单精度浮点数,vBB>vCC,则结果为1,vBB==vCC,则结果为0,vBB<vCC,则结果为-1,最终结果存放到vAA中
cmpl-double vAA,vBB,vCC;比较两个双精度浮点数,vBB>vCC,则结果为-1,vBB==vCC,则结果为0,vBB<vCC,则结果为1,最终结果存放到vAA中
cmpg-double vAA,vBB,vCC;比较两个双精度浮点数,vBB>vCC,则结果为1,vBB==vCC,则结果为0,vBB<vCC,则结果为-1,最终结果存放到vAA中
cmp-long vAA,vBB,vCC;比较两个长整型数,vBB>vCC,则结果为1,vBB==vCC,则结果为0,vBB<vCC,则结果为-1,最终结果存放到vAA中

2.11 字段操作指令

助记符:iinstaceop、sstaticop

指令:字段操作指令用来对对象实例的字段进入读写操作,字段的类型可以是Java中任何有效的数据类型。

1 普通字段指令

iget、iget-wide、iget-object等

iinstanceop vA,vB,field@CCCCC

2 静态字段指令

sget、sget-wide、sget-object等

sstaicop vA,vB,field@CCCCC

2.11 方法调用指令

助记符:invoke

指令:调用实例的方法。

invoke-virtual;调用实例的虚方法
invoke-super;调用实例的父类方法
invoke-direct;调用实例的直接方法
invoke-static;调用实例的静态方法
invoke-interface;调用实例的接口方法

以下方式和上面的写法并无差别,只是后者在设置参数寄存器时使用了range来指定寄存器的范围。

invoke-virtual/range;调用实例的虚方法
invoke-super/range;调用实例的父类方法
invoke-direct/range;调用实例的直接方法
invoke-static/range;调用实例的静态方法
invoke-interface/range;调用实例的接口方法

方法调用指令的放回值需要用“move-result”指令来获取:

invoke-static {}, Landroid/os/Parcel:->obtain()Landroid/Parcel;
move-result-object v0

2.12 数据转换指令

助记符:unop

指令:用于将一种类型的数据转换为另一种数据类型。

neg-int;整数求补
not-int;整数求反
long-to-int;长整型数转换为整型数

2.13 数据运算指令

助记符:add、sub、mul、div、rem、and、or、xor、shl、shr、ushr

指令:算术运算:加、减、乘、除、模、移位。逻辑运算:与、或、非、抑或。

add等操作符后还可以添加后缀2addr、lit16和lit8等后缀

add-type vAA,vBB,vCC;vBB + vCC将结果保存到vAA
sub-type vAA,vBB,vCC;vBB - vCC将结果保存到vAA
mul-type vAA,vBB,vCC;vBB x vCC将结果保存到vAA
div-type vAA,vBB,vCC;vBB / vCC将结果保存到vAA
rem-type vAA,vBB,vCC;vBB % vCC将结果保存到vAA
and-type vAA,vBB,vCC;vBB AND vCC将结果保存到vAA
or-type vAA,vBB,vCC;vBB OR vCC将结果保存到vAA
xor-type vAA,vBB,vCC;vBB XOR vCC将结果保存到vAA
shl-type vAA,vBB,vCC;vBB << vCC将结果保存到vAA
shr-type vAA,vBB,vCC;vBB >> vCC将结果保存到vAA
ushr-type vAA,vBB,vCC;vBB >> vCC将结果保存到vAA

所有原创文章采用 知识共享署名-非商业性使用 4.0 国际许可协议 进行许可。
您可以自由的转载和修改,但请务必注明文章来源并且不可用于商业目的。
本站部分内容收集于互联网,如果有侵权内容、不妥之处,请联系我们删除。敬请谅解!

评论已关闭

生活其实很简单,过了今天就是明天。

低头哭过别忘了抬头继续走。

不要被任何人打乱自的脚步,因为没有谁会像你一样清楚和在乎自己梦想。

没有人可以打倒我,除非我自己先趴下!

你要记住你不是为别人而活,你是为自己而活。