一、前言
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 檔案,直接上程式碼,再來看每行的含義。
第 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:
source.android.com/devices/tec…
Dex 格式:
三、編譯 smali
編寫完 smali 程式碼之後,接下來就要將它編譯成 dex 檔案了,這就需要用到 smali.jar 這個工具。你可以在 Bitbucket 上直接下載到 jar 包。
smali.jar 最新的版本版本是 2.2.1,所以這裡下載這個版本就可以了。(不方便下載的話,文末有下載方式)
先來看看 smali.jar 的幫助文件,直接使用 java -jar
命令即可。
我們這裡主要會用到它的 assemble
命令,再來看看 assemble 的幫助文件,使用 java -jar snali.jar a
命令即可檢視,a
是 assemble
的縮寫。
可以看到,使用 -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
命令,檢視幫助文件,文件比較長,這裡擷取關鍵部分。
這裡我們主要是使用 -cp
指定 classpath 即可執行,它後續接收的是類的完整簽名,包含包名。
然後我們就需要使用 dalvikvm -cp
命令即可執行,主要指定要執行的類,需要包含包名的全類名。
這裡,就可以輸出我們之前編寫的 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 的時候,一個標準的 Java 程式,有個 main 函式為程式的入口函式。
五、小結
本文到這裡就算是完成了整個逆向的反逆向流程,相信能讓你加深對反編譯和 smali 的理解。
有些工具如果不方便下載(原因你懂的),可以在承香墨影公眾號回覆 smali工具 進行下載,可以下載到本文所有涉及到的資原始檔。
更多反編譯的細節,可以在承香墨影公眾號回覆 Android反編譯,你將獲得我整理好的一些關於反編譯的資料。
今天在承香墨影公眾號的後臺,回覆 成長。我會送你一些我整理的學習資料,包含:Android反編譯、演算法、設計模式、Web專案原始碼。
推薦閱讀: