Android逆向基础——Dalvik 指令集

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值