很多的Android应用都是用JAVA语言进行开发,最后都会将各种.java文件,res、assets等静态资源文件,和lib库等打包在一起,生成.apk文件,其中的java源代码变成了.dex文件。所以,在对某个app进行反编译后,apk中的classes.dex会变成.smali文件,因此我们需要学会分析smali代码才能大概猜到源码所表达的意思。
其中,主要分析MainActivity$1.smali,它与MainActivity.smali最大的区别是,MainActivity.smali中不含有内部类,只是与onCreate()同级的其它方法或函数,而要分析内部类就只能去看MainActivity$1.smali。
下面进行smali语法的总结:
(一)数据类型
smali中的基本数据类型仅仅通过大写的首字母来表示。其中,long与boolean比较特殊,分别用J与Z表示。
B---byte C---char D---double F---float I---int J---long S---short V---void Z---boolean
需要特殊记忆的两个:
数组类型用 [XX 表示,在基本类型前加上左中括号 [ ,比如 [B 表示byte类型的数组。
对象类型用 LpackageName/objectName 表示,比如 Ljava/lang/String 表示String对象,它所在的包是java.lang包。
(二)方法
定义:Func-Name(Para-Type1Para-Type2Para-Type3...)Return-Type
参数间没有逗号、空格等任何间隔,比如:
Hello(ICF)V 表示:Void hello(int para1 , char para2 , float para3)
Hello(Z[I[ILjava/lang/String;J)Ljava/lang/String; 表示String hello(boolean para1 , int [] , int[] , String para2 , long para3)
(三)关键字
.field public/private [static][final] varname:类型 定义变量
.method 方法
.end method 方法结束
.parameter/.param 方法参数
.prologue 方法开始
.line 123 此方法位于源码的第123行
.locals 2 表示本方法中要用到2个本地寄存器v0,v1
invoke-super 调用父函数,父函数会写在smali的顶部位置,如.super Landroid/app/Activity;
invoke-direct 调用private函数
invoke-virtual 调用public或protected函数
invoke-static 调用静态函数
const/high16 v0, 0x7fo3 把0x7fo3赋值给寄存器v0
return-void 函数返回
return-object 返回的是一个对象
new-instance 创建实例
iput/sput-object 对象赋值
iget/sget-object 调用对象
比如:sget-object v0, Lcom/main;->a:Ljava/lang/String 表示获取main类中,String类型,名为a的成员变量,并把它保存到寄存器v0中。
(四)条件跳转
:cond_**代表代码块
"if-eq vA, vB, :cond_**" 如果vA等于vB则跳转到:cond_**
"if-ne vA, vB, :cond_**" 如果vA不等于vB则跳转到:cond_**
"if-lt vA, vB, :cond_**" 如果vA小于vB则跳转到:cond_**
"if-ge vA, vB, :cond_**" 如果vA大于等于vB则跳转到:cond_**
"if-gt vA, vB, :cond_**" 如果vA大于vB则跳转到:cond_**
"if-le vA, vB, :cond_**" 如果vA小于等于vB则跳转到:cond_**
"if-eqz vA, :cond_**" 如果vA等于0则跳转到:cond_**
"if-nez vA, :cond_**" 如果vA不等于0则跳转到:cond_**
"if-ltz vA, :cond_**" 如果vA小于0则跳转到:cond_**
"if-gez vA, :cond_**" 如果vA大于等于0则跳转到:cond_**
"if-gtz vA, :cond_**" 如果vA大于0则跳转到:cond_**
"if-lez vA, :cond_**" 如果vA小于等于0则跳转到:cond_**
(五)寄存器语法
在smali里的所有操作都必须经过寄存器来进行。
本地寄存器:用v开头数字结尾的符号来表示,如v0,v1,v2…,用于方法内数值之间的传递;
参数寄存器:用p开头数字结尾的符号来表示,如p0,p1,p2…,表示该方法依次接收过来的参数值。
【注】p0不一定是函数中的第一个参数。在非static函数中,p0代指“this”,p1表示函数的第一个参数,p2代表函数中的第二个参数…而在static函数中p0才对应第一个参数。因为Java的static方法中没有this方法。
(六)实例