比如以下java代码:
用smali代码表示为:
凡是L开头全包名路径结尾都需要加分号
字段声明(成员/全局变量)
1 .field 权限修饰符+静态修饰符 +变量名:变量全类名路径;
比如以下java代码:
用smali代码表示为:
# 如果是非静态,只需将static去掉即可
1 .field 权限修饰符+静态修饰符 +final+变量名:变量全类名路径;=常量值
比如以下java代码:
用smali代码表示为:
1 .method 权限修饰符+静态修饰符 +方法名(参数类型)返回值类型
比如以下java代码:
用smali代码表示为:
4 # 如果是非静态,只需将static去掉即可
如果是带参并且带有返回值的方法
比如以下java代码:
用smali代码表示为:
数据类型对应关系表如下:
构造方法/构造函数声明
比如以下java代码:
用smali代码表示为:
比如以下java代码:
用smali代码表示为:
比如以下java代码:
用smali代码表示为:
私有方法或者构造方法的调用
比如以下java代码:
用smali代码表示为:
比如以下java代码:
用smali代码表示为:
4 # 匿名内部类的声明 14 # 初始化父类构造方法
静态方法的调用并获取返回值(不区分私有公有 静态优先)
比如以下java代码:
用smali代码表示为:
用smali代码表示为
比如以下java代码:
用smali代码表示为:
对象的创建分多步进行:
3 # 调用构造方法 (如果构造方法内还定义了成员变量,那么在调用之前需要提前声明,然后在invoke的时候当作参数一并传入)
1 第一种 const开头 占用一个容器(寄存器) 32位/容器
4 * const/16 最大值允许存放16位数值 第一位默认为符号位 所以计算后15位的数值
5 # 以下数据定义高位默认为符号位
算法:正数的符号位是0,负数的符号位是1。正数的反码、补码与原码一样。负数的反码是让符号位不变,数据位按位取反;补码是将反码加1。
分多步进行 关键代码:
比如以下java代码:
用smali代码表示为:
8 #类初始化方法 被jvm执行 优先于构造方法
分多步进行 关键代码:
比如以下java代码:
用smali代码表示为:
11 # 初始化父类构造方法 13 # 声明字符串内容
比如以下java代码:
用smali代码表示为:
8 #类初始化方法 被jvm执行 优先于构造方法
比如以下java代码:
用smali代码表示为:
17 # 初始化成员变量 28 # 类非静态字段取值
注意:以上取值赋值方法都是以String对象举例,如果是基本数据类型,那么按照如下表处理:
smali取值赋值和值定义关键字
|
|
|
|
|
|
|
|
|
#### 如果是基本数据类型,那么按照如下表处理:
|
以上表结果示例java代码如下,可自行试验:
逻辑语句之条件跳转分支
表示与java源文件代码的映射关系,比如:
1 .line 3 # 代表以下代码还原成java代码在源文件第三行
删除该关键字不影响程序执行,该关键字在反编译时能很好地帮助我们阅读smali代码,以该关键字当作代码块的分割线,方便快速阅读执行内容
条件分支,配合if使用
表示程序的开始 可省略
goto跳转分支,配合goto关键字使用
显示局部变量别名信息,作用等同.line
注意这个和上面local的区别多加了一个s
标明了你在这个函数中最少要用到的本地寄存器的个数 也即是指明了在这个方法中非参(non-parameter)寄存器的数量
在Smali中,如果需要存储变量,必须先声明足够数量的寄存器,1个寄存器可以存储32位长度的类型,比如Int,而两个寄存器可以存储64位长度类型的数据,比如Long或Double
声明可使用的寄存器数量的方式为:.registers N
,N代表需要的寄存器的总个数
那么,如何确定需要使用的寄存器的个数?
由于非static方法,需要占用一个寄存器以保存this指针,那么这类方法的寄存器个数,最低就为1,如果还需要处理传入的参数,则需要再次叠加,此时还需要考虑Double和Float这种需要占用两个寄存器的参数类型,举例来看:
如果一个Java方法声明如下:
那么对应的Smali则为:
此时,寄存器的对应情况如下:
那么最少需要的寄存器个数则为:5
如果方法体内含有常量、变量等定义,则需要根据情况增加寄存器个数,数量只要满足需求,保证需要获取的值不被后面的赋值冲掉即可,方法有:存入类中的字段中(存入后,寄存器可被重新赋值),或者长期占用一个寄存器
寄存器数量只能多不能少
如果需要使用Smali编写程序,还需要掌握常用的Dalvik虚拟机指令,其合集称为Dalvik指令集。这些指令有点类似x86汇编的指令,但指令更多,使用也非常简单方便。最详尽的介绍,可以参考Android官方的Dalvik相关文档:
这里也列举一些常用的指令,并结合Smali进行说明:
|
将v2中的值移入到v1寄存器中(4位,支持int型)
|
将16位的v2寄存器中的值移入到8位的v1寄存器中
|
将16位的v2寄存器中的值移入到16位的v1寄存器中
|
将寄存器对(一组,用于支持双字型)v2中的值移入到v1寄存器对中(4位,猜测支持float、double型)
|
将16位的v2寄存器对(一组)中的值移入到8位的v1寄存器中
|
将16位的v2寄存器对(一组)中的值移入到16位的v1寄存器中
|
将v2中的对象指针移入到v1寄存器中
|
将16位的v2寄存器中的对象指针移入到v1(8位)寄存器中
|
将16位的v2寄存器中的对象指针移入到v1(16位)寄存器中
|
将这个指令的上一条指令计算结果,移入到v1寄存器中(需要配合invoke-static、invoke-virtual等指令使用)
|
将上条计算结果的对象指针移入v1寄存器
|
将上条计算结果(双字)的对象指针移入v1寄存器
|
将异常移入v1寄存器,用于捕获try-catch语句中的异常
|
用于返回值,对应Java中的return语句
这种错误一般很难定位,因为没有提示具体原因或者具体的行数,有可能是静态方法调用你写成了虚方法的调用,或者是构造函数调用没有加尖括号, 甚至是寄存器数量过少 等等
为什么方法中包括参数在内需要3个寄存器,但是在定义的时候只写了两个却也不报错呢?
答案是:系统会更具最大寄存器的位置进行判断,从v0到vN,数量必须大于N,