最近在完成移動智能終端安全的課程實驗時研究了一下smali的語法,順便記錄一下幾個比較具備迷惑性的地方 好比如下兩行代碼:java
invoke-virtual {p0, v0}, Lcom/example/smali/MainActivity;->setContentView(I)V
複製代碼
iput-object v0, p0, Lcom/example/smali/MainActivity;->tv:Landroid/widget/TextView;
複製代碼
在這兩行代碼中,寄存器v0和p0到底是拿來幹什麼的?android
個人研究方法是:先寫一段java源代碼,把它反編譯成smali文件,研究二者對應的關係,若是有發現結果,還能夠適當的更改源代碼以驗證本身的結論。這樣的優勢是結論是本身發現的,印象會更深入;缺點是頻繁的反編譯比較繁瑣,要是在AS上有即時的反編譯插件就行了。 話很少說,源代碼以下,一個按鈕註冊點擊事件,點擊後改變TextView內容的demo:api
public class MainActivity extends AppCompatActivity {
TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv = findViewById(R.id.tv);
Button btn = findViewById(R.id.btn);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
tv.setText("Hello Xidian!");
}
});
}
複製代碼
對應的smali代碼以下:安全
.class public Lcom/example/smali/MainActivity;
.super Landroid/support/v7/app/AppCompatActivity;
.source "MainActivity.java"
.field tv:Landroid/widget/TextView;
.method public constructor <init>()V #MainActivity的構造器
.locals 0
invoke-direct {p0}, Landroid/support/v7/app/AppCompatActivity;-><init>()V
return-void .end method
.method protected onCreate(Landroid/os/Bundle;)V #重寫的onCreate方法
.locals 2
.param p1, "savedInstanceState"
invoke-super {p0, p1}, Landroid/support/v7/app/AppCompatActivity;->onCreate(Landroid/os/Bundle;)V
const v0, 0x7f09001c
invoke-virtual {p0, v0}, Lcom/example/smali/MainActivity;->setContentView(I)V #setContentView()
const v0, 0x7f07008e
invoke-virtual {p0, v0}, Lcom/example/smali/MainActivity;->findViewById(I)Landroid/view/View;
move-result-object v0
check-cast v0, Landroid/widget/TextView; #強制類型轉換
iput-object v0, p0, Lcom/example/smali/MainActivity;->tv:Landroid/widget/TextView; #tv的findViewById
const v0, 0x7f070022
invoke-virtual {p0, v0}, Lcom/example/smali/MainActivity;->findViewById(I)Landroid/view/View;
move-result-object v0
check-cast v0, Landroid/widget/Button; #btn的findViewById
.local v0, "btn":Landroid/widget/Button;
new-instance v1, Lcom/example/smali/MainActivity$1; #new了一個OnClickListener,它是一個匿名內部類
invoke-direct {v1, p0}, Lcom/example/smali/MainActivity$1;-><init>(Lcom/example/smali/MainActivity;)V
invoke-virtual {v0, v1}, Landroid/widget/Button;->setOnClickListener(Landroid/view/View$OnClickListener;)V
return-void .end method
複製代碼
比對後得出的幾個結論就直接寫了:app
.param
定義,且從p1開始計數,由於p0
爲非靜態方法自動建立的寄存器,存儲着this,也就是該方法所屬類的實例自己.locals
在方法開頭表示所需的內部變量數,在聲明變量時用.local ...
聲明。值得一提的是方法中的內部類也屬於方法的內部變量,從上述例子中能夠看出這種狀況下使用new instance ...
聲明而非.local ...
。.field
表示invoke-virtual
,invoke-direct
和invoke-static
,invoke-super
,
invoke-virtual
表示android原生api,即不是你寫的方法invoke-direct
表示因你而產生的方法,包含你定義的類的構造器invoke-super
和invoke-static
比上述二者具備更高優先級,當屬於靜態或父類方法時,優先使用invoke-static
或invoke-static
而非 invoke-static
和invoke-static
invoke-virtual {v0, p0~pn}...
中,v0存有該方法所屬的類,p0至pn表示該方法須要的傳參,...表示v0所存類與該方法的聯繫iget-object v0, p0,...
的源代碼爲a = b
,則v0存有b,p0存有a所在的類的實例,...爲p0到a的聯繫若是B爲A的內部類,則B在Smali中的表示爲一個單獨的類文件,而且ide
A$B
,若是B爲匿名內部類,好比OnClickListener,則B的命名方式爲A$1
.field final synthetic this$0
,而且在B的構造器中需傳入一個A的實例,JVM用這種方法保證內部類對外部類的持有在B中調用A的外部方法,調用指令統一採用invoke-static
,方法名會採用acces$000
,acces$100
......之類的命名形式,而且方法內的參數裏會默認添加一個外部類ui