smali語法中的疑惑與易混淆點

最近在完成移動智能終端安全的課程實驗時研究了一下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

1.類和方法中的變量

  • 方法內的變量寄存器有兩種
    • 寄存參數使用p寄存器,形如p0,p1,用.param定義,且從p1開始計數,由於p0爲非靜態方法自動建立的寄存器,存儲着this,也就是該方法所屬類的實例自己
    • 寄存方法中的內部變量使用v寄存器,形如v0,v1,用.locals在方法開頭表示所需的內部變量數,在聲明變量時用.local ...聲明。值得一提的是方法中的內部類也屬於方法的內部變量,從上述例子中能夠看出這種狀況下使用new instance ...聲明而非.local ...
  • 至於類中的域則用.field表示

2.方法調用指令的區別與後置寄存器的做用

  • invoke指令最經常使用的爲invoke-virtualinvoke-directinvoke-staticinvoke-super
    • invoke-virtual表示android原生api,即不是你寫的方法
    • invoke-direct表示因你而產生的方法,包含你定義的類的構造器
    • invoke-superinvoke-static比上述二者具備更高優先級,當屬於靜態或父類方法時,優先使用invoke-staticinvoke-static而非 invoke-staticinvoke-static
  • invoke-virtual {v0, p0~pn}...中,v0存有該方法所屬的類,p0至pn表示該方法須要的傳參,...表示v0所存類與該方法的聯繫

3.變量操做指令的區別與後置寄存器的做用

  • s開頭的指令表示對靜態變量的指令符,普通的指令符則用i開頭
  • 若是iget-object v0, p0,...的源代碼爲a = b,則v0存有b,p0存有a所在的類的實例,...爲p0到a的聯繫

4.內部類的特殊格式

  • 若是B爲A的內部類,則B在Smali中的表示爲一個單獨的類文件,而且ide

    • 在A中B的命名方式爲A$B,若是B爲匿名內部類,好比OnClickListener,則B的命名方式爲A$1
    • 在B中會有一個類型爲A的域,該域的命名方式爲.field final synthetic this$0,而且在B的構造器中需傳入一個A的實例,JVM用這種方法保證內部類對外部類的持有
  • 在B中調用A的外部方法,調用指令統一採用invoke-static,方法名會採用acces$000acces$100......之類的命名形式,而且方法內的參數裏會默認添加一個外部類ui

相關文章
相關標籤/搜索