Smali汇编
Smali
简介:
Smali是用于Dalvik(Android虚拟机)的反汇编程序实现,汇编工具(将Smali代码汇编为dex文件)为smali.jar。
Smali支持注解、调试信息、行数信息等Java的基本特性,可以说是很接近Java编译在JVM上的中间语言了,一般用来做Android程序的逆向工程
Smali基础
下面的内容涉及一些Smali编程的结构和基本语法
Smali文件结构
一个Smali文件对应的是一个Java的类,更准确的说是一个.class文件,如果有内部类,需要写成ClassName$InnerClassA、ClassName$InnerClassB…这样的形式
基本类型
对象
Object类型,即引用类型的对象,在引用时,使用L开头,后面紧跟着的是完整的包名,比如:java.lang.String对应的Smali语法则是Ljava/lang/String
数组
数组定义比较有意思,一维数组在类型的左边加一个方括号,比如:[I等同于Java的int[]],每多一维就加一个方括号,最多可以设置255维
方法声明及调用
官方Wiki中给出的Smali引用的方法的模板如下:
Lpackage/name/ObjectName;->MethodName(III)Z |
第一部分Lpackage/name/ObjectName;用于声明具体的类型,以便JVM寻找
第二部分MethodName(III)Z,其中MethodName为具体的方法名,()中的字符,表示了参数数量和类型,即3个int型参数,Z为返回值的类型,即返回Boolean类型
由于方法的参数列表没有使用逗号这样的分隔符进行划分,所有只能从左到右,根据类型定义来区分参数个数,这一点需要比较仔细来观察
如果需要调用构造方法,则MethodName为:
寄存器声明及使用
在Smali中,如果需要存储变量,必须先声明足够数量的寄存器,一个寄存器可以存储32位长的的类型,比如Int,而两个寄存器可以存储64位长度类型的数据,比如Long或Double
声明可使用的寄存器数量的方式为:.registers N,N代表需要的寄存器的总个数,同时,还有一个关键字.locals,它用于声明非参数的寄存器的个数(包含在registers声明的个数当中),也叫做本地寄存器,只在一个方法内有效,但不常用,一般使用registers即可
示例:
.method private test(I)V |
如何确定需要使用的寄存器的个数
由于非static方法,需要占用一个寄存器以保存this指针,那么这类方法的寄存器个数,最低就为1,如过还需要处理传入的参数,则需要再次叠加,此时还需要考虑Double和Float这种需要占用两个寄存器的参数类型,举例来看:
如果一个Java方法声明如下:
myMethod(nt p1, float p2, boolean p3) |
那么对应的Smali则为:
method LMyObject;->myMethod(IJZ)V |
此时,寄存器的对应情况如下:
那么最少需要的寄存器的个数则为:5
如果方法内含有常量、变量等定义,则需要根据情况增加寄存器个数,数量只要满足需求,保证需求要获取的值不被后面复制冲掉即可,方法有:存入类中的字段中(存入后,寄存器可被重新赋值),或者长期占用一个寄存器
Dalvik指令集
移位操作
返回操作
用于返回值,对应Java中的return语句
常量操作
用于声明常量,比如字符串常量(仅声明,String a = “abc”这种语句包含声明和赋值)
调用操作
用于调用方法,基本格式:invoke-kind{vC,vD,vE,vF,vG},meth@BBBB,其中,BBBB代表方法引用(参见上面介绍的方法定义及其调用),vC~G为需要的参数,根据顺序一一对应
判断操作:
判断操作用来比较一个寄存器中的值,是否与目标寄存器中的值相等或不等,对应Java中的if语句,格式为:if-[test] v1,v2, [condition],其衍生操作还有专门与0进行比较的if-[test]z v1, [condition],其中[condition]为符合判断结果后的跳转条件,需要提前定义好。判断操作也通常和goto配合使用,用来实现循环或者if-else语句
需要注意的是,在Java中编写的if语句,往往在对应的Smali中,会变成相反的判断逻辑,如下面所示:
private void test() { |
上面的Java代码逻辑很简单——一个很简单的if语句,为了在Smali中看的更清楚,我只做了字符串赋值操作。下面是对应的Smali代码
.method private test()V |
属性操作:
属性操作的分为:取值(get)和赋值(put)
目标类型分为:数组(array)、实例(instance)和静态(static)三种,对应的缩写前缀就是a、i、s
长度类型分为:默认(什么都不写)、wide(宽,64位)、object(对象)、boolean、byte、char、short(后面几种就不解释了,和Java一致)
指令格式:[指令名] [源寄存器], [目标字段所在对象寄存器], [字段指针],示例代码如下,操作是为int型的类成员变量mIntA赋值为100:
const/16 v0, 0x64 |
举例:
以下Java代码是进行的是最基本的类成员变量的赋值、取值操作
private String mStringA; |
对应的Smali代码如下:
# instance fields |
其他指令