一篇文章带你搞懂 DEX 文件的结构
?
From:https://blog.csdn.net/sinat_18268881/article/details/55832757
Dex文件格式詳解:https://www.jianshu.com/p/f7f0a712ddfe
dex文件解析(第三篇):https://blog.csdn.net/tabactivity/article/details/78950379
深入理解DEX文件格式( 有個(gè) Python 讀 dex 文件?):https://mybeibei.net/archives/1103
一文讀懂 DEX 文件格式解析:https://cloud.tencent.com/developer/article/1663852
Android逆向之旅—解析編譯之后的Dex文件格式:http://www.520monkey.com/archives/579
圖解Dex文件結(jié)構(gòu)及解析要點(diǎn):https://blog.csdn.net/beyond702/article/details/52460721
從 Android 運(yùn)行時(shí)出發(fā),打造我們的脫殼神器:https://blog.csdn.net/earbao/article/details/51516116
? ? ? ? ? ??http://www.droidsec.cn/從android運(yùn)行時(shí)出發(fā),打造我們的脫殼神器/
?
參考資料:《Android軟件安全與逆向分析》.非蟲
?
?
?
0x00■ ?構(gòu)造 DEX 文件
?
什么是 dex 文件?
DEX 文件就是 Android Dalvik 虛擬機(jī)運(yùn)行的程序,關(guān)于 DEX 文件的結(jié)構(gòu)的重要性我就不多說了。
dex ?是 Android 系統(tǒng)的可執(zhí)行文件,包含應(yīng)用程序的全部操作指令以及運(yùn)行時(shí)數(shù)據(jù)。
簡單的說,就是優(yōu)化后的 android版.exe。每個(gè)apk安裝包里都有。相對(duì)于PC上的 java 虛擬機(jī)能運(yùn)行.class;android上的 Davlik 虛擬機(jī)能運(yùn)行.dex。
?
為何要研究 dex 格式 ?
因?yàn)?dex 里面包含了所有 app 代碼,利用反編譯工具可以獲取 java 源碼。理解并修改 dex 文件,就能更好的 逆向?APK。
由于dalvik是一種針對(duì)嵌入式設(shè)備而特殊設(shè)計(jì)的java虛擬機(jī),所以dex文件與標(biāo)準(zhǔn)的class文件在結(jié)構(gòu)設(shè)計(jì)上有著本質(zhì)的區(qū)別
當(dāng) java 程序編譯成 class 后,還需要使用 dx 工具將所有的 class 文件整合到一個(gè) dex 文件,目的是其中各個(gè)類能夠共享數(shù)據(jù),在一定程度上降低了冗余,同時(shí)也是文件結(jié)構(gòu)更加經(jīng)湊,實(shí)驗(yàn)表明,dex 文件是傳統(tǒng) jar 文件大小的 50% 左右
可以看見:dex 將原來 class 每個(gè)文件都有的共有信息合成一體,這樣減少了 class 的冗余
數(shù)據(jù)結(jié)構(gòu)
| u1 | unit8_t,1字節(jié)無符號(hào)數(shù) |
| u2 | unit16_t,2字節(jié)無符號(hào)數(shù) |
| u4 | unit32_t,4字節(jié)無符號(hào)數(shù) |
| u8 | unit64_t,8字節(jié)無符號(hào)數(shù) |
| sleb128 | 有符號(hào)LEB128,可變長度1~5 |
| uleb128 | 無符號(hào)LEB128, |
| uleb128p1 | 無符號(hào)LEB128值加1, |
其中 u1~u8 很好理解,表示 1 到 8 個(gè)字節(jié)的無符號(hào)數(shù),后面三個(gè)是dex特有的數(shù)據(jù)類型,
不理解的可以參考這里(?https://blog.csdn.net/zklth/article/details/7978362 )
更詳細(xì)的參考:(深入到源碼解析leb128數(shù)據(jù)類型)[http://i.woblog.cn/2016/07/23/leb128-format/]
?
下面開練。。。。。
建議:不要只看,跟著做。看再多遍不如自己親自實(shí)踐一遍來的可靠,別問我為什么知道。淚崩ing.....
下面會(huì)自己構(gòu)造一個(gè) dex 文件,因?yàn)樽约簶?gòu)造的比較簡單,分析起來比較容易。等你簡單的會(huì)了,難的自然也就懂了。
首先,編寫一個(gè)簡單的 Java 程序,如下:
public class HelloWorld { int a = 0; static String b = "HelloDalvik"; public int getNumber(int i, int j) { int e = 3; return e + i + j; } public static void main(String[] args) { int c = 1; int d = 2; HelloWorld helloWorld = new HelloWorld(); String sayNumber = String.valueOf(helloWorld.getNumber(c, d)); System.out.println("HelloDex!" + sayNumber); } }然后將其編譯成 dex 文件:打開命令行,進(jìn)入 HelloWorld.class 所在文件夾下,執(zhí)行命令:
javac HelloWorld.java接下來會(huì)出現(xiàn)一個(gè)HelloWorld.class文件,然后繼續(xù)執(zhí)行命令( dx 工具需要安裝Android SDK才能有的工具 ):
dx --dex --output=HelloWorld.dex HelloWorld.class就會(huì)出現(xiàn) HelloWorld.dex 文件了。
在當(dāng)前工作路徑下 , 編譯方法如下 :
1. 編譯成 java class 文件,執(zhí)行命令 : javac Hello.java? 。編譯完成后 ,目錄下生成 Hello.class 文件 。可以使用命令 java Hello 來測試下 ,會(huì)輸出代碼中的 “Hello, Android!” 的字符串 。
2. 編譯成 dex 文件
編譯工具在 Android SDK 的路徑如下 ,其中 19.0.1 是Android SDK build_tools 的版本 ,請(qǐng)按照在本地安裝的 build_tools 版本來 。建議該路徑加載到 PATH 路徑下 ,否則引用 dx 工具時(shí)需要使用絕對(duì)路徑 :./build-tools/19.0.1/dx
執(zhí)行命令 :?dx –dex –output=Hello.dex Hello.class
編譯正常會(huì)生成 Hello.dex 文件 。
3. 使用 ADB 運(yùn)行測試
? ? ? ? 測試命令和輸出結(jié)果如下 :
? ? ? ? $ adb root
? ? ? ? $ adb push Hello.dex /sdcard/
? ? ? ? $ adb shell
? ? ? ? root@maguro:/ # dalvikvm -cp /sdcard/Hello.dex Hello
4. 重要說明
(1) 測試環(huán)境使用真機(jī)和 Android 虛擬機(jī)都可以的 。核心的命令是
dalvikvm -cp /sdcard/Hello.dex Hello
-cp 是 class path 的縮寫 ,后面的 Hello 是要運(yùn)行的 Class 的名稱 。網(wǎng)上有描述說輸入 dalvikvm –help
可以看到 dalvikvm 的幫助文檔 ,但是在 Android4.4 的官方模擬器和自己的手機(jī)上測試都提示找不到
Class 路徑 ,在Android 老的版本 ( 4.3 ) 上測試還是有輸出的 。
(2) 因?yàn)槊钤趫?zhí)行時(shí) , dalvikvm 會(huì)在 /data/dalvik-cache/ 目錄下創(chuàng)建 .dex 文件 ,因此要求 ADB 的
執(zhí)行 Shell 對(duì)目錄 /data/dalvik-cache/ 有讀、寫和執(zhí)行的權(quán)限 ,否則無法達(dá)到預(yù)期效果 。
?
這時(shí),我們需要下載一個(gè)十六進(jìn)位文本編輯器,因?yàn)橛盟梢越馕龆M(jìn)制文件,我們用它打開 dex 文件就會(huì)全部以十六進(jìn)制的數(shù)進(jìn)行展現(xiàn)了。
這里推薦 010Editor,下載地址:https://www.sweetscape.com/010editor/(收費(fèi)軟件,可以免費(fèi)試用30天)。
下載完成之后,我們可以用它打開dex文件了,打開之后,你的界面應(yīng)該是這樣的:
一下子看到這些東西,是不是立馬懵逼了,正常,我剛開始看的時(shí)候也是,這什么玩意兒啊!其實(shí),這就是二進(jìn)制流文件中的內(nèi)容,010Editor把它轉(zhuǎn)化成了16進(jìn)制的內(nèi)容,以方便我們閱讀的。
?
?
0x01■ ?DEX文件結(jié)構(gòu)總覽
?
一張圖搞懂dex
( 可以 右鍵 ---> 在新標(biāo)簽頁中打開圖片 ,就可以看清圖片)
不要慌,下面我跟你解釋,這些東西我們雖然看了懵逼,但是 Dalvik 虛擬機(jī)不會(huì),因?yàn)樗褪墙馕鲞@些東西的,這些東西雖然看起來頭大,但是它是有自己的格式標(biāo)準(zhǔn)的。dex文件的結(jié)構(gòu)如下圖所示:
這就是 dex 的文件格式了,下面我們從最上面的 Header 說起,Header 中存儲(chǔ)了什么內(nèi)容呢?下面我們還得來一張圖:
從宏觀上來說 dex 的文件結(jié)果很簡單,實(shí)際上是由多個(gè)不同結(jié)構(gòu)的數(shù)據(jù)體以首尾相接的方式拼接而成。如下圖:
| header | dex文件頭部,記錄整個(gè)dex文件的相關(guān)屬性 |
| string_ids | 字符串?dāng)?shù)據(jù)索引,記錄了每個(gè)字符串在數(shù)據(jù)區(qū)的偏移量 |
| type_ids | 類似數(shù)據(jù)索引,記錄了每個(gè)類型的字符串索引 |
| proto_ids | 原型數(shù)據(jù)索引,記錄了方法聲明的字符串,返回類型字符串,參數(shù)列表 |
| field_ids | 字段數(shù)據(jù)索引,記錄了所屬類,類型以及方法名 |
| method_ids | 類方法索引,記錄方法所屬類名,方法聲明以及方法名等信息 |
| class_defs | 類定義數(shù)據(jù)索引,記錄指定類各類信息,包括接口,超類,類數(shù)據(jù)偏移量 |
| data | 數(shù)據(jù)區(qū),保存了各個(gè)類的真是數(shù)據(jù) |
| link_data | 連接數(shù)據(jù)區(qū) |
/dalvik/libdex/DexFile.h? 定義如下:
struct DexFile {const DexHeader* pHeader;const DexStringId* pStringIds;const DexTypeId* pTypeIds;const DexFieldId* pFieldIds;const DexMethodId* pMethodIds;const DexProtoId* pProtoIds;const DexClassDef* pClassDefs;const DexLink* pLinkData; }注意:其中一些定義的字段是在內(nèi)存中并沒有存到真實(shí)的 dex 文件中
header 簡單記錄了dex文件的一些基本信息,以及大致的數(shù)據(jù)分布。長度固定為0x70,其中每一項(xiàng)信息所占用的內(nèi)存空間也是固定的,好處是虛擬機(jī)在處理 dex 時(shí)不用考慮 dex 文件的多樣性
| magic | 0x0 | 8 | 魔數(shù)字段,值為"dex\n035\0" |
| checksum | 0x8 | 4 | 校驗(yàn)碼 |
| signature | 0xc | 20 | sha-1簽名 |
| file_size | 0x20 | 4 | dex文件總長度 |
| header_size | 0x24 | 4 | 文件頭長度,009版本=0x5c,035版本=0x70 |
| endian_tag | 0x28 | 4 | 標(biāo)示字節(jié)順序的常量 |
| link_size | 0x2c | 4 | 鏈接段的大小,如果為0就是靜態(tài)鏈接 |
| link_off | 0x30 | 4 | 鏈接段的開始位置 |
| map_off | 0x34 | 4 | map數(shù)據(jù)基址 |
| string_ids_size | 0x38 | 4 | 字符串列表中字符串個(gè)數(shù) |
| string_ids_off | 0x3c | 4 | 字符串列表基址 |
| type_ids_size | 0x40 | 4 | 類列表里的類型個(gè)數(shù) |
| type_ids_off | 0x44 | 4 | 類列表基址 |
| proto_ids_size | 0x48 | 4 | 原型列表里面的原型個(gè)數(shù) |
| proto_ids_off | 0x4c | 4 | 原型列表基址 |
| field_ids_size | 0x50 | 4 | 字段個(gè)數(shù) |
| field_ids_off | 0x54 | 4 | 字段列表基址 |
| method_ids_size | 0x58 | 4 | 方法個(gè)數(shù) |
| method_ids_off | 0x5c | 4 | 方法列表基址 |
| class_defs_size | 0x60 | 4 | 類定義標(biāo)中類的個(gè)數(shù) |
| class_defs_off | 0x64 | 4 | 類定義列表基址 |
| data_size | 0x68 | 4 | 數(shù)據(jù)段的大小,必須4k對(duì)齊 |
| data_off | 0x6c | 4 | 數(shù)據(jù)段基址 |
/dalvik/libdex/DexFile.h? 定義如下:
struct DexHeader {u1 magic[8]; /* includes version number */u4 checksum; /* adler32 checksum */u1 signature[kSHA1DigestLen]; /* SHA-1 hash */u4 fileSize; /* length of entire file */u4 headerSize; /* offset to start of next section */u4 endianTag;u4 linkSize;u4 linkOff;u4 mapOff;u4 stringIdsSize;u4 stringIdsOff;u4 typeIdsSize;u4 typeIdsOff;u4 protoIdsSize;u4 protoIdsOff;u4 fieldIdsSize;u4 fieldIdsOff;u4 methodIdsSize;u4 methodIdsOff;u4 classDefsSize;u4 classDefsOff;u4 dataSize;u4 dataOff; };我們可以用:hexdump -c classes.dex 查看 dex 單字節(jié)顯示的結(jié)果,如下:
0000000 d e x \n 0 3 5 \0 022 217 ? w z ? 031 221 0000010 ? \f ? ? ? ? ? ? 217 235 200 z ? 030 I ? 0000020 ? 003 \0 \0 p \0 \0 \0 x V 4 022 \0 \0 \0 \0 0000030 \0 \0 \0 \0 ? 002 \0 \0 024 \0 \0 \0 p \0 \0 \0 0000040 \b \0 \0 \0 ? \0 \0 \0 005 \0 \0 \0 ? \0 \0 \0 0000050 001 \0 \0 \0 034 001 \0 \0 005 \0 \0 \0 $ 001 \0 \0 0000060 001 \0 \0 \0 L 001 \0 \0 8 002 \0 \0 l 001 \0 \0 0000070 l 001 \0 \0 t 001 \0 \0 201 001 \0 \0 204 001 \0 \0 0000080 222 001 \0 \0 226 001 \0 \0 ? 001 \0 \0 ? 001 \0 \0 0000090 ? 001 \0 \0 ? 001 \0 \0 004 002 \0 \0 \a 002 \0 \0 00000a0 \v 002 \0 \0 002 \0 \0 ( 002 \0 \0 . 002 \0 \0 00000b0 4 002 \0 \0 9 002 \0 \0 B 002 \0 \0 L 002 \0 \0 00000c0 003 \0 \0 \0 005 \0 \0 \0 006 \0 \0 \0 \a \0 \0 \0 00000d0 \b \0 \0 \0 \t \0 \0 \0 \n \0 \0 \0 \f \0 \0 \0 00000e0 002 \0 \0 \0 003 \0 \0 \0 \0 \0 \0 \0 004 \0 \0 \0 00000f0 004 \0 \0 \0 x 002 \0 \0 \n \0 \0 \0 006 \0 \0 \0 0000100 \0 \0 \0 \0 \v \0 \0 \0 006 \0 \0 \0 x 002 \0 \0 0000110 \v \0 \0 \0 006 \0 \0 \0 p 002 \0 \0 005 \0 001 \0 0000120 020 \0 \0 \0 \0 \0 004 \0 017 \0 \0 \0 001 \0 003 \0 0000130 021 \0 \0 \0 004 \0 002 \0 \0 \0 \0 \0 004 \0 001 \0 0000140 \r \0 \0 \0 004 \0 \0 \0 022 \0 \0 \0 \0 \0 \0 \0 0000150 001 \0 \0 \0 002 \0 \0 \0 \0 \0 \0 \0 ? ? ? ? 0000160 \0 \0 \0 \0 ? 002 \0 \0 \0 \0 \0 \0 006 < i n 0000170 i t > \0 \v H e l l o W o r l d 0000180 \0 001 L \0 \f L H e l l o W o r l d 0000190 ; \0 002 L L \0 025 L j a v a / i o / 00001a0 P r i n t S t r e a m ; \0 022 L j 00001b0 a v a / l a n g / O b j e c t ; 00001c0 \0 022 L j a v a / l a n g / S t r 00001d0 i n g ; \0 031 L j a v a / l a n g 00001e0 / S t r i n g B u i l d e r ; \0 00001f0 022 L j a v a / l a n g / S y s t 0000200 e m ; \0 001 V \0 002 V L \0 023 [ L j a 0000210 v a / l a n g / S t r i n g ; \0 0000220 006 a p p e n d \0 004 a r g s \0 004 m 0000230 a i n \0 003 o u t \0 \a p r i n t l 0000240 n \0 \b t o S t r i n g \0 016 ? ? 231 0000250 ? 230 ? ? ? 200 ? ? ? ? 211 213 ? 206 231 ? 0000260 232 204 s m a l i ? ? 236 ? ? 213 \0 \0 \0 0000270 001 \0 \0 \0 \a \0 \0 \0 001 \0 \0 \0 003 \0 \0 \0 0000280 \0 \0 \0 \0 \0 \0 \0 \0 \0 001 017 \a \0 \0 \0 \0 0000290 \v \0 001 \0 002 \0 \0 \0 210 002 \0 \0 ( \0 \0 \0 00002a0 b \0 \0 \0 \0 \0 \0 \0 \0 \0 022 2 023 003 ? ? 00002b0 030 004 \0 \0 001 \0 \0 \0 \0 \0 034 005 003 \0 001 & 00002c0 " \a 004 \0 p 020 002 \0 \a \0 032 \b 023 \0 n 00002d0 003 \0 207 \0 \f \a n 020 004 \0 \a \0 \f \t n 00002e0 001 \0 220 \0 032 001 001 \0 n 001 \0 020 \0 016 \0 00002f0 \0 \0 001 \0 \0 \t 220 005 016 \0 \0 \0 \0 \0 \0 \0 0000300 001 \0 \0 \0 \0 \0 \0 \0 001 \0 \0 \0 024 \0 \0 \0 0000310 p \0 \0 \0 002 \0 \0 \0 \b \0 \0 \0 ? \0 \0 \0 0000320 003 \0 \0 \0 005 \0 \0 \0 ? \0 \0 \0 004 \0 \0 \0 0000330 001 \0 \0 \0 034 001 \0 \0 005 \0 \0 \0 005 \0 \0 \0 0000340 $ 001 \0 \0 006 \0 \0 \0 001 \0 \0 \0 L 001 \0 \0 0000350 002 \0 \0 024 \0 \0 \0 l 001 \0 \0 001 020 \0 \0 0000360 002 \0 \0 \0 p 002 \0 \0 003 020 \0 \0 002 \0 \0 \0 0000370 200 002 \0 \0 003 \0 \0 001 \0 \0 \0 210 002 \0 \0 0000380 001 \0 \0 001 \0 \0 \0 220 002 \0 \0 \0 \0 \0 0000390 001 \0 \0 \0 ? 002 \0 \0 \0 020 \0 \0 001 \0 \0 \0 00003a0 ? 002 \0 \0 00003a4還可以用 -C 顯示 16 進(jìn)制 和 ASCII碼:hexdump -C classes.dex
00000000 64 65 78 0a 30 33 35 00 12 8f b1 77 7a e9 19 91 |dex.035....wz...| 00000010 f2 0c ff ce a0 ce aa cd 8f 9d 80 7a ac 18 49 bf |...........z..I.| 00000020 a4 03 00 00 70 00 00 00 78 56 34 12 00 00 00 00 |....p...xV4.....| 00000030 00 00 00 00 f8 02 00 00 14 00 00 00 70 00 00 00 |............p...| 00000040 08 00 00 00 c0 00 00 00 05 00 00 00 e0 00 00 00 |................| 00000050 01 00 00 00 1c 01 00 00 05 00 00 00 24 01 00 00 |............$...| 00000060 01 00 00 00 4c 01 00 00 38 02 00 00 6c 01 00 00 |....L...8...l...| 00000070 6c 01 00 00 74 01 00 00 81 01 00 00 84 01 00 00 |l...t...........| 00000080 92 01 00 00 96 01 00 00 ad 01 00 00 c1 01 00 00 |................| 00000090 d5 01 00 00 f0 01 00 00 04 02 00 00 07 02 00 00 |................| 000000a0 0b 02 00 00 20 02 00 00 28 02 00 00 2e 02 00 00 |.... ...(.......| 000000b0 34 02 00 00 39 02 00 00 42 02 00 00 4c 02 00 00 |4...9...B...L...| 000000c0 03 00 00 00 05 00 00 00 06 00 00 00 07 00 00 00 |................| 000000d0 08 00 00 00 09 00 00 00 0a 00 00 00 0c 00 00 00 |................| 000000e0 02 00 00 00 03 00 00 00 00 00 00 00 04 00 00 00 |................| 000000f0 04 00 00 00 78 02 00 00 0a 00 00 00 06 00 00 00 |....x...........| 00000100 00 00 00 00 0b 00 00 00 06 00 00 00 78 02 00 00 |............x...| 00000110 0b 00 00 00 06 00 00 00 70 02 00 00 05 00 01 00 |........p.......| 00000120 10 00 00 00 00 00 04 00 0f 00 00 00 01 00 03 00 |................| 00000130 11 00 00 00 04 00 02 00 00 00 00 00 04 00 01 00 |................| 00000140 0d 00 00 00 04 00 00 00 12 00 00 00 00 00 00 00 |................| 00000150 01 00 00 00 02 00 00 00 00 00 00 00 ff ff ff ff |................| 00000160 00 00 00 00 f0 02 00 00 00 00 00 00 06 3c 69 6e |.............<in| 00000170 69 74 3e 00 0b 48 65 6c 6c 6f 20 57 6f 72 6c 64 |it>..Hello World| 00000180 00 01 4c 00 0c 4c 48 65 6c 6c 6f 57 6f 72 6c 64 |..L..LHelloWorld| 00000190 3b 00 02 4c 4c 00 15 4c 6a 61 76 61 2f 69 6f 2f |;..LL..Ljava/io/| 000001a0 50 72 69 6e 74 53 74 72 65 61 6d 3b 00 12 4c 6a |PrintStream;..Lj| 000001b0 61 76 61 2f 6c 61 6e 67 2f 4f 62 6a 65 63 74 3b |ava/lang/Object;| 000001c0 00 12 4c 6a 61 76 61 2f 6c 61 6e 67 2f 53 74 72 |..Ljava/lang/Str| 000001d0 69 6e 67 3b 00 19 4c 6a 61 76 61 2f 6c 61 6e 67 |ing;..Ljava/lang| 000001e0 2f 53 74 72 69 6e 67 42 75 69 6c 64 65 72 3b 00 |/StringBuilder;.| 000001f0 12 4c 6a 61 76 61 2f 6c 61 6e 67 2f 53 79 73 74 |.Ljava/lang/Syst| 00000200 65 6d 3b 00 01 56 00 02 56 4c 00 13 5b 4c 6a 61 |em;..V..VL..[Lja| 00000210 76 61 2f 6c 61 6e 67 2f 53 74 72 69 6e 67 3b 00 |va/lang/String;.| 00000220 06 61 70 70 65 6e 64 00 04 61 72 67 73 00 04 6d |.append..args..m| 00000230 61 69 6e 00 03 6f 75 74 00 07 70 72 69 6e 74 6c |ain..out..printl| 00000240 6e 00 08 74 6f 53 74 72 69 6e 67 00 0e e8 bf 99 |n..toString.....| 00000250 e6 98 af e4 b8 80 e4 b8 aa e6 89 8b e5 86 99 e7 |................| 00000260 9a 84 73 6d 61 6c 69 e5 ae 9e e4 be 8b 00 00 00 |..smali.........| 00000270 01 00 00 00 07 00 00 00 01 00 00 00 03 00 00 00 |................| 00000280 00 00 00 00 00 00 00 00 00 01 0f 07 00 00 00 00 |................| 00000290 0b 00 01 00 02 00 00 00 88 02 00 00 28 00 00 00 |............(...| 000002a0 62 00 00 00 00 00 00 00 00 00 12 32 13 03 ff ff |b..........2....| 000002b0 18 04 00 00 01 00 00 00 00 00 1c 05 03 00 01 26 |...............&| 000002c0 22 07 04 00 70 10 02 00 07 00 1a 08 13 00 6e 20 |"...p.........n | 000002d0 03 00 87 00 0c 07 6e 10 04 00 07 00 0c 09 6e 20 |......n.......n | 000002e0 01 00 90 00 1a 01 01 00 6e 20 01 00 10 00 0e 00 |........n ......| 000002f0 00 00 01 00 00 09 90 05 0e 00 00 00 00 00 00 00 |................| 00000300 01 00 00 00 00 00 00 00 01 00 00 00 14 00 00 00 |................| 00000310 70 00 00 00 02 00 00 00 08 00 00 00 c0 00 00 00 |p...............| 00000320 03 00 00 00 05 00 00 00 e0 00 00 00 04 00 00 00 |................| 00000330 01 00 00 00 1c 01 00 00 05 00 00 00 05 00 00 00 |................| 00000340 24 01 00 00 06 00 00 00 01 00 00 00 4c 01 00 00 |$...........L...| 00000350 02 20 00 00 14 00 00 00 6c 01 00 00 01 10 00 00 |. ......l.......| 00000360 02 00 00 00 70 02 00 00 03 10 00 00 02 00 00 00 |....p...........| 00000370 80 02 00 00 03 20 00 00 01 00 00 00 88 02 00 00 |..... ..........| 00000380 01 20 00 00 01 00 00 00 90 02 00 00 00 20 00 00 |. ........... ..| 00000390 01 00 00 00 f0 02 00 00 00 10 00 00 01 00 00 00 |................| 000003a0 f8 02 00 00 |....| 000003a4?
?
0x02■ ?DEX文件結(jié)構(gòu)解析
先看下就行,不用著急,下面我們一步一步來,首先點(diǎn)擊你的010Editor的這里:
對(duì),就是箭頭指的那里,點(diǎn)擊之后,你會(huì)發(fā)現(xiàn)上面的有一片區(qū)域成了選中的顏色,這部分里面存儲(chǔ)的就是Header中的數(shù)據(jù)了,下面我們根據(jù)Header的數(shù)據(jù)圖以此來進(jìn)行分析。
首先,我們看到DexHeader中每個(gè)數(shù)據(jù)前面有個(gè)u1或者u4,這個(gè)是什么意思呢?它們其實(shí)就是代表1個(gè)或者4個(gè)字節(jié)的無符號(hào)數(shù)。下面我們依次根據(jù)Header中的數(shù)據(jù)段進(jìn)行解釋。
?
1. 從第一個(gè)看起,magic[8];它代表dex中的文件標(biāo)識(shí),一般被稱為魔數(shù)。是用來識(shí)別dex這種文件的,它可以判斷當(dāng)前的dex文件是否有效,可以看到它用了8個(gè)1字節(jié)的無符號(hào)數(shù)來表示,我們?cè)?10Editor中可以看到也就是“64 65 78 0A 30 33 35 00 ”這8個(gè)字節(jié),這些字節(jié)都是用16進(jìn)制表示的,用16進(jìn)制表示的話,兩個(gè)數(shù)代表一個(gè)字節(jié)(一個(gè)字節(jié)等于8位,一個(gè)16進(jìn)制的數(shù)能表示4位)。這8個(gè)字節(jié)用ASCII碼表轉(zhuǎn)化一下可以轉(zhuǎn)化為:dex.035(點(diǎn)擊這里可以進(jìn)行十六進(jìn)制轉(zhuǎn)ASCII,你可以試試:其中,'.' 不是轉(zhuǎn)化來的)。目前,dex的魔數(shù)固定為dex.035。
?
2.?第二個(gè)是,checksum; ?它是dex文件的校驗(yàn)和,通過它可以判斷dex文件是否被損壞或者被篡改。它占用4個(gè)字節(jié),也就是“5D 9D F9 59”。這里提醒一下,在010Editor中,其實(shí)可以分別識(shí)別我們?cè)贒exHeader中看到的這些字段的,你可以點(diǎn)一下這里:
你可以看到這個(gè)header列表展開了,其實(shí)我們分析下來就和它這個(gè)結(jié)構(gòu)是一樣的,你可以先看下,我們現(xiàn)在分析到了checksum中了,你可以看到后面對(duì)應(yīng)的值是“59 F9 9D 5D”。咦?這好像和上面的字節(jié)不是一一對(duì)應(yīng)的啊。對(duì)的,你可以發(fā)現(xiàn)它是反著寫的。這是由于dex文件中采用的是小字節(jié)序的編碼方式,也就是低位上存儲(chǔ)的就是低字節(jié)內(nèi)容,所以它們應(yīng)該要反一下。
?
3.?第三個(gè)到了 signature[kSHA1DigestLen] 了,signature字段用于檢驗(yàn)dex文件,其實(shí)就是把整個(gè)dex文件用SHA-1簽名得到的一個(gè)值。這里占用20個(gè)字節(jié),你可以自己點(diǎn)010Editor看一看。
?
4.?第四個(gè) fileSize ;表示整個(gè)文件的大小,占用4個(gè)字節(jié)。
?
5.?第五個(gè) headerSize ;表示 DexHeader 頭結(jié)構(gòu)的大小,占用4個(gè)字節(jié)。
可以看到它一共占用了112個(gè)字節(jié),112對(duì)應(yīng)的16進(jìn)制數(shù)為70h。
?
?6. 第6個(gè)是 endianTag ;代表 字節(jié)序標(biāo)記,用于指定dex運(yùn)行環(huán)境的cpu,預(yù)設(shè)值為0x12345678,對(duì)應(yīng)在101Editor中為“78 56 34 12”(小字節(jié)序)。
?
7.?接下來兩個(gè)分別是 linkSize 和 u4 ?linkOff ;這兩個(gè)字段,它們分別指定了鏈接段的大小和文件偏移,通常情況下它們都為0。linkSize 為 0 的話表示靜態(tài)鏈接。
?
8.?再下來就是 mapOff 字段了,它指定了DexMapList的文件偏移,這里我們先不過多介紹它,你可以看一下它的值為“14 04 00 00”,它其實(shí)對(duì)應(yīng)的16進(jìn)制數(shù)就是414h(別忘了小字節(jié)序),我們可以在414h的位置看一下它在哪里:
其實(shí)就是dex文件最后一部分內(nèi)容。關(guān)于這部分內(nèi)容里面是什么,我們先不說,繼續(xù)往下看。
?
9.?stringIdsSize 和 stringIdsOff 字段:這兩個(gè)字段指定了dex文件中所有用到的字符串的個(gè)數(shù)和位置偏移,我們先看stringIdsSize,它的值為:“1C 00 00 00”,16進(jìn)制的1C也就是十進(jìn)制的28,也就是說我們這個(gè)dex文件中一共有28個(gè)字符串,然后stringIdsOff為:“70 00 00 00”,代表字符串的偏移位置為70h,這下我們找到70h的地方:
這下我們就要先介紹一下DexStringId這個(gè)結(jié)構(gòu)了,圖中從70h開始,所有被選中的都是DexStringId這種數(shù)據(jù)結(jié)構(gòu)的內(nèi)容,DexStringId代表的是字符串的位置偏移,每個(gè)DexStringId占用4個(gè)字節(jié),也就是說它里面存的還不是真正的字符串,它們只是存儲(chǔ)了真正字符串的偏移位置。
下面我們先分析幾個(gè)看看,
① 取第一個(gè)“B2 02 00 00”,它代表的位置偏移是2B2h,我們先找到這個(gè)位置: ? ?
可以發(fā)現(xiàn)我一共選中了10個(gè)字節(jié),這10個(gè)字節(jié)就表示了一個(gè)字符串。下面我們看一下dex文件中的字符串是如何表示的。dex中的字符串采用了一種叫做MUTF-8這樣的編碼,它是經(jīng)過傳統(tǒng)的UTF-8編碼修改的。在MTUF-8中,它的頭部存放的是由uleb128編碼的字符的個(gè)數(shù)。(至于uleb128編碼是什么編碼,這里我不詳細(xì)展開說,有興趣的可以搜索看看。)
也就是說在“08 3C 63 6C 69 6E 69 74 3E 00”這些字節(jié)中,第一個(gè)08指定的是后面需要用到的編碼的個(gè)數(shù),也就是8個(gè),即“ 3C 63 6C 69 6E 69 74 3E”這8個(gè),但是我們?yōu)槭裁匆还策x中了10個(gè)字節(jié)呢,因?yàn)樽詈笠粋€(gè)空字符“0”表示的是字符串的結(jié)尾,字符個(gè)數(shù)沒有把它算進(jìn)去。下面我們來看看“ 3C 63 6C 69 6E 69 74 3E”這8個(gè)字符代表了什么字符串:
依舊可以點(diǎn)這里查詢ASCII (?http://www.ab126.com/goju/1711.html )。(要說明的一點(diǎn)是,這里湊巧這幾個(gè)uleb128編碼的字符都用了1個(gè)字節(jié),所以我們可以這樣進(jìn)行查詢,uleb128編碼標(biāo)準(zhǔn)用的是1~5個(gè)字節(jié), 這里只是恰好都是一個(gè)字節(jié))。也就是說上面的70h開始的第一個(gè)DexStringId指向的其實(shí)是字符串“<clinit>”(但是貌似我們的代碼中沒有用到這個(gè)字符串啊,先不用管,我們接著分析)。再看到這里:
② 剛剛我們分析到“B2 02 00 00”所指向的真實(shí)字符串了,下面我們接著再分析一個(gè),我們直接分析第三個(gè),不分析第二個(gè)了。第三個(gè)為“C4 02 00 00”,對(duì)應(yīng)的位置也就是2C4h,我們找到它:
看這里,這就是2C4h的位置了。我們首先看第一個(gè)字符,它的值為0Bh,也就是十進(jìn)制的11,也就是說接下來的11個(gè)字符代表了它的字符串,我們依舊是查看接下來11個(gè)字符代表的是什么,經(jīng)過查詢整理: ??
依舊可以點(diǎn)這里查詢ASCII (?http://www.ab126.com/goju/1711.html )。上面就是“HelloDalvik”這個(gè)字符串,可以看看我們的代碼,我們確實(shí)用了一個(gè)這樣的字符串,bingo。下面剩下的字符串就不分析了。經(jīng)過整理,可以整理出我們一共用到的28個(gè)字符串為:
ok,字符串這里告一段落,下面我們繼續(xù)看DexHeader的下面的字段。頭好暈~乎乎
?噢,讀了,還不能結(jié)束呢,你現(xiàn)在可以看一下最開始發(fā)的那張dex結(jié)構(gòu)圖了:
看到了吧,我們這半天分析的stringIdsSize 和 stringIdsOff字段指向的位置就是上面那個(gè)箭頭指向的位置,它們里面存儲(chǔ)的是真實(shí)字符串的位置偏移,它們都存儲(chǔ)在data區(qū)域。(先透露一下,后面我們要分析的幾個(gè)也和stringIdsSize 與stringIdsOff字段類似,它們里面存儲(chǔ)的基本都是位置偏移,并不是真正的數(shù)據(jù),真正的數(shù)據(jù)都在data區(qū)域)
好,我們繼續(xù)。
?
10. 繼續(xù)看DexHeader圖,我們現(xiàn)在該typeIdsSize和typeIdsOff了。它們代表什么呢?它們代表的是類的類型的數(shù)量和位置偏移,也是都占4個(gè)字節(jié),下面我們看它們的值
可以看到,typeIdsSize的值為9h,也就是我們dex文件中用到的類的類型一共有9個(gè),位置偏移在E0h位置,下面我們找到這個(gè)位置
看到了吧,我選中的位置就是了。這里我們又得介紹一種數(shù)據(jù)結(jié)構(gòu)了,因?yàn)檫@里的數(shù)據(jù)也是一種數(shù)據(jù)結(jié)構(gòu)的數(shù)據(jù)組成的。那就是DexTypeId,也就是說選中的內(nèi)容都是DexTypeId這種數(shù)據(jù),這種數(shù)據(jù)結(jié)構(gòu)中只有一個(gè)變量,如下所示:
struct DexTypeId{u4 descriptorIdx; /*指向DexStringId列表的索引*/ }看到了吧,這就是DexTypeId數(shù)據(jù)結(jié)構(gòu),它里面只有一個(gè)數(shù)據(jù)descriptorIdx,它的值的內(nèi)容是DexStringId列表的索引。還記得DexStringId是什么嗎?在上面我們分析字符串時(shí),字符串的偏移位置就是由DexStringId這種數(shù)據(jù)結(jié)構(gòu)描述的,也就是說descriptorIdx指向的是所有的DexStringId組成的列表的索引。上面我們整理出了所有的字符串,你可以翻上去看看圖。然后我們看這里一共是9個(gè)類的類型代表的都是什么。先看第一個(gè)“05 00 00 00”,也就是05h,即十進(jìn)位的5。然后我們?cè)谏厦嫠姓沓龅淖址纯?索引的是什么?翻上去可以看到是“I”。接下來我們依次整理這些類的類型,也可以得到類的類型的列表
看到了吧,這就是我們dex文件中所有用到的類的類型。比如“I”代表的就是int,LHelloWorld代表的就是HelloWorld,Ljava/io/PrintStream代表的就是java.io.PrintStream。后面的幾個(gè)先就不說了。我們接著往下分析。
?
11. 這下到了protoIdsSize和protoIdsOff了,它們代表的是dex文件中方法原型的個(gè)數(shù)和位置偏移。我們先看它們的值
如上圖就是它們的值了,protoIdsSize的值為十進(jìn)制的7,說明有7個(gè)方法原型,然后位置偏移為104h,我們找到這個(gè)位置
看到了吧,這里就是了。對(duì),下面又有新的數(shù)據(jù)結(jié)構(gòu)了。這下一個(gè)數(shù)據(jù)結(jié)構(gòu)不能滿足這塊的內(nèi)容了,我們先看第一個(gè)數(shù)據(jù)結(jié)構(gòu),DexProtoId
struct DexProtoId{u4 shortyIdx; /*指向DexStringId列表的索引*/u4 returnTypeIdx; /*指向DexTypeId列表的索引*/u4 parametersOff; /*指向DexTypeList的位置偏移*/ }可以看到,這個(gè)數(shù)據(jù)結(jié)構(gòu)由三個(gè)變量組成。第一個(gè)shortyIdx它指向的是我們上面分析的DexStringId列表的索引,代表的是方法聲明字符串。第二個(gè)returnTypeIdx它指向的是 我們上邊分析的DexTypeId列表的索引,代表的是方法返回類型字符串。第三個(gè)parametersOff指向的是DexTypeList的位置索引,這又是一個(gè)新的數(shù)據(jù)結(jié)構(gòu)了,先說一下這里面 存儲(chǔ)的是方法的參數(shù)列表。可以看到這三個(gè)參數(shù),有方法聲明字符串,有返回類型,有方法的參數(shù)列表,這基本上就確定了我們一個(gè)方法的大體內(nèi)容。
我們接著看看DexTypeList這個(gè)數(shù)據(jù)結(jié)構(gòu),看看參數(shù)列表是如何存儲(chǔ)的。
struct DexTypeList{u4 size; /*DexTypeItem的個(gè)數(shù)*/DexTypeItem list[1]; /*DexTypeItem結(jié)構(gòu)*/ }看到了嘛,它有兩個(gè)參數(shù),其中第一個(gè)size說的是DexTypeItem的個(gè)數(shù),那DexTypeItem又是啥咧?它又是一種數(shù)據(jù)結(jié)構(gòu)。我們繼續(xù)看看
struct DexTypeItem{u2 typeIdx; /*指向DexTypeId列表的索引*/ }恩,還好,里面就一個(gè)參數(shù)。也比較簡單,就是一個(gè)指向DexTypeId列表的索引,也就是代表參數(shù)列表中某一個(gè)具體的參數(shù)的位置。
分析完這幾個(gè)數(shù)據(jù)結(jié)構(gòu)了,下面我們具體地分析一個(gè)類吧。別走神,我們?cè)搹纳蠄D的104h開始了。
在104h這里,由于 都是DexProtoId這種數(shù)據(jù)結(jié)構(gòu)的數(shù)據(jù),一個(gè)DexProtoId一共占用12個(gè)字節(jié)。所以,我們?nèi)∏?2個(gè)字節(jié)進(jìn)行分析。“06 00 00 00,00 00 00 00,94 02 00 00”,這就是那12個(gè)字節(jié)了。首先“06 00 00 00”代表的是shortyIdx,它的值是指向DexStringId列表的索引,我們找到DexStringId列表中第6個(gè)對(duì)應(yīng)的值,也就是III,說明這個(gè)方法中聲明字符串為三個(gè)int。接著,“00 00 00 00”代表的是returnTypeIdx,它的值指向的是DexTypeId列表的索引,我們找到對(duì)應(yīng)的值,也就是I,說明這個(gè)方法的返回值是int類型的。最后,我們看“94 02 00 00”,它代表的是DexTypeList的位置偏移,它的值為294h,我們找到這個(gè)位置
這里是DexTypeList結(jié)構(gòu),首先看前4個(gè)字節(jié),代表的是DexTypeItem的個(gè)數(shù),“02 00 00 00 ”也就是2,說明接下來有2個(gè)DexTypeItem的數(shù)據(jù),每個(gè)DexTypeItem占用2個(gè)字節(jié),也就是兩個(gè)都是“00 00”,它們的值是DexTypeId列表的索引,我們?nèi)フ乙幌?#xff0c;發(fā)現(xiàn)0對(duì)應(yīng)的是I,也就是說它的兩個(gè)參數(shù)都是int型的。因此這個(gè)方法的聲明我們也就確定了。也就是int(int,int),可以看看我們的源代碼,getNumber方法確實(shí)是這樣的。好,第一個(gè)方法就這樣分析完了,下面我們依舊是將這些方法的聲明整理成列表,后面可能有數(shù)據(jù)會(huì)指向它們的索引。
終于又完了一個(gè)。我們準(zhǔn)備繼續(xù)下面的。累了就先去聽聽歌吧,歇一歇再看 -_-
?
12.?fieldIdsSize和fieldIdsOff字段。這兩個(gè)字段指向的是dex文件中字段名的信息。我們看到這里
可以看到,fieldIdsSize為3h,說明共有3個(gè)字段。fieldIdsOff為158h,說明偏移為158h,我們繼續(xù)看到158h這里
咳咳,又該新的數(shù)據(jù)結(jié)構(gòu)了,再忍一忍,接下來的數(shù)據(jù)結(jié)構(gòu)是DexFieldId,我們看下
struct DexFieldId{u2 classIdx; /*類的類型,指向DexTypeId列表的索引*/u2 typeIdx; /*字段類型,指向DexTypeId列表的索引*/u4 nameIdx; /*字段名,指向DexStringId列表的索引*/ }可以看到,這三個(gè)數(shù)據(jù)都是指向的索引值,具體的就不說了,看后面的備注就是。我們依舊是分析一下第一個(gè)字段,“01 00 ,00 00,13 00 00 00”,類的類型為DexTypeId列表的索引1,也就是HelloWorld,字段的類型為DexTypeId列表中的索引0,也就是int,字段名為DexStringId列表中的索引13h,即十進(jìn)制的19,找一下,是a,也就是說我們這個(gè)字段就確認(rèn)了,即int HelloWorld.a。這不就是我們?cè)贖elloWorld.java文件里定義的變量a嘛。然后我們依次把我們所有的3個(gè)字段都列出來:
〇int HelloWorld.a , ①java.lang.String HelloWorld.b ,②java.io.PrintStream java.lang.System.out
ok,先告一段落。繼續(xù)分析下一個(gè)
?
13.?methodIdsSize和methodIdsOff字段。這倆字段指明了方法所在的類、方法的聲明以及方法名。我們看看
先是,methodIdsSize,為Ah,即十進(jìn)制的10,說明共有10個(gè)方法。methodIdsOff,為170h,說明它們的位置偏移在170h。我們看到這里
對(duì)對(duì)對(duì),又是新的數(shù)據(jù)結(jié)構(gòu),不過這個(gè)和上個(gè)一樣簡單,請(qǐng)看DexMethodId
struct DexMethodId{u2 classIdx; /*類的類型,指向DexTypeId列表的索引*/u2 protoIdx; /*聲明類型,指向DexProtoId列表的索引*/u4 nameIdx; /*方法名,指向DexStringId列表的索引*/ }對(duì)吧,這個(gè)也簡單,三個(gè)數(shù)據(jù)也都是指向?qū)?yīng)的結(jié)構(gòu)的索引值。我們直接分析一下第一個(gè)數(shù)據(jù),“01 00, 04 00, 00 00 00 00”,首先,classIdx,為1,對(duì)應(yīng)DexTypeId列表的索引1,也就是HelloWorld;其次,protoIdx,為4,對(duì)應(yīng)DexProtoId列表中的索引4,也就是void();最后,nameIdx,為0,對(duì)應(yīng)DexStringId列表中的索引0,也就是<clinit>。因此,第一個(gè)數(shù)據(jù)就出來了,即void HelloWorld.<clinit>() 。后面的不進(jìn)行分析了,我們依舊是把其余的9個(gè)方法列出來
好了,這個(gè)就算分析完了。下面真正開始我們的重頭戲了。先緩一緩再繼續(xù)吧。
?
14.?classDefsSize和classDefsOff字段。這兩個(gè)字段指明的是dex文件中類的定義的相關(guān)信息。我們先找到它們的位置。
classDefsSize字段,為1,也就是只有一個(gè)類定義,classDefsOff,為1C0h,我們找到它的偏移位置。
這里就是了,到了這里,你現(xiàn)在應(yīng)該也知道又有新的數(shù)據(jù)結(jié)構(gòu)了。對(duì)的,接下來的數(shù)據(jù)結(jié)構(gòu)是DexClassDef,請(qǐng)看
struct DexClassDef{u4 classIdx; /*類的類型,指向DexTypeId列表的索引*/u4 accessFlags; /*訪問標(biāo)志*/u4 superclassIdx; /*父類類型,指向DexTypeId列表的索引*/u4 interfacesOff; /*接口,指向DexTypeList的偏移*/u4 sourceFileIdx; /*源文件名,指向DexStringId列表的索引*/u4 annotationsOff; /*注解,指向DexAnnotationsDirectoryItem結(jié)構(gòu)*/u4 classDataOff; /*指向DexClassData結(jié)構(gòu)的偏移*/u4 staticValuesOff; /*指向DexEncodedArray結(jié)構(gòu)的偏移*/ }不多說了,我們直接根據(jù)結(jié)構(gòu)開始分析吧,反正就只有一個(gè)類定義。classIdx為1,對(duì)應(yīng)DexTypeId列表的索引1,找到是HelloWorld,確實(shí)是我們?cè)闯绦蛑械念惖念愋汀ccessFlags為1,它是類的訪問標(biāo)志,對(duì)應(yīng)的值是一個(gè)以ACC_開頭的枚舉值,1對(duì)應(yīng)的是 ACC_PUBLIC,你可以在010Editor中看一下,說明我們的類是public的。superclassIdx的值為3,找到DexTypeId列表中的索引3,對(duì)應(yīng)的是java.lang.object,說明我們的類的父類類型是Object的。interfaceOff指向的是DexTypeList結(jié)構(gòu),我們這里是0說明沒有接口。如果有接口的話直接對(duì)應(yīng)到DexTypeList,就和之前我們分析的一樣了,這里不多解釋,有興趣的可以寫一個(gè)有接口的類驗(yàn)證下。再下來sourceFileIdx指向的是DexStringId列表的索引,代表源文件名,我們這里位4,找一下對(duì)應(yīng)到了字符串"HelloWorld.java",說明我們類程序的源文件名為HelloWorld.java。annotationsOff字段指向注解目錄接口,根據(jù)類型不同會(huì)有注解類、注解方法、注解字段與注解參數(shù),我們這里的值為0,說明沒有注解,這里也不過多解釋,有興趣可以自己試試。
接下來是classDataOff了,它指向的是DexClassData結(jié)構(gòu)的位置偏移,DexClassData中存儲(chǔ)的是類的數(shù)據(jù)部分,我們開始詳細(xì)分析一下它,首先,還是先找到偏移位置3F8h
接著,我們看看DexClassData數(shù)據(jù)結(jié)構(gòu)
struct DexClassData{DexClassDataHeader header; /*指定字段與方法的個(gè)數(shù)*/DexField* staticFields; /*靜態(tài)字段,DexField結(jié)構(gòu)*/DexField* instanceFields; /*實(shí)例字段,DexField結(jié)構(gòu)*/DexMethod* directMethods; /*直接方法,DexMethod結(jié)構(gòu)*/DexMethod* virtualMethods; /*虛方法,DexMethod結(jié)構(gòu)*/ }可以看到,在DexClassData結(jié)構(gòu)中又引入了三種結(jié)構(gòu),我們一起寫出來看一下吧
struct DexClassDataHeader{u4 staticFieldsSize; /*靜態(tài)字段個(gè)數(shù)*/u4 instanceFieldsSize; /*實(shí)例字段個(gè)數(shù)*/u4 directMethodsSize; /*直接方法個(gè)數(shù)*/u4 virtualMethodsSize; /*虛方法個(gè)數(shù)*/ }struct DexField{u4 fieldIdx; /*指向DexFieldId的索引*/u4 accessFlags; /*訪問標(biāo)志*/ }struct DexMethod{u4 methodIdx; /*指向DexMethodId的索引*/u4 accessFlags; /*訪問標(biāo)志*/u4 codeOff; /*指向DexCode結(jié)構(gòu)的偏移*/ }/*指向DexFieldId的索引*/u4 accessFlags; /*訪問標(biāo)志*/ }struct DexMethod{u4 methodIdx; /*指向DexMethodId的索引*/u4 accessFlags; /*訪問標(biāo)志*/u4 codeOff; /*指向DexCode結(jié)構(gòu)的偏移*/ }代碼中的注釋寫的也都很清楚了,我們就不多說了。但是請(qǐng)注意,在這些結(jié)構(gòu)中的u4不是指的占用4個(gè)字節(jié),而是指它們是uleb128類型(占用1~5個(gè)字節(jié))的數(shù)據(jù)。關(guān)于uleb128還是不多說,想了解的可以自己查查看。
好,接下來開始分析,對(duì)于DexClassData,第一個(gè)為DexClassDataHeader,我們找到相應(yīng)的位置,第一個(gè)staticFieldsSize其實(shí)只占用了一個(gè)字節(jié),即01h就是它的值,也就是說共有一個(gè)靜態(tài)字段,接下來instanceFieldsSize,directMethodsSize,virtualMethodsSize也都是只占用了一個(gè)字節(jié),即實(shí)例字段的個(gè)數(shù)為1,直接方法的個(gè)數(shù)為3,虛方法的個(gè)數(shù)為1。(這里只是湊巧它們幾個(gè)都占用一個(gè)字節(jié),并不一定是只占用一個(gè)字節(jié),這關(guān)于到uleb128數(shù)據(jù)類型,具體可以自己了解下)。
然后接下來就是staticFields了,它對(duì)應(yīng)的數(shù)據(jù)結(jié)構(gòu)為DexField,可以看到,第一個(gè)fieldIdx,是指向DexFieldId的索引,值為1,找到對(duì)應(yīng)的索引值為java.lang.String HelloWorld.b。第二個(gè)accessFlags,值為8,對(duì)應(yīng)的ACC_開頭的數(shù)據(jù)為ACC_STATIC(可以在010Editor中對(duì)應(yīng)查看一下),說明我們這個(gè)靜態(tài)字段為:static java.lang.String HelloWorld.b。可以對(duì)應(yīng)我們的源代碼看一下,我們確實(shí)定義了一個(gè)static的b變量。
接著看instanceFields,它和staticFields對(duì)應(yīng)的數(shù)據(jù)結(jié)構(gòu)是一樣的,我們直接分析,第一個(gè)fieldIdx,值為0,對(duì)應(yīng)的DexField的索引值為int HelloWorld.a。第二個(gè)accessFlags,值為0,對(duì)應(yīng)的ACC_開頭的數(shù)據(jù)為空,就是什么也沒有。說明我們這個(gè)實(shí)例字段為:int HelloWorld.a。可以對(duì)應(yīng)我們的源碼 看看,我們確實(shí)定義了一個(gè)a實(shí)例變量。
再接著,根據(jù)directMethodsSize,有3個(gè)直接方法,我們先看第一個(gè),它對(duì)應(yīng)的數(shù)據(jù)結(jié)構(gòu)是DexMethod,首先methodIdx指向的是DexMethodId的索引,值為0,找到對(duì)應(yīng)的索引值為void HelloWorld.<clinit>()。然后accessFlages為......為......為....我的個(gè)天!我以為就這樣能蒙混過關(guān)了,沒想到還真碰到一個(gè)uleb128數(shù)據(jù)不是占用一個(gè)字節(jié)的,這個(gè)accessFlags對(duì)應(yīng)的值占用了三個(gè)字節(jié),“88 80 04”,為什么?因?yàn)槭前凑誹leb128格式的數(shù)據(jù)讀出來的(還是自己去查查吧,這個(gè)坑先不填了,其實(shí)這種數(shù)據(jù)也不麻煩,就是前面字節(jié)上的最高位指定了是否需要下一個(gè)字節(jié)上的內(nèi)容)。“88 80 04”對(duì)應(yīng)的ACC_開頭的數(shù)據(jù)為 ACC_STATIC ACC_CONSTRUCTOR,表明這個(gè)方法是靜態(tài)的,并且是構(gòu)造方法。最后,看看codeOff,它對(duì)應(yīng)了DexCode結(jié)構(gòu)的偏移,DexCode中存放了方法的指令集等信息,也就是真正的代碼了。我們暫且不分析DexCode,就先看看它的偏移位置為“E0 03”,這個(gè)等于多少呢?uleb128轉(zhuǎn)化為16進(jìn)制數(shù)結(jié)果為:1E0h。也就是DexCode存放在偏移位置1E0h的位置上。
具體的DexCode我們就先不分析了,因?yàn)樗锩娲娣诺囊恍┲噶罹中枰鶕?jù)相關(guān)資料一一查找,有興趣的自己可以找資料看看。剩下的兩個(gè)直接方法我們也不分析了。
接下來,我們看根據(jù)virtualMethodsSize,有1個(gè)虛方法,我們直接看。首先methodIdx的值為2,對(duì)應(yīng)的DexMethodId的索引值為int HelloWorld.getNumber(int, int)。然后accessFlags為1,對(duì)應(yīng)的值為ACC_PUBLIC,表明這是一個(gè)public類。codeOff為“FC 04”,對(duì)應(yīng)的位置為27Ch,這里就不上圖了,自己找找吧。
好了,我們整個(gè)DEX文件的結(jié)構(gòu)就這樣從DexHeader開始基本分析完了,好累啊,不過這樣分析一遍,對(duì)DEX文件的格式會(huì)有更深刻的認(rèn)識(shí)。總是看別人的真不如自己來一遍來的實(shí)在!
?
?
0x03■ ?參考資料
參考資料:
《Android軟件安全與逆向分析》.非蟲
?
?
?
?
總結(jié)
以上是生活随笔為你收集整理的一篇文章带你搞懂 DEX 文件的结构的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 简明Python教程学习笔记_3_模块
- 下一篇: MFC 最详细入门教程