1. 程式人生 > >詳解smali檔案

詳解smali檔案

詳解smali檔案

上面我們介紹了Dalvik的相關指令,下面我們則來認識一下smali檔案.儘管我們使用java來寫Android應用,但是Dalvik並不直接載入.class檔案,而是通過dx工具將.class檔案優化成.dex檔案,然後交由Dalvik載入.這樣說來,我們無法通過分析.class來直接分析apk檔案,而是需要藉助工具baksmali.jar反編譯dex檔案來獲得對應smali檔案,smali檔案可以認為是Davilk的位元組碼檔案,但是並兩者並不完全等同.通過baksmali.jar反編譯出來每個.smali,都對應與java中的一個類,每個smali檔案都是Davilk指令組成的,並遵循一定的結構.smali存在很多的指令用於描述對應的java檔案,所有的指令都以”.”開頭,常用的指令如下:

關鍵詞 說明
filed 定義欄位
.method…end method 定義方法
.annotation…end annotation 定義註解
.implements 定義介面指令
.local 指定了方法內區域性變數的個數
.registers 指定方法內使用暫存器的總數
.prologue 表示方法中程式碼的開始處
.line 表示java原始檔中指定行
.paramter 指定了方法的引數
.param 和.paramter含義一致,但是表達格式不同

檔案頭描述

關鍵詞 說明
.class <訪問許可權修飾符> [非許可權修飾符] <類名>
.super <父類名>
.source <原始檔名稱>

<>中的內容表示必不可缺的,[]表示的是可選擇的.
訪問許可權修飾符即所謂的public,protected,private即default.而非許可權修飾符則指的是final,abstract.

關鍵詞 說明
.class public final Lcom/sbbic/demo/Device;
.super Ljava/lang/Object;
.source “Device.java”

檔案正文
在檔案頭之後便是檔案的正文,即類的主體部分,包括類實現的介面描述,註解描述,欄位描述和方法描述四部分.下面我們就分別看看欄位和方法的結構.(別忘了我們在Davilk中說過的方法和欄位的表示)

介面描述

關鍵詞 說明
.implements <介面名稱>
.implements Landroid/view/View$OnClickListener;

普通欄位:

訪問許可權修飾符相比各位已經非常熟了,而此處非許可權修飾符則可是final,volidate,transient.

關鍵詞 說明
.field <訪問許可權修飾符> [非許可權修飾符] <欄位名>:<欄位型別>
.field private TAG:Ljava/lang/String;

靜態欄位

需要注意:smali檔案還為靜態欄位,普通欄位分別新增#static field和#instan filed註釋.

關鍵詞 說明
.field <訪問許可權> static [修飾詞] <欄位名>:<欄位型別>
.field private static final pi:F = 3.14f

直接方法

重點解釋一下parameter:
parameter的個數和方法引數的數量相對應,即有幾個引數便有幾個.parameter,預設從1開始,即p1,p2,p2….
熟悉java的童鞋一定會記得該型別的方法有個預設的引數指向當前物件,在smali中,方法的預設物件引數用p0表示.
直接方法即所謂的direct methods,還記的Davilk中方法呼叫指令invoke-direct麼。

#direct methods
.method <訪問許可權修飾符> [非訪問許可權修飾符] <方法原型>
      <.locals>
      [.parameter]
      [.prologue]
      [.line]
      <程式碼邏輯>
.end
# direct methods
.method public constructor <init>()V
    .registers 2

    .prologue
    .line 8
    invoke-direct {p0}, Landroid/app/Activity;-><init>()V

    .line 10
    const-string v0, "MainActivity"

    iput-object v0, p0, Lcom/social_touch/demo/MainActivity;->TAG:Ljava/lang/String;

    .line 13
    const/4 v0, 0x0

    iput-boolean v0, p0, Lcom/social_touch/demo/MainActivity;->running:Z

    return-void
.end method

虛方法

虛方法的定義會和直接方法唯一的不同就是註釋不同:#virtual methods,其格式如下:

#virtual methods
.method <訪問許可權> [修飾關鍵詞] <方法原想>
      <.locals>
      [.parameter1]
      [.parameter2]
      [.prologue]
      [.line]
      <程式碼邏輯>
.end

Activity類Java程式碼和Smali程式碼對比

下面以一個Activity類的Java程式碼

public class MainActivity extends Activity implements View.OnClickListener {

    private String TAG = "MainActivity";
    private static final float pi = (float) 3.14;

    public volatile boolean running = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    public void onClick(View view) {
        int result = add(4, 5);
        System.out.println(result);

        result = sub(9, 3);

        if (result > 4) {
            log(result);
        }
    }

    public int add(int x, int y) {
        return x + y;
    }

    public synchronized int sub(int x, int y) {
        return x + y;
    }

    public static void log(int result) {
        Log.d("MainActivity", "the result:" + result);
    }


}

以下是該類的smali程式碼

#檔案頭描述

.class public Lcom/social_touch/demo/MainActivity;
.super Landroid/app/Activity;#指定MainActivity的父類
.source "MainActivity.java"#原始檔名稱

#表明實現了View.OnClickListener介面
# interfaces
.implements Landroid/view/View$OnClickListener;

#定義float靜態欄位pi
# static fields
.field private static final pi:F = 3.14f

#定義了String型別欄位TAG
# instance fields
.field private TAG:Ljava/lang/String;

#定義了boolean型別的欄位running
.field public volatile running:Z

#構造方法,如果你還納悶這個方法是怎麼出來的化,就去看看jvm的基礎知識吧
# direct methods
.method public constructor <init>()V
    .locals 1#表示函式中使用了一個區域性變數

    .prologue#表示方法中程式碼正式開始
    .line 8#表示對應與java原始檔的低8行
    #呼叫Activity中的init()方法
    invoke-direct {p0}, Landroid/app/Activity;-><init>()V

    .line 10
    const-string v0, "MainActivity"

    iput-object v0, p0, Lcom/social_touch/demo/MainActivity;->TAG:Ljava/lang/String;

    .line 13
    const/4 v0, 0x0

    iput-boolean v0, p0, Lcom/social_touch/demo/MainActivity;->running:Z

    return-void
.end method

#靜態方法log()
.method public static log(I)V
    .locals 3
    .parameter "result"#表示result引數

    .prologue
    .line 42
    #v0暫存器中賦值為"MainActivity"
    const-string v0, "MainActivity"
    #建立StringBuilder物件,並將其引用賦值給v1暫存器
    new-instance v1, Ljava/lang/StringBuilder;

    #呼叫StringBuilder中的構造方法
    invoke-direct {v1}, Ljava/lang/StringBuilder;-><init>()V

    #v2暫存器中賦值為ther result:
    const-string v2, "the result:"

    #{v1,v2}大括號中v1暫存器中儲存的是StringBuilder物件的引用.
    #呼叫StringBuilder中的append(String str)方法,v2暫存器則是引數暫存器.
    invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;

    #獲取上一個方法的執行結果,此時v1中儲存的是append()方法執行後的結果,此處之所以仍然返回v1的    #原因在與append()方法返回的就是自身的引用
    move-result-object v1

    #繼續呼叫append方法(),p0表示第一個引數暫存器,即上面提到的result引數
    invoke-virtual {v1, p0}, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder;

    #同上
    move-result-object v1

    #呼叫StringBuilder物件的toString()方法
    invoke-virtual {v1}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;

    #獲取上一個方法執行結果,toString()方法返回了一個新的String物件,因此v1中此時儲存了String物件的引用
    move-result-object v1

    #呼叫Log類中的靜態方法e().因為e()是靜態方法,因此{v0,v1}中的成了引數暫存器
    invoke-static {v0, v1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I

    .line 43
    #呼叫返回指令,此處沒有返回任何值
    return-void
.end method


# virtual methods
.method public add(II)I
    .locals 1
    .parameter "x"#第一個引數
    .parameter "y"#第二個引數

    .prologue
    .line 34

    #呼叫add-int指令求和之後將結果賦值給v0暫存器
    add-int v0, p1, p2

    #返回v0暫存器中的值
    return v0
.end method


.method public onClick(Landroid/view/View;)V
    .locals 4
    .parameter "view" #引數view

    .prologue
    const/4 v3, 0x4 #v3暫存器中賦值為4

    .line 23#java原始檔中的第23行
    const/4 v1, 0x5#v1暫存器中賦值為5

    #呼叫add()方法
    invoke-virtual {p0, v3, v1}, Lcom/social_touch/demo/MainActivity;->add(II)I

    #從v0暫存器中獲取add方法的執行結果
    move-result v0

    .line 24#java原始檔中的24行
    .local v0, result:I

    #v1暫存器中賦值為PrintStream物件的引用out
    sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;

    #執行out物件的println()方法
    invoke-virtual {v1, v0}, Ljava/io/PrintStream;->println(I)V

    .line 26

    const/16 v1, 0x9#v1暫存器中賦值為9
    const/4 v2, 0x3#v2暫存器中賦值為3

    #呼叫sub()方法,{p0,v1,v2},p0指的是this,即當前物件,v1,v2則是引數
    invoke-virtual {p0, v1, v2}, Lcom/social_touch/demo/MainActivity;->sub(II)I
    #從v0暫存器中獲取sub()方法的執行結果
    move-result v0

    .line 28
    if-le v0, v3, :cond_0#如果v0暫存器的值小於v3暫存器中的值,則跳轉到cond_0處繼續執行

    .line 29

    #呼叫靜態方法log()
    invoke-static {v0}, Lcom/social_touch/demo/MainActivity;->log(I)V

    .line 31
    :cond_0
    return-void
.end method

.method protected onCreate(Landroid/os/Bundle;)V
    .locals 1
    .parameter "savedInstanceState" #引數savedInstancestate

    .prologue
    .line 17

    #呼叫父類方法onCreate()
    invoke-super {p0, p1}, Landroid/app/Activity;->onCreate(Landroid/os/Bundle;)V

    .line 18

    const v0, 0x7f04001a#v0暫存器賦值為0x7f04001a

    #呼叫方法setContentView()
    invoke-virtual {p0, v0}, Lcom/social_touch/demo/MainActivity;->setContentView(I)V

    .line 19
    return-void
.end method

#declared-synchronized表示該方法是同步方法
.method public declared-synchronized sub(II)I
    .locals 1
    .parameter "x"
    .parameter "y"

    .prologue
    .line 38

    monitor-enter p0#為該方法新增鎖物件p0
     add-int v0, p1, p2
    #釋放鎖物件
    monitor-exit p0

    return v0
.end method