类声明
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 0000 → 0; 11 0001 → 1; 12 0010 → 2; 13 0011 → 3; 14 0100 → 4; 15 0101 → 5; 16 0110 → 6; 17 0111 → 7。
算法:正数的符号位是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,v2
,move-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就非常好用,如图:
从错误总学习
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,