Google雖然借鑑了Java的上層設計,但是虛擬機使用的是自己設計的Dalvik,Dalvik虛擬機讀取、解釋的文件是DEX文件。雖然在Android 4.0開始,開始引入類似C#的二次編譯技術(Android自稱ART),Android 5.0已經完全放棄Dalvik虛擬機(所以源碼裏已經沒有Dalvik設計文檔了),但是,二次編譯技術是安裝apk時候的事,我們用Java語言所寫的代碼,還是要經過.java->.class->.dex->.apk的過程,認識Dex文件格式,還是有助於反彙編成Smali語言,有助於逆向。
dex生成流程
以下Makefile腳本展示了dex文件生成流程:
DX
:= ${SDK_PATH}\sdk\build-tools\19.1.0\dx.bat |
INSTALL_PATH
:= /data/local/tmp |
adb
push ${MODULE_NAME}.dex ${INSTALL_PATH} |
adb
shell dalvikvm - cp ${INSTALL_PATH}/${MODULE_NAME}.dex
${START_CLASS} |
dx:
${MODULE_NAME}.dex ${MODULE_NAME}.class |
${MODULE_NAME}.dex:
${MODULE_NAME}.class |
${DX}
--dex --output=${MODULE_NAME}.dex ${MODULE_NAME}.class |
javac:
${MODULE_NAME}.class |
javac
- source 1.6
-target 1.6 ${MODULE_NAME}.java |
學習Dex文件的工具
推薦使用010 Editor軟件,它自帶模板插件功能,並且已經有他人製作好的針對Dex文件的模版。可以高亮顯示各字段,解析各字段,還附帶各字段的註釋。對照着Dalvik自帶的文檔看十六進制,事半功倍
Dex文件組成
按順序解析dex文件,有以下幾個部分:
-
struct header_item dex_header
-
struct string_id_list dex_string_ids
-
struct type_id_list dex_type_ids
-
struct proto_id_list dex_proto_ids
-
struct field_id_list dex_field_ids
-
struct method_id_list dex_method_ids
-
struct class_def_item_list dex_class_defs
-
struct map_list_type dex_map_list
其中兩頭兩尾的header_item與map_list是兩張“總表”,包含着其它的表的偏移信息等,兩者信息有重疊(冗餘)。
各個表的功能及要求:
-
string_id_item[]: 文件中要用到的所有字符串,按UTF-16編碼有序排列。
-
type_id_item[]: 文件中要用到的所有類型信息,但裏面並不直接記錄着字符串,而是string_id_item[]中的數組下標,且按string_id_item中的數組下標有序排列。
-
proto_id_item[]: 文件中要用到的所有方法原型,相當於所有函數指針的類型信息,也是有序排列的,主序是返回類型,次序是參數類型。
-
field_id_item[]: 文件中要用到的所有成員(含靜態與普通),按類的類型、成員名、成員類型優先級遞減有序排列。
-
method_id_item[]: 文件中要用到的所有方法(含靜態與普通), 按類的類型、方法名、方法原型優先級遞減有序排列。
-
class_def_item[]: 類的信息在此表中,從此表中,可以解析出內部類、靜態與普通成員/方法、Dalvik字符碼的位置等與類有關的具體信息,都在此表中
文件頭中的校驗
在文件頭中,以下幾項比較關鍵,影響到dex文件能否被dalvikvm正常執行:
-
uint checksum: 文件0x8偏移處,通過alder32算法,針對文件0xC至文件尾得到的的校驗值。
-
SHA1 signature[20]: 文件0xC偏移處,通過SHA1算法,鎮上 對文件0x20至文件尾得到的HASH值。
-
uint file_size: 文件0x20偏移處,記錄文件長度
對Dex文件進行修改後,要依次更新file_size, signature[20], checksum三個字段,Dex才能正常運行。
dex的一些加固思路
大小尾標籤
在header_item中有一個uint endian_tag字段,記錄了常量0x12345678,dalvikvm在運行Dex文件時,會通過檢查這個字段,來決定是以小尾還是大尾方式來解釋dex文件(中的相關變量)。由於ARM與x86芯片都是小尾方式,現有的反彙編引擎都不會考慮這個字段(默認小尾),因此,若我們人爲修改這個字段爲大尾,並將dex文件中相關數據都反過來,可以對抗當前的各種反彙編引擎。
字符串混淆
因爲dex文件的信息都很集中,所以將string_id_item[]中的字符串替換爲無意義的字符串,整個文件中相關要用到的地方也同步更新了,可用此來混淆。有時候防守方做得太噁心了(如兩個函數名:”oooo0o”與”o0oooo”),可以用比較弱的混淆器對抗下(abc一類的名稱比數圈圈好)。
實踐方案
以上的實驗還只是針對dex的標識符的加固,不涉及代碼。實際操作中,可以:
-
得到正常的dex
-
修改dex中的各表項,使閱讀困難
-
用baksmali,反彙編成smali語法
-
針對smali文件,進行代碼混淆
-
重新編譯smali文件,得到加固後的dex文件
不過,不管怎樣混淆,dalvikvm畢竟是開源的,dex文件的強度都比較弱。