Android逆向基础——Dalvik 指令集
Dalvik指令集是学习Android虚拟机中必不可少的知识点,它是被Android虚拟机所识别且直接执行的指令。
Dalvik指令集是学习Android虚拟机中必不可少的知识点,它是被Android虚拟机所识别且直接执行的指令。
Dalvik是基于寄存器指令集,他的几乎所有指令操作都来自对寄存器的操作,而不像x86/ARM因为寄存器个数不够导致运算结果存不下,需要依靠堆栈来存储情况。Dalvik一共有65536个寄存器,执行一个函数调用后虚拟机内部会保存当前方法中用到的寄存器,并且重置所有寄存器供被调用方法的新环境使用。
由于Dalvik是虚拟机指令,最终依然会被解析成机器指令执行,因此65536个寄存器中大多数寄存器实际上仍然会被存储于堆栈中。少量运算中所使用的寄存器会被放在物理机的寄存器中使用。Dalvik虚拟机内部维护了调用栈,调用新的方法时会将寄存器与返回地址存放于调用栈中,返回时取回。
1. Dalvik寄存器
Dalvik中所有的寄存器均为32位长度。当表示64位数值时则使用相邻的两个32位寄存器存放,在使用时指定相邻的第一个寄存器,第二个寄存器也会参与运算。用.registers伪指令描述当前方法中使用的寄存器个数(包括形参)。
寄存器的命名有两种:
v0~vN+M-1:当前方法中运算所使用的寄存器。(var)
p0~pM-1:当前方法中的形参。(param)
其中N为当前方法的局部变量个数,M为当前方法形参个数。 vN~vN+M-1与p0~pM-1是一样的。
如果被调用的方法不是静态的,则p0为方法所属类的实例。
假设这里有个方法Add:
class Clazz{
public int Add(int a, int b)
{
int c = a + b;
return c;
}
}
在排除编译器自动生成的临时变量与优化以外,这里使用了四个寄存器,即.registers 4
其中形参p0为Clazz的实例,p1为a, p2为b。也可以使用v1、v2、v3,不过命名通常以pM的居多。
其中局部变量v0为c。
2. 数据类型
java类型 | dalvik类型 |
void | V |
boolean | Z |
byte | B |
char | C |
short | S |
int | I |
long | J |
float | F |
double | D |
class | L类全路径; |
int[]、char[][]、... | [I、[[C、... |
基本数据类型中除了boolean->V、long->J以外,均为首字母大写。
类类型为L加类的全路径,且以/分割,比如有个类的全路径为com.example.MainClass,那么在Dalvik中命名为Lcom/example/MainClass; 注意分号。
3. 类的描述:
有如下类:
package com.example;
public class User{
private String name;
private String nick;
public String getName()
{return name;}
public void setName(String name)
{this.name = name;}
}
注:其中String 的全路径为 java.lang.String。
.class 指令用于描述该类的起始位置:.class public User
.super 紧跟在.class后用于描述该类的父类(没有父类时继承自Object):.super Ljava/lang/Object;
.implements 描述类实现的接口:.implements 接口名
.annotation 描述类注解:
.annotation [注解的属性] <注解类名>
[注解字段=值]
...
.end
.field 指令用于描述类中的字段:.field private name:Ljava/lang/String;
.method 描述方法的开始:.method public getName()Ljava/lang/String;(返回值类型写在最后面)
方法特例:
.method public constructor <init>()V 描述构造函数
.method static constructor <clinit>()V 描述静态构造函数
.end method 描述方法的结束
当外部使用类中的方法时将会被描述为
getName:Lcom/example/User;->getName(Ljava/lang/String)V;
setName:Lcom/example/User;->setName()Ljava/lang/String;
jeb Dalvik汇编窗口与Java代码对比样例:
4. 常见指令
v*表示任意寄存器,#+*表示任意常量
其中A、B、C等表示指令的参数,其个数表示它的取值范围。
寄存器:
A、B、C等的个数表示指令能接受的寄存器下标范围。
如vA表示2^4-1内的寄存器可以使用,vAA则表示2^8-1内的寄存器可以使用。
最多为4个,如vAAAA表示2^16-1内的寄存器可以使用(v0~v65535)
常量:
如#+B为4位常量(通常为boolean)。
#+BBBBBBBBBBBBBBBB则为64位常量(long / double)。最多为16个
[下文整合分类自https://source.android.com/docs/core/runtime/dalvik-bytecode]
数据定义指令
用于将一个常量放入寄存器中
opcode | 指令 | 参数 | 描述 |
12 11n | const/4 vA, #+B | A: 目标寄存器(4 位) | 将给定的字面量值(符号扩展为 32 位)移到指定的寄存器中。 |
B: 有符号整数(4 位) | |||
13 21s | const/16 vAA, #+BBBB | A: 目标寄存器(8 位) | 将给定的字面量值(符号扩展为 32 位)移到指定的寄存器中。 |
B: 有符号整数(16 位) | |||
14 31i | const vAA, #+BBBBBBBB | A: 目标寄存器(8 位) | 将给定的字面量值移到指定的寄存器中。 |
B: 任意 32 位常量 | |||
15 21h | const/high16 vAA, #+BBBB0000 | A: 目标寄存器(8 位) | 将给定的字面量值(右零扩展为 32 位)移到指定的寄存器中。 |
B: 有符号整数(16 位) | |||
16 21s | const-wide/16 vAA, #+BBBB | A: 目标寄存器(8 位) | 将给定的字面量值(符号扩展为 64 位)移到指定的寄存器对中。 |
B: 有符号整数(16 位) | |||
17 31i | const-wide/32 vAA, #+BBBBBBBB | A: 目标寄存器(8 位) | 将给定的字面量值(符号扩展为 64 位)移到指定的寄存器对中。 |
B: 有符号整数(32 位) | |||
18 51l | const-wide vAA, #+BBBBBBBBBBBBBBBB | A: 目标寄存器(8 位) | 将给定的字面量值移到指定的寄存器对中。 |
B: 任意双字宽度(64 位)常量 | |||
19 21h | const-wide/high16 vAA, #+BBBB000000000000 | A: 目标寄存器(8 位) | 将给定的字面量值(右零扩展为 64 位)移到指定的寄存器对中。 |
B: 有符号整数(16 位) | |||
1a 21c | const-string vAA, string@BBBB | A: 目标寄存器(8 位) | 将通过给定索引指定的字符串的引用移到指定的寄存器中。 |
B: 字符串索引 | |||
1b 31c | const-string/jumbo vAA, string@BBBBBBBB | A: 目标寄存器(8 位) | 将通过给定索引指定的字符串的引用移到指定的寄存器中。 |
B: 字符串索引 | |||
1c 21c | const-class vAA, type@BBBB | A: 目标寄存器(8 位) | 将通过给定索引指定的类的引用移到指定的寄存器中。如果指定的类型是原始类型,则将存储 |
B: 类型索引 |
数据操作指令:
移动数值到新的地方
opcode | 指令 | 参数 | 描述 |
01 12x | move vA, vB | A: 目标寄存器(4 位) | 将一个非对象寄存器的内容移到另一个非对象寄存器中。 |
B: 源寄存器(4 位) | |||
02 22x | move/from16 vAA, vBBBB | A: 目标寄存器(8 位) | 将一个非对象寄存器的内容移到另一个非对象寄存器中。 |
B: 源寄存器(16 位) | |||
03 32x | move/16 vAAAA, vBBBB | A: 目标寄存器(16 位) | 将一个非对象寄存器的内容移到另一个非对象寄存器中。 |
B: 源寄存器(16 位) | |||
04 12x | move-wide vA, vB | A: 目标寄存器对(4 位) | 将一个寄存器对的内容移到另一个寄存器对中。 |
B: 源寄存器对(4 位) | 注意:可以从 vN 移到 vN-1 或 vN+1,因此必须在执行写入运算之前,为要读取的寄存器对的两部分均安排实现。 | ||
05 22x | move-wide/from16 vAA, vBBBB | A: 目标寄存器对(8 位) | 将一个寄存器对的内容移到另一个寄存器对中。 |
B: 源寄存器对(16 位) | 注意:实现的注意事项与上文的 move-wide 相同。 | ||
06 32x | move-wide/16 vAAAA, vBBBB | A: 目标寄存器对(16 位) | 将一个寄存器对的内容移到另一个寄存器对中。 |
B: 源寄存器对(16 位) | 注意:实现的注意事项与上文的 move-wide 相同。 | ||
07 12x | move-object vA, vB | A: 目标寄存器(4 位) | 将一个对象传送寄存器的内容移到另一个对象传送寄存器中。 |
B: 源寄存器(4 位) | |||
08 22x | move-object/from16 vAA, vBBBB | A: 目标寄存器(8 位) | 将一个对象传送寄存器的内容移到另一个对象传送寄存器中。 |
B: 源寄存器(16 位) | |||
09 32x | move-object/16 vAAAA, vBBBB | A: 目标寄存器(16 位) | 将一个对象传送寄存器的内容移到另一个对象传送寄存器中。 |
B: 源寄存器(16 位) | |||
0a 11x | move-result vAA | A: 目标寄存器(8 位) | 将最新的 invoke-kind 的单字非对象结果移到指定的寄存器中。该指令必须紧跟在其(单字非对象)结果不会被忽略的 invoke-kind 之后执行,否则无效。 注:invoke-king表示一系列invoke-开头的指令,用于调用方法 |
0b 11x | move-result-wide vAA | A: 目标寄存器对(8 位) | 将最新的 invoke-kind 的双字结果移到指定的寄存器对中。该指令必须紧跟在其(双字)结果不会被忽略的 invoke-kind 之后执行,否则无效。 |
0c 11x | move-result-object vAA | A: 目标寄存器(8 位) | 将最新的 invoke-kind 的对象结果移到指定的寄存器中。该指令必须紧跟在其(对象)结果不会被忽略的 invoke-kind 或 filled-new-array 之后执行,否则无效。 |
0d 11x | move-exception vAA | A: 目标寄存器(8 位) | 将刚刚捕获的异常保存到给定寄存器中。该指令必须为捕获的异常不会被忽略的任何异常处理程序的第一条指令,且该指令必须仅作为异常处理程序的第一条指令执行,否则无效。 |
运算指令格式
opcode | 指令 | 参数 | 描述 |
7b..8f 12x | unop vA, vB | A: 目标寄存器或寄存器对(4 位) | 对源寄存器执行已确定的一元运算,并将结果存储到目标寄存器中。 |
90..af 23x | binop vAA, vBB, vCC | A: 目标寄存器或寄存器对(8 位) | 对两个源寄存器执行已确定的二元运算,并将结果存储到目标寄存器中。 注意:与其他 -long 数学运算(对第一个和第二个源采用寄存器对)相反,shl-long、shr-long 和 ushr-long 会对其第一个源采用寄存器对(存放要移位的值),但会对其第二个源采用单个寄存器(存放移位的距离)。 |
b0..cf 12x | binop/2addr vA, vB | A: 目标寄存器或寄存器对和第一个源寄存器或寄存器对(4 位) | 对两个源寄存器执行已确定的二元运算,并将结果存储到第一个源寄存器中。 注意:与其他 -long/2addr 数学运算(对其目标/第一个源和第二个源都采用寄存器对)相反,shl-long/2addr、shr-long/2addr 和 ushr-long/2addr 会对其目标/第一个源采用寄存器对(存放要移位的值),但会对其第二个源采用单个寄存器(存放移位的距离)。 |
d0..d7 22s | binop/lit16 vA, vB, #+CCCC | A: 目标寄存器(4 位) | 对指定的寄存器(第一个参数)和字面量值(第二个参数)执行指定的二元运算,并将结果存储到目标寄存器中。 注意:rsub-int 不含后缀,因为此版本是其一系列运算码中的主运算码。另外,有关语义的详细信息,请参阅下文。 |
d8..e2 22b | binop/lit8 vAA, vBB, #+CC | A: 目标寄存器(8 位) | 对指定的寄存器(第一个参数)和字面量值(第二个参数)执行指定的二元运算,并将结果存储到目标寄存器中。 注意:有关 rsub-int 语义的详细信息,请参阅下文。 |
运算指令
运算码 | C 语义 | 备注 |
neg-int | int32 a; | 一元二进制补码。 |
not-int | int32 a; | 一元反码。 |
neg-long | int64 a; | 一元二进制补码。 |
not-long | int64 a; | 一元反码。 |
neg-float | float a; | 浮点否定。 |
neg-double | double a; | 浮点否定。 |
int-to-long | int32 a; | 将 int32 符号扩展为 int64。 |
int-to-float | int32 a; | 使用最近舍入,将 int32 转换为 float。这会导致某些值不够精准。 |
int-to-double | int32 a; | 将 int32 转换为 double。 |
long-to-int | int64 a; | 将 int64 截断为 int32。 |
long-to-float | int64 a; | 使用最近舍入,将 int64 转换为 float。这会导致某些值不够精准。 |
long-to-double | int64 a; | 使用最近舍入,将 int64 转换为 double。这会导致某些值不够精准。 |
float-to-int | float a; | 使用向零舍入,将 float 转换为 int32。NaN 和 -0.0(负零)转换为整数 0。无穷数和因所占幅面过大而无法表示的值根据符号转换为 0x7fffffff 或 -0x80000000。 |
float-to-long | float a; | 使用向零舍入,将 float 转换为 int64。适用于 float-to-int 的特殊情况规则也适用于此,但超出范围的值除外,这些值根据符号转换为 0x7fffffffffffffff 或 -0x8000000000000000。 |
float-to-double | float a; | 将 float 转换为 double,值依然精准。 |
double-to-int | double a; | 使用向零舍入,将 double 转换为 int32。适用于 float-to-int 的特殊情况规则也适用于此。 |
double-to-long | double a; | 使用向零舍入,将 double 转换为 int64。适用于 float-to-long 的特殊情况规则也适用于此。 |
double-to-float | double a; | 使用最近舍入,将 double 转换为 float。这会导致某些值不够精准。 |
int-to-byte | int32 a; | 将 int32 截断为 int8,对结果进行符号扩展。 |
int-to-char | int32 a; | 将 int32 截断为 uint16,无需进行符号扩展。 |
int-to-short | int32 a; | 将 int32 截断为 int16,对结果进行符号扩展。 |
add-int | int32 a, b; | 二进制补码加法。 |
sub-int | int32 a, b; | 二进制补码减法。 |
rsub-int | int32 a, b; | 二进制补码反向减法。 |
mul-int | int32 a, b; | 二进制补码乘法。 |
div-int | int32 a, b; | 二进制补码除法,向零舍入(即截断为整数)。如果 b == 0,则会抛出 ArithmeticException。 |
rem-int | int32 a, b; | 二进制补码除后取余数。结果的符号与 a 的符号相同,可更精确地定义为 result == a - (a / b) * b。如果 b == 0,则会抛出 ArithmeticException。 |
and-int | int32 a, b; | 按位与运算。 |
or-int | int32 a, b; | 按位或运算。 |
xor-int | int32 a, b; | 按位异或运算。 |
shl-int | int32 a, b; | 按位左移(带掩码参数)。 |
shr-int | int32 a, b; | 按位有符号右移(带掩码参数)。 |
ushr-int | uint32 a, b; | 按位无符号右移(带掩码参数)。 |
add-long | int64 a, b; | 二进制补码加法。 |
sub-long | int64 a, b; | 二进制补码减法。 |
mul-long | int64 a, b; | 二进制补码乘法。 |
div-long | int64 a, b; | 二进制补码除法,向零舍入(即截断为整数)。如果 b == 0,则会抛出 ArithmeticException。 |
rem-long | int64 a, b; | 二进制补码除后取余数。结果的符号与 a 的符号相同,可更精确地定义为 result == a - (a / b) * b。如果 b == 0,则会抛出 ArithmeticException。 |
and-long | int64 a, b; | 按位与运算。 |
or-long | int64 a, b; | 按位或运算。 |
xor-long | int64 a, b; | 按位异或运算。 |
shl-long | int64 a; | 按位左移(带掩码参数)。 |
shr-long | int64 a; | 按位有符号右移(带掩码参数)。 |
ushr-long | uint64 a; | 按位无符号右移(带掩码参数)。 |
add-float | float a, b; | 浮点加法。 |
sub-float | float a, b; | 浮点减法。 |
mul-float | float a, b; | 浮点乘法。 |
div-float | float a, b; | 浮点除法。 |
rem-float | float a, b; | 浮点除后取余数。该函数不同于 IEEE 754 取余,定义为 result == a - roundTowardZero(a / b) * b。 |
add-double | double a, b; | 浮点加法。 |
sub-double | double a, b; | 浮点减法。 |
mul-double | double a, b; | 浮点乘法。 |
div-double | double a, b; | 浮点除法。 |
rem-double | double a, b; | 浮点除后取余数。该函数不同于 IEEE 754 取余,定义为 result == a - roundTowardZero(a / b) * b。 |
对象操作指令
与对象实例相关的操作,如对象创建、类型检查、类型转换等。
opcode | 指令 | 参数 | 描述 |
22 21c | new-instance vAA, type@BBBB | A: 目标寄存器(8 位) | 根据指定的类型构造新实例,并将对该新实例的引用存储到目标寄存器中。该类型必须引用非数组类。 |
20 22c | instance-of vA, vB, type@CCCC | A: 目标寄存器(4 位) | 如果指定的引用是给定类型的实例,则为给定目标寄存器赋值 1,否则赋值 0。 注意:由于 B 必须一律为引用(而非基元值),因此如果 C 引用基元类型,则始终赋值 0。 基元值:基本数据类型,如int,long之类。 |
1f 21c | check-cast vAA, type@BBBB | A: 引用传送寄存器(8 位) | 如果给定寄存器中的引用不能转型为指定的类型,则抛出 ClassCastException。 注意:由于 A 必须一律为引用(而非基元值),因此如果 B 引用基元类型,则必然会在运行时失败(即抛出异常)。 |
52..5f 22c | iinstanceop vA, vB, field@CCCC | A: 值寄存器或寄存器对;可以是源寄存器或寄存器对,也可以是目标寄存器或寄存器对(4 位) | 对已标识的字段执行已确定的对象实例字段运算,并将结果加载或存储到值寄存器中。iget取值,iput存值 |
60..6d 21c | sstaticop vAA, field@BBBB | A: 值寄存器或寄存器对;可以是源寄存器或寄存器对,也可以是目标寄存器或寄存器对(8 位) | 对已标识的静态字段执行已确定的对象静态字段运算,并将结果加载或存储到值寄存器中。 注意:这些运算码是静态链接的合理候选项,将字段参数更改为更直接的偏移量。sget 取值,sput存值 |
6e..72 35c | invoke-kind {vC, vD, vE, vF, vG}, method_id@BBBB | A: 参数字数(4 位) | 调用指定的方法。可使用相应的 move-result* 变体将所得结果(如果有的话)存储为紧跟其后的指令。 invoke-virtual 用于调用正常的虚拟方法(该方法不是 private、static 或 final,也不是构造函数)。 当 method_id 引用非接口类方法时,使用 invoke-super 调用最近父类的虚拟方法(这与调用类中具有相同 method_id 的方法相反)。invoke-virtual 具有相同的方法限制。 在版本 037 或更高版本的 Dex 文件中,如果 method_id 引用接口方法,则使用 invoke-super 调用在该接口上定义的该方法的最具体、未被覆盖的版本。invoke-virtual 具有相同的方法限制。在版本 037 之前的 Dex 文件中,具有接口 method_id 是违反规则且未定义的。 invoke-direct 用于调用非 static 直接方法(也就是说,本质上不可覆盖的实例方法,即 private 实例方法或构造函数)。 invoke-static 用于调用 static 方法(该方法始终被视为直接方法)。 invoke-interface 用于调用 interface 方法,也就是说,对具体类未知的对象使用引用 interface 的 method_id。 |
数组操作指令
dalvik中专门为数组的操作制定了一系列指令
opcode | 指令 | 参数 | 描述 |
23 22c | new-array vA, vB, type@CCCC | A: 目标寄存器(4 位) | 根据指定的类型和大小构造新数组。该类型必须是数组类型。 |
B: 大小寄存器 | |||
C: 类型索引 | |||
24 35c | filled-new-array {vC, vD, vE, vF, vG}, type@BBBB | A: 数组大小和参数字数(4 位) | 根据给定类型和大小构造数组,并使用提供的内容填充该数组。该类型必须是数组类型。数组的内容必须是单字类型(即不接受 long 或 double 类型的数组,但接受引用类型的数组)。构造的实例会存储为一个“结果”,方式与方法调用指令存储其结果的方式相同,因此构造的实例必须移到后面紧跟 move-result-object 指令(如果要使用的话)的寄存器。 |
B: 类型索引(16 位) | |||
C..G: 参数寄存器(每个寄存器各占 4 位) | |||
25 3rc | filled-new-array/range {vCCCC .. vNNNN}, type@BBBB | A: 数组大小和参数字数(8 位) | 根据给定类型和大小构造数组,并使用提供的内容填充该数组。相关的说明和限制与上文所述 filled-new-array 相同。 |
B: 类型索引(16 位) | |||
C: 第一个参数寄存器(16 位) | |||
N = A + C - 1 | |||
26 31t | fill-array-data vAA, +BBBBBBBB (有关补充数据,请参阅下文的“fill-array-data-payload 格式”) | A: 数组引用(8 位) | 用指定的数据填充给定数组。必须引用基元类型的数组,且数据表格的类型必须与数组匹配;此外,数据表格所包含的元素个数不得超出数组中的元素个数。也就是说,数组可以比表格大;如果是这样,仅设置数组的初始元素,而忽略剩余元素。 |
B: 到表格数据伪指令的有符号“分支”偏移量(32 位) | |||
21 12x | array-length vA, vB | A: 目标寄存器(4 位) | 将指定数组的长度(条目个数)赋值给给定目标寄存器 |
44..51 23x | arrayop vAA, vBB, vCC | A: 值寄存器或寄存器对;可以是源寄存器或寄存器对,也可以是目标寄存器或寄存器对(8 位) | 在给定数组的已标识索引处执行已确定的数组运算,并将结果加载或存储到值寄存器中。aget取值,aput存值 |
方法返回指令
opcode | 指令 | 参数 | 描述 |
0e 10x | return-void |
| 从 void 方法返回。 |
0f 11x | return vAA | A: 返回值寄存器(8 位) | 从单字宽度(32 位)非对象值返回方法返回。 |
10 11x | return-wide vAA | A: 返回值寄存器对(8 位) | 从双字宽度(64 位)值返回方法返回。 |
11 11x | return-object vAA | A: 返回值寄存器(8 位) | 从对象返回方法返回。 |
跳转指令
opcode | 指令 | 参数 | 描述 |
2d..31 23x | cmp kind vAA, vBB, vCC | A: 目标寄存器(8 位) | 执行指定的浮点或 long 比较,如果 b == c,则将 a 设为 0,如果 b > c,则设为 1,如果 b < c,则设为 -1。为浮点运算列出的“bias”表示如何处理 NaN 比较运算:对于 NaN 比较,“gt bias”指令返回 1,而“lt bias”指令返回 -1。 例如,建议使用 cmpg-float 检查浮点数是否满足条件 x < y;如果结果是 -1,则表示测试结果为 true,其他值则表示测试结果为 false,原因是当前比较是有效比较但是结果不符合预期或其中一个值是 NaN。 |
32..37 22t | if-test vA, vB, +CCCC | A: 要测试的第一个寄存器(4 位) | 如果两个给定寄存器的值比较结果符合预期,则分支到给定目标寄存器。 注意:分支偏移量不得为 0。(自旋循环可以通过围绕后向 goto 进行分支或通过在分支之前添加 nop 作为目标来正常构造。) |
38..3d 21t | if-testz vAA, +BBBB | A: 要测试的寄存器(8 位) | 如果给定寄存器的值与 0 的比较结果符合预期,则分支到给定目标寄存器。 注意:分支偏移量不得为 0。(自旋循环可以通过围绕后向 goto 进行分支或通过在分支之前添加 nop 作为目标来正常构造。) |
28 10t | goto +AA | A: 有符号分支偏移量(8 位) | 无条件地跳转到指定的指令。 注意:分支偏移量不得为 0。(自旋循环可以用 goto/32 正常构造,也可以通过在分支之前添加 nop 作为目标来正常构造。) |
29 20t | goto/16 +AAAA | A: 有符号分支偏移量(16 位) | 无条件地跳转到指定的指令。 注意:分支偏移量不得为 0。(自旋循环可以用 goto/32 正常构造,也可以通过在分支之前添加 nop 作为目标来正常构造。) |
2a 30t | goto/32 +AAAAAAAA | A: 有符号分支偏移量(32 位) | 无条件地跳转到指定的指令。 |
2b 31t | packed-switch vAA, +BBBBBBBB (有关补充数据,请参阅下文的“packed-switch-payload 格式”) | A: 要测试的寄存器 | 通过使用与特定整数范围内的每个值相对应的偏移量表,基于给定寄存器中的值跳转到新指令;如果没有匹配项,则跳转到下一条指令。 |
2c 31t | sparse-switch vAA, +BBBBBBBB (有关补充数据,请参阅下文的“packed-switch-payload 格式”) | A: 要测试的寄存器 | 通过使用偏移值对的有序表,基于给定寄存器中的值跳转到新指令;如果没有匹配项,则跳转到下一条指令。 |
packed-switch-payload 格式
名称 | 格式 | 说明 |
ident | ushort = 0x0100 | 识别伪运算码 |
size | ushort | 表格中的条目数 |
first_key | int | 第一位(即最低位)switch case 的值 |
targets | int[] | 与 size 相对的分支目标的列表。这些目标相对应的是 switch 运算码的地址(而非此表格的地址)。 |
注意:此表格一个实例的代码单元总数为 (size * 2) + 4。
sparse-switch-payload 格式
名称 | 格式 | 说明 |
ident | ushort = 0x0200 | 识别伪运算码 |
size | ushort | 表格中的条目数 |
keys | int[] | size 键值列表,从低到高排序 |
targets | int[] | 与 size 相对的分支目标的列表,每一个目标对应相同索引下的键值。这些目标相对应的是 switch 运算码的地址(而非此表格的地址)。 |
注意:此表格一个实例的代码单元总数为 (size * 4) + 2。
fill-array-data-payload 格式
名称 | 格式 | 说明 |
ident | ushort = 0x0300 | 识别伪运算码 |
element_width | ushort | 每个元素的字节数 |
size | uint | 表格中的元素数 |
data | ubyte[] | 数据值 |
注意:此表格一个实例的代码单元总数为 (size * element_width + 1) / 2 + 4。
更多指令参考https://source.android.com/docs/core/runtime/dalvik-bytecode
更多推荐
所有评论(0)