什么是dex文件
他是Android系統(tǒng)的可執(zhí)行文件,包含應(yīng)用程序的全部操作指令以及運(yùn)行時(shí)數(shù)據(jù)舆乔。
由于dalvik是一種針對嵌入式設(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%左右
下面來剖析一個(gè)類文件編譯后生成的dex文件,看看具體有哪些東西鳞上。
使用軟件:
16近制查看器 ?010Editor
java編輯器
在開始前首先需要了解下dex文件包含的大體內(nèi)容:
java文件編譯成dex文件
編寫一個(gè)簡單的Java類这吻,命名為HelloWorld.class 然后將其編譯成dex文件:打開命令行,進(jìn)入 HelloWorld.class 所在文件夾下篙议,執(zhí)行命令:
javacHelloWorld.java
接下來會出現(xiàn)一個(gè)HelloWorld.class文件橘原,然后繼續(xù)執(zhí)行命令:
dx--dex--output=HelloWorld.dexHelloWorld.class
用010editor查看dex文件內(nèi)容
下載完成之后,我們可以用它打開dex文件了涡上,打開之后趾断,你的界面應(yīng)該是這樣的:
大體可以分為3個(gè)區(qū):三個(gè)區(qū)
那么我們來一步步分析,看下圖:
首先吩愧,我們看到 DexHeader 中每個(gè)數(shù)據(jù)前面有個(gè)u1或者u4芋酌,它們代表的意思就是 1個(gè) 或者 4個(gè) 字節(jié)的無符號數(shù)。
依次根據(jù) Header 中的數(shù)據(jù)段進(jìn)行解釋為:
magic[8]雁佳;它代表dex中的文件標(biāo)識脐帝,一般被稱為魔數(shù)。是用來識別dex這種文件的糖权,它可以判斷當(dāng)前的dex文件是否有效堵腹,可以看到它用了8個(gè)1字節(jié)的無符號數(shù)來表示,我們在010Editor中可以看到也就是“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(其中,'.' 不是轉(zhuǎn)化來的)如暖。目前笆檀,dex的魔數(shù)固定為dex.035。
checksum;? 它是dex文件的校驗(yàn)和盒至,通過它可以判斷dex文件是否被損壞或者被篡改酗洒。它占用4個(gè)字節(jié)士修,也就是“5D 9D F9 59”。這里提醒一下樱衷,在010Editor中棋嘲,其實(shí)可以分別識別我們在DexHeader中看到的這些字段的,你可以點(diǎn)一下這里:
你可以看到這個(gè) header列表 展開了箫老,其實(shí)我們分析下來就和它這個(gè)結(jié)構(gòu)是一樣的封字,你可以先看下黔州,我們現(xiàn)在分析到了 checksum 中了耍鬓,你可以看到后面對應(yīng)的值是“59 F9 9D 5D”。咦流妻?這好像和上面的字節(jié)不是一一對應(yīng)的啊牲蜀。對的涣达,你可以發(fā)現(xiàn)它是反著寫的度苔。這是由于dex文件中采用的是小字節(jié)序的編碼方式寇窑,也就是低位上存儲的就是低字節(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對應(yīng)的16進(jìn)制數(shù)為70h壳炎,你可以選中頭文件看看010Editor是不是真的占用了這么多:
6.第6個(gè)是endianTag;代表 字節(jié)序標(biāo)記,用于指定dex運(yùn)行環(huán)境的cpu撒汉,預(yù)設(shè)值為0x12345678睬辐,對應(yīng)在101Editor中為“78 56 34 12”(小字節(jié)序)。
7.接下來兩個(gè)分別是linkSize和 u4linkOff;這兩個(gè)字段丰刊,它們分別指定了鏈接段的大小和文件偏移啄巧,通常情況下它們都為0码泛。linkSize為0的話表示靜態(tài)鏈接噪珊。
8.再下來就是mapOff字段了痢站,它指定了 DexMapList 的文件偏移搀捷,這里我們先不過多介紹它嫩舟,你可以看一下它的值為“14 04 00 00”播玖,它其實(shí)對應(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é),也就是說它里面存的還不是真正的字符串涧郊,它們只是存儲了真正字符串的偏移位置彤灶。
下面我們先分析幾個(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)是练湿,這里湊巧這幾個(gè)uleb128編碼的字符都用了1個(gè)字節(jié)疾渣,所以我們可以這樣進(jìn)行查詢朱浴,uleb128編碼標(biāo)準(zhǔn)用的是1~5個(gè)字節(jié), 這里只是恰好都是一個(gè)字節(jié))廷支。也就是說上面的70h開始的第一個(gè) DexStringId 指向的其實(shí)是字符串“”(但是貌似我們的代碼中沒有用到這個(gè)字符串啊,先不用管,我們接著分析)。再看到這里:
②剛剛我們分析到“B2 02 00 00”所指向的真實(shí)字符串了先壕,下面我們接著再分析一個(gè)规辱,我們直接分析第三個(gè),不分析第二個(gè)了仰猖。第三個(gè)為“C4 02 00 00”,對應(yīng)的位置也就是2C4h煮甥,我們找到它:
看這里砚偶,這就是2C4h的位置了单鹿。我們首先看第一個(gè)字符儒喊,它的值為0Bh芯义,也就是十進(jìn)制的11鬼癣,也就是說接下來的11個(gè)字符代表了它的字符串章郁,我們依舊是查看接下來11個(gè)字符代表的是什么惹悄,經(jīng)過查詢整理:
上面就是“HelloDalvik”這個(gè)字符串踩窖,可以看看我們的代碼徐矩,我們確實(shí)用了一個(gè)這樣的字符串窒百,bingo。
下面剩下的字符串就不分析了绪励。經(jīng)過整理,可以整理出我們一共用到的28個(gè)字符串為:
ok懈凹,字符串這里告一段落爬舰,下面我們繼續(xù)看DexHeader的下面的字段们陆。頭好暈~乎乎
噢,對了情屹,還不能結(jié)束呢坪仇,你現(xiàn)在可以看一下最開始發(fā)的那張dex結(jié)構(gòu)圖了:
看到了吧,我們這半天分析的 stringIdsSize 和 stringIdsOff 字段指向的位置就是上面那個(gè)箭頭指向的位置垃你,它們里面存儲的是真實(shí)字符串的位置偏移椅文,它們都存儲在data區(qū)域喂很。(先透露一下,后面我們要分析的幾個(gè)也和stringIdsSize 與 stringIdsOff 字段類似皆刺,它們里面存儲的基本都是位置偏移少辣,并不是真正的數(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è)變量,如下所示:
看到了吧乾颁,這就是 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送丰。然后我們在上面所有整理出的字符串看看5索引的是什么?翻上去可以看到是“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è)位置
看到了吧碴倾,這里就是了逗噩。對,下面又有新的數(shù)據(jù)結(jié)構(gòu)了跌榔。這下一個(gè)數(shù)據(jù)結(jié)構(gòu)不能滿足這塊的內(nèi)容了异雁,我們先看第一個(gè)數(shù)據(jù)結(jié)構(gòu),DexProtoId
可以看到僧须,這個(gè)數(shù)據(jù)結(jié)構(gòu)由三個(gè)變量組成纲刀。第一個(gè) shortyIdx 它指向的是我們上面分析的 DexStringId 列表的索引,代表的是方法聲明字符串担平。第二個(gè) returnTypeIdx 它指向的是 我們上邊分析的 DexTypeId 列表的索引示绊,代表的是方法返回類型字符串。第三個(gè) parametersOff 指向的是 DexTypeList 的位置索引暂论,這又是一個(gè)新的數(shù)據(jù)結(jié)構(gòu)了面褐,先說一下這里面存儲的是方法的參數(shù)列表】樟恚可以看到這三個(gè)參數(shù)盆耽,有方法聲明字符串,有返回類型扼菠,有方法的參數(shù)列表摄杂,這基本上就確定了我們一個(gè)方法的大體內(nèi)容。
我們接著看看 DexTypeList 這個(gè)數(shù)據(jù)結(jié)構(gòu)循榆,看看參數(shù)列表是如何存儲的析恢。
看到了嘛,它有兩個(gè)參數(shù)秧饮,其中第一個(gè) size 說的是 DexTypeItem 的個(gè)數(shù)映挂,那 DexTypeItem 又是啥咧泽篮?它又是一種數(shù)據(jù)結(jié)構(gòu)。我們繼續(xù)看看
恩柑船,還好帽撑,里面就一個(gè)參數(shù)。也比較簡單鞍时,就是一個(gè)指向 DexTypeId 列表的索引亏拉,也就是代表參數(shù)列表中某一個(gè)具體的參數(shù)的位置。
分析完這幾個(gè)數(shù)據(jù)結(jié)構(gòu)了逆巍,下面我們具體地分析一個(gè)類吧及塘。別走神,我們該從上圖的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è)對應(yīng)的值全陨,也就是III,說明這個(gè)方法中聲明字符串為三個(gè)int衷掷。接著辱姨,“00 00 00 00”代表的是 returnTypeIdx,它的值指向的是 DexTypeId 列表的索引戚嗅,我們找到對應(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)フ乙幌伦墩l(fā)現(xiàn)0對應(yīng)的是I陡蝇,也就是說它的兩個(gè)參數(shù)都是int型的。因此這個(gè)方法的聲明我們也就確定了哮肚。也就是int(int,int),可以看看我們的源代碼广匙,getNumber方法 確實(shí)是這樣的允趟。好,第一個(gè)方法就這樣分析完了鸦致,下面我們依舊是將這些方法的聲明整理成列表潮剪,后面可能有數(shù)據(jù)會指向它們的索引。
終于又完了一個(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怖辆,我們看下
可以看到,這三個(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愁茁。這不就是我們在HelloWorld.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。我們看到這里
對對對襟士,又是新的數(shù)據(jù)結(jié)構(gòu)盗飒,不過這個(gè)和上個(gè)一樣簡單,請看 DexMethodId
對吧陋桂,這個(gè)也簡單逆趣,三個(gè)數(shù)據(jù)也都是指向?qū)?yīng)的結(jié)構(gòu)的索引值。我們直接分析一下第一個(gè)數(shù)據(jù)嗜历,“01 00, 04 00宣渗, 00 00 00 00”,首先梨州,classIdx痕囱,為1,對應(yīng) DexTypeId 列表的索引1暴匠,也就是HelloWorld鞍恢;其次,protoIdx,為4帮掉,對應(yīng) DexProtoId 列表中的索引4弦悉,也就是void();最后蟆炊,nameIdx稽莉,為0,對應(yīng) DexStringId 列表中的索引0涩搓,也就是污秆。因此昧甘,第一個(gè)數(shù)據(jù)就出來了疾层,即void HelloWorld.() 将饺。后面的不進(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)了庄涡。對的,接下來的數(shù)據(jù)結(jié)構(gòu)是DexClassDef撕捍,請看:
不多說了忧风,我們直接根據(jù)結(jié)構(gòu)開始分析吧狮腿,反正就只有一個(gè)類定義。classIdx 為1贴硫,對應(yīng) DexTypeId 列表的索引 1夜畴,找到是HelloWorld删壮,確實(shí)是我們源程序中的類的類型。
accessFlags 為 1苞也,它是類的訪問標(biāo)志如迟,對應(yīng)的值是一個(gè)以ACC_開頭的枚舉值攻走,1對應(yīng)的是 ACC_PUBLIC昔搂,你可以在010Editor中看一下,說明我們的類是public的贤斜。
superclassIdx的值為 3瘩绒,找到DexTypeId列表中的索引3带族,對應(yīng)的是java.lang.object炉菲,說明我們的類的父類類型是Object的拍霜。interfaceOff指向的是DexTypeList結(jié)構(gòu)薪介,我們這里是0說明沒有接口汁政。如果有接口的話直接對應(yīng)到 DexTypeList记劈,就和之前我們分析的一樣了,這里不多解釋换途,有興趣的可以寫一個(gè)有接口的類驗(yàn)證下军拟。
再下來 sourceFileIdx 指向的是 DexStringId 列表的索引懈息,代表源文件名,我們這里位4怒见,找一下對應(yīng)到了字符串"HelloWorld.java"速种,說明我們類程序的源文件名為 HelloWorld.java低千。
annotationsOff 字段指向注解目錄接口示血,根據(jù)類型不同會有注解類难审、注解方法告喊、注解字段與注解參數(shù),我們這里的值為0拢切,說明沒有注解淮椰,這里也不過多解釋纳寂,有興趣可以自己試試毙芜。
接下來是 classDataOff 了爷肝,它指向的是 DexClassData 結(jié)構(gòu)的位置偏移,DexClassData 中存儲的是類的數(shù)據(jù)部分金赦,我們開始詳細(xì)分析一下它夹抗,首先漠烧,還是先找到偏移位置3F8h
接著已脓,我們看看 DexClassData 數(shù)據(jù)結(jié)構(gòu):
可以看到度液,在 DexClassData 結(jié)構(gòu)中又引入了三種結(jié)構(gòu)堕担,我們一起寫出來看一下吧
代碼中的注釋寫的也都很清楚了霹购,我們就不多說了齐疙。但是請注意,在這些結(jié)構(gòu)中的 u4 不是指的占用4個(gè)字節(jié)把将,而是指它們是 uleb128類型(占用1~5個(gè)字節(jié))的數(shù)據(jù)。關(guān)于uleb128還是不多說催训,想了解的可以自己查查看漫拭。
好混稽,接下來開始分析膳叨,對于 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 了铡俐,它對應(yīng)的數(shù)據(jù)結(jié)構(gòu)為 DexField审丘,可以看到勾给,第一個(gè) fieldIdx播急,是指向 DexFieldId 的索引桩警,值為1捶枢,找到對應(yīng)的索引值為 java.lang.String HelloWorld.b烂叔。第二個(gè) accessFlags,值為8胯努,對應(yīng)的ACC_開頭的數(shù)據(jù)為ACC_STATIC(可以在010Editor中對應(yīng)查看一下)康聂,說明我們這個(gè)靜態(tài)字段為:static java.lang.String HelloWorld.b」嘉埃可以對應(yīng)我們的源代碼看一下导狡,我們確實(shí)定義了一個(gè)static的b變量旱捧。
接著看 instanceFields枚赡,它和 staticFields 對應(yīng)的數(shù)據(jù)結(jié)構(gòu)是一樣的贫橙,我們直接分析,第一個(gè) fieldIdx疲迂,值為0尤蒿,對應(yīng)的 DexField 的索引值為int HelloWorld.a土铺。第二個(gè) accessFlags媒峡,值為0,對應(yīng)的ACC_開頭的數(shù)據(jù)為空甘桑,就是什么也沒有跑杭。說明我們這個(gè)實(shí)例字段為:int HelloWorld.a德谅≌觯可以對應(yīng)我們的源碼看看,我們確實(shí)定義了一個(gè)a實(shí)例變量组砚。
再接著糟红,根據(jù) directMethodsSize盆偿,有3個(gè)直接方法陈肛,我們先看第一個(gè)句旱,它對應(yīng)的數(shù)據(jù)結(jié)構(gòu)是 DexMethod谈撒,首先 methodIdx 指向的是 DexMethodId 的索引啃匿,值為0溯乒,找到對應(yīng)的索引值為 void HelloWorld.()裆悄。然后 accessFlages 為......為......為....我的個(gè)天光稼!我以為就這樣能蒙混過關(guān)了艾君,沒想到還真碰到一個(gè)uleb128數(shù)據(jù)不是占用一個(gè)字節(jié)的冰垄,這個(gè) accessFlags 對應(yīng)的值占用了三個(gè)字節(jié),“88 80 04”伴奥,為什么?因?yàn)槭前凑?uleb128格式 的數(shù)據(jù)讀出來的(還是自己去查查吧感局,這個(gè)坑先不填了询微,其實(shí)這種數(shù)據(jù)也不麻煩,就是前面字節(jié)上的最高位指定了是否需要下一個(gè)字節(jié)上的內(nèi)容)藻雌】韬迹“88 80 04”對應(yīng)的ACC_開頭的數(shù)據(jù)為 ACC_STATIC ACC_CONSTRUCTOR做个,表明這個(gè)方法是靜態(tài)的居暖,并且是構(gòu)造方法太闺。
最后跟束,看看 codeOff冀宴,它對應(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涂召,對應(yīng)的 DexMethodId 的索引值為 int HelloWorld.getNumber(int, int)。然后accessFlags 為1敏沉,對應(yīng)的值為 ACC_PUBLIC果正,表明這是一個(gè) public 類。codeOff 為“FC 04”盟迟,對應(yīng)的位置為27Ch秋泳,這里就不上圖了,自己找找吧攒菠。
此篇文章由來:?
?部分來源于郭霖的微信公眾號Tryking的投稿迫皱。由操作和練習(xí)一遍有感而寫。作為學(xué)習(xí)記錄于此辖众。
參考自http://www.cnblogs.com/dacainiao/p/6035274.html
http://mp.weixin.qq.com/s/UgamIOqKd7McXUd1DosZcQ
http://blog.csdn.net/jiangwei0910410003/article/details/50668549
感謝以上作者卓起。