现在的位置: 首页 > 综合 > 正文

Dalvik和Java字节码的对比

2013年12月04日 ⁄ 综合 ⁄ 共 1811字 ⁄ 字号 评论关闭

英文原文:Forensics Blog,编译:ImportNew - 黄小非

导读:本文主要介绍了Dalvik和Java字节码之间的异同。了解这些异同对于理解Android应用程序的特性,以及分析针对Android应用的恶意行为,都十分重要。

Android应用程序通常使用Java语言编写,并在Dalvik虚拟机(DVM)中运行。DVM是一种完全不同于经典Java虚拟机(JVM)的虚拟机机制,它由Google开发,并专门针对移动操作系统(尤其是Android平台)的特性进行了优化。通过一个叫做dx的转换工具,可以将传统的JVM虚拟机中的字节码(.class格式文件)转换为在Dalvik虚拟机中运行的字节码(dex格式)。与DVM不同,JVM使用纯Java类文件。如果你想对Android应用程序实施逆向工程,那么你就必须了解Dalvik字节码的格式,同时还需要对静态和动态状态机制有深入的理解。William
Enck 等人通过他们合写的论文《A Study of Android Application Security | Android应用程序安全性研究》,对JVM和DVM字节码的不同之处进行了总结。

 

1. Android应用程序架构

JVM字节码由一个或者多个.class文件组成(每个文件包含一个类)。在运行时,JVM会动态地从各个.class文件中将字节码加载到内存中。而Dalvik字节码只由一个.dex文件构成,这个文件里包含了应用程序需要的所有类。下图展示了.dex文件的生成过程。在Java编译器生成了JVM字节码后,Dalvik dx编译器会删除所有的.class文件,并将JVM字节码重新编译为Dalvik字节码。如图所示,生成过程包括了翻译,重构,基本元素的解释(常量池,类定义和数据段)几个部分。常量池描述了所有的常量,包括引用、方法名和数字常量。类定义包括访问权限标志,类名等等。数据段则包括所有目标虚拟机执行的方法代码,以及类和方法相关的数据信息(例如DVM使用的寄存器数量、本地变量列表、以及操作指令栈)和类实例变量。

2. 寄存器结构

不同于JVM基于堆栈,DVM基于寄存器。在JVM字节码中,本地变量存放在本地变量列表中,然后被压入堆栈,由操作码进行操作。或者,JVM也可以不强制将本地变量存入本地变量列表,而是直接压入堆栈进行操作。在Dalvik字节码中,本地变量会被分配到2的16次方个可用寄存器中的任意一个。Dalvik操作码不会从堆栈中取值,而是直接从寄存器中取值进行操作。

 

3. 指令集

Dalvik有218个操作码,这些操作码与Java中的200个操作码有本质的不同。举例来说,JVM中一系列用于在堆栈和本地变量列表之间传递数据的操作码,在Dalvik中完全被舍弃了。Dalvik中的操作码指令比Java中的更长,因为大多数指令都包含了操作所对应的源寄存器和目标寄存器的地址。如果你想对Dalvik操作码有一个大致的了解,可以参看Gabor
Paller
Android Developers 上的博客文章。

 

4. 常量池

JVM字节码通常会遍历所有.class文件中的常量池,例如,保存方法名称的常量池。在Dalvik中,为了让一个常量池能够供所有的类使用,dx编译器舍弃了这种遍历的过程。而且,dx还使用了“内联”(inline)的技术消除了一些常量。因此,整型,长整型以及单精度和双精度的浮点数常量在dx编译中会被替换掉,从而不再存在。


5. 二义性的原始类型

在JVM中,整型操作数的指令码和单精度浮点数常量的操作码是不同的,同时,长整型常量和双精度浮点数常量的操作码也是不同的。在Dalvik的实现里,上述的操作码,无论是整型还是浮点型,都是相同的。


6. 空引用

Dalvik字节码没有专门的null引用类型。取而代之的是,Dalvik用常数0来表示null. 因此,对于常数0的含义,往往需要进行适当的区分,从而避免引起歧义。

 

7. 对象引用

JVM字节码使用不同的操作码来进行对象比较和null类型比较,而Dalvik则使用同一操作码进行比较。因此,在反编译的过程中,被比较对象的类型信息需要专门进行恢复。

 

8. 原始类型数组的存储

Dalvik使用非确定的操作码来操作一个数组,与之相反的是,JVM使用确定的操作码进行数组操作。因此,为了在翻译的时候能得到正确的数组类型,Dalvik中的数组类型必须专门恢复。

抱歉!评论已关闭.