专栏首页乱码三千Android逆向开发之smali语言的学习

Android逆向开发之smali语言的学习

smali和java基本数据类型对比

smali

java

B

byte

S

short

I

int

J

long

F

float

D

double

C

char

Z

boolean

V

void

[

数组

L+全类名路径用/分割

object

注释

在smali语言中注释使用#表示

# 我是注释

类声明

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

比如以下java代码:

public class Test
{
}

用smali代码表示为:

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

关于分号;

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

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

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

比如以下java代码:

public class Test
{
	private static String a;
}

用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

常量声明

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

比如以下java代码:

public class Test
{
	private static final String a=”hello“;
}

用smali代码表示为:

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

.field public static final a:Ljava/lang/String; = "hello"

成员方法/函数声明

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

比如以下java代码:

public class Test
{
	public static void getName(){}
}

用smali代码表示为:

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

# 如果是非静态,只需将static去掉即可
.method public static getName()V
 
    return-void
.end method

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

比如以下java代码:

public class Test
{
    public  String getName(String p){
        return "hello";
    }
}

用smali代码表示为:

.method public getName(Ljava/lang/String;)Ljava/lang/String;

    const-string v0, "hello"

    return-object v0
.end method

关于方法返回关键字

主要有以下四种

return-void
return-object
return
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

构造方法/构造函数声明

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

比如以下java代码:

public class Test
{
   public Test(String a){
   }
}

用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

静态代码块的声明

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

比如以下java代码:

public class Test
{
	public static String a="a";

   static{
   
   }
}

用smali代码表示为:

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


.method public static constructor <clinit>()V
  
 
    return-void
.end method

方法调用

关键字

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

非私有实例方法的调用

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

比如以下java代码:

public class Test
{
  public Test(String a){
    getName();
   }
  public String getName(){
    return "hello";
   }
}

用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 #调用父类构造方法 
	invoke-virtual {p0}, LTest;->getName()Ljava/lang/String;# 调用普通成员getName方法

    return-void
.end method

#声明getName方法
.method public getName()Ljava/lang/String;
  
    const-string v0, "hello"# 定义局部字符串常量
    
    return-object v0 # 返回常量
.end method

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

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

私有方法调用:

比如以下java代码:

public class Test
{
  public Test(String a){
    getName();
   }
   //私有方法
  private String getName(){
    return "hello";
   }
}

用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 #调用父类构造方法 
	invoke-direct {p0}, LTest;->getName()Ljava/lang/String;# 调用私有getName方法

    return-void
.end method

#声明getName方法
.method private getName()Ljava/lang/String;
  
    const-string v0, "hello"# 定义局部字符串常量
    
    return-object v0 # 返回常量
.end method

构造方法调用:

比如以下java代码:

public class Test
{
  public Test(String a){
    new Test2("hello"); 
   }
    public class Test2
    {
      public Test2(String a){    
       }
    }
}

用smali代码表示为:

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

# 匿名内部类的声明
.annotation system Ldalvik/annotation/MemberClasses;
    value = {
        LTest$Test2;
    }
.end annotation


# 构造方法
.method public constructor <init>(Ljava/lang/String;)V
	# 初始化父类构造方法
    invoke-direct {p0}, Ljava/lang/Object;-><init>()V
	# 创建对象
    new-instance v0, LTest$Test2;
	# 定义常量
    const-string v1, "hello"
	# 调用构造方法
    invoke-direct {v0, p0, v1}, LTest$Test2;-><init>(LTest;Ljava/lang/String;)V

    return-void
.end method

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

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

比如以下java代码:

public class Test
{
  public Test(String a){
     String b=getName();
     System.out.print(b);
   }
  private static String getName(){
    return "hello";
   }

}

用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 #调用父类构造方法 
	invoke-static {p0}, LTest;->getName()Ljava/lang/String;# 调用普通成员getName方法
	move-result-object v0 #将返回值赋给v0
    return-void
.end method

#声明getName方法
.method public getName()Ljava/lang/String;
  
    const-string v0, "hello"# 定义局部字符串常量
    
    return-object v0 # 返回常量
.end method

父类成员的方法调用

invoke-super

比如以下java代码

@Override
protected void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);

}

用smali代码表示为

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

接口的调用

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

比如以下java代码:

public class Test
{
    private  InterTest a=new Test2();
    public Test(String a){
    }
    public void setAa(){
       InterTest aa=a;
       # 调用接口方法
        aa.est2();
   }
  public class Test2 implements InterTest
  {
        public Test2(){}
            
  		public void est2(){}
   }
  interface InterTest
  {
      public void est2();
  }
}

用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 #调用父类构造方法 
	invoke-static {p0}, LTest;->getName()Ljava/lang/String;# 调用普通成员getName方法

    return-void
.end method

#声明setAagetName方法
.method public setAa()V
    .registers 2


    iget-object v0, p0, LTest;->a:LTest$InterTest;
	# 调用接口方法
    invoke-interface {v0}, LTest$InterTest;->est2()V

    return-void
.end method

创建对象

对象的创建分多步进行:

# 声明实例
new-instance +变量名, 对象全包名路径;
# 调用构造方法 (如果构造方法内还定义了成员变量,那么在调用之前需要提前声明,然后在invoke的时候当作参数一并传入)
invoke-direct {变量名}, 对象全包名路径;-><init>(参数)返回类型
数组的创建
const/4 v0, 0x4
new-array v0, v0, [I

fill-array-data v0, :array_a

  :array_a
    .array-data 4 # 表示占用四个字节
        0x0
        0x1
        0x2
        0x3
    .end array-data

数据的定义

分三大类

字符串类型数据
字节码数据
数值型数据

数值类型数据拆分

第一种 const开头 占用一个容器(寄存器) 32位/容器
const v0,30
  * const/4 最大只允许存放4位数值(4个二进制位) 1 111 7
  * const/16 最大值允许存放16位数值 第一位默认为符号位 所以计算后15位的数值
  * const 32位 最大32位
  * const/high16 v0,0xFF7f0000
  

第二种 const-wide 占用两个容器 64位
const-wide v0,30 #占用v0和v1

总结

const-string  v0 , "hello"# 定义字符串 将字符串hello赋值给v0

const-class v0,LGoActivity; # 定义字节码对象 将GoActivity.class对象赋值给v0

# 以下数据定义高位默认为符号位
const/4 v0,0x2 # 定义一个容器 最大只允许存放半字节4位数据 取值范围为 -8 and 7
const/16 v0 , 0xABCD # 定义定义一个容器 最大只允许存放16位数据 比如short类型数据 取值范围为-32768~32767
const v0 , 0xA# 定义一个容器 最大只允许存放32位数据,比如int类型数据 将数字10赋值给v0 取值范围-2147483647~2147483647
const/high16 #定义一个容器 最大只允许存放高16位数值 比如0xFFFF0000末四位补0 存入高四位0XFFFF

# const-wide 占用两个寄存器vx和vx+1, 数值必须以L结尾 否则编译不通过
const-wide/16 # 定义两个相连容器 最大只允许存放16位数据
const-wide/32 # 定义两个相连容器 最大只允许存放32位数据
const-wide # 定义两个相连容器 最大只允许存放64位数据
const-wide/high16 # 定义两个相连容器 只允许存放高16位数据

数据取值范围算法

1000 → -8;
1001 → -7;
1010 → -6;
1011 → -5;
1100 → -4;
1101 → -3;
1110 → -2;
1111 → -1;

0000 → 0;
0001 → 1;
0010 → 2;
0011 → 3;
0100 → 4;
0101 → 5;
0110 → 6;
0111 → 7。

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

静态字段赋值

分多步进行 关键代码:

sput-object # s代指static

比如以下java代码:

public class Test
{
	private static String a=”hello“;
}

用smali代码表示为:

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

# 声明静态字段
.field private static a:Ljava/lang/String;

#类初始化方法 被jvm执行 优先于构造方法
.method static constructor <clinit>()V

    const-string v0, "hello"# 定义常量值

    sput-object v0, LTest;->a:Ljava/lang/String;#常量赋值

    return-void
.end method

类非静态字段赋值

分多步进行 关键代码:

iput-object  # i代表instance

比如以下java代码:

public class Test
{
    private  String a="g";
    public Test(String a){
       
    }
    public void setAa(){
        a="b"; 
   }
    
}

用smali代码表示为:

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

# 声明字段
.field private a:Ljava/lang/String;

# 构造方法初始化值a="g"
.method public constructor <init>(Ljava/lang/String;)V
    .registers 3
	# 初始化父类构造方法
    invoke-direct {p0}, Ljava/lang/Object;-><init>()V
    # 声明字符串内容
    const-string v0, "g"
	# 赋值
    iput-object v0, p0, LTest;->a:Ljava/lang/String;

  
    return-void
.end method

# 成员方法修改变量a="b"
.method public setAa()V
    .registers 2

    .prologue

    const-string v0, "b"

    iput-object v0, p0, LTest;->a:Ljava/lang/String;

    return-void
.end method

静态字段取值

关键代码

sget-object # s代指static

比如以下java代码:

public class Test
{
    private  static  String a="hello";
    public Test(String a){
    }
    public void getA(){
       String aa=a;
   }
    
}

用smali代码表示为:

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

# 声明静态字段
.field private static a:Ljava/lang/String;

#类初始化方法 被jvm执行 优先于构造方法
.method static constructor <clinit>()V

    const-string v0, "hello"# 定义常量值

    sput-object v0, LTest;->a:Ljava/lang/String;#常量赋值

    return-void
.end method

# 取值方法
.method public getA()V
    .registers 2

	# 静态字段取值
    sget-object v0, LTest;->a:Ljava/lang/String;

    return-void
.end method

类非静态字段取值

关键代码:

iget-object  # i代表instance

比如以下java代码:

public class Test
{
    private  String a="hello";
    public Test(String a){
    }
    public void getA(){
       String aa=a;
   }
    
}

用smali代码表示为:

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

# 声明静态字段
.field private static a:Ljava/lang/String;

#构造方法
.method public constructor <init>(Ljava/lang/String;)V
    .registers 3

    .prologue

    invoke-direct {p0}, Ljava/lang/Object;-><init>()V

    const-string v0, "hello"
	# 初始化成员变量
    iput-object v0, p0, LTest;->a:Ljava/lang/String;

    return-void
.end method


# 取值方法
.method public getA()V
    .registers 2

	# 类非静态字段取值
    iget-object v0, LTest;->a:Ljava/lang/String;

    return-void
.end method

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

值定义

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

取值:

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

赋值

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

下表以实例变量举例:

smali取值赋值和值定义关键字

java

iget-byte iput-byteconst/4

byte

iget-short iput-shortconst/4

short

iget iputconst/4

int

iget-wideiput-wideconst-wide/16

long

iget-iputconst/high16

float

iget-wide-iput-wideconst/high16

double

iget-char-iput-charconst/16

char

iget-boolean-iput-booleanconst/4

boolean

#### 如果是基本数据类型,那么按照如下表处理:

smali取值赋值和值定义关键字

java

iget-object-iput-objectnew-array v0, v0, [数据类型签名fill-array-data v0, :array_c

数组

iget-object-iput-object以下两步为类对象定义new-instance v0, 全包名类路径;invoke-direct #调用构造方法

类和接口

iget-object-iput-objectsget-object

枚举

iget-object-iput-objectconst-string

String

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

public class Test
{
     private  Test2 a=Test2.a;
    public Test(String a){
    }
    public void setAa(){
       Test2 aa=a;
   }
      public enum Test2
        {
            a,b;
        }
}

$$

$$

public class Test
{
    private  String a="a";
    public Test(String a){
    }
    public void setAa(){
       String aa=a;
   }
    
}

逻辑语句之条件跳转分支

"if-eq vA, vB, :cond_**"  如果vA等于vB则跳转到:cond_** #equal

"if-ne vA, vB, :cond_**"  如果vA不等于vB则跳转到:cond_**  # not  equal

"if-lt vA, vB, :cond_**"  如果vA小于vB则跳转到:cond_**    #less than

"if-ge vA, vB, :cond_**"  如果vA大于等于vB则跳转到:cond_**  # greater equal

"if-gt vA, vB, :cond_**"  如果vA大于vB则跳转到:cond_**  # greater than

"if-le vA, vB, :cond_**"  如果vA小于等于vB则跳转到:cond_** # less equal

"if-eqz vA, :cond_**"  如果vA等于0则跳转到:cond_** #zero
"if-nez vA, :cond_**"  如果vA不等于0则跳转到:cond_**
"if-ltz vA, :cond_**"  如果vA小于0则跳转到:cond_**
"if-gez vA, :cond_**"  如果vA大于等于0则跳转到:cond_**
"if-gtz vA, :cond_**"  如果vA大于0则跳转到:cond_**
"if-lez vA, :cond_**"  如果vA小于等于0则跳转到:cond_**

逻辑语句之循环

比如以下java代码

public class Test {
    public static void main(String[] args) {
   
        for(int i=0; i<3;i++){
        }
    }
}

对应的smali代码为:

.method public static main([Ljava/lang/String;)V
    const/4 v0, 0x0
    :goto_1
    const/4 v1, 0x3
    if-ge v0, v1, :cond_7
    add-int/lit8 v0, v0, 0x1 # 加法运算符 v0=v0+0x1

    goto :goto_1

    :cond_7
    return-void
.end method

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

.method public static main([Ljava/lang/String;)V
    .registers 5
    .prologue
    .line 4
    const-wide/16 v0, 0x0
    :goto_2
    const-wide/16 v2, 0x3
    cmp-long v2, v0, v2 # cmp-long为固定写法 如果v0大于v2 则返回1 赋值给v2  等于为0 小于则为-1
    if-gez v2, :cond_c
    const-wide/16 v2, 0x1
    add-long/2addr v0, v2
    goto :goto_2
    .line 6
    :cond_c
    return-void
.end method

smali语法关键字

.line

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

.line 3  # 代表以下代码还原成java代码在源文件第三行 
const/4 v0, 0x1

iput v0, p0, LTest;->a:I

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

:cond_0

条件分支,配合if使用

.prologue

表示程序的开始 可省略

:goto_0

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

.local

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

move-result-object v0 # 调用方法后结果储存在v0中
.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代表需要的寄存器的总个数

示例:

.method private test(I)V
    .registers 4  # 声明总共需要使用4个寄存器

    const-string v0, "LOG"  # 将v0寄存器赋值为字符串常量"LOG"

    move v1, p1  # 将int型参数的值赋给v1寄存器

    return-void
.end method

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

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

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

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

那么对应的Smali则为:

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语句

指令

说明

return-void

返回void,即直接返回

return v1

返回v1寄存器中的值

return-object v1

返回v1寄存器中的对象指针

return-wide v1

返回双字型结果给v1寄存器

另外

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

从错误总学习

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

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

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

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

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

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

思考

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

如:

.method public static print(Ljava/lang/String;)V
	.registers 2 #不报错
	.prologue
	
	invoke-static {p0},Lcom/pangshu/SLog;->wrapTag(Ljava/lang/String;)Ljava/lang/String;
	move-result-object v1 #如果这个地方改成v2以上那么报错

	# log---System.out.print()
	sget-object v0,Ljava/lang/System;->out:Ljava/io/PrintStream;

	
	# 方法调用
	invoke-virtual {v0,v1},Ljava/io/PrintStream;->print(Ljava/lang/String;)V
	
return-void
.end method

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

本文分享自微信公众号 - 乱码三千(infree6),作者:有瓣知识

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2021-08-12

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Android 逆向--BUFF | Smali 介绍与学习

    静态分析反编译代码往往是逆向分析的第一步,在对代码逻辑大致了解后,我们就可以使用动态分析的方法验证猜想、加速分析和实现破解。

    Python编程与实战
  • 天空飘来五字:Android逆向smali

    本期,我们将继续Android逆向动态分析之smali篇。内容包括smali语言介绍与动态调试。

    漏斗社区
  • 修改包名

    那么,在解包生成的目录下找到AndroidManifest.xml,着手修改package以及对应引用。

    HLQ_Struggle
  • Android逆向之静态分析

    想必打过CTF的小伙伴多多少少都触过Android逆向,所以斗哥将给大家整一期关于Android逆向的静态分析与动态分析。本期先带来Android逆向的静态分析...

    漏斗社区
  • 震惊!我逆向了Android代码居然看见……

    用户1907613
  • android 逆向 初级系列(二)

    用户1127566
  • Android Studio 3.6 调试 smali的全过程

    Smali是用于Dalvik(Android虚拟机)的反汇编程序实现,汇编工具(将Smali代码汇编为dex文件)为smali.jar,与之对应的baksmal...

    砸漏
  • Android 逆向笔记 —— 一个简单 CrackMe 的逆向总结

    无意中在看雪看到一个简单的 CrackMe 应用,正好就着这个例子总结一下逆向过程中基本的常用工具的使用,和一些简单的常用套路。感兴趣的同学可以照着尝试操作一下...

    路遥TM
  • Android逆向调试|AndroidCrackMe挑战

    Android应用安全是当下流行的话题之一,网络上有很多论坛都会不定时发布某些APP破解的话题。目前为了维护Android应用的安全,各大厂商都会采用不同的加密...

    算法与编程之美
  • Android 逆向工具篇—反编译工具的选择与使用

    在过去,当我们想要了解一个 app 内部运作细节时,往往先通过 ApkTool 反编译 APK,生成 smali 格式的反汇编代码[1],然后大佬和老手直接阅读...

    Python编程与实战
  • Android 逆向笔记 —— 说说 Dalvik 及其指令集

    在进入正题之前,推荐阅读一下之前的两篇文章。第一篇是我的一篇译文 —— 译文找不到了,就放一下原文吧。

    路遥TM
  • s002android逆向安全初级篇之android smali语法总结

    smali中有两类数据类型:基本类型和引用类型。 引用类型是指数组和对象,其他都是基础类型。

    上善若水.夏
  • 看我逆向小米rom层应用做碎片化适配

    Android的碎片化不用我说大家都懂的,五花八门。时常应用层开发者痛不欲生,明明在我这里开发时运行没有问题,tm的一上线就出问题。

    陈宇明
  • Android 安全逆向:篡改你的位置信息

    前几天,一个哥们向我提了一个需求,怎么能改变当前应用的位置信息。当时工作比较忙,就说有时间研究下。想了下,这个可以有三种方式来解决。

    陈宇明
  • Android 无需权限显示悬浮窗, 兼谈逆向分析 App

    前言 最近 UC 浏览器中文版出了一个快速搜索的功能, 在使用其他 app 的时候, 如果复制了一些内容, 屏幕顶部会弹一个窗口, 提示一些操作, 点击后跳转...

    非著名程序员
  • 如何用kwetza给安卓应用加后门

    *本文仅供安全学习、测试或信息安全教学用途,禁止非法使用 简介 这篇文章将会描述一种在Android可执行文件中种后门的方法。在接下来的动手操作部分,我将使用到...

    FB客服
  • App安全测试——Android APK反编译

    我们下载到的Android App 安装包是 Apk文件(Android Application Package) 。通过 Apk 文件,我们也可以得到这个应用...

    清风穆云
  • APKLab:针对VS Code的Android逆向工程工具

    APKLab是一款针对VS Code的高级Android逆向工程工具,在该工具的帮助下,广大研究人员可以轻松在Visual Studio中测试你的Android...

    FB客服
  • 把 Android App 逆向分为几步?三步

    逆向在很多领域都有应用,比如如今爬虫技术已经遍地走,甚至不用写代码都可以爬取数据,导致前端开发的反爬意识也逐步提升。因此 JS、Android 等领域的逆向,已...

    张凯强

扫码关注云+社区

领取腾讯云代金券