你們都應該知道APK文件其實就是一個MIME爲ZIP的壓縮包,咱們修改ZIP後綴名方式能夠看到內部的文件結構,例如修改後綴後用RAR打開鱷魚小頑皮APK能看到的是(Google Play下載的完整版版本): java
Where's My Water.zip\ android
可有可無地注:asset和res資源目錄的不一樣在於: 數組
1. res目錄下的資源文件在編譯時會自動生成索引文件(R.java),在Java代碼中用R.xxx.yyy來引用;而asset目錄下的資源文件不須要生成索引,在Java代碼中須要用AssetManager來訪問; 架構
2. 通常來講,除了音頻和視頻資源(須要放在raw或asset下),使用Java開發的Android工程使用到的資源文件都會放在res下;使用C++遊戲引擎(或使用Lua binding等)的資源文件均須要放在asset下。 app
由於Where's My Water是使用迪斯尼公司自家的DMO遊戲引擎開發,因此遊戲中用到的全部資源文件都存放在asset下,除了應用圖標這些資源仍須要放在res下。 框架
Dalvik是google專門爲Android操做系統設計的一個虛擬機,通過深度的優化。雖然Android上的程序是使用java來開發的,可是Dalvik和標準的java虛擬機JVM仍是兩回事。Dalvik VM是基於寄存器的,而JVM是基於棧的;Dalvik有專屬的文件執行格式dex(dalvik executable),而JVM則執行的是java字節碼。Dalvik VM比JVM速度更快,佔用空間更少。 eclipse
經過Dalvik的字節碼咱們不能直接看到原來的邏輯代碼,這時須要藉助如Apktool或dex2jar+jd-gui工具來幫助查看。可是,注意的是最終咱們修改APK須要操做的文件是.smali文件,而不是導出來的Java文件從新編譯(何況這基本上不可能)。 函數
好了,對Dalvik有必定認識後,下面介紹重點:smali,及其語法。 工具
簡單的說,smali就是Dalvik VM內部執行的核心代碼。它有本身的一套語法,下面即將介紹,若是有JNI開發經驗的童鞋則可以很快明白。 佈局
1、smali的數據類型
在smali中,數據類型和Android中的同樣,只是對應的符號有變化:
這裏解析下最後兩項,數組的表示方式是:在基本類型前加上前中括號「[」,例如int數組和float數組分別表示爲:[I、[F;對象的表示則以L做爲開頭,格式是LpackageName/objectName;(注意必須有個分號跟在最後),例如String對象在smali中爲:Ljava/lang/String;,其中java/lang對應java.lang包,String就是定義在該包中的一個對象。
或許有人問,既然類是用LpackageName/objectName;來表示,那類裏面的內部類又如何在smali中引用呢?答案是:LpackageName/objectName$subObjectName;。也就是在內部類前加「$」符號,關於「$」符號更多的規則將在後面談到。
2、函數的定義
函數的定義通常爲:
Func-Name (Para-Type1Para-Type2Para-Type3...)Return-Type
注意參數與參數之間沒有任何分隔符,一樣舉幾個例子就容易明白了:
1. foo ()V
沒錯,這就是void foo()。
2. foo (III)Z
這個則是boolean foo(int, int, int)。
3. foo (Z[I[ILjava/lang/String;J)Ljava/lang/String;
看出來這是String foo (boolean, int[], int[], String, long) 了嗎?
3、smali文件內容具體介紹
下面開始進一步分析smali中的具體例子,取鱷魚小頑皮中的WMWActivity.smali來分析(怎麼得到請參考下一節的APK反編譯之二:工具介紹,暫時先介紹smali語法),它的內容大概是這樣子的:
smali中的函數和成員變量同樣也分爲兩種類型,可是不一樣成員變量中的static和instance之分,而是direct和virtual之分。那麼direct method和virtual method有什麼區別呢?直白地講,direct method就是private函數,其他的public和protected函數都屬於virtual method。因此在調用函數時,有invoke-direct,invoke-virtual,另外還有invoke-static、invoke-super以及invoke-interface等幾種不一樣的指令。固然其實還有invoke-XXX/range 指令的,這是參數多於4個的時候調用的指令,比較少見,瞭解下便可。
(1)、invoke-static:顧名思義就是調用static函數的,由於是static函數,因此比起其餘調用少一個參數,例如:
這裏注意到invoke-static後面有一對大括號「{}」,實際上是調用該方法的實例+參數列表,因爲這個方法既不需參數也是static的,因此{}內爲空,再看一個例子:
(2)、invoke-super:調用父類方法用的指令,在onCreate、onDestroy等方法都能看到,略。
(3)、invoke-direct:調用private函數的,例如:
這裏GlobalPurchaseHandler getGlobalIapHandler()就是定義在WMWActivity中的一個private函數,若是修改smali時錯用invoke-virtual或invoke-static將在回編譯後程序運行時引起一個常見的VerifyError(更多錯誤彙總可參照APK反編譯之番外三:常見錯誤彙總)。
(4)、invoke-virtual:用於調用protected或public函數,一樣注意修改smali時不要錯用invoke-direct或invoke-static,例子:
有人也許注意到,剛纔看到的例子都是「調用函數」這個操做而已,貌似沒有取函數返回的結果的操做?
在Java代碼中調用函數和返回函數結果是一條語句完成的,而在smali裏則須要分開來完成,在使用上述指令後,若是調用的函數返回非void,那麼還須要用到move-result(返回基本數據類型)和move-result-object(返回對象)指令:
v2保存的則是調用String.length()返回的整型。
下面開始介紹函數實體,其實沒有什麼特別的地方,只是在植入代碼時有一點須要特別注意,舉例說明: