一、什么是Smali?
- Smali语言是Davlik的寄存器语言,语法上和汇编语言相似,Dalvik VM与JVM的最大的区别之一就是Dalvik VM是基于寄存器的。基于寄存器的意思是,在smali里的所有操作都必须经过寄存器来进行。
- Smali,Baksmali 分别是指安卓系统里的 Java 虚拟机(Dalvik)所使用的一种 dex 格式文件的汇编器,反汇编器。其语法是一种宽松式的 Jasmin/dedexer 语法,而且它实现了 .dex 格式所有功能(注解,调试信息,线路信息等)
- 当我们对 APK 文件进行反编译后,便会生成此类文件。在Davlik字节码中,寄存器都是32位的,能够支持任何类型,64位类型(Long/Double)用2个寄存器表示;Dalvik字节码有两种类型:原始类型;引用类型(包括对象和数组)
二、基本数据类型
1.原始类型
2.对象类型
3.数组类型
[I
:表示一个整形的一维数组,相当于java的int[];
对于多维数组,只要增加[
就行了,[[I = int[][];
注:每一维最多255个;
对象数组的表示形式:[Ljava/lang/String
表示一个String的对象数组;
三、Smali语法
1、寄存器与变量
Android变量都是存放在寄存器中的,寄存器为32位,可以支持任何类型,其中long和double是64为的,需要使用两个寄存器保存。寄存器采用v和p来命名,v表示本地寄存器,p表示参数寄存器。
解读:
.registers 3
说明该方法有三个寄存器,其中一个本地寄存器v0,两个参数寄存器p0,p1,细心的人可能会注意到没有看到p0,原因是p0存放的是this。如果是静态方法的话就只有2个寄存器了,不需要存this了。
-
本地寄存器(local register,非参寄存器)用v开头数字结尾的符号来表示,如v0、v1、v2、…,
-
参数寄存器(parameter register)用p开头数字结尾的符号来表示,如p0、p1、p2、…,
-
.registers
用来标明方法中寄存器的总数,即参数寄存器和非参寄存器的总数。 -
.local 0
,标明在这个函数中最少要用到的本地寄存器的个数,出现在方法中的第一行。在这里,由于只需要调用一个父类的onDestroy()处理,所以只需要用到p0,所以使用到的本地寄存器数为0,在植入代码后不要忘记可能要修改.local的值。如.local 4
,则可以使用的寄存器是v0-v3。 -
当一个方法被调用的时候,方法的参数被置于最后N个寄存器中。
-
在实例函数中,
p0
代指“this
”,p1表示函数的第一个参数,p2代表函数中的第二个参数…, -
在static函数中,p1表示函数的第一个参数,p2代表函数中的第二个参数…,因为Java的static方法中没有this方法。
2、基本指令
3、条件跳转指令
4、函数调用
smali中的函数调用也分为direct
和virtual
两种类型,direct method就是private
函数,public和protected函数都属于virtual method。在调用函数时,有invoke-direct,invoke-virtual,invoke-static、invoke-super以及invoke-interface等几种不同的指令。
invoke-static:就是调用static函数的,示例:
上句invoke-static后面有一对大括号“{}”,内部是调用该方法的实例和参数列表,由于这是static方法也不需要参数,所以{}内为空。
invoke-super:
调用父类方法,在onCreate、onDestroy等方法都能看到。
invoke-direct:
调用private函数,示例:
上句即this->getGlobalIapHandler(),函数GlobalPurchaseHandler getGlobalIapHandler()是定义在Class1中的一个private函数。
invoke-virtual:用于调用protected或public函数,示例:
上句v0是shareHandler android/os/Handler,v3是传递给removeCallbackAndMessage方法的Ljava/lang/Object参数。
5、获取函数返回结果
在Smali里调用函数和返回函数结果需要分开来完成,在调用的函数返回非void
后,用move-result
(返回基本数据类型)和move-result-object
(返回对象)指令获取返回结果。
上句v1保存的就是调用this.getPreferences(int)方法返回的SharedPreferences实例。
6、函数体
.method
和 .end method
之间。
上段是onDestroy()函数。
.line 277
,标注了该代码在原Java文件中的行数,它不是必须的,去掉没有编译问题。它在出错时可以指出错误位置,jd-gui工具即是通过分析这些信息将smali代码还原成Java代码的。
四、基本语句语法
1、if语句
2、for语句
3、switch分支语句
packed-switch 指令。p1为传递进来的 int 类型的数值,pswitch_data_0 为case 区域,在 case 区域中,第一条指令“.packed-switch”指定了比较的初始值为0 ,pswitch_0~ pswitch_3分别是比较结果为“case 0 ”到“case 3 ”时要跳转到的地址。可以发现,标号的命名采用 pswitch_ 开关,后面的数值为 case 分支需要判断的值,并且它的值依次递增。再来看看这些标号处的代码,每个标号处都使用v0 寄存器初始化一个字符串,然后跳转到了goto_0 标号处,可见goto_0 是所有的 case 分支的出口。另外,“.packed-switch”区域指定的case 分支共有4 条,对于没有被判断的 default 分支,会在代码的 packed-switch指令下面给出。
java语言如下:
4、try/catch语句
代码中的try语句块使用try_start_开头的标号注明,以try_end_开头的标号结束。第一个try语句的开头标号为try_start_0,结束标号为 try_end_0。使用多个try语句块时标号名称后面的数值依次递增,本实例代码中最多使用到了try_end_2。
在try_end_0 标号下面使用“.catch”指令指定处理到的异常类型与catch的标号,格式如下。
五、最后
你能否将下面的Smali代码变成Java代码呢?
如果可以的话,你已经基本掌握了Smali语法了!
先按顺序写出代码:
其中0、1用v2寄存器传值,v1是length,v3存储的是0x28,v0存的是for循环计数器i
然后观察语句特点,发现像for循环,修改代码:
🔗原文链接:https://blog.csdn.net/cpongo3/article/details/89708340
评论区