用 Smali 手寫一個可運行的 HelloWorld!!!

top

一、前言

Android 的 App 實際上并不是運行在 Java 虛擬機中,而是運行在 Dalvik 虛擬機中。Dalvik 虛擬機對 Java 虛擬機做了一些額外的優化,讓它更適用于移動設備。而 Dalvik 也有自己獨特的匯編語言,Dalvik 就是通過這些匯編的指令集,來運行我們編譯好的 Apk 程序。

一般這些內容,我們正常開發 App 是接觸不到的,但是如果你有反編譯的需求,那你就需要花點時間研究一下它。本文不會介紹 Dalvik 的匯編指令集,它本身已經有完備的文檔,沒什么好說的。

本文就從逆向思維的路子,教你如何寫一個可在 Dalvik 上獨立運行的 Hello World 程序。

在這個過程中,我們需要了解 smali 語法,smali 是一種寬松的 Jasmin/dedexer 語法,它可以通過 baksmali 將我們已經編譯好的 dex 格式的匯編語言,反匯編成 smali 文件,供我們閱讀。

那么,我們的第一個 Dalvik 版本的 Hello World ,就從一個編寫一個 smali 文件開始吧。

二、開始編寫 Smali

既然是 smali 文件,當然是以 .smali 為文件后綴,這里先創建一個 SmaliHello.smali 文件,直接上代碼,再來看每行的含義。

smali-code

第 1~3 行,實際上是聲明了 smali 文件的頭,每個 smali 文件都會有它們。.class 表示類名,這里定義了一個 public 的類,全類名是 com.cxmyDev.smalidemo.SmaliHelo.super 表示它的父類,這里是 Object。.source 表示它對應的 Java 文件的文件名,這只是個標記,實際上在真實反編譯的場景下,如果代碼被混淆了,.source 可能會沒有值。

第 6 行,定義了一個 # direct methos ,它是 baksmali 為我們添加的一行注釋,表示之后緊跟著這個類相對應的方法,需要注意的是,只會包含構造方法和靜態方法,這里不展開討論了。

第 7~14 行,以一個 .method 開始,.end 結尾,表示它是一個方法,而 publi constructor 表示它是一個公有的構造方法,這里其實就是 Java 類默認的構造方法,如果我們不聲明構造方法,編譯器會為我們創建一個?無參的構造方法,這里就是它了。沒啥好說的,直接寫就好了。

第 16~28 行,它也是一個方法,public static 表示它是一個公有的靜態方法,方法名是 main。而之后緊跟的 ([LJava/lang/String;])V 表示它需要傳遞一個 String 數組,并且返回值是 void。再來看看方法內部的代碼,第 17 行,.registers 表示寄存器的聲明,這里聲明了 3 個寄存器,供后面使用,.param 表示了方法傳遞的參數,參數名叫 args ,并且是一個 String 數組類型。.prologue 表示一個開場,之后跟隨的才是我們業務邏輯的代碼。

第 21 行,sget-object 表示創建了一個 PrintStream 對象,并存入 v0 寄存器中。

第 23 行,const-string 表示什么了一個字符串 "Hello CxmyDev!",并存入 v1 寄存器中。

第 25行,invoke-virtual 表示調用了 PrintStream 中的 printIn() 方法,參數傳遞的是 v1 寄存器中的值,就是之前存儲的 "Hello CxmyDev!"。

到這里,smali 中的代碼,我們已經逐行認清楚它是干嘛的了,有些細節就不展開講了,不了解的可以看看 Dalvik 的語法和 smali 的語法,有興趣可以先看看這兩個鏈接。

Dalvik-bytecode:

https://source.android.com/devices/tech/dalvik/dalvik-bytecode

Dex 格式:

https://source.android.com/devices/tech/dalvik/dex-format

三、編譯 smali

編寫完 smali 代碼之后,接下來就要將它編譯成 dex 文件了,這就需要用到 smali.jar 這個工具。你可以在 Bitbucket 上直接下載到 jar 包。

https://bitbucket.org/JesusFreke/smali/downloads/

smali.jar 最新的版本版本是 2.2.1,所以這里下載這個版本就可以了。(不方便下載的話,文末有下載方式)

先來看看 smali.jar 的幫助文檔,直接使用 java -jar 命令即可。

smalijar-help

我們這里主要會用到它的 assemble 命令,再來看看 assemble 的幫助文檔,使用 java -jar snali.jar a 命令即可查看,aassemble 的縮寫。

smalijar-a-help

可以看到,使用 -o 就可以指定輸出的 dex 文件,然后再指定編譯的 smali 文件即可。

java -jar smali-2.2.1.jar a -o hello.dex SmaliHello.smali 

執行完成,如果沒有報錯的話,可以在當前目錄下,生成一個 hello.dex 文件。如果有其它輸出,應該就是報錯了,查看一下報錯信息解決它就好了。

得到 hello.dex 文件之后,我們還需要將它放到我們的 Android 設備上,才可以運行,這個非常簡單,使用 adb push 命令即可。

最終運行這個 dex 文件,還需要使用到 dalvikvm ,使用 adb shell dalvikvm -h 命令,查看幫助文檔,文檔比較長,這里截取關鍵部分。

dalvik-help

這里我們主要是使用 -cp 指定 classpath 即可執行,它后續接收的是類的完整簽名,包含包名。

然后我們就需要使用 dalvikvm -cp 命令即可執行,主要指定要執行的類,需要包含包名的全類名。

smali-run

這里,就可以輸出我們之前編寫的 Hello CxmyDev! 了。

下面備份一下輸入的命令。

adb shell push hello.dex /sdcard/
adb shell dalvikvm -cp /sdcard/hello.dex com.cxmydev.smalidemo.SmaliHello 

四、Dex 的 Java 代碼

到這里就算是將清楚,從零編寫一個 smali 代碼,到編譯成 dex 并成功執行的所有過程了。

我們再來看看,我們編輯的 smali 代碼,到底用 Java 代碼編寫,是什么內容,可以幫助我們更好的理解它。

其實很簡單,再使用 jadx 工具,對 dex 進行反編譯。因為我們這里也不涉及混淆,所以代碼結構非常的清晰。

java-code

這里就是初學 Java 的時候,一個標準的 Java 程序,有個 main 函數為程序的入口函數。

五、小結

本文到這里就算是完成了整個逆向的反逆向流程,相信能讓你加深對反編譯和 smali 的理解。

有些工具如果不方便下載(原因你懂的),可以在承香墨影公眾號回復 smali工具 進行下載,可以下載到本文所有涉及到的資源文件。

更多反編譯的細節,可以在承香墨影公眾號回復 Android反編譯,你將獲得我整理好的一些關于反編譯的資料。

今天在承香墨影公眾號的后臺,回復 成長。我會送你一些我整理的學習資料,包含:Android反編譯、算法、設計模式、Web項目源碼。

推薦閱讀:

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容