1. 程式人生 > >Dalvik 指令集練習 及 smali檔案的編寫

Dalvik 指令集練習 及 smali檔案的編寫

一、實驗題目
1.輸出“Hello Xidian”字串
2.編寫smali檔案,計算(7+5)*(7-5)並輸出結果
二、smali檔案的編寫
1.HelloXidian.smali的編寫
smali 是一種寬鬆的 Jasmin/dedexer 語法,它可以通過 baksmali 將我們已經編譯好的 dex 格式的組合語言,反彙編成 smali 檔案,供我們閱讀。
HelloXidian.smali的程式碼如下:

   .class public LHelloXidian;  
    .super Ljava/lang/Object;  
    .method public static main([Ljava/lang/String;)V  
        .registers 2  
        sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;  
        const-string v1, "HelloXidian!"  
        invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V  
        return-void  
    .end method 

Smali檔案的前三行聲明瞭smali檔案的頭,每個smali檔案都會有它們。
.class表示類名,這裡定義了一個 public 的類,全類名是HelloXidian。
.super表示它的父類,這裡是Object。
.source表示它對應的Java檔案的檔名,這只是個標記,實際上在真實反編譯的場景下,如果程式碼被混淆了,.source可能會沒有值。在編寫的HelloXidian.smali中,這個smali檔案是直接手動編寫的,沒有經歷從java程式碼演化的過程,故沒有.source。
第 3-9 行,以一個.method開始,.end結尾,表示它是一個方法。public static表示它是一個公有的靜態方法,方法名是 main。而之後緊跟的([LJava/lang/String;])V表示它需要傳遞一個String陣列,並且返回值是void。[ 型別可以表示基本型別的陣列。[ 後面緊跟基本型別描述符,如 [I 表示一個整形一維陣列,相當於Java中的int[],多個 [ 在一起表示多維陣列,如 [[I 表示int[][],[[[I 表示int[][][]。L與 [ 可以同時使用用來表示物件陣列,如 [Ljava/lang/String; 就是Java中的字串陣列。Dalvik位元組碼有兩種型別,基本型別和引用型別。物件和陣列是引用型別,其它都是基本型別。其資料型別除boolean型別是由大寫的“Z”表示之外,剩下都是是由Java基本資料型別的首字母大寫表示。 L型別可以表示Java型別中的任何類。在Dalvik中,這些類以Lpackage/name/ObjectName;形式表示,最後有個分號,L表示後面跟著一個Java類。package/name表示物件所在的包,ObjectName表示物件的名稱,最後的分號表示物件名結束。例如:Ljava/lang/String相當於java.lang.String。
第4行,.registers表示暫存器的宣告,這裡聲明瞭 2個暫存器,供後面使用。第5 行,sget-object 表示建立了一個 PrintStream 物件,並存入 v0 暫存器中。第6行,const-string 表示什麼了一個字串 “Hello CxmyDev!”,並存入 v1 暫存器中。第7行,invoke-virtual 表示呼叫了 PrintStream 中的 printIn() 方法,引數傳遞的是 v1 暫存器中的值,就是之前儲存的 “HelloXidian! ----16180120002”。
2.Testsmali.smali的編寫
Android程式設計師用Java語言開發APP,編譯工具會將Java原始檔(.java)編譯成Dalvik可執行檔案(.dex)。Android系統中Dalvik Virtual Machine 會執行該檔案。smali/baksmali則是Dalvik VM可執行檔案的彙編器/反彙編器。反彙編Dalvik可執行檔案(.dex)後,將會得到.smali字尾檔案。smali程式碼擁有特定的語法。
相比於.dex檔案,smali檔案的語法更容易理解些。下面是.dex檔案、.smali檔案、.java檔案三者之間的轉化關係。
在這裡插入圖片描述


smali檔案的語法相對複雜,所以我打算先編寫java檔案,通過轉化來得到smali檔案。
三、實驗操作過程及實驗結果
實驗一
在SDK的tools資料夾下,新建文字檔案,命名為HelloXidian.smali,輸入程式碼:
.class public LHelloXidian;
.super Ljava/lang/Object;
.method public static main([Ljava/lang/String;)V
.registers 2
sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
const-string v1, “HelloXidian! ----16180120002”
invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
return-void
.end method
開啟命令提示符視窗,進入到SDK下的tools資料夾
在這裡插入圖片描述

輸入命令: java –jar smali-2.1.0.jar –o classes.dex HelloXidian.smali
當執行成功時,會在當前目錄下生成classes.dex檔案。
在這裡插入圖片描述
在這裡插入圖片描述
將classes.dex檔案打包為HelloXidian.zip檔案,放到當前目錄下。
在這裡插入圖片描述
至此,smali檔案編譯完畢,接下來啟動Android執行環境,我打開了Android Studio的模擬器。
在命令提示符視窗中執行以下命令(手機要ROOT):
adb push HelloXidian.zip /data/local
adb shell dalvikvm –cp /data/local/HelloXidian.zip HelloXidian
在這裡插入圖片描述
實驗二
新建文字,改名為Test2.java,輸入使用java語言編寫的程式碼。
程式碼如下:
public class Test2
{
public static void main(String[] args)
{
int sum;
int a=7,b=5;
sum=(a+b)(a-b);
System.out.println("The result of (7+5)
(7-5) is "+sum);
}
}
為了方便起見,將Test2.java與baksmali-2.1.0.jar和dx.jar放在同一路徑下。
在這裡插入圖片描述
編譯java程式碼為class檔案,開啟命令提示符,命令執行成功後會生成Test2.class檔案。
執行命令:javac Test2.java
然後執行命令 java Test2 可以輸出計算結果。
在這裡插入圖片描述
至此,我們已經得到計算結果,接下來就來通過java程式碼轉換成Test2的smali程式碼。
把class檔案轉成dex檔案,apk包裡java程式碼最後生成的是class.dex檔案,把class轉化成dex檔案就需要用到android SDK提供的一個工具dx,所以我已經提前將這個包一同放在了同一路徑下。
執行命令:java -jar dx.jar --dex --output=Test2.dex Test2.class
在這裡插入圖片描述
命令分別執行成功,資料夾中依次出現Test2.class和Test2.dex檔案。
在這裡插入圖片描述
下面需要把dex轉化成smali檔案,這時會使用到工具baksmali,同樣已經提前放進資料夾裡了。當以下命令執行成功後,會生成一個out目錄,在out目錄下的Test2.smali就是我們要的smali程式碼了。
命令:java -jar baksmali-2.1.0.jar Test2.dex
在這裡插入圖片描述
在這裡插入圖片描述
在這裡插入圖片描述
使用notepad++開啟Test2.smali,得到smali程式碼。
我發現這裡的.source “Test2.java”,驗證了前文中所提到的“.source表示它對應的Java檔案的檔名”而實驗一中的smali檔案由於是手工編寫的,所以.source為空值。
在這裡插入圖片描述
在這裡插入圖片描述
使用命令 java Test2 輸出結果
在這裡插入圖片描述
至此,第二部分的實驗就完成了。不過我結合生成的程式碼和網上的一些資料以及第一個實驗的啟發,自己又寫了一份Test3.smali。
在這裡插入圖片描述
接下來的操作和實驗一一樣,這裡就不再贅述了。
在這裡插入圖片描述
經過驗證,自己手寫的smali程式碼執行得到的結果也是正確的。
因為這個實驗不復雜,所以smali程式碼手寫也是可以實現的,但是對於邏輯複雜的程式設計,使用java書寫更為合適,所以掌握由java檔案轉化成smali檔案的方法是很有必要的。

四、遇到的問題及解決方法
1.
遇到問題:
當輸入命令: java –jar smali.jar –o classes.dex HelloXidian.smali時,首先需要注意smali.jar的版本號,我最初的是smali-2.2.5.jar,出現了報錯。
在這裡插入圖片描述
解決方法:
這是由於版本的問題導致的,當我換成smali-2.1.0.jar再次執行此命令時,可以順利執行。
2.
遇到問題:
出現報錯 permission denied。
在這裡插入圖片描述
解決方法:
使用命令adb root即可解決。因為手機需要root,沒有root獲取不到許可權刪除某些目錄的檔案。

五、原始碼
HelloXidian.smali

    .class public LHelloXidian;  
    .super Ljava/lang/Object;  
    .method public static main([Ljava/lang/String;)V  
        .registers 2  
        sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;  
        const-string v1, "HelloXidian!  ----16180120002"  
        invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V  
        return-void  
.end method  

Test2.java

public class Test2
{
  public static void main(String[] args)
  {
                 int sum;
	             int a=7,b=5;
				 sum=(a+b)*(a-b);
	 System.out.println("The result of (7+5)*(7-5) is "+sum);
   }
}

Test3.smali

.class public LTest3;
.super Ljava/lang/Object;
#主函式

    .method public static main([Ljava/lang/String;)V
        .registers 5
    
        const/16 v0, 0x07
        const/16 v1, 0x05
    
        #兩個引數相加,值存在v3
        add-int v2,v0,v1
    	#兩個引數相減,值存在v4
        sub-int v3,v0,v1
    	#兩個引數相乘,值存在v3
        mul-int v2,v2,v3
        #將v3的值輸出
        sget-object v4, Ljava/lang/System;->out:Ljava/io/PrintStream;
    invoke-virtual {v4, v2}, Ljava/io/PrintStream;->println(I)V
    
        return-void
    .end method