Decompile-smali file interpretation

The Dalvik virtual machine loads a dex file. The Dex file is the executable file format of the Dalvik virtual machine. The dex file is difficult to understand. baksmali and smali are disassemblers and assemblers for Dex files. By decoding Dex FileDecompile to get the smali file. The smali file is an interpretation (it can also be said to be a translation) of the Dalvik virtual machine bytecode, not a Official standard language. Source code information can be obtained by interpreting the smali file.

Dalvik instructions are the main component of Dex files:Dalvik Guide

The first 3 lines of the smali file

# Specifies the class name of the current class. The access permission is public. The L at the beginning of the class name follows the relevant conventions of Dalvik bytecode.

.class public Lcom/example/administrator/myapplication/Demo;  

# The super instruction specifies the parent class of the current class, which is Object

.super Ljava/lang/Object;  


//Specifies the source file name of the current class. Note: After obfuscating the dex file, the decompiled smali code may not have source file information, and the source may be empty.

.source "Demo.java"  


Field declaration:


The field declaration of the smali file uses the ".field" directive. There are two types of fields: static fields and instance fields:
Static field format:  .field access permission static modified keyword field name field type 

.field public static HELLO:Ljava/lang/String;

The above smali code converted to java code is:

public static String HELLO = "hello";
.field private button:Landroid/widget/Button;
.field public number:I

The above smali code converted to java code is:

private Button button;
 public  int number =5;

 

method declaration

Using the ".method" directive, it is divided into direct methods (modified with private) and virtual methods (modified with public and protected). The declarations of direct methods and virtual methods are the same. When calling a function, there are several different instructions such as invoke-direct, invoke-virtual, invoke-static, invoke-super and invoke-interface. There is also the invoke-XXX/range instruction, which is called when there are more than 4 parameters. It is relatively rare:
 

.method private static getCount(II)V       
    .registers 2      
    .param p0, "x"    
    .param p1, "y"    
 
    .prologue       
    .line 28     
    return-void       
.end method      

The first line is the beginning of the method. The modifier of the method here is static, the access permission is private, the method name is getCount, and there are two parameters, both of type int (I represents int), and V represents no return value.

The second line specifies the size of the register.

The third and fourth lines are the parameters of the method. For each parameter, write a parameter. There are two parameters here.

The fifth behavior is the main part of the method (.prologue)

The sixth line specifies the line number of the instruction in the source code, which starts from the bottom 28 lines of the java source code.

The seventh line return-void means no return value

The eighth line (.end method) method ends

The above smali to java code is:

private static void   getCount(int x,int y){
    
    
 
    }

Class implements interface

If a class implements the interface, it will be pointed out using the ".implements " directive in the smali file. The corresponding format declaration is as follows:< /span>

# interfaces
.implements Lcom/example/administrator/myapplication/TestParent;  

The above smali code shows that the TestParent interface is implemented.

Class usage annotations

If a class uses annotations, it will be pointed out using the ".annotation " directive in the smali file. The annotation format is declared as follows: < /span>       [annotation field = value]   .end annotation   .annotation [annotation attribute] < annotation class name>  
# annotations  


The scope of annotations can be classes, methods or fields. If the scope of the annotation is a class, the ".annotation" directive will be directly defined in the smali file. If it is a method or field, the ".annotation" directive will be included in the method or field definition.

.field public sayWhat:Ljava/lang/String;           
    .annotation runtime Lcom/droider/anno/MyAnnoField;  
        info = ”Hello my friend”  
    .end annotation  
.end field  

The above String type uses the com.droider.anno.MyAnnoField annotation. The annotation field info value is "Hello my friend"  
is converted into java code:

@com.droider.anno.MyAnnoField(info = ”Hello my friend”)
public String sayWhat;


Classes in the program


1) Inner classes: Inner classes can have member inner classes, static nested classes, method inner classes, and anonymous inner classes.
      Format: external class $ internal class.smali

class Outer{
    
    
 
      class Inner{
    
    }
 
}

After baksmali decompiles the above code, it will generate two files: Outer.smali and Outer$Inner.smali

The Inner.smali file is as follows:

.class public Lcom/example/myapplication/Outer$Inner;
.super Ljava/lang/Object;
.source "Outer.java"
 
# annotations
.annotation system Ldalvik/annotation/EnclosingClass;
    value = Lcom/example/myapplication/Outer;
.end annotation
 
.annotation system Ldalvik/annotation/InnerClass;
    accessFlags = 0x1
    name = "Inner"
.end annotation
 
# instance fields
.field final synthetic this$0:Lcom/example/myapplication/Outer;
 
# direct methods
.method public constructor <init>(Lcom/example/myapplication/Outer;)V
    .registers 2
    .param p1, "this$0"    # Lcom/example/myapplication/Outer;
 
    .prologue
    .line 4
    iput-object p1, p0, Lcom/example/myapplication/Outer$Inner;->this$0:Lcom/example/myapplication/Outer;
 
    invoke-direct {
    
    p0}, Ljava/lang/Object;-><init>()V
    
    return-void
.end method

 

this$0 is of Outer type, and the synthesizeitc keyword indicates that it is "synthetic".

.field final synthetic this$0:Lcom/example/myapplication/Outer;

What is this$0?

this$0 is a parameter automatically reserved by the inner class that points to the outer class. The this on the left represents a reference to the parent class, and the value 0 on the right represents the number of reference levels:

public class Outer {
    
    
    public class FirstInner{
    
    }  //this$0
    public class SecondInner{
    
    } //this$1
    public class ThirdInner{
    
    }  //this$2
}

The value on the right side of the inner layer is incremented by one. For example, the reference of the ThirdInner class accessing the FirstInner class is this$1. In the generated disassembly code, this$X-type fields are assigned the synthetic attribute, indicating that they are compiled by the compiler. The author of the synthetic, fictitious code has no life in this field.
 

Next, let’s look at the constructor of Inner.smali:

As you can see from the constructor below, .param specifies a parameter, but uses two registers, p0 and p1, because the Dalvik virtual machine will implicitly use the p0 register as the this of the class for a non-static method. Use, therefore, 2 registers (.registers 2) are indeed used here. p0 represents the reference of Outer$Inner.smali itself, and p1 represents this$0, which is the reference of Outer.

# direct methods
.method public constructor <init>(Lcom/example/myapplication/Outer;)V
    .registers 2
    .param p1, "this$0"    # Lcom/example/myapplication/Outer;
 
    .prologue
    .line 4
    iput-object p1, p0, Lcom/example/myapplication/Outer$Inner;->this$0:Lcom/example/myapplication/Outer;
 
    invoke-direct {
    
    p0}, Ljava/lang/Object;-><init>()V
 
    return-void
.end method

analyse as below:

 .param p1, "this$0"    # Lcom/example/myapplication/Outer;

This is the default constructor with no parameters, but there is a default parameter which is this$0, which is the application of Outer. If the constructor has parameters, it will be in

.param p1,"this$0"The list continues below.

iput-object p1, p0, Lcom/example/myapplication/Outer$Inner;->this$0:Lcom/example/myapplication/Outer;

Assign the Outer reference to this$0

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

Call the default constructor.

 

2) Listener

A large number of listeners are used in Android program development, such as Button's click event OnClickListener, etc. Since the listener is only used temporarily and has little use value, it is often implemented in the form of anonymous internal classes when writing code.

java source code:

button.setOnClickListener(new View.OnClickListener() {
    
    
            @Override
            public void onClick(View v) {
    
    
                
            }
        });

The corresponding smali code:

.method protected onCreate(Landroid/os/Bundle;)V
    .registers 4
    .param p1, "savedInstanceState"    # Landroid/os/Bundle;
    .prologue
    .line 12
    invoke-super {
    
    p0, p1}, Landroid/app/Activity;->onCreate(Landroid/os/Bundle;)V
 
    .line 13
    const v1, 0x7f09001c
    invoke-virtual {
    
    p0, v1}, Lcom/example/myapplication/MainActivity;->setContentView(I)V
    .line 15
    const v1, 0x7f070022
    invoke-virtual {
    
    p0, v1}, Lcom/example/myapplication/MainActivity;->findViewById(I)Landroid/view/View;
    move-result-object v0
    check-cast v0, Landroid/widget/Button;
    .line 16
    .local v0, "button":Landroid/widget/Button;
    #新建一个MainActivity$1实例
    new-instance v1, Lcom/example/myapplication/MainActivity$1; 
    invoke-direct {
    
    v1, p0}, Lcom/example/myapplication/MainActivity$1;-><init>(Lcom/example/myapplication/MainActivity;)V
    #设置按钮点击事件监听器
    invoke-virtual {
    
    v0, v1}, Landroid/widget/Button;->setOnClickListener(Landroid/view/View$OnClickListener;)V
    .line 22
    return-void
.end method
MainActivity$1代码如下:
.class Lcom/example/myapplication/MainActivity$1;
.super Ljava/lang/Object;
.source "MainActivity.java"
 
# interfaces
.implements Landroid/view/View$OnClickListener;
 
# annotations
.annotation system Ldalvik/annotation/EnclosingMethod;
    value = Lcom/example/myapplication/MainActivity;->onCreate(Landroid/os/Bundle;)V
.end annotation
.annotation system Ldalvik/annotation/InnerClass;
    accessFlags = 0x0
    name = null
.end annotation
 
# instance fields
.field final synthetic this$0:Lcom/example/myapplication/MainActivity;
 
# direct methods
.method constructor <init>(Lcom/example/myapplication/MainActivity;)V
    .registers 2
    .param p1, "this$0"    # Lcom/example/myapplication/MainActivity;
 
    .prologue
    .line 16
    iput-object p1, p0, Lcom/example/myapplication/MainActivity$1;->this$0:Lcom/example/myapplication/MainActivity;
    invoke-direct {
    
    p0}, Ljava/lang/Object;-><init>()V
    return-void
.end method
 
# virtual methods
.method public onClick(Landroid/view/View;)V
    .registers 2
    .param p1, "v"    # Landroid/view/View;
 
    .prologue
    .line 20
    return-void
.end method

At the beginning of the MainActivity$1.smali file, the ",implements" directive is used to specify that this class implements the listener interface for button click events. Therefore, this class implements its OnClick() method. This is where we are concerned when analyzing the program. . The annotations in the program and the constructor of the listener are all generated by the compiler for us, so we don’t need to care about them during the actual analysis process.

3)R.java

Below is part of the R.java file:

public final class R {
    
    
  public static final class anim {
    
    
    public static final int abc_fade_in=0x7f010000;
  }
}

Since these resource file classes are all internal classes of the R class, they will generate a class file independently. In the decompiled code, you can find R.smali, R$attr.smali, R$dimen.smali, R $drawable.smali and so on.

 

various statements

1) switch statement

.method public testSwitch(I)Ljava/lang/String;
    .registers 3
    .param p1, "index"    # I
 
    .prologue
    .line 21
    const-string v0, ""
 
    .line 22
    .local v0, "name":Ljava/lang/String;
    packed-switch p1, :pswitch_data_14 #packed-switch分支,pswitch_data_14指定case区域
 
    .line 36            #default
    const-string v0, "This index is 100"
 
    .line 38
    :goto_7         #所有case出口
    return-object v0
 
    .line 24
    :pswitch_8      #case 0
    const-string v0, "This index is 0"
 
    .line 25
    goto :goto_7    #跳转到goto_7出口
 
    .line 27
    :pswitch_b       #case 1
    const-string v0, "This index is 1"
 
    .line 28
    goto :goto_7
 
    .line 30
    :pswitch_e       #case 2
    const-string v0, "This index is 2"
 
    .line 31
    goto :goto_7
 
    .line 33
    :pswitch_11       #case 3
    const-string v0, "This index is 3"
 
    .line 34
    goto :goto_7
 
    .line 22
    :pswitch_data_14
    .packed-switch 0x0    #case区域,从0开始,依次递增
        :pswitch_8
        :pswitch_b
        :pswitch_e
        :pswitch_11
    .end packed-switch
.end method
代码中的switch分支使用的是packed-switch指令,p1为传递进来的int类型的数值,pswitch_data_10为case区域,在case区域中,第一条指令".packed-switch"指定了比较的初始值为0
:pswitch_8, :pswitch_b,:pswitch_e,:pswitch_11 分别是比较结果为"case 0""case 3"时要跳转的地址,标号的命名采用pswitch_开关,后面的数值为case分支需要判断的值,并且它的值依次递增。
每个标号处都使用v0寄存器初始化一个字符串,然后跳转到了goto_7标号处,可见goto_7是所有case分支的出口。       
         
整理为java代码如下:         
public String testSwitch(int index) {
    
    
       String name = "";
        switch (index) {
    
    
            case 0:
                name = "This index is 0";
                break;
            case 1:
                name = "This index is 1";
                break;
            case 2:
                name = "This index is 2";
                break;
            case 3:
                name = "This index is 3";
                break;
            default:
                name = "This index is 100";
        }
        return  name;
    }

The above is a regular switch branch, and the following is an irregular branch.

.method public testSwitch(I)Ljava/lang/String;
    .registers 3
    .param p1, "index"    # I
 
    .prologue
    .line 27
    const-string v0, ""
 
    .line 28
    .local v0, "name":Ljava/lang/String;
    sparse-switch p1, :sswitch_data_14
 
    .line 42
    const-string v0, "This index is 100"
 
    .line 44
    :goto_7          #所有case的出口
    return-object v0
 
    .line 30
    :sswitch_8       #case 5
    const-string v0, "This index is 0"
 
    .line 31
    goto :goto_7    #跳转到goto_7标号处
 
    .line 33
    :sswitch_b        #case 10
    const-string v0, "This index is 1"
 
    .line 34
    goto :goto_7
 
    .line 36
    :sswitch_e        #case 15
    const-string v0, "This index is 2"
 
    .line 37
    goto :goto_7
 
    .line 39
    :sswitch_11        #case 25
    const-string v0, "This index is 3"
 
    .line 40
    goto :goto_7
 
    .line 28
    :sswitch_data_14
    .sparse-switch              #case区域
        0x5 -> :sswitch_8       #case5
        0xa -> :sswitch_b       #case10
        0xf -> :sswitch_e       #case15
        0x19 -> :sswitch_11     #case25
    .end sparse-switch
.end method
直接查看sswitch_data_14标号处的内容,可以看到“.sparse-switch”指令并没有给出初始化case的值,所有的case值都使用"case值->case标号"的形式给出,此处共有4case,它们的内容都是构造一个字符串,然后跳转到goto_7标号处,加码架构上与packed-switch方式的switch分支一样。

Organized into java code as follows:

public String testSwitch(int index) {
    
    
       String name = "";
        switch (index) {
    
    
            case 5:
                name = "This index is 0";
                break;
            case 10:
                name = "This index is 1";
                break;
            case 15:
                name = "This index is 2";
                break;
            case 25:
                name = "This index is 3";
                break;
            default:
                name = "This index is 100";
        }
        return  name;
    }

2) try/catch statement

.method public test(I)V
    .registers 7
    .param p1, "index"    # I
 
    .prologue
    .line 20
    const/4 v3, 0x7
 
    new-array v2, v3, [I
 
    fill-array-data v2, :array_24
 
    .line 23
    .local v2, "numbers":[I
    const/4 v1, 0x0
 
    .local v1, "i":I
    :goto_7
    :try_start_7           #第一个try开始
    array-length v3, v2
    :try_end_8             #第一个try结束
    .catch Ljava/lang/IndexOutOfBoundsException; {
    
    :try_start_7 .. :try_end_8} :catch_11  #指定处理到的异常类型和catch的标号
    .catch Ljava/lang/IllegalArgumentException; {
    
    :try_start_7 .. :try_end_8} :catch_1a
 
    if-ge v1, v3, :cond_19
 
    .line 24
    const/16 v3, 0xa
 
    if-ne v1, v3, :cond_e
 
    .line 23
    :cond_e
    add-int/lit8 v1, v1, 0x1
 
    goto :goto_7
 
    .line 28
    :catch_11
    move-exception v0
 
    .line 29
    .local v0, "e":Ljava/lang/IndexOutOfBoundsException;
    const-string v3, "log"
 
    const-string v4, "\u6570\u7ec4\u8d8a\u754c\uff01"    #数组越界!
 
    invoke-static {
    
    v3, v4}, Landroid/util/Log;->i(Ljava/lang/String;Ljava/lang/String;)I
 
    .line 33
    .end local v0    # "e":Ljava/lang/IndexOutOfBoundsException;
    :cond_19
    :goto_19
    return-void
 
    .line 30
    :catch_1a
    move-exception v0
 
    .line 31
    .local v0, "e":Ljava/lang/IllegalArgumentException;
    const-string v3, "log"
 
    const-string v4, "index\u4e0d\u80fd\u662fString\u7c7b\u578b\uff01"   #index不能是String类型!
 
    invoke-static {
    
    v3, v4}, Landroid/util/Log;->i(Ljava/lang/String;Ljava/lang/String;)I
 
    goto :goto_19
 
    .line 20
    nop
 
    :array_24
    .array-data 4
        0x1
        0x4
        0x5
        0x6
        0x3
        0x22
        0x285
    .end array-data
.end method

      The try statement block in the code is marked with a label starting with try_start_ and ends with a label starting with try_end_. In this example, there is only one try statement block, which captures two exceptions. When using multiple try statement blocks, the value after the label name increases in sequence.

       Use the "catch" instruction under the try_end_8 label to specify the exception type to be processed and the catch label. The format is as follows:

.catch<异常类型>{
    
    <try起始标号>...<try结束标号>}<catch标号>

      For the Chinese characters in the code, Unicode is used for encoding during decompilation. Therefore, it is necessary to use relevant encoding conversion tools for conversion before reading.

Organized into java code:

public void test(int index){
    
    
        int[] numbers = {
    
    1,4,5,6,3,34,645};
 
        try {
    
    
            for(int i=0;i<numbers.length;i++){
    
    
                if(i==10){
    
    
 
                }
            }
        }catch (IndexOutOfBoundsException e){
    
    
            Log.i("log","数组越界!");
        }catch (IllegalArgumentException e){
    
    
            Log.i("log","index不能是String类型!");
        }
    }

 

 

Reprint: https://blog.csdn.net/qq_32113133/article/details/85163277

Guess you like

Origin blog.csdn.net/gqg_guan/article/details/134662029