❶ 如何反編譯android 的apk/dex/odex,獲得源碼
關於APK,DEX的介紹
當我們編譯一個安卓項目的時候,整個項目會被打包成一個 .apk文件。這個文件其實是一個標準的zip文件,因此可以用解壓縮工具打開。這個apk文件一般都包含程序的代碼(在classes.dex文件中), 資源文件, 證書, manifest 文件等。 其中對我們最重要的是classes.dex文件,因為編譯後的位元組碼(bytecode)都是放在這個文件中。我們後面講的反編譯就是針對這個dex文件來的。
反編譯普通的APK文件:
對於普通的APK/DEX文件的反編譯,其實工具有很多, 包括:
ByteCode Viewer: 一個可視化的集成工具,說實話,不太好用,不夠穩定,生成代碼質量中等。
dex2jar + jd_gui: 這兩個工具組合還可以, 用起來比ByteCode Viewer麻煩一些,但比較穩定,生成代碼質量中等。
在線反編譯工具JADX: http://www.javadecompilers.com/apk , 這是基於SourceForge上的JADX的開源工具來實現的。本來以為在線反編譯質量不會好,但出人意料的是:JADX是我發現的最好的反編譯工具, 不但使用簡單(直接上傳,轉換,下載就ok),而且反編譯出來的代碼質量很高,特別是變數命名方面,可讀性很不錯。
反編譯ODEX文件:
Android 5.0 Lollipop以後,Google用ART代替了以前的Dalvik,對於普通的app來說我們仍然可以用上面的方法來把dex文件反編譯成Java源代碼。但對於系統預裝的App,特別是類似應用商店,播放器等, 你會發現這些應用的apk文件中找不到對應的classes.dex文件,而是會發現在其子目錄下有個.odex文件。 那如何反編譯這個odex文件呢?我通過google查了查,知道應該用baksmali,但從github上下載了幾個版本都不行,報各種不同錯誤。經過反復搜索和嘗試,終於找到了這篇文章
: http://www.naldotech.com/how-to-deodex-applications-on-android-5-0-lollipop/ 。 具體方法如下:
1. 從這里下載工具包, 解壓縮到本地。 這里的baksmali的版本是2.0.3. 不同版本的baksmali針對的Android內核不同。有時候高版本反倒不好用。
2. 打開工具所在目錄, 按住shift鍵, 點擊滑鼠右鍵,打開windows命令窗口
3. 把 odex文件拷貝到該目錄
4. 在命令窗口運行: oat2dex.bat *.odex. 正常情況下,應該顯示OK等信息。如果報錯的話,說明這個文件無法轉換,後面的也不用試了。
5. 運行 oat2dex.bat *.odex temp.dex . 運行後會創建一個temp.dex文件。
6. 運行 java -jar baksmali-2.0.3.jar -a 21 -x temp.dex -o source . 運行後會創建一個source的文件夾,並將temp.dex反編譯到該文件夾。-a 21 表明的是Android內核的版本21
7. 運行 java -jar small-2.0.3.jar -a 21 source -o classes.dex, 反編譯為classes.dex文件。
需要注意的是:由這種方式反編譯成的classes.dex 文件相比原生的classes.dex 還是缺少了些信息,因此反編譯這種classes.dex 文件後生成的java代碼可讀性會更差些。
8. 用在線工具JADX 來把 classes.dex 最終反編譯為java代碼。
❷ 如何反編譯 android 中 /data/dalvik-cache/arm 下的文件
所有的 apk 內包含一個 classes.dex 文件。在 Dalvik上,apk包里的 dex文件在安裝的時候會通過 dexopt 轉化成另一個格式,叫odex(Opitimized dex),然後存在 /data/dalvik-cache裡面,如:
/data/dalvik-cache/data@[email protected]@classes.dex
雖然文件後綴還是 .dex,但是這個dex和apk內的那個已經不一樣了。這個文件是針對當前機器的硬體對 dex 文件進行了定製化,也就是說把這個放到別的設備上,不一定能運行。
PS: 在要編譯 rom 的時候,如果參數加上 "WITH_DEXPREOPT=true",會在 /system/app/ 下同時生成 .apk 和 .odex 文件(注意,這里後綴又用的 .odex,但實際上和系統在 /data/dalvik-cache/ 下的 .dex文件是一樣的)
ART
在 ART上,apk 包里的 dex文件在安裝的時候通過 dex2oat,也會生成一個後綴為 .dex 的文件,放在 /data/dalvik-cache中,如:
/data/dalvik-cache/arm/system@app@[email protected]@classes.dex
/data/dalvik-cache/arm64/system@vendor@app@[email protected]@classes.dex
這個文件後綴叫 .dex ,但是這個文件又不一樣了,這個既不是 dex 也不是 odex,用 dex2jar 的無法進行反編譯的。文件格式也完全不同,因為這其實就是一個實打實的 elf文件,這個文件已經可以直接在機器上運行了。
為何 pm.jar 是空的?
首先來了解一下 ROM 的編譯選項,看一下編譯的時候能做什麼事情, 大致了解就行了 。
編譯選項
WITH_DEXPREOPT
使能編譯時生成 OAT,避免第一次開機時編譯耗時,但會增大 system分區的空間消耗
DONT_DEXPREOPT_PREBUILTS
使能後,將不會對 Android.mk中包含了 include $(BUILD_PREBUILT)的 Apk進行 oat,例如 Gmail,它很可能會在後期通過商店自行升級,而升級後系統中的 oat文件則沒有意義了,但又無法刪除,會造成空間的浪費(oat比dex文件要大)
WITH_DEXPREOPT_BOOT_IMG_ONLY
僅僅針對 boot.img進行oat優化(boot.img中包含 boot.art和 boot.oat)
LOCAL_DEX_PREOPT ture|false|nostripping
可用於各個 Android.mk,對每個 package進行單獨配置,當設置為 true時,dex文件將會從 apk中剔除,如果不想剔除可使用 nostripping WPRODUCT _DEX PREOPT_*
WPRODUCT__DEX_PREOPT_*
PRODUCT_DEX_PREOPT_BOOT_FLAGS
這里的參數將會傳至 dex2oat,控制 boot.img的編譯優化行為。
PRODUCT_DEX_PREOPT_DEFAULT_FLAGS
控制除 boot.img 外,其他(如 jar, apk)的 OAT編譯行為 例如:
PRODUCT_DEX_PREOPT_DEFAULT_FLAGS := --compiler- filter=interpret-only
$(call add-proct-dex-preopt-mole- config,services,--compiler-filter=space)
WITH_DEXPREOPT_PIC ture|false
使能 position-independent code,這樣在dex2oat編譯生成的 odex文件在運行時將不必再從 /system 下拷貝到 /data/dalvik-cache/ 目錄下, 可以節省 /data 空間
WITH_ART_SMALL_MODE true|false
設置為 true 時,將只編譯處於 boot classpath 里的類,其他的均不編譯,這樣既能加快第一次開機時間,因為大部分必要的類已經編譯過了; 同時也能節省不少空間,因為 APP 都未進行編譯。缺點是可能損失一性能,這可能要平時覺察不出,但在跑分軟體上會有所體現
編譯選項的經典配置
為了提高第一次開機速度,WITH_DEXPREOPT是必須使能的,這樣則在編譯階段會完成 dex2oat的操作,避免在開機時間去做這個轉碼,節省了開機時間(6min以上縮短2min內)。
但會引起一個缺點,那就是 apk中還是包含了 class.dex(dexopt生成的),同時在對應的apk文件夾中又生成了已經轉碼成oat的 class.odex(dex2oat生成的),相當於這部分重復,造成了大量的空間浪費。
為了把 apk包里的 class.dex去除,節省空間,可以打開 DEX PREOPT DEFAULT := ture。
然而,這樣開機速度是快了,而且節省了不少system空間,但開機後,我們會發現即使在 system中已經存在 class.odex的 apk,第一次開機後還是會在 /data下面生成 class.odex,如data/dalvik-cache/arm64/system@app@[email protected]@classes.dex,這是何解?原來 Google為了提高安全性,在每一台機器開機時都會在之前的機器碼加一個隨機的偏移量,這個偏移量是隨機的,每台機器都不相同,而 data分區下的這些文件就是從 system下的 class.odex加上偏移而來。