跟我学安卓逆向附录A - smali文件语法手册

反编译安卓APK都会生成一个smali文件,本篇文章为smali文件的语法(即Dalvik指令)速查手册,速速收藏备忘。

0.类型

Dalvik指令中的类型和java的对应关系如下:

语法Java类型

Vvoid,只用于返回值类型

Zboolean

Bbyte

Sshort

Cchar

Iint

Jlong

Ffloat

Ddouble

LJava类类型

[数组类型

默认Dalvik寄存器是32位,J和D是64位类型,需要使用两个相邻的寄存器来存储。

1.方法

方法表示

方法用.method表示开始,.end method表示结束。用#来添加注释,# virtual methods表示这是一个虚方法,# direct methods表示一个直接方法

方法表示语法为:

Lpackage/name/ObjectName;->MethodName(III)Z

Lpackage/name/ObjectName;表示类型

MethodName表示ObjectName类下面的方法

III表示三个整形参数

Z表示为返回bool类型的值

例子:

method(I[[IILjava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;

对应java为:

String method(int,int[][],int,String,Object[])

方法调用

指令含义

invoke-virtual(/range)用于调用实例的虚方法

invoke-super(/range)用于调用实例的父类方法

invoke-direct(/range)用于调用实例的直接方法

invoke-static(/range)用于调用实例的静态方法

invoke-interface(/range)用于调用实例的接口方法

2.字段

字段表示

字段与方法类型,使用.field 表示。# instance fields表示实例字段,# static fields表示静态字段

字段表示语法为:

Lpackage/name/ObjectName;->FieldName:Ljava/lang/String;

字段操作指令

对普通字段操作,读使用iget指令,写使用iput指令。例如iget、iget-wide、iget-object、iget-boolean、iget-type、iget-char、iget-short

静态字段操作,读使用siget指令,写使用sput指令

3.寄存器

寄存器声明,可以理解为变量开辟的存储数量。使用关键.registers  、.local 、.param

.registers N表示寄存器总数为N

.local N表示为方法内的寄存器数量,通常用v字母开头表示。如:v0、v1、v2....

.param 指定方法参数名,通常用p字母开头表示。如:p0、p1、p2...

4.类型变量

声明变量,使用关键字const

64位的常规类型的字节码添加-wide后缀,像long,double等,占两个寄存器;

特殊类型的字节码,添加特殊的后缀。可以是--boolean,-byte,-char,-short,-int,-long,-float,-double,-object,-string,-class,-void等。

对于会产生歧义的,会添加/字节码来消除歧义。

指令move-wide/from16 vAA,vBBBB为例,move为基础字节码,表示一个基本操作。-wide为名称后缀,表示操作的数据宽度为64位。from16为字节码后缀,表示一个来自16位的寄存器引用变量。vAA为目标寄存器,vBBBB为源寄存器

常量、字符串、类

const (/4/16/high16) vA,xx表示将xx赋值给vA,/后面根据xx长度选择

const-wide(/16/32/high16) vAA,xxxx表示将xxxx赋值给寄存器vAA、vAA+1 ,/后面根据xxxx长度选择

const-string(/jumbo) vAA,xxxx表示通过字符串索引构造一个字符串并赋值给vAA,较大时使用/jumbo 后缀

const-class(/jumbo) vAA,xxxx表示通过类型索引获取一个类引用,并赋值给vAA

数组

array-length vA,vB指令用于获取给定vB寄存器中数组的长度,并将值赋予给vA寄存器

new-array(/jumbo) vA,vB,type@CCCC指令用于构造制定类型(type@CCCC)和大小(vB)的数组,并将值赋予vA寄存器

filled-new-array(/range/jumbo) {vC,vD,vE,vF,vG} , type@BBBB指令用于构造制定类型(type@BBBB)和大小 (vA)的数组并填充内容。vA是隐含使用,除了制定数组的小,还指定参数个数。vC-vG是使用的参数寄存器序列。需要指定取值范围时,filled-new-array/range {vCCCC....vNNNN} 这种格式。

fill-array-data vAA,xxxx指令用指定的数据来填充数组

arrayop vAA,vBB,vCC指令用于对vBB寄存器指定的数组元素进行取值与赋值。vCC 寄存器用于指定数组元素的索引。vAA寄存器用于存放读取和需要设置数据组元素的值。读取元素时使用aget指令,赋值时使用aput指令。

5.数据操作

数据操作的指令位move,数据返回指令为return

move (/16/from16) vA,vB用于将vB寄存器的值赋予vA寄存器。

move-wide(/from16) vAAAA,vBBBB用于将vBBBB寄存器的值赋予vAAAA寄存器

move-object(/16/from16) vAAAA,vBBBB用于为对象赋值

move-result vAA用于将上一个invoke调用的方法非对象结果赋予vAA寄存器。move-result-wide 表示占用连续寄存器赋值,move-result-object 表示方法对象结果赋值

move-exception vAA表示将运行时异常保存到vAA寄存器。必须在异常处理器内使用,否则无效

return-void表示函数从一个 void方法返回

return vAA表示函数返回一个32为非对象值,使用8位寄存器。-wide表示64位非对象值,使用相邻两个8位寄存器,-object表示对象类型返回值。

6.类型转换与检查

check-cast(/jumbo) vAA,type@BBBB表示将vAA中的对象引用转换成指定类型

instance-of(/jumbo) vA,vB,type@CCCC用于判断vB中的对象引用是否可以转换成type@CCCC类型,可以vA寄存器中值位1,不可以vA值为0

new-instance(/jumbo) vAA,type@BBBB指令用于构造一个指定类型对象的新实例,并将对象引用赋值给vAA寄存器

基本类型转换

指令含义

neg-int(long/float/double) 对数值做减法运算

not-int(long/float/double) 对数值取反

int(long/float/double)-long(/float/double/int) 表示数值转换

int-to-byte 用于将整形转换为字节

int-to-char用于将整形转换为字符串

int-to-shot 用于将整形转换为短整形

7.数据运算

常用的运算符:

运算符含义

add-type加法(+)

sub-type减法(-)

mul-type乘法(*)

div-type除法(/)

rem-type模运算(%)

and-type与运算(AND)

or-type或运算(OR)

xor-type异或运算(XOR)

shl-type左移运算(<<)

shr-type右移运算(>>)

ushr-type无符号数右移运算(>>)

上述运算符 vAA,vBB,vCC 指令用于将vBB与vCC进行运算,将结果保存到vAA寄存器

上述运算符/2ddr vA,vB 用于将vA与vB进行运算,将结果保存到vA寄存器中

上述运算符/lit16/lit8 vA,vB,xxx 用于将vB与常量xxx进行运算,将结果保存到vA寄存器中

8.比较

比较指令是用于对两个寄存器的值进行比较,格式为cmpxxx vAA,vBB,vCC。其中vBB与vCC是需要比较的两个寄存器或寄存器对,比较的结果放到vAA中

关键字含义

cmpl-float比较两个单精度浮点数,如果vBB大于vCC,则vAA为-1,等于为0,小于为1

cmpg-float比较两个单精度浮点数,如果vBB大于vCC,则vAA为1,等于为0,小于为-1

cmpl-double比较两个双精度浮点数,如果vBB大于vCC,则vAA为-1,等于为0,小于为1

cmpg-double比较两个双精度浮点数,如果vBB大于vCC,则vAA为1,等于为0,小于为-1

cmp-long比较两个长整形,如果vBB大于vCC,则vAA为1,等于为0,小于为-1

9.条件和跳转

指令跳转分为三种类型,一种goto跳转,分支switch跳转,条件跳转if

goto +AA表示指令用于无条件跳转至指定便宜处,偏移量AA不为0

packed-switch vAA,+BBBB分支跳转指令,vAA寄存器为switch分支中需要判断的值,BBBB指向一个packed-switch-payload格式的偏移表,表中的值是递增的偏移量

sparse-swicth vAA,+BBBB分支跳转指令,vAA寄存器为switch分支中需要判断的值,BBBB指向一个sparse-switch-payload格式的偏移表,表中的值是递增的偏移量

if-test vA,VB,+CCCC条件跳转质量用于比较vA寄存器和vB寄存器中的值。如果条件满足就跳转至CCCC偏移处,偏移量不能为0

if-test类型指令如下:

if-test指令含义

if-eq等于(==)

if-ne不等于(!=)

if-gt大于(>)

if-ge大于等于(>=)

if-lt小于 (<)

if-le小于等于 (<=)

if-testz vAA,+BBBB 条件跳转指令将vAA寄存器的值与0比较。如果结果满足则跳转至BBBB的偏移处,偏移量不为0

if-testz指令含义

if-eqz为0  if(!vAA)

if-nez不为0    if(vAA)

if-ltz小于0  if(vAA<0)

if-gez大于等于0  if(vAA>=0)

if-gtz大于0  if(vAA>0)

if-lez小于等于0  if(!vAA<=0)

10.其他

类声明

.class 类的包名及完整签名

.super 类的父类包名及完整签名

注释

smail文件用#表示注释,且只支持单行

注解

注解会添加#annotaions注释,并用.annotation指令开始,以.end annotation指令结束

行号

.line表示java源文件中的行信息

锁指令

monitor-enter vAA表示为vAA加锁

monitor-exit vAA 表示释放指定对象的锁

异常指令

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

你可能感兴趣的:(跟我学安卓逆向附录A - smali文件语法手册)