安卓移动逆向(三)-Android Dalvik虚拟机

大家都知道Java程序是运行在Java虚拟机上,Android程序呢?

虽然Android平台使用Java语言来开发应用程序,但是Android程序却不是运行在标准的Java虚拟机上的. Google为Android平台专门设计了一套虚拟机来运行Android程序–Dalvik Virtual Machine,也就是Dalvik虚拟机了

本篇作用:

  • 扫盲Dalvik虚拟机
  • 了解Smail的语法,能读懂Smail文件

Dalvik概述

Dalvik的特点(相对于JVM)

  • 体积小,占用内存小;
  • 专有的DEX可执行文件格式,体积更小,执行速度更快;
  • 常量池采用32位索引值,寻址类方法名,字段名,常亮更快;
  • 基于寄存器架构,并拥有一套完成的指令系统
  • 提供了对象生命周期管理,堆栈管理,线程管理,安全和异常管理以及垃圾回收等重要功能;
  • 所有的Android程序都运行在Android系统进程里,每个进程对应着一个Dalvik虚拟机实例;

Dalvik虚拟机与Java虚拟机的区别

  1. Java虚拟机运行的Java字节码,Dalvik虚拟机运行的是Dalvik字节码
  2. Dalvik可执行文件的体积更小

    稍作解析:

    SDK中有一个叫做dx的工具负责将Java字节码转换为Dalvik字节码.dx工具对Java类文件重新排列,消除在类文件中出现的所有冗余信息,避免虚拟机在初始化时出现重复的文件加载与解析过程.

    举个栗子:

    在Java中有大量的字符串常量在多个类文件中被重复使用,这些荣誉信息会直接增加文件的体积,同事也会严重影响虚拟机解析文件的效率.dx工具针对这个问题做了专门的处理,它将所有Java类文件中的常量池进行分解,消除其中的冗余信息,重新组合成一个常量池,所有的类文件共享同一个常量池.dx工具转换过程如图所示,由于dx工具对常量池的压缩,是的相同的字符串,常量在DEX文件中只出现一次,从而减小了文件的体积.

3. Java虚拟机与Dalvik虚拟机架构不同

简单说一下:Java虚拟机基于栈架构,Dalvik基于寄存器架构;


Dalvik指令格式

一般Dalvik汇编代码由一系列的Dalvik指令组成,指令语法由指令的位描述与指令格式标识来决定.位描述约定如下

  • 每16位的字采用空格分割开来;
  • 每个字母表示四位,每个字母按顺序从高字节开始,排列到低字节.每四位之间可能用竖线”|”来表示不同的内容;
  • 顺序采用A-Z的翻个大写字母作为一个4位的操作码,op表示一个8位的操作码;
  • “?”来表示这个字段的所有位为0值;

栗子

“A|B|op BBBB F|E|D|C”

指令中间有两个空格,每个分开的部分是16位,共有3个16位组成这条指令;

第一个16位是”A|B|op” 高8位由A和B组成,低字节由操作码op组成;

第二个16位由BBBB组成,他表示一个16位的偏移值;

第三个16位分别由F,E,D,C共四个字节组成,在这里他们表示寄存器的参数.

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

  • 指令格式标识大多由三个字符组成,前两个是数字,最后一个是字母;
  • 第一个数字式标识指令由多少个16位的字组成;
  • 第二个数字标识指令最多使用寄存器的个数,特殊标记”r”标识使用一定范围内的寄存器;
  • 第三个字母为类型码,标识指令用到的额外数据的类型,见下图:
  • 还有一种特殊的情况 是末尾可能还会多出另一个字母,如果是”s”表示指令采用静态链接,如果是”i”表示指令应该被内联处理.

栗子

“22x” 有三条信息可以读出

  1. 指令由2个16位字组成
  2. 指令使用2个寄存器
  3. 没有使用到额外的数据

另外,Dalvik指令对语法做了一些说明,约定如下

  • 每条指令从操作码开始,后面紧跟参数,参数个数不定,每个参数之间采用逗号分开;
  • 每条指令的参数从指令的第一部分开始,op位于低8位,高8位可以是一个8位的参数也可以是两个4位的参数,还可以为空.如果指令超过16位,则后面的部分依次作为参数;
  • 如果参数使用”vX”的方式标识,表明它是一个寄存器,如v0,v1等;
  • 如果参数采用”#+X”的方式,表明它是一个常量数字;
  • 如果参数采用”+X”的方式,表明它是一个相对指令的地址偏移;
  • 如果参数采用”[email protected]”的方式,表明它是一个常量池索引值.其中kind表示常量池类型,例如[email protected],表示的就是字符串常量池索引BBBB;

栗子

“op vAA [email protected]”

高8位为空,用到1个寄存器参数vAA,还用到一个字符串常量池索引BBBB;


Dalvik寄存器

扫盲结束了,开始重点了

Dalvik字节码的类型,方法,与字段表示方法

  1. 类型

    Dalvik字节码只有两种类型,基本与引用,话不多说,看图;

    每个Dalvik寄存器都是32位大小,对于小鱼或者等于32位长度的类型来说,一个寄存器就可以存放该类型的值,而像J(long),D(double)等64位的类型,它们的值是使用相邻量个寄存器来存储的,v0和v1或者vN与vN+1等;

    L就好理解了,表示任何一个Java类,在Dalvik汇编代码中,它们以” Lpackage/name/ObjectName; “表示,注意最后一个分号,比如” Ljava/lang/String;”相当于String;

    [类型就是所有的数组,[后面紧跟基本类型的描述符,如[I表示一个整型一维数组,->int[],[[I表示int[][]<—> [Ljava/lang/String; 表示对象数组 String [];

  2. 方法

    Dalvik使用方法名,类型参数与返回值来描述一个方法;

    格式如下:

    Lpackage/name/ObjectName;->MethodName(III)Z

    说明:

    Lpackage/name/ObjectName;是一个类型;

    MethodName方法名

    (III)参数,三个int参数

    Z返回值void

    栗子

    method(I[[IILjava/lang/String;[Ljava/lang/String;)Ljava/lang/String;

    咳咳,按照上面的知识,将其转换为Java形式的代码为:

    String method(int ,int[][],String,String[])

  3. 字段

    字段和方法很相似,就是没有参数和返回值,取而代之的是字段的类型,格式如下

    Lpackage/name/ObjectName;->FieldName:TYPE

    说明:

    Lpackage/name/ObjectName;是一个类型;

    FieldName字段名

    TYPE字段类型

    FieldName与TYPE用冒号隔开

    栗子

    name:Ljava/lang/String;

    转换:

    String name;

    Dalvik代码中的字段代码以.field指令开头,根据字段类型不同,在字段指令的开始,可能会用到井号”#”加以注释;


Dalvik指令集

指令特点

Dalvik指令在调用格式上模仿了C语言的调用约定.Dalvik指令语法与助词符有如下特点:

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

栗子

“move-wide/from16 vAA,vBBBB”

move为基础字节码.标识这是基本操作;

wide为名称后缀.标识指令操作的数据宽度(64位);

from16位字节码后缀.标识源为一个16位的寄存器引用变量;

vAA为目的寄存器,它始终在源的前面 取值范围为v0-v2^8-1(255);

vBBBB为源寄存器,取值范围为v0-v2^16-1(65535)

空操作指令

空操作指令的助记符为nop,他的值是00,通常nop指令被用过对齐代码用途,没啥大用;

数据操作指令

数据操作指令为move.move指令的原型为move destination,source或者move destination,move指令根据字节码的大小与类型不同,后面会跟上不同的后缀.

栗子(表示太多直接上图,都差不多)

返回指令

返回指令指的是函数结尾时运行的最后一条指令.他的基础字节码位return,共有以下四条返回指令

栗子

  1. “return-void” 返回一个void
  2. “return vAA” 返回一个32位非对象类型的值,返回值寄存器位8位的寄存器vAA;
  3. “return-wide vAA” 返回一个64位非对象类型的值,返回值寄存器位8位的寄存器vAA;
  4. “return-object vAA” 返回一个对象类型的值,返回值寄存器位8位的寄存器vAA;

数据定义指令

数据定义指令用来定义程序中用到的变量,字符串,类等数据,他的基础字节码为const

栗子(表示太多直接上图-_-)

锁指令

锁指令多用在多线程程序中对同一对象的操作,Dalvik指令集中有两条锁指令.

  1. “monitor-enter vAA” 为指定的对象获取锁
  2. “monitor-exit vAA”为指定的对象释放锁

实例操作指令

与实例相关的操作包括实例的类型传换,检查及新建等;

  • “check-case vAA,[email protected]” 将vAA中的对象引用强转为BBBB类型;
(BBBB)vAA;
  • “instance-of vA,vB,[email protected]” 判断vB中的对象引用是否能转成CCCC类型,能vA=1,不能vA=0;
if(vB.instanceof(type@CCCC)){
    vA =1;
}else{
    vA = 0;
}
  • “new-instance vAA,[email protected]” 新建一个BBBB的对象vAA,BBBB不能为数组
BBBB vAA = new BBBB();
  • “check-cast/jumbo vAAAA,[email protected]” 与”check-case vAA,[email protected]”作用相同,只是取值范围更大(Android 4.0新增)
  • “instance-of/jumbo vAAAA,vBBBB,[email protected]”与”instance-of vAA,vBB,[email protected]”作用相同,只是取值范围更大(Android 4.0新增)
  • “new-instance/jumbo vAAAA,[email protected]”与”new-instance vAA,[email protected]”作用相同,只是取值范围更大(Android 4.0新增)

数组操作指令

数组操作包括获取数组长度(指的是数组的条目个数),新建数组,数组赋值,数组元素取值与赋值等操作;

  • “array-length vA,vB”
vA = vB.length; //  将vB的长度赋值给vA
  • “new-array vA,vB,[email protected]”
vA = CCCC[vB]; //   构建一个vB大的CCCC类型的数组赋值给vA
  • 其余的附图

异常指令

Dalvik 指令集中有一条指令用于抛出异常

  • “throw vAA” 抛出vAA寄存器中指定类型的异常

跳转指令

Dalvik指令集中有三种跳转指令:无条件跳转(goto),分支跳转(switch),条件跳转(if)

  • “goto +AA” 无条件跳转到指定偏移处,偏移量AA不能为0;
  • “goto/16+AAAA” 无条件跳转到指定偏移处,偏移量AAAA不能为0;
  • “goto/32+AAAAAAAA” 无条件跳转到指定偏移处;
  • “packed-switch vAA,+BBBBBBBB” 分支跳转指令. vAA寄存器为switch分支中需要判断的值即(switch(vAA)),BBBBBBBB指向一个packed-switch-payload格式的偏移表,表中的值是规律递增的.(先这么记住就好,感兴趣可以找百度..)
  • “sparse-switch vAA,+BBBBBBBB”分支跳转指令,vAA寄存器为switch分支中需要判断的值即(switch(vAA)),BBBBBBBB指向一个sparse-switch-payload格式的偏移表,表中的值是无规律的偏移量.
  • “if-test vA,vB,+CCCC” 条件跳转指令,比较vA与vB的值,如果比较结果满足就跳转到CCCC指定的偏移处,偏移量CCCC不能为0,if-test类型的指令有以下几条:
    • “if-eq vA, vB, :cond_xx” 如果vA等于vB则跳转到:cond_xx
    • “if-ne vA, vB, :cond_xx” 如果vA不等于vB则跳转到:cond_xx
    • “if-lt vA, vB, :cond_xx” 如果vA小于vB则跳转到:cond_xx
    • “if-ge vA, vB, :cond_xx” 如果vA大于等于vB则跳转到:cond_xx
    • “if-gt vA, vB, :cond_xx” 如果vA大于vB则跳转到:cond_xx
    • “if-le vA, vB, :cond_xx” 如果vA小于等于vB则跳转到:cond_xx
  • “if-testz vAA,+BBBB”条件跳转指令,那vAA与0作比较,满足结果或者不满足结果就跳转到BBBB的指定偏移处BBBB不能为0, if-testz类型的指令有以下几条:
    • “if-eqz vA, :cond_xx” 如果vA等于0则跳转到:cond_xx
    • “if-nez vA, :cond_xx” 如果vA不等于0则跳转到:cond_xx
    • “if-ltz vA, :cond_xx” 如果vA小于0则跳转到:cond_xx
    • “if-gez vA, :cond_xx” 如果vA大于等于0则跳转到:cond_xx
    • “if-gtz vA, :cond_xx” 如果vA大于0则跳转到:cond_xx
    • “if-lez vA, :cond_xx” 如果vA小于等于0则跳转到:cond_xx

比较指令

比较指令用于对两个寄存器的值(浮点型或者长整型)进行比较格式为:

“cmpkind vAA,vBB,vCC”

Dalvik指令集中共有5条比较指令:

  • “cmpl-float” 比较两个float值;
if(vBB == vCC){
    vAA =0;
}else if(vBB>vCC){
    vAA = -1;
}else if(vBB<vCC>){
    vAA = 1;
}
  • “cmpg-float” 比较两个float的值
if(vBB == vCC){
    vAA =0;
}else if(vBB<vCC){
    vAA = -1;
}else if(vBB>vCC>){
    vAA = 1;
}

当cmpg或者cmp时,B > C时A = 1,反之-1;当cmpl时,B > C时A = -1反之1;

  • “cmpg-double” 比较两个double的值
  • “cmpl-double” 比较两个double的值
  • “cmp-long” 比较两个long的值

字段操作指令

字段操作指令用来对对象实例的字段进行读写操作.

字段的类型可以是Java中有效的数据类型,对普通字段与静态字段操作有两种指令集,分别是”iinstanceop vA,vB,[email protected]”与”sstaticop vAA,[email protected]”.

在Android 4.0系统中,有”iinstanceop /jumbovAAAA,vBBBB,[email protected]”与”sstaticop/jumbo vAAAA,[email protected]”.和上面的两种作用相同,只是加了jmpbo后缀,寄存器与指令索引取值范围更大(后面的只会说有/jumbo指令后缀的指令集,作用就不指明了)

普通字段指令的指令前缀为i,如.对普通字段读操作使用iget指令,写操作使用iput指令;静态字段的指令前缀为s,如.对静态字段的读操作为sget,写操作为sput;

根据访问的字段类型不同,字段操作指令后面会紧跟字段类型的后缀,如iget-byte指令表示读取实例字段的值类型为byte;

方法调用指令

方法调用指令负责调用类实例的方法,它的基础指令为invoke,方法调用指令有”invoke-kind{vC,vD,vE,vF,vG},[email protected]”与”invoke-kind/range{vCCCC…VNNNN},[email protected]”两类,这两类指令作用没啥不同,后者在设置参数寄存器时使用了range来指定寄存器的范围,根据方法类型的不同,共有如下5条方法调用指令:

  1. “invoke-virtual” 调用实例的虚方法
  2. “invoke-super” 调用实例的父类方法
  3. “invoke-direct” 调用实例的直接方法
  4. “invoke-static” 调用实例的静态方法
  5. “invoke-interface” 调用实例的接口方法

    Android 4.0有jumbo的指令集;

方法调用指令的返回值必须使用move-result*指令来获取:

invoke-static{},Landroid/os/Parcel;->obtain()Landroid/os/Parcel;

move-result-object v0;

数据转换指令

数据转换指令用于将一种类型的数值转换为另一种类型,他的格式为”unop vA,vB” 把vB中的数据做一定运算(转换)放在vA中:(比较简单,直接上图)

数据运算指令

数据运算指令包括算数运算指令与逻辑运算指令:

  • 算数运算指令:加,减,乘,除,模,移位等
  • 逻辑运算指令:间与,或,非,抑或等;

    上个图吧

    其中基础字节码后面的-type可以是-int,-long,-float,-double,后面3类指令也差不多,就不列了,触类旁通;

== == == == == == == == == == == == == == == == == == == == == ==

时间: 2025-01-03 20:44:35

安卓移动逆向(三)-Android Dalvik虚拟机的相关文章

Chapter3——进入Android Dalvik虚拟机

虽然Android平台使用Java来开发应用程序,但Android程序却不是运行在标准Java虚拟机上的. 可能是出于效率和版权的考虑,Google为Android专门设计了一套虚拟机Dalvik Virtual Machine. 上面是第三章的前言,一年多以前,作者写这本书的时候,Kitkat(Android 4.4)还没有出来,作者也声明这本书默认使用的是Android4.1和Linux3.4的环境. 而4.4版本的Android的「开发者选项」中已经添加了Art模式和Dalvik模式切换的

Android Dalvik虚拟机简述(与Java虚拟机的区别和简要的执行原理)

先一睹Dalvik虚拟机在Android系统框架图中位置: 文章目录: 一.虚拟机简述二.Java虚拟机简述三.Dalvik虚拟机简述四.Dalvik虚拟机与Java虚拟机的区别五.Dalvik虚拟机执行原理简述 此文章原始是PPT格式已转换为PDF,请直接下载文档阅读: AndroidDalvikVMOverview

Chapter3——进入Android Dalvik虚拟机(二)

Dalvik汇编语言基础 Dalvik虚拟机为自己设计了一套指令集,并制定了自己的指令格式和调用规范. 位描述约定如下: 每16位的字采用空格分隔开来 每个字母表示4位,每个字母按顺序从高字节开始,排列到低字节.每4位之间可能使用竖线「|」表示不同的内容. 顺序采用A~Z的单个大写字母作为一个4位操作码,op表示一个8位的操作码. 「∅」来表示这字段所有位为0值. 以「A|G|op BBBB F|E|D|C」为例, 指令中间两个空格每个分开的部分大小为16位: 第一个16位A|G|op,高8位由

进入Android Dalvik虚拟机之Dalvik虚拟机的特点

Google于2007年底正式发布了Android SDK,Dalvik虚拟机也第一次进入了人们的视野.它的作者是丹.伯恩斯坦(Dan Bornstein).Dalvik虚拟机作为Android平台的核心组件,拥有如下几个特点: 体积小,占用内存空间小: 专有的DEX可执行文件格式,体积更小,执行速度更快: 常量池采用32位索引值,寻址类方法名,字段名,常量更快: 基于寄存器架构,并拥有一套完整的指令系统: 提供了对象生命周期管理,堆栈管理,线程管理,安全和异常管理以及垃圾回收等重要功能: 所有

Android Dalvik虚拟机概述

Dalvik虚拟机概述 Dalvik是Google公司自己设计用于Android平台的Java虚拟机.Dalvik虚拟机是Google等厂商合作开发的Android移动设备平台的核心组成部分之一.它可以支持已转换为 .dex(即Dalvik Executable)格式的Java应用程序的运行,.dex格式是专为Dalvik设计的一种压缩格式,适合内存和处理器速度有限的系统.Dalvik 经过优化,允许在有限的内存中同时运行多个虚拟机的实例,并且每一个Dalvik 应用作为一个独立的Linux 进

Android Dalvik虚拟机和ART虚拟机对比

1.概述 Android4.4以上开始使用ART虚拟机,在此之前我们一直使用的Dalvik虚拟机,那么为什么Google突然换了Android运行的虚拟机呢?答案只有一个:ART虚拟机更优秀. 2.Dalvik vs ARTDalvik Android4.4及以前使用的都是Dalvik虚拟机,我们知道Apk在打包的过程中会先将java等源码通过javac编译成.class文件,但是我们的Dalvik虚拟机只会执行.dex文件,这个时候dx会将.class文件转换成Dalvik虚拟机执行的.dex

转 Android Dalvik虚拟机初识

首先,让我们来思考下面几个问题: 什么是Dalvik虚拟机? Dalvik VM与JVM有什么区别? Dalvik VM有什么新的特点? Dalvik VM的架构是怎么样的? 首先,我得承认第一个问题问得很傻:什么是Dalvik虚拟机?没有人给出过一个明确的定义,但是,我们似乎可以从人们对Java虚拟机的描述中得到些信息. Java虚拟机(JVM)是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的.它有自己完善的硬件架构(如处理器.堆 栈.寄存器等),还具有相应的指令系

Android Dalvik虚拟机初识

摘自:http://blog.csdn.net/andyxm/article/details/6126907 首先,让我们来思考下面几个问题: 什么是Dalvik虚拟机? Dalvik VM与JVM有什么区别? Dalvik VM有什么新的特点? Dalvik VM的架构是怎么样的? 首先,我得承认第一个问题问得很傻:什么是Dalvik虚拟机?没有人给出过一个明确的定义,但是,我们似乎可以从人们对Java虚拟机的描述中得到些信息. Java虚拟机(JVM)是一个虚构出来的计算机,是通过在实际的计

【Android Dalvik虚拟机好学易用系列】之二:Dalvik汇编语言

一 Dalvik指令格式 1.1 位描述 Dalvik汇编代码由Dalvik指令组成,指令语法由指令的位描述与指令格式辨识来决定.位描述的约定如下所示: 每16位的字采用空格分隔开来: 每个字母表示四位,每个字符顺序从高字节开始,排列到低字节,每四位之间可能使用"|"来表示不同的内容. 顺序采用A~Z的单个大写字母作为一个4位操作码,op表示一个8位的操作码: "?"表示这字段所有位为0值. 举例,如以下指令: A|G|op BBBB F|E|D|C 两个空格:每个