一、前言
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:
https://source.android.com/devices/tech/dalvik/dalvik-bytecode
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項目源碼。
推薦閱讀: