詳解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