smali语法笔记

参考资料

https://ctf-wiki.github.io/ctf-wiki/android/basic_operating_mechanism/java_layer/smali/smali-zh/

简述

在执行 Android Java 层的代码时,其实就是 Dalvik(ART) 虚拟机(使用 C 或 C++ 代码实现)在解析 Dalvik 字节码,从而模拟程序的执行过程。而smali则是Dalvik的指令集。Java文件中的每一个内部类,都会产生一个单独的smali文件。

文件描述

.class < 访问权限> [ 修饰关键字] < 类名>
.super < 父类名>
.source <源文件名>

如果一个类实现了接口还有以下的说明

#interfaces
.implements <接口名称>

数据类型

描述符 Java类型 说明
B byte 字节
C char 字符
D double 双精度浮点数
F float 单精度浮点数
I int 整型
J long 长整型
S short 短整型
V void 无类型
Z boolean 布尔型
[ array [ XXX ,XXX为数组类型有几个就为几维数组
L object Lxxx/yyy,xxx/yyy为对象的包名路径

运算符

运算类型 说明
add-type vBB + vCC
sub-type vBB - vCC
mul-type vBB * vCC
div-type vBB / vCC
rem-type vBB % vCC
and-type vBB & vCC
or-type vBB \ vCC
xor-type vBB ^ vCC
shl-type vBB << vCC ,有符号数左移
shr-type vBB >> vCC,有符号数右移
ushr-type vBB >>> vCC,无符号数右移

寄存器的命名

Dalvik字节码中,register(寄存器)一定是32位
p 开头的寄存器都是参数寄存器
v 开头的寄存器都是局部变量寄存器
两者的数量之和为方法申请的寄存器数量
方法自身占用一个寄存器 引用自身类的this 如果是子类还会隐含父类的this

函数描述

调用函数

指令 说明
invoke-super 调用父函数
invoke-direct 调用private函数
invoke-static 调用static函数
invoke-virtual 调用protected或public函数
invoke-xxxx/range {v0 .. vn} 多于5个时(含5个)在后面加上“/range”,range表示范围 n为参数个数 xxxx为类型

if 指令

指令 说明
if-eq vA,vB,target 如果 vA=vB,跳转
if-ne vA,vB,target 如果 vA!=vB,跳转
if-lt vA,vB,targe 如果 vA<vB,跳转
if-gt vA,vB,target 如果 vA>vB,跳转
if-ge vA,vB,target 如果 vA>=vB,跳转
if-le vA,vB,target 如果 vA<=vB,跳转
if-eqz vAA,target 如果 vA=0,跳转
if-nez vAA,target 如果 vA!=0,跳转
if-ltz vAA,target 如果 vA<0,跳转
if-gtz vAA,target 如果 vA>0,跳转
if-lez vAA,target 如果 vA<=0,跳转
if-gtz vAA,target 如果 vA>=0,跳转

注解

注解的作用范围可以是类、方法或字段。如果注解的作用范围是类,“.annotation”指令会直接定义在 smali 文件中,如果是方法或字段,“.annotation”指令则会包含在方法或字段定义中
格式为

1
2
3
4
5
#annotations
.annotation [注解的属性] <注解范围>
[注解字段=值]
...
.end

常用系统注释

MemberClasses注解

MemberClasses注解是编译时自动加上的,Memberclasses是一个子类的集合,说明当前类有多少子类。

1
2
3
4
5
6
# annotations
.annotation system Ldalvik/annotation/MemberClasses;
value = {
Lxxx/yyy;
}
.end annotation

EnclosingMethod注解

EnclosingMethod 注解用于说明当前smali文件的作用范围,
Method说明它作用于一个方法。
只要有EnclosingMethod 那么InnerClass一定出现,而且一个类不得同时包含 EnclosingClass 和 EnclosingMethod 注释。

1
2
3
4
# annotations
.annotation system Ldalvik/annotation/EnclosingMethod;
value = Lcom/cczhr/MainActivity;->onCreate(Landroid/os/Bundle;)V
.end annotation

EnclosingClass

EnclosingClass 注解用于说明当前smali文件的作用范围,
Class说明它作用于一个类。
只要有EnclosingClass那么InnerClass一定出现,而且一个类不得同时包含 EnclosingClass 和 EnclosingMethod 注释。

1
2
3
4
# annotations
.annotation system Ldalvik/annotation/EnclosingClass;
value = Lcom/cczhr/utils/TimeCalibrateHelper;
.end annotation

InnerClass

表明是一个内部类 accessFlags为访问标志 name为内部类的名称

访问标志名称
ACC_ABSTRACT 0x0400
ACC_FINAL 0x0010
ACC_INTERFACE 0x0200
ACC_NATIVE 0x0100
ACC_PRIVATE 0x0002
ACC_PROTECTED 0x0004
ACC_PUBLIC 0x0001
ACC_STATIC 0x0008
ACC_STRICT 0x0800
ACC_SUPER 0x0020
ACC_SYNCHRONIZED 0x0020
ACC_TRANSIENT 0x0080
ACC_VOLATILE 0x0040

下面的0x9 则是ACC_STATIC | ACC_PUBLIC 计算出来的

1
2
3
4
.annotation system Ldalvik/annotation/InnerClass;
accessFlags = 0x9
name = "SntpClient"
.end annotation

对应java文件

1
2
3
public class TimeCalibrateHelper {
public static class SntpClient {}
}

数据移动

move系列指令以及move-result 用于处理小于等于 32 位的基本类型。
move-wide系列指令和move-result-wide用于处理 64 位类型,包括long和double类型。
move-object系列指令和move-result-object用于处理对象引用。
move-result 是把上一条执行的代码的返回值放到指定寄存器上

move A: 目标寄存器 B: 源寄存器

数组定义

指令 说明
new-array v0, v1, I v0为存放数组的寄存器,v1为数组长度,I为类型
filled-new-array {v1, v2, v3, v4, v5}, I move-result v0 v0为存放数组的寄存器, v1-v5为存放的参数, I为类型

“特殊”的synthetic方法

在smail文件中会看到定义为 .method static synthetic access$xxx 的方法,这些方法是由编译器生成的,目的是方便内部类/外部类 访问 外部类/内部类 的私有方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
.method static synthetic access$100(Lcom/ccahr/myxposed/MainActivity;)Z
.registers 2
.param p0, "x0" # Lcom/cczhr/myxposed/MainActivity;
.prologue
.line 13
invoke-direct {p0}, Lcom/cczhr/myxposed/MainActivity;->test()Z
move-result v0
return v0
.end method
.method private test()Z
.registers 2
.prologue
.line 70
const/4 v0, 0x1
return v0
.end method