参考资料
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”指令则会包含在方法或字段定义中
格式为
常用系统注释
MemberClasses注解
MemberClasses注解是编译时自动加上的,Memberclasses是一个子类的集合,说明当前类有多少子类。
EnclosingMethod注解
EnclosingMethod 注解用于说明当前smali文件的作用范围,
Method说明它作用于一个方法。
只要有EnclosingMethod 那么InnerClass一定出现,而且一个类不得同时包含 EnclosingClass 和 EnclosingMethod 注释。
EnclosingClass
EnclosingClass 注解用于说明当前smali文件的作用范围,
Class说明它作用于一个类。
只要有EnclosingClass那么InnerClass一定出现,而且一个类不得同时包含 EnclosingClass 和 EnclosingMethod 注释。
|
|
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 计算出来的
对应java文件
数据移动
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 的方法,这些方法是由编译器生成的,目的是方便内部类/外部类 访问 外部类/内部类 的私有方法。