• 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:

    1. class Clazz{
    2. public int Add(int a, int b)
    3. {
    4. int c = a + b;
    5. return c;
    6. }
    7. }

    在排除编译器自动生成的临时变量与优化以外,这里使用了四个寄存器,即.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. 类的描述:

    有如下类:

    1. package com.example;
    2. public class User{
    3. private String name;
    4. private String nick;
    5. public String getName()
    6. {return name;}
    7. public void setName(String name)
    8. {this.name = name;}
    9. }

    注:其中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 ()V 描述构造函数

            .method static constructor ()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

  • 相关阅读:
    Neo4j入门教程2(看不懂评论区随便骂)
    鸿蒙-实践课程六 android、HarmonyOS 权限对比
    Databend 特性系列(1)|Databend 数据生命周期
    iOS AVPlayer
    “华为30岁以下员工仅占28%” 上热搜,网友:说好的35岁天花板呢?
    IP地址欺骗的危害与后果
    Serverless实战 - 2分钟,教你用Serverless每天给女朋友自动发土味情话
    NotePad++安装一些插件的使用
    Linux之ansible(playbook)超详解
    【Vue易混点】
  • 原文地址:https://blog.csdn.net/xuandao_ahfengren/article/details/127805967