Smali语法


数据类型

Dalvik字节码只有两种格式:基本类型和引用类型。对象和数组属于引用类型

语法含义
Vvoid,只用于返回值类型
Zboolean
Bbyte
Sshort
Cchar
Iint
Jlong
Fflot
Ddouble
LJava类 类型
[数组类型

Ljava/lang/String; 相当于java.lang.String [I 相当于一维int数组,int[] [[I 相当于int[][]

方法

它使用方法名,参数类型和返回值来描述一个方法 package/name/ObjectName;->methodName(III)Z

package/name/ObjectName:一个类 methodName:方法名 III:参数类型 Z:返回值

(III)Z:方法签名

BakSmali生成的方法代码以.method指令开始,以.end method指令结束,根据方法的类型不同,可以会在方法前加#表示方法类型

# vitual methods:虚方法,如:

# direct methods:直接方法,如:

有些方法没有这样的注释

静态方法:

字段

与方法表示很相似,只是字段没有方法签名和返回值,取而代之的是字段类型 Lpackage/name/ObjectName;->FiedlName:Ljava/lang/String;

其中字段名与字段类型用冒号“:”分割

其中: # static fields:静态字段 # instance fields:实例字段

Dalvik指令集

他在调用格式上模仿了C语言的调用约定,官方地址,指令语法与助词有如下特点:

  1. 采用采用从目标(destination)到源(source)的方法
  2. 根据字节码的大小与类型不同,一些字节码添加了名称后缀已消除歧义 2.1 32位常规类型的字节码未添加任何后缀 2.2 64位常规类型的字节码添加 -wide后缀 3.3 特殊类型的字节码根据具体类型添加后缀,-boolean,-byte,-char,-short,-int,-long,-float,-double,-object,-string,-class,-void之一
  3. 根据字节码的布局和选项不同,一些字节码添加了字节后缀消除歧义,后缀与字节码直接用/分割
  4. 在指令集的描述中,宽度值中每个字母表示宽度为4位

如: move-wide/from16 vAA, vBBBB move-wide/from16 v18, v0

move:基础字节码(base opcode),标示是基本操作 wide:标示指令操作的数据宽度为64位宽度 from16:字节码后缀(opcode suffix),标示源(vBBBB)为一个16的寄存器引用变量 vAA:目的寄存器,v0~v255 vBBBB:源寄存器,v0~v65535

指令

nop

空操作,被用来做对齐代码

数据定义

用来定义程序中用到的常量,字符串,类等数据 const/4 vA, #+B :将数组扩展为32位后赋给寄存器vA const/16 vAA, #+BBBB const vAA, #+BBBBBBBB:将数组赋值给寄存器vAA const-wide/16 vAA, #+BBBBB :将数值扩展为64位后赋给寄存器vAA const-string vAA, string@BBBB:将字符串索引构造一个字符串并赋给vAA const-class vAA, type@BBBB:通过类型索引获取一个类的引用并赋给寄存器vAA

数据操作指令

move destination, source 根据字节码大小和类型不同,后面回天津不同的后缀 move vA, vB:vB寄存器值赋值给vA寄存器,都为4位 move-object vA,vB move-result vAA:将上一个invoke类型的指令操作的单字非对象结果负责vAA寄存器 move-result-object vAA:将上一个invoke类型指令操作的对象赋值给vAA move-exception vAA:保存一个运行时发生的异常vAA寄存器,必须是异常发生时的异常处理的第一条指令

返回指令

return-void :返回一个void return vAA:返回一个32位非对象类型的值,返回寄存器为8位 return-wide vAA:返回一个64位非对象类型的值,返回寄存器为8位 return-object vAA:返回一个对象类型

锁指令

锁指令多用在多线程程序中对同一对象的操作 monitor-enter vAA 为指定的对象获取锁 monitor-exit vAA 释放指定的对象的锁

实例操作

包括类型转换,检查和创建新实例 check-cast vAA, type@BBBB:将vAA中的对象转为指定类型,如果失败会抛出ClassCastException异常,如果类型B是基本类型,对于分基本类型的A来说运行始终是失败的 instance-of vA, vB, type@CCCC:判断vB寄存器的对象是否可以转为指定类型,如果可以vA为1,否则为0 new-instance vAA, type@BBBB:构造一个指定类型的对象,并赋值给vAA寄存器,不能是数组类型

数组操作

包括获取数组长度,新建数组,数组赋值,数组元素取值与赋值等 array-length vA, vB:获取vB寄存器中数组的长度并赋值给vA寄存器 new-array vA, vB, type@CCCC:构造指定类型(type@CCCC)与大小(vB)的数组,并赋值给vA寄存器 filled-new-array {vC,vD,vE,vF,vG}, type@BBBB:构造指定类型(type@BBBB)与大小vA的数组并填充数组内容,除了指定数组的大小还指定了参数个数 filled-new-array/range {vCCCC .. vNNNN}, type@BBBB:与上一条类似,只是参数使用取值范围,vC是第一个参数寄存器,N=A+C-1 fill-array-data vAA, +BBBBBBBB:vAA为寄存器数组引用,后面跟一个数据表 arrayop vAA, vBB, vCC:对vBB寄存器指定的数组元素进入取值或赋值。vCC指定数组元素索引,vAA寄存器用来存放读取的或需要设置的值。读取元素使用age类指令,赋值使用aput类指令,根据数组中存储的类指令后面会跟不同的后缀: aget,aget-wide,aget-object,aget-boolean,aget-byte,aget-char,aget-short aput,aput-wide,aput-object,aput-boolean,aput-byte,aput-char,aput-short

异常指令

throw vAA:抛出vAA寄存器中指定类型的异常

跳转指令

用于从当前地址跳转到指定的偏移处,提供了三种指令:无条件(goto),分支跳转(switch),条件跳转(if) goto +AA:无条件跳转到指定偏移处,AA不能为0 goto/16 +AAAA goto/32 +AAAAAAAA

packed-switch vAA, +BBBBBBBB:分支跳转,vAA寄存器为switch分支需要判断的值

if-test vA, vB, +CCCC 条件跳转指令,比较vA寄存器与vB寄存器的值,如果比较结果满足就跳转到CCCC指定的偏移处,不能为0,有以下几条:

if-eq:if(vA==vB) if-ne:vA!=vB if-lt:vAvB if-gt:vAvB if-le:vA=vB if-ge:vA=vB

if-testz vAA, +BBBB:条件转移,拿vAA寄存器与0比较,如果比较结果满足或值为0就跳转到BBBB指定的偏移处,不为0

if-eqz:vAA==0 if-nez:vAA!=0 if-ltz:vAA0 if-gtz:vAA0 if-lez:vAA=0 if-gez:vAA=0

比较指令

用于对两个寄存器的值比较 cmpkind vAA, vBB, vCC:vBB和vCC为要比较的值,结果放到vAA中 cmpl-float:单精度,vBB大于vCC,vAA=-1,等于vAA=0,小于vAA=1 cmpg-float:单精度,vBB大于vCC,vAA=1,等于vAA=0,小于vAA=-1 cmpl-double:双精度 cmpg-double:双精度 cmp-long:长整形

字段操作指令

用来对 对象实例的字段进行读写操作。字段类型可以是Java中有效的类型,对于实例字段和静态字段有两类指令: iget,iput对实例字段进行读,写 sget,sput对静态字段

会根据类型不同添加不同的后缀 iget,iget-wide,iget-object,iget-boolean,iget-byte,iget-char,iget-short iput,iput-wide,iput-object,iput-boolean,iput-byte,iput-char,iput-short

sget,sget-wide,sget-object,sget-boolean,sget-byte,sget-char,sget-short ...

方法调用

在方法调用者我们可以看到有:

数据转换

数据转换指令用于将一种数据类型转换为另一个类型,unop vA, vB:寄存器存储要转换的数据,vA存储转换后的数据 neg-int:整形求补 not-int:整形求反 neg-long:长整型求补 not-long:长整型求反 neg-float:单精度求补 not-float: neg-double: not-double:

int-to-long:整型转为长整型 int-to-float:整型转单精度浮点型 int-to-double:整型转双精度浮点型

int-to-byte:整型转字节型 int-to-char:整型转字符串 int-to-short:整型转短整型

long-to-int long-to-float long-to-double

float-to-int float-to-long float-to-double

double-to-int double-to-long double-to-float

数据运行指令

算术运算:加,减,乘,除,模,移位等 逻辑运算:与,或,非,异或等

binop vAA, vBB, vCC:将vBB寄存器与vCC寄存器进行运算,结果保存到vAA

上面的指令会根据数据类型的不同在基础后面添加数据类型后缀,如:-int或-long add-type vBB:vBB寄存器与vCC寄存器值进行加法运算,+ sub-type vBB:- mul-type vBB:* div-type vBB:/ rem-type vBB:% and-type vBB:and or-type vBB:or xor-type vBB:xor shl-type vBB:左移vCC位,<< shr-type vBB:右移vCC位,>> ushr-type vBB:无符号>>

其中type可以为int,long,float,double

binop/2addr vA, vB:将vA寄存器与vB寄存器进行运算,结果保存到vA binop/lit16 vA, vB, #+CCCC:将vB寄存器与常量CCCC进行运算,结果保存到vA binop/lit8 vAA, vBB, #+CC:将vBB寄存器与常量CC进行运行,结果保存到vAA

Dalvik hello world

首先写一个基本框架

完整版如下:

编译smali

我们去官网下载smali.jar,然后运行

编译完后我们把classes.dex push到手机里面

运行

加强版本

如果我的文章对来带来的帮助或者有不明白的地方,可加QQ群:129961195,大家一起交流