由于研究生阶段的方向是Android逆向,然而破解一个Android程序的通常方法是将apk文件利用工具反编译,生成smail格式的反汇编代码,然后阅读smail文件的代码来理解程序的运行机制,找到程序的突破口以后再进行修改和调试。
所以掌握好smail汇编代码,对于安卓逆向很重要,故在此学习并记录。
一、基础知识
1、Dalvik虚拟机
了解Dalvik虚拟机的基本特性和工作原理,对掌握Android运行程序十分重要。
1.1 Dalvik虚拟机特点
(1)体积小,占用内存空间小;
(2)专有的DEX可执行文件格式,体积更小,执行速度更快;
(3)常量池采用32位索引值,寻址类方法名、字段名、常量更快;
(4)基于寄存器架构,并拥有一套完整的指令系统;
(5)提供了对象生命周期管理、堆栈管理、线程管理、安全和异常管理以及垃圾回收等重要功能;(6)所有的Android程序都运行在Android系统进程里,每个进程对应着一个Dalvik虚
拟机实例。
1.2 Dalvik虚拟机与java虚拟机的区别
(1) Java虚拟机运行的是Java字节码,Dalvik虚拟机运行的是Dalvik字节码。传统的Java程序经过编译,生成Java字节码保存在class文件中,Java虚拟机通过解码class文件中的内容来运行程序。而Dalvik虚拟机运行的是Dalvik字节码,所有的Dalvik字节码由Java字节码转换而来,并被打包到-一个DEX (Dalvik Executable) 可执行文件中,Dalvik虚拟机通过解释DEX文件来执行这些字节码。
(2)Dalvik可执行文件体积更小,Android SDK中有一个叫dx的工具负责将java字节码转换成Dalvik字节码,由于dx工具对常量池的压缩,使得相同的字符串、常量在DEX文件中只出现一次,从而大大减小了文件的体积。
(3)java虚拟机与Dalvik虚拟机的架构不同,Java虚拟机基于栈架构。程序在运行时虛拟机需要频繁的从栈上读取或写入数据,这个过程需要更多的指令分派与内存访问次数,会耗费不少CPU时间,对于像手机设备资源有限的设备来说,这是相当大的一笔开销。Dalvik虚拟机基于寄存器架构。数据的访问通过寄存器间直接传递,这样的访问方式比基于栈方式要快很多。
2、Dalvik寄存器
2.1 了解Dalvik寄存器
Dalvik虚拟机基于寄存器架构,在代码中大量地使用到了寄存器。Dalvik虚拟机是作用.
于特定架构的CPU.上运行的,在设计之初采用了ARM架构,ARM架构的CPU本身集成
了多个寄存器,Dalvik将部分寄存器映射到了ARM寄存器上,还有--部分则通过调用栈进
行模拟。注意: Dalvik 中用到的寄存器都是32位的,支持任何类型,64位类型用2个相邻
寄存器表示。
Dalvik虚拟机支持多少个虚拟寄存器呢?通过查看Dalvik指令格式表,可以发现类似“00)|op
AAAA BBBB”的指令,它的语法为“op vAAAA, vBBBB”,其中每个大写字母代表4位, AAAA、
BBBB最大值是2的16次方即65536,寄存器采用v0作起始值,因此,它的取值范围是v0 ~v65535。
2.2 两种不同的寄存器的表示方法——v命名法与p命名法
假设一一个函数使用到M个寄存器,并且该函数有N个参数,根据Dalvik虚拟机参数传递
方式中的规定:参数使用最后的N个寄存器中,局部变量使用从v0开始的前M-N个寄存器。
v命名法采用以小写字母“v”开头的方式表示函数中用到的局部变量与参数,所有的
寄存器命名从v0开始,依次递增。
p命名法对函数的局部变量寄存器命名没有影响,它的命名规则是:函数中引入的参数
命名从p0开始,依次递增。对于foo0 函数,p命名法会用到v0、v1、p0、 p1、p2等五个
寄存器,v0与v1同样用来表示函数的局部变量寄存器,p0表示被传入的Hello对象的引用,
p1与p2分别表示两个传入的整形参数。
(由此可以看出,以p命名法来命名寄存器的话,在以后寄存器数量很多的情况下,分析起来将会更加方便,所以p命名法出现的次数应该会更多,优势更加明显。)
2.3 Dalvik字节码的类型、方法与字段表示法
Dalvik字节码有着一套自己的类型、方法与字段表示方法,这些方法与Dalvik 虚拟机
指令集- -起组成了一条 条的Dalvik汇编代码。
1、类型
Dalvik字节码只有两种类型,基本类型与引用类型。Dalvik使用这两种类型来表示Java
语言的全部类型,除了对象与数组属于引用对象外,其他的Java类型都是基本类型。BakSmali
严格遵守了DEX文件格式中的类型描述符(DEX文件格式将在后面进行介绍)定义。
每个Dalvik寄存器都是32位大小,对于小于或等于32位长度的类型来说,一个寄存
器就可以存放该类型的值,而像J D等64位的类型,它们的值是使用相邻两个寄存器来存
储的,如v0与v1、v3与v4等。
L类型可以表示Java类型中的任何类。这些类在Java代码中以package.name.ObjectName
方式引用,到了Dalvik汇编代码中,它们以Lpackage/name/ObjectName;形式表示,注意最
后有个分号,L表示后面跟着一 一个Java类,package/name/表示对象所在的包,ObjectName
表示对象的名称,最后的分号表示对象名结束。例如: Ljava/lang/String;相当于
java.lang.String。
[类型可以表示所有基本类型的数组。[后面紧跟基本类型描述符,如[I 表示一- 个整型一
维数组,相当于Java中的int[]。多个[在-起时可用来表示多维数组,如[I表示int[]D], [[I
表示int[]D]。注意多维数组的维数最大为255个。
L与[可以同时使用用来表示对象数组。如[Ljava/lang/String;就 表示Java 中的字符串
数组。