smali语法

 

类声明

1 .class +权限修饰符 +类名;

例如:
.class public Lcom/test/Test;

# 类名Test
# public公共属性
# 凡是L开头全包名路径结尾都需要加分号
# com/test/Test Test类的全包名路径

比如以下java代码:

1 public class Test
2 {
3 }

用smali代码表示为:

1 .class public LTest;#声明类 (必须)
2 .super Ljava/lang/Object;#声明父类 默认继承Object (必须)
3 .implements Ljava/lang/CharSequence; #如果实现了接口 则添加接口代码
4 .source "Test.java" # 源码文件 (非必须)

关于分号;

凡是L开头全包名路径结尾都需要加分号

字段声明(成员/全局变量)

1 .field 权限修饰符+静态修饰符 +变量名:变量全类名路径;

比如以下java代码:

1 public class Test
2 {
3     private static String a;
4 }

用smali代码表示为:

.class public LTest;#声明类 (必须)
.super Ljava/lang/Object;#声明父类 默认继承Object (必须)
.source "Test.java" # 源码文件 (非必须)

# 如果是非静态,只需将static去掉即可
.field private static a:Ljava/lang/String;

补充:

基本数据类型示例:
.method public final pubFinalMethod()V //返回值
.field private boType:Z    // boolean
.field private byteType:B  // byte
.field private shortType:S // short
.field private charType:C  // char
.field private intType:I   // int
.field private longType:J  //long
.field private floatType:F // float
.field private doubleType:D // double

常量声明

1 .field 权限修饰符+静态修饰符 +final+变量名:变量全类名路径;=常量值

比如以下java代码:

1 public class Test
2 {
3     private static final String a=”hello“;
4 }

用smali代码表示为:

1 .class public LTest;#声明类 (必须)
2 .super Ljava/lang/Object;#声明父类 默认继承Object (必须)
3 
4 .field public static final a:Ljava/lang/String; = "hello"

成员方法/函数声明

1 .method 权限修饰符+静态修饰符 +方法名(参数类型)返回值类型
2 #方法体
3 .end method #方法结尾标志

比如以下java代码:

1 public class Test
2 {
3     public static void getName(){}
4 }

用smali代码表示为:

1 .class public LTest;#声明类 (必须)
2 .super Ljava/lang/Object;#声明父类 默认继承Object (必须)
3 
4 # 如果是非静态,只需将static去掉即可
5 .method public static getName()V
6  
7     return-void
8 .end method

如果是带参并且带有返回值的方法

比如以下java代码:

1 public class Test
2 {
3     public  String getName(String p){
4         return "hello";
5     }
6 }

用smali代码表示为:

1 .method public getName(Ljava/lang/String;)Ljava/lang/String;
2 
3     const-string v0, "hello"
4 
5     return-object v0
6 .end method

关于方法返回关键字

主要有以下四种

1 return-void
2 return-object
3 return
4 return-wide

数据类型对应关系表如下:

smali方法返回关键字 java
return byte
return short
return int
return-wide long
return float
return-wide double
return char
return boolean
return-void void
return-object 数组
return-object object

 

 

 

 

 

 

 

 

 

 

 

 

 

 

构造方法/构造函数声明

1 .method 权限修饰符 +constructor <init>(参数类型)返回值类型
2 #方法体
3 .end method #方法结尾标志

比如以下java代码:

1 public class Test
2 {
3    public Test(String a){
4    }
5 }

用smali代码表示为:

.class public LTest;#声明类 (必须)
.super Ljava/lang/Object;#声明父类 默认继承Object (必须)


.method public constructor <init>(Ljava/lang/String;)V
  
    invoke-direct {p0}, Ljava/lang/Object;-><init>()V #调用父类构造方法 

    return-void
.end method

静态代码块的声明

1 .method static +constructor <clinit>()V
2 #方法体
3 .end method #方法结尾标志

比如以下java代码:

1 public class Test
2 {
3     public static String a="a";
4 
5    static{
6    
7    }
8 }

用smali代码表示为:

1 .class public LTest;#声明类 (必须)
2 .super Ljava/lang/Object;#声明父类 默认继承Object (必须)
3 
4 
5 .method public static constructor <clinit>()V
6   
7  
8     return-void
9 .end method

方法调用

关键字

1 invoke-virtual //用于非私有实例方法的调用
2 invoke-direct //用于构造方法以及私有方法的调用
3 invoke-static //调用静态方法
4 invoke-super //调用父类的方法
5 invoke-interface //调用接口方法

非私有实例方法的调用

invoke-virtual {参数}, 方法所属类名;->方法名(参数类型)返回值类型;

比如以下java代码:

1 public class Test
2 {
3   public Test(String a){
4     getName();
5    }
6   public String getName(){
7     return "hello";
8    }
9 }

用smali代码表示为:

 1 .class public LTest;#声明类 (必须)
 2 .super Ljava/lang/Object;#声明父类 默认继承Object (必须)
 3 
 4 
 5 .method public constructor <init>( Ljava/lang/String;)V
 6  
 7     invoke-direct {p0}, Ljava/lang/Object;-><init>()V #调用父类构造方法 
 8     invoke-virtual {p0}, LTest;->getName()Ljava/lang/String;# 调用普通成员getName方法
 9 
10     return-void
11 .end method
12 
13 #声明getName方法
14 .method public getName()Ljava/lang/String;
15   
16     const-string v0, "hello"# 定义局部字符串常量
17     
18     return-object v0 # 返回常量
19 .end method

私有方法或者构造方法的调用

1 invoke-direct {参数}, 方法所属类名;->方法名(参数类型)返回值类型;

私有方法调用:

比如以下java代码:

 1 public class Test
 2 {
 3   public Test(String a){
 4     getName();
 5    }
 6    //私有方法
 7   private String getName(){
 8     return "hello";
 9    }
10 }

用smali代码表示为:

 1 .class public LTest;#声明类 (必须)
 2 .super Ljava/lang/Object;#声明父类 默认继承Object (必须)
 3 
 4 
 5 .method public constructor <init>(Ljava/lang/String;)V
 6   
 7     invoke-direct {p0}, Ljava/lang/Object;-><init>()V #调用父类构造方法 
 8     invoke-direct {p0}, LTest;->getName()Ljava/lang/String;# 调用私有getName方法
 9 
10     return-void
11 .end method
12 
13 #声明getName方法
14 .method private getName()Ljava/lang/String;
15   
16     const-string v0, "hello"# 定义局部字符串常量
17     
18     return-object v0 # 返回常量
19 .end method

构造方法调用:

比如以下java代码:

 1 public class Test
 2 {
 3   public Test(String a){
 4     new Test2("hello"); 
 5    }
 6     public class Test2
 7     {
 8       public Test2(String a){    
 9        }
10     }
11 }

用smali代码表示为:

 1 .class public LTest;#声明类 (必须)
 2 .super Ljava/lang/Object;#声明父类 默认继承Object (必须)
 3 
 4 # 匿名内部类的声明
 5 .annotation system Ldalvik/annotation/MemberClasses;
 6     value = {
 7         LTest$Test2;
 8     }
 9 .end annotation
10 
11 
12 # 构造方法
13 .method public constructor <init>(Ljava/lang/String;)V
14     # 初始化父类构造方法
15     invoke-direct {p0}, Ljava/lang/Object;-><init>()V
16     # 创建对象
17     new-instance v0, LTest$Test2;
18     # 定义常量
19     const-string v1, "hello"
20     # 调用构造方法
21     invoke-direct {v0, p0, v1}, LTest$Test2;-><init>(LTest;Ljava/lang/String;)V
22 
23     return-void
24 .end method

静态方法的调用并获取返回值(不区分私有公有 静态优先)

1 invoke-static {参数}, 方法所属类名;->方法名(参数类型)返回值类型;

比如以下java代码:

 1 public class Test
 2 {
 3   public Test(String a){
 4      String b=getName();
 5      System.out.print(b);
 6    }
 7   private static String getName(){
 8     return "hello";
 9    }
10 
11 }

用smali代码表示为:

 1 .class public LTest;#声明类 (必须)
 2 .super Ljava/lang/Object;#声明父类 默认继承Object (必须)
 3 
 4 
 5 .method public constructor <init>(Ljava/lang/String;)V
 6   
 7     invoke-direct {p0}, Ljava/lang/Object;-><init>()V #调用父类构造方法 
 8     invoke-static {p0}, LTest;->getName()Ljava/lang/String;# 调用普通成员getName方法
 9     move-result-object v0 #将返回值赋给v0
10     return-void
11 .end method
12 
13 #声明getName方法
14 .method public getName()Ljava/lang/String;
15   
16     const-string v0, "hello"# 定义局部字符串常量
17     
18     return-object v0 # 返回常量
19 .end method

父类成员的方法调用

1 invoke-super

比如以下java代码

1 @Override
2 protected void onCreate(Bundle savedInstanceState) {
3     super.onCreate(savedInstanceState);
4 
5 }

用smali代码表示为

1 .method protected onCreate(Landroid/os/Bundle;)V
2     .registers 2
3  
4     invoke-super {p0, p1}, Landroid/app/Activity;->onCreate(Landroid/os/Bundle;)V
5     return-void
6 .end method

接口的调用

1 invoke-interface  {参数}, 方法所属类名;->方法名(参数类型)返回值类型;

比如以下java代码:

 1 public class Test
 2 {
 3     private  InterTest a=new Test2();
 4     public Test(String a){
 5     }
 6     public void setAa(){
 7        InterTest aa=a;
 8        # 调用接口方法
 9         aa.est2();
10    }
11   public class Test2 implements InterTest
12   {
13         public Test2(){}
14             
15           public void est2(){}
16    }
17   interface InterTest
18   {
19       public void est2();
20   }
21 }

用smali代码表示为:

 1 .class public LTest;#声明类 (必须)
 2 .super Ljava/lang/Object;#声明父类 默认继承Object (必须)
 3 
 4 
 5 .method public constructor <init>(Ljava/lang/String;)V
 6   
 7     invoke-direct {p0}, Ljava/lang/Object;-><init>()V #调用父类构造方法 
 8     invoke-static {p0}, LTest;->getName()Ljava/lang/String;# 调用普通成员getName方法
 9 
10     return-void
11 .end method
12 
13 #声明setAagetName方法
14 .method public setAa()V
15     .registers 2
16 
17 
18     iget-object v0, p0, LTest;->a:LTest$InterTest;
19     # 调用接口方法
20     invoke-interface {v0}, LTest$InterTest;->est2()V
21 
22     return-void
23 .end method

 

创建对象

对象的创建分多步进行:

1 # 声明实例
2 new-instance +变量名, 对象全包名路径;
3 # 调用构造方法 (如果构造方法内还定义了成员变量,那么在调用之前需要提前声明,然后在invoke的时候当作参数一并传入)
4 invoke-direct {变量名}, 对象全包名路径;-><init>(参数)返回类型
数组的创建
 1 const/4 v0, 0x4
 2 new-array v0, v0, [I
 3 
 4 fill-array-data v0, :array_a
 5 
 6   :array_a
 7     .array-data 4 # 表示占用四个字节
 8         0x0
 9         0x1
10         0x2
11         0x3
12     .end array-data

数据的定义

分三大类

1 字符串类型数据
2 字节码数据
3 数值型数据

数值类型数据拆分

 1 第一种 const开头 占用一个容器(寄存器) 32位/容器
 2 const v0,30
 3   * const/4 最大只允许存放4位数值(4个二进制位) 1 111 7
 4   * const/16 最大值允许存放16位数值 第一位默认为符号位 所以计算后15位的数值
 5   * const 32位 最大32位
 6   * const/high16 v0,0xFF7f0000
 7   
 8 
 9 第二种 const-wide 占用两个容器 64位
10 const-wide v0,30 #占用v0和v1

总结

 1 const-string  v0 , "hello"# 定义字符串 将字符串hello赋值给v0
 2 
 3 const-class v0,LGoActivity; # 定义字节码对象 将GoActivity.class对象赋值给v0
 4 
 5 # 以下数据定义高位默认为符号位
 6 const/4 v0,0x2 # 定义一个容器 最大只允许存放半字节4位数据 取值范围为 -8 and 7
 7 const/16 v0 , 0xABCD # 定义定义一个容器 最大只允许存放16位数据 比如short类型数据 取值范围为-32768~32767
 8 const v0 , 0xA# 定义一个容器 最大只允许存放32位数据,比如int类型数据 将数字10赋值给v0 取值范围-2147483647~2147483647
 9 const/high16 #定义一个容器 最大只允许存放高16位数值 比如0xFFFF0000末四位补0 存入高四位0XFFFF
10 
11 # const-wide 占用两个寄存器vx和vx+1, 数值必须以L结尾 否则编译不通过
12 const-wide/16 # 定义两个相连容器 最大只允许存放16位数据
13 const-wide/32 # 定义两个相连容器 最大只允许存放32位数据
14 const-wide # 定义两个相连容器 最大只允许存放64位数据
15 const-wide/high16 # 定义两个相连容器 只允许存放高16位数据

数据取值范围算法

 1 1000 → -8 2 1001 → -7 3 1010 → -6 4 1011 → -5 5 1100 → -4 6 1101 → -3 7 1110 → -2 8 1111 → -1 9 
10 0000011 0001112 0010213 0011314 0100415 0101516 0110617 01117

算法:正数的符号位是0,负数的符号位是1。正数的反码、补码与原码一样。负数的反码是让符号位不变,数据位按位取反;补码是将反码加1。

静态字段赋值

分多步进行 关键代码:

1 sput-object # s代指static

比如以下java代码:

1 public class Test
2 {
3     private static String a=”hello“;
4 }

用smali代码表示为:

 1 .class public LTest;#声明类 (必须)
 2 .super Ljava/lang/Object;#声明父类 默认继承Object (必须)
 3 .source "Test.java" # 源码文件 (非必须)
 4 
 5 # 声明静态字段
 6 .field private static a:Ljava/lang/String;
 7 
 8 #类初始化方法 被jvm执行 优先于构造方法
 9 .method static constructor <clinit>()V
10 
11     const-string v0, "hello"# 定义常量值
12 
13     sput-object v0, LTest;->a:Ljava/lang/String;#常量赋值
14 
15     return-void
16 .end method

类非静态字段赋值

分多步进行 关键代码:

1 iput-object  # i代表instance

比如以下java代码:

 1 public class Test
 2 {
 3     private  String a="g";
 4     public Test(String a){
 5        
 6     }
 7     public void setAa(){
 8         a="b"; 
 9    }
10     
11 }

用smali代码表示为:

 1 .class public LTest;#声明类 (必须)
 2 .super Ljava/lang/Object;#声明父类 默认继承Object (必须)
 3 .source "Test.java" # 源码文件 (非必须)
 4 
 5 # 声明字段
 6 .field private a:Ljava/lang/String;
 7 
 8 # 构造方法初始化值a="g"
 9 .method public constructor <init>(Ljava/lang/String;)V
10     .registers 3
11     # 初始化父类构造方法
12     invoke-direct {p0}, Ljava/lang/Object;-><init>()V
13     # 声明字符串内容
14     const-string v0, "g"
15     # 赋值
16     iput-object v0, p0, LTest;->a:Ljava/lang/String;
17 
18   
19     return-void
20 .end method
21 
22 # 成员方法修改变量a="b"
23 .method public setAa()V
24     .registers 2
25 
26     .prologue
27 
28     const-string v0, "b"
29 
30     iput-object v0, p0, LTest;->a:Ljava/lang/String;
31 
32     return-void
33 .end method

静态字段取值

关键代码

1 sget-object # s代指static

比如以下java代码:

 1 public class Test
 2 {
 3     private  static  String a="hello";
 4     public Test(String a){
 5     }
 6     public void getA(){
 7        String aa=a;
 8    }
 9     
10 }

用smali代码表示为:

 1 .class public LTest;#声明类 (必须)
 2 .super Ljava/lang/Object;#声明父类 默认继承Object (必须)
 3 .source "Test.java" # 源码文件 (非必须)
 4 
 5 # 声明静态字段
 6 .field private static a:Ljava/lang/String;
 7 
 8 #类初始化方法 被jvm执行 优先于构造方法
 9 .method static constructor <clinit>()V
10 
11     const-string v0, "hello"# 定义常量值
12 
13     sput-object v0, LTest;->a:Ljava/lang/String;#常量赋值
14 
15     return-void
16 .end method
17 
18 # 取值方法
19 .method public getA()V
20     .registers 2
21 
22     # 静态字段取值
23     sget-object v0, LTest;->a:Ljava/lang/String;
24 
25     return-void
26 .end method

类非静态字段取值

关键代码:

1 iget-object  # i代表instance

 

比如以下java代码:

 1 public class Test
 2 {
 3     private  String a="hello";
 4     public Test(String a){
 5     }
 6     public void getA(){
 7        String aa=a;
 8    }
 9     
10 }

用smali代码表示为:

 1 .class public LTest;#声明类 (必须)
 2 .super Ljava/lang/Object;#声明父类 默认继承Object (必须)
 3 .source "Test.java" # 源码文件 (非必须)
 4 
 5 # 声明静态字段
 6 .field private static a:Ljava/lang/String;
 7 
 8 #构造方法
 9 .method public constructor <init>(Ljava/lang/String;)V
10     .registers 3
11 
12     .prologue
13 
14     invoke-direct {p0}, Ljava/lang/Object;-><init>()V
15 
16     const-string v0, "hello"
17     # 初始化成员变量
18     iput-object v0, p0, LTest;->a:Ljava/lang/String;
19 
20     return-void
21 .end method
22 
23 
24 # 取值方法
25 .method public getA()V
26     .registers 2
27 
28     # 类非静态字段取值
29     iget-object v0, LTest;->a:Ljava/lang/String;
30 
31     return-void
32 .end method

注意:以上取值赋值方法都是以String对象举例,如果是基本数据类型,那么按照如下表处理:

值定义

1 const/4 v0, 0x1 # 实例变量值内容定义 值皆为十六进制

取值:

1 iget #实例变量int型取值
2 sget #静态变量int型取值

赋值

1 iput #实例变量int型赋值
2 sput #静态变量int型赋值

 

下表以实例变量举例:

 

smali取值赋值和值定义关键字 java
iget-byte
iput-byte
const/4
byte
iget-short
iput-short
const/4
short
iget
iput
const/4
int
iget-wide
iput-wide
const-wide/16
long
iget-
iput
const/high16
float
iget-wide-
iput-wide
const/high16
double
iget-char-
iput-char
const/16
char
iget-boolean-
iput-boolean
const/4
boolean
#### 如果是基本数据类型,那么按照如下表处理:  

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

smali取值赋值和值定义关键字 java
iget-object-
iput-object
new-array v0, v0, [数据类型签名

fill-array-data v0, :array_c
数组
iget-object-
iput-object

以下两步为类对象定义
new-instance v0, 全包名类路径;

invoke-direct #调用构造方法
类和接口
iget-object-
iput-object
sget-object
枚举
iget-object-
iput-object
const-string
String

 

 

 

 

 

 

 

 

 

 

 

 

 

以上表结果示例java代码如下,可自行试验:

 1 public class Test
 2 {
 3      private  Test2 a=Test2.a;
 4     public Test(String a){
 5     }
 6     public void setAa(){
 7        Test2 aa=a;
 8    }
 9       public enum Test2
10         {
11             a,b;
12         }
13 }

$$

$$

 1 public class Test
 2 {
 3     private  String a="a";
 4     public Test(String a){
 5     }
 6     public void setAa(){
 7        String aa=a;
 8    }
 9     
10 }

逻辑语句之条件跳转分支

 1 "if-eq vA, vB, :cond_**"  如果vA等于vB则跳转到:cond_** #equal
 2 
 3 "if-ne vA, vB, :cond_**"  如果vA不等于vB则跳转到:cond_**  # not  equal
 4 
 5 "if-lt vA, vB, :cond_**"  如果vA小于vB则跳转到:cond_**    #less than
 6 
 7 "if-ge vA, vB, :cond_**"  如果vA大于等于vB则跳转到:cond_**  # greater equal
 8 
 9 "if-gt vA, vB, :cond_**"  如果vA大于vB则跳转到:cond_**  # greater than
10 
11 "if-le vA, vB, :cond_**"  如果vA小于等于vB则跳转到:cond_** # less equal
12 
13 "if-eqz vA, :cond_**"  如果vA等于0则跳转到:cond_** #zero
14 "if-nez vA, :cond_**"  如果vA不等于0则跳转到:cond_**
15 "if-ltz vA, :cond_**"  如果vA小于0则跳转到:cond_**
16 "if-gez vA, :cond_**"  如果vA大于等于0则跳转到:cond_**
17 "if-gtz vA, :cond_**"  如果vA大于0则跳转到:cond_**
18 "if-lez vA, :cond_**"  如果vA小于等于0则跳转到:cond_**

逻辑语句之循环

比如以下java代码

1 public class Test {
2     public static void main(String[] args) {
3    
4         for(int i=0; i<3;i++){
5         }
6     }
7 }

对应的smali代码为:

 1 .method public static main([Ljava/lang/String;)V
 2 
 3     const/4 v0, 0x0
 4 
 5     :goto_1
 6     const/4 v1, 0x3
 7 
 8     if-ge v0, v1, :cond_7
 9 
10     add-int/lit8 v0, v0, 0x1 # 加法运算符 v0=v0+0x1
11 
12     goto :goto_1
13 
14     :cond_7
15     return-void
16 .end method

如果将int改成long, 结果又不一样,这里使用到了比较运算符cmp(comparable)

 1 .method public static main([Ljava/lang/String;)V
 2     .registers 5
 3 
 4     .prologue
 5     .line 4
 6     const-wide/16 v0, 0x0
 7 
 8     :goto_2
 9     const-wide/16 v2, 0x3
10 
11     cmp-long v2, v0, v2 # cmp-long为固定写法 如果v0大于v2 则返回1 赋值给v2  等于为0 小于则为-1
12 
13     if-gez v2, :cond_c
14 
15     const-wide/16 v2, 0x1
16 
17     add-long/2addr v0, v2
18 
19     goto :goto_2
20 
21     .line 6
22     :cond_c
23     return-void
24 .end method

smali语法关键字

.line

表示与java源文件代码的映射关系,比如:

1 .line 3  # 代表以下代码还原成java代码在源文件第三行 
2 const/4 v0, 0x1
3 
4 iput v0, p0, LTest;->a:I

删除该关键字不影响程序执行,该关键字在反编译时能很好地帮助我们阅读smali代码,以该关键字当作代码块的分割线,方便快速阅读执行内容

:cond_0

条件分支,配合if使用

.prologue

表示程序的开始 可省略

:goto_0

goto跳转分支,配合goto关键字使用

.local

显示局部变量别名信息,作用等同.line

1 move-result-object v0 # 调用方法后结果储存在v0中
2 .local v0, "b":Ljava/lang/String;  # 局部变量v0别名为b 是一个String类型 也就是 String b=v0

.locals N

注意这个和上面local的区别多加了一个s

标明了你在这个函数中最少要用到的本地寄存器的个数 也即是指明了在这个方法中非参(non-parameter)寄存器的数量

locals和registers具体区别参见:点击跳转

.registers N

在Smali中,如果需要存储变量,必须先声明足够数量的寄存器,1个寄存器可以存储32位长度的类型,比如Int,而两个寄存器可以存储64位长度类型的数据,比如Long或Double

声明可使用的寄存器数量的方式为:.registers N,N代表需要的寄存器的总个数

示例:

1 .method private test(I)V
2     .registers 4  # 声明总共需要使用4个寄存器
3 
4     const-string v0, "LOG"  # 将v0寄存器赋值为字符串常量"LOG"
5 
6     move v1, p1  # 将int型参数的值赋给v1寄存器
7 
8     return-void
9 .end method

那么,如何确定需要使用的寄存器的个数?

由于非static方法,需要占用一个寄存器以保存this指针,那么这类方法的寄存器个数,最低就为1,如果还需要处理传入的参数,则需要再次叠加,此时还需要考虑Double和Float这种需要占用两个寄存器的参数类型,举例来看:

如果一个Java方法声明如下:

1 myMethod(int p1, float p2, boolean p3)1

那么对应的Smali则为:

1 method LMyObject;->myMethod(IJZ)V1

此时,寄存器的对应情况如下:

寄存器名称 对应的引用
p0 this
p1 int型的p1参数
p2, p3 float型的p2参数
p4 boolean型的p3参数

那么最少需要的寄存器个数则为:5

如果方法体内含有常量、变量等定义,则需要根据情况增加寄存器个数,数量只要满足需求,保证需要获取的值不被后面的赋值冲掉即可,方法有:存入类中的字段中(存入后,寄存器可被重新赋值),或者长期占用一个寄存器

寄存器数量只能多不能少

Dalvik指令集

如果需要使用Smali编写程序,还需要掌握常用的Dalvik虚拟机指令,其合集称为Dalvik指令集。这些指令有点类似x86汇编的指令,但指令更多,使用也非常简单方便。最详尽的介绍,可以参考Android官方的Dalvik相关文档:

https://source.android.com/devices/tech/dalvik/dalvik-bytecode#instructions

 

一般的指令格式为:[op]-[type](可选)/[位宽,默认4位] [目标寄存器],[源寄存器](可选),比如:move v1,v2move-wide/from16 v1,v2

这里也列举一些常用的指令,并结合Smali进行说明:

  • 移位操作:

此类操作常用于赋值

指令 说明
move v1,v2 将v2中的值移入到v1寄存器中(4位,支持int型)
move/from16 v1,v2 将16位的v2寄存器中的值移入到8位的v1寄存器中
move/16 v1,v2 将16位的v2寄存器中的值移入到16位的v1寄存器中
move-wide v1,v2 将寄存器对(一组,用于支持双字型)v2中的值移入到v1寄存器对中(4位,猜测支持float、double型)
move-wide/from16 v1,v2 将16位的v2寄存器对(一组)中的值移入到8位的v1寄存器中
move-wide/16 v1,v2 将16位的v2寄存器对(一组)中的值移入到16位的v1寄存器中
move-object v1,v2 将v2中的对象指针移入到v1寄存器中
move-object/from16 v1,v2 将16位的v2寄存器中的对象指针移入到v1(8位)寄存器中
move-object/16 v1,v2 将16位的v2寄存器中的对象指针移入到v1(16位)寄存器中
move-result v1 将这个指令的上一条指令计算结果,移入到v1寄存器中(需要配合invoke-static、invoke-virtual等指令使用)
move-result-object v1 将上条计算结果的对象指针移入v1寄存器
move-result-wide v1 将上条计算结果(双字)的对象指针移入v1寄存器
move-exception v1 将异常移入v1寄存器,用于捕获try-catch语句中的异常

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 返回操作:

用于返回值,对应Java中的return语句

1 指令     说明
2 return-void     返回void,即直接返回
3 return v1     返回v1寄存器中的值
4 return-object v1     返回v1寄存器中的对象指针
5 return-wide v1     返回双字型结果给v1寄存器

另外

android studio自带的.class转smali就非常好用,如图:

smali语法

 

 

从错误总学习

1 SLog.smali[24,4] Invalid register: v-1. Must be between v0 and v15, inclusive.

寄存器命名从v0-v15 一共15个

1 SLog.smali[17,0] A .registers or .locals directive must be present for a non-abstract/non-final method

.registers或者.locals必须存在, 除非是抽象方法或者final方法

1 java.lang.VerifyError: Rejecting class com.pangshu.SLog because it failed compile-time verification (declaration of com.pangshu.SLog appears in /sdcard/ex.dex)
2         at com.pangshu.HelloTest.main(HelloTest.java

 

这种错误一般很难定位,因为没有提示具体原因或者具体的行数,有可能是静态方法调用你写成了虚方法的调用,或者是构造函数调用没有加尖括号, 甚至是寄存器数量过少 等等

思考

为什么方法中包括参数在内需要3个寄存器,但是在定义的时候只写了两个却也不报错呢?

如:

 1 .method public static print(Ljava/lang/String;)V
 2     .registers 2 #不报错
 3     .prologue
 4     
 5     invoke-static {p0},Lcom/pangshu/SLog;->wrapTag(Ljava/lang/String;)Ljava/lang/String;
 6     move-result-object v1 #如果这个地方改成v2以上那么报错
 7 
 8     # log---System.out.print()
 9     sget-object v0,Ljava/lang/System;->out:Ljava/io/PrintStream;
10 
11     
12     # 方法调用
13     invoke-virtual {v0,v1},Ljava/io/PrintStream;->print(Ljava/lang/String;)V
14     
15 return-void
16 .end method

答案是:系统会更具最大寄存器的位置进行判断,从v0到vN,数量必须大于N,

 

smali语法

上一篇:关于安装ceres库时遇到的一些问题


下一篇:移动端知识点