一篇文章帶你搞懂DEX文件的結構

*本篇文章已受權微信公衆號 guolin_blog (郭霖)獨家發佈
html


DEX文件就是Android Dalvik虛擬機運行的程序,關於DEX文件的結構的重要性我就很少說了。下面,開練!java

建議:不要只看,跟着我作。看再多遍不如本身親自實踐一遍來的可靠,別問我爲何知道。淚崩ing.....安全

首先,咱們須要本身構造一個dex文件,由於本身構造的比較簡單,分析起來比較容易。等你簡單的會了,難的天然也就懂了。微信


0x00■  構造DEX文件

首先,咱們編寫一個簡單的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文件:打開命令行,進入HelloWorld.class所在文件夾下,執行命令:

javac HelloWorld.java編輯器

接下來會出現一個HelloWorld.class文件,而後繼續執行命令:
編碼

dx --dex --output=HelloWorld.dex HelloWorld.classspa

就會出現HelloWorld.dex文件了。這時,咱們須要下載一個十六進位文本編輯器,由於用它能夠解析二進制文件,咱們用它打開dex文件就會所有以十六進制的數進行展示了。這裏推薦010Editor,下載地址:010Editor(收費軟件,能夠免費試用30天)。
命令行

下載完成以後,咱們能夠用它打開dex文件了,打開以後,你的界面應該是這樣的:
3d


一會兒看到這些東西,是否是立馬懵逼了,正常,我剛開始看的時候也是,這什麼玩意兒啊!其實,這就是二進制流文件中的內容,010Editor把它轉化成了16進制的內容,以方便咱們閱讀的。


0x01■  DEX文件結構總覽

不要慌,下面我跟你解釋,這些東西咱們雖然看了懵逼,可是Dalvik虛擬機不會,由於它就是解析這些東西的,這些東西雖然看起來頭大,可是它是有本身的格式標準的。dex文件的結構以下圖所示:


這就是dex的文件格式了,下面咱們從最上面的Header提及,Header中存儲了什麼內容呢?下面咱們還得來一張圖:



0x02■  DEX文件結構解析

先看下就行,不用着急,下面咱們一步一步來,首先點擊你的010Editor的這裏:


對,就是箭頭指的那裏,點擊以後,你會發現上面的有一片區域成了選中的顏色,這部分裏面存儲的就是Header中的數據了,下面咱們根據Header的數據圖以此來進行分析。

首先,咱們看到DexHeader中每一個數據前面有個u1或者u4,這個是什麼意思呢?它們其實就是表明1個或者4個字節的無符號數。下面咱們依次根據Header中的數據段進行解釋。

1. 從第一個看起,magic[8];它表明dex中的文件標識,通常被稱爲魔數。是用來識別dex這種文件的,它能夠判斷當前的dex文件是否有效,能夠看到它用了8個1字節的無符號數來表示,咱們在010Editor中能夠看到也就是「64 65 78 0A 30 33 35 00 」這8個字節,這些字節都是用16進製表示的,用16進製表示的話,兩個數表明一個字節(一個字節等於8位,一個16進制的數能表示4位)。這8個字節用ASCII碼錶轉化一下能夠轉化爲:dex.035(點擊這裏能夠進行十六進制轉ASCII,你能夠試試:其中,'.' 不是轉化來的)。目前,dex的魔數固定爲dex.035。

2. 第二個是,checksum;  它是dex文件的校驗和,經過它能夠判斷dex文件是否被損壞或者被篡改。它佔用4個字節,也就是「5D 9D F9 59」。這裏提醒一下,在010Editor中,其實能夠分別識別咱們在DexHeader中看到的這些字段的,你能夠點一下這裏:


你能夠看到這個header列表展開了,其實咱們分析下來就和它這個結構是同樣的,你能夠先看下,咱們如今分析到了checksum中了,你能夠看到後面對應的值是「59 F9 9D 5D」。咦?這好像和上面的字節不是一一對應的啊。對的,你能夠發現它是反着寫的。這是因爲dex文件中採用的是小字節序的編碼方式,也就是低位上存儲的就是低字節內容,因此它們應該要反一下。

3. 第三個到了signature[kSHA1DigestLen]了,signature字段用於檢驗dex文件,其實就是把整個dex文件用SHA-1簽名獲得的一個值。這裏佔用20個字節,你能夠本身點010Editor看一看。

4. 第四個fileSize;表示整個文件的大小,佔用4個字節。

5. 第五個headerSize;表示DexHeader頭結構的大小,佔用4個字節。這裏能夠看到它一共佔用了112個字節,112對應的16進制數爲70h,你能夠選中頭文件看看010Editor是否是真的佔用了這麼多:


6. 第6個是endianTag;表明 字節序標記,用於指定dex運行環境的cpu,預設值爲0x12345678,對應在101Editor中爲「78 56 34 12」(小字節序)。

7. 接下來兩個分別是linkSize;和u4  linkOff;這兩個字段,它們分別指定了連接段的大小和文件偏移,一般狀況下它們都爲0。linkSize爲0的話表示靜態連接。

8. 再下來就是mapOff字段了,它指定了DexMapList的文件偏移,這裏咱們先不過多介紹它,你能夠看一下它的值爲「14 04 00 00」,它其實對應的16進制數就是414h(別忘了小字節序),咱們能夠在414h的位置看一下它在哪裏:


其實就是dex文件最後一部份內容。關於這部份內容裏面是什麼,咱們先不說,繼續往下看。

9. stringIdsSize stringIdsOff字段:這兩個字段指定了dex文件中全部用到的字符串的個數和位置偏移,咱們先看stringIdsSize,它的值爲:「1C 00 00 00」,16進制的1C也就是十進制的28,也就是說咱們這個dex文件中一共有28個字符串,而後stringIdsOff爲:「70 00 00 00」,表明字符串的偏移位置爲70h,這下咱們找到70h的地方:   


這下咱們就要先介紹一下DexStringId這個結構了,圖中從70h開始,全部被選中的都是DexStringId這種數據結構的內容,DexStringId表明的是字符串的位置偏移,每一個DexStringId佔用4個字節,也就是說它裏面存的還不是真正的字符串,它們只是存儲了真正字符串的偏移位置。

下面咱們先分析幾個看看,

①取第一個「B2 02 00 00」,它表明的位置偏移是2B2h,咱們先找到這個位置:    


能夠發現我一共選中了10個字節,這10個字節就表示了一個字符串。下面咱們看一下dex文件中的字符串是如何表示的。dex中的字符串採用了一種叫作MUTF-8這樣的編碼,它是通過傳統的UTF-8編碼修改的。在MTUF-8中,它的頭部存放的是由uleb128編碼的字符的個數。(至於uleb128編碼是什麼編碼,這裏我不詳細展開說,有興趣的能夠搜索看看。)

也就是說在「08 3C 63 6C 69 6E 69 74 3E 00」這些字節中,第一個08指定的是後面須要用到的編碼的個數,也就是8個,即「 3C 63 6C 69 6E 69 74 3E」這8個,可是咱們爲何一共選中了10個字節呢,由於最後一個空字符「0」表示的是字符串的結尾,字符個數沒有把它算進去。下面咱們來看看「 3C 63 6C 69 6E 69 74 3E」這8個字符表明了什麼字符串:


依舊能夠點這裏查詢ASCII 。(要說明的一點是,這裏湊巧這幾個uleb128編碼的字符都用了1個字節,因此咱們能夠這樣進行查詢,uleb128編碼標準用的是1~5個字節, 這裏只是剛好都是一個字節)。也就是說上面的70h開始的第一個DexStringId指向的實際上是字符串「<clinit>」(可是貌似咱們的代碼中沒有用到這個字符串啊,先不用管,咱們接着分析)。再看到這裏:

 

②剛剛咱們分析到「B2 02 00 00」所指向的真實字符串了,下面咱們接着再分析一個,咱們直接分析第三個,不分析第二個了。第三個爲「C4 02 00 00」,對應的位置也就是2C4h,咱們找到它:


看這裏,這就是2C4h的位置了。咱們首先看第一個字符,它的值爲0Bh,也就是十進制的11,也就是說接下來的11個字符表明了它的字符串,咱們依舊是查看接下來11個字符表明的是什麼,通過查詢整理:   


依舊能夠在這裏查詢ASCII。上面就是「HelloDalvik」這個字符串,能夠看看咱們的代碼,咱們確實用了一個這樣的字符串,bingo。

下面剩下的字符串就不分析了。通過整理,能夠整理出咱們一共用到的28個字符串爲:


ok,字符串這裏告一段落,下面咱們繼續看DexHeader的下面的字段。頭好暈~乎乎

 噢,讀了,還不能結束呢,你如今能夠看一下最開始發的那張dex結構圖了:


看到了吧,咱們這半天分析的stringIdsSize 和 stringIdsOff字段指向的位置就是上面那個箭頭指向的位置,它們裏面存儲的是真實字符串的位置偏移,它們都存儲在data區域。(先透露一下,後面咱們要分析的幾個也和stringIdsSize 與stringIdsOff字段相似,它們裏面存儲的基本都是位置偏移,並非真正的數據,真正的數據都在data區域)

好,咱們繼續。

10. 繼續看DexHeader圖,咱們如今該typeIdsSizetypeIdsOff了。它們表明什麼呢?它們表明的是類的類型的數量和位置偏移,也是都佔4個字節,下面咱們看它們的值

能夠看到,typeIdsSize的值爲9h,也就是咱們dex文件中用到的類的類型一共有9個,位置偏移在E0h位置,下面咱們找到這個位置


看到了吧,我選中的位置就是了。這裏咱們又得介紹一種數據結構了,由於這裏的數據也是一種數據結構的數據組成的。那就是DexTypeId,也就是說選中的內容都是DexTypeId這種數據,這種數據結構中只有一個變量,以下所示:

struct DexTypeId{
	u4 descriptorIdx;	/*指向DexStringId列表的索引*/
}
看到了吧,這就是DexTypeId數據結構,它裏面只有一個數據descriptorIdx,它的值的內容是DexStringId列表的索引。還記得DexStringId是什麼嗎?在上面咱們分析字符串時,字符串的偏移位置就是由DexStringId這種數據結構描述的,也就是說descriptorIdx指向的是全部的DexStringId組成的列表的索引。上面咱們整理出了全部的字符串,你能夠翻上去看看圖。而後咱們看這裏一共是9個類的類型表明的都是什麼。先看第一個「05 00 00 00」,也就是05h,即十進位的5。而後咱們在上面全部整理出的字符串看看5索引的是什麼?翻上去能夠看到是「I」。接下來咱們依次整理這些類的類型,也能夠獲得類的類型的列表

看到了吧,這就是咱們dex文件中全部用到的類的類型。好比「I」表明的就是int,LHelloWorld表明的就是HelloWorld,Ljava/io/PrintStream表明的就是java.io.PrintStream。後面的幾個先就不說了。咱們接着往下分析。

11. 這下到了protoIdsSizeprotoIdsOff了,它們表明的是dex文件中方法原型的個數和位置偏移。咱們先看它們的值


如上圖就是它們的值了,protoIdsSize的值爲十進制的7,說明有7個方法原型,而後位置偏移爲104h,咱們找到這個位置


看到了吧,這裏就是了。對,下面又有新的數據結構了。這下一個數據結構不能知足這塊的內容了,咱們先看第一個數據結構,DexProtoId

struct DexProtoId{
	u4 shortyIdx;			/*指向DexStringId列表的索引*/
	u4 returnTypeIdx;		/*指向DexTypeId列表的索引*/
	u4 parametersOff;		/*指向DexTypeList的位置偏移*/
}
能夠看到,這個數據結構由三個變量組成。第一個shortyIdx它指向的是咱們上面分析的DexStringId列表的索引,表明的是方法聲明字符串。第二個returnTypeIdx它指向的是 咱們上邊分析的DexTypeId列表的索引,表明的是方法返回類型字符串。第三個parametersOff指向的是DexTypeList的位置索引,這又是一個新的數據結構了,先說一下這裏面 存儲的是方法的參數列表。能夠看到這三個參數,有方法聲明字符串,有返回類型,有方法的參數列表,這基本上就肯定了咱們一個方法的大致內容。

咱們接着看看DexTypeList這個數據結構,看看參數列表是如何存儲的。

struct DexTypeList{
	u4 size;		/*DexTypeItem的個數*/
	DexTypeItem list[1];	/*DexTypeItem結構*/
}
看到了嘛,它有兩個參數,其中第一個size說的是DexTypeItem的個數,那DexTypeItem又是啥咧?它又是一種數據結構。咱們繼續看看

struct DexTypeItem{
	u2 typeIdx;				/*指向DexTypeId列表的索引*/
}
恩,還好,裏面就一個參數。也比較簡單,就是一個指向DexTypeId列表的索引,也就是表明參數列表中某一個具體的參數的位置。

分析完這幾個數據結構了,下面咱們具體地分析一個類吧。別走神,咱們該從上圖的104h開始了。

在104h這裏,因爲 都是DexProtoId這種數據結構的數據,一個DexProtoId一共佔用12個字節。因此,咱們取前12個字節進行分析。「06 00 00 00,00 00 00 00,94 02 00 00」,這就是那12個字節了。首先「06 00 00 00」表明的是shortyIdx,它的值是指向DexStringId列表的索引,咱們找到DexStringId列表中第6個對應的值,也就是III,說明這個方法中聲明字符串爲三個int。接着,「00 00 00 00」表明的是returnTypeIdx,它的值指向的是DexTypeId列表的索引,咱們找到對應的值,也就是I,說明這個方法的返回值是int類型的。最後,咱們看「94 02 00 00」,它表明的是DexTypeList的位置偏移,它的值爲294h,咱們找到這個位置


這裏是DexTypeList結構,首先看前4個字節,表明的是DexTypeItem的個數,「02 00 00 00 」也就是2,說明接下來有2個DexTypeItem的數據,每一個DexTypeItem佔用2個字節,也就是兩個都是「00 00」,它們的值是DexTypeId列表的索引,咱們去找一下,發現0對應的是I,也就是說它的兩個參數都是int型的。所以這個方法的聲明咱們也就肯定了。也就是int(int,int),能夠看看咱們的源代碼,getNumber方法確實是這樣的。好,第一個方法就這樣分析完了,下面咱們依舊是將這些方法的聲明整理成列表,後面可能有數據會指向它們的索引。



終於又完了一個。咱們準備繼續下面的。累了就先去聽聽歌吧,歇一歇再看 -_-

12. fieldIdsSizefieldIdsOff字段。這兩個字段指向的是dex文件中字段名的信息。咱們看到這裏


能夠看到,fieldIdsSize爲3h,說明共有3個字段。fieldIdsOff爲158h,說明偏移爲158h,咱們繼續看到158h這裏


咳咳,又該新的數據結構了,再忍一忍,接下來的數據結構是DexFieldId,咱們看下

struct DexFieldId{
	u2 classIdx;		/*類的類型,指向DexTypeId列表的索引*/
	u2 typeIdx;		/*字段類型,指向DexTypeId列表的索引*/
	u4 nameIdx;		/*字段名,指向DexStringId列表的索引*/
}
能夠看到,這三個數據都是指向的索引值,具體的就不說了,看後面的備註就是。咱們依舊是分析一下第一個字段,「01 00 ,00 00,13 00 00 00」,類的類型爲DexTypeId列表的索引1,也就是HelloWorld,字段的類型爲DexTypeId列表中的索引0,也就是int,字段名爲DexStringId列表中的索引13h,即十進制的19,找一下,是a,也就是說咱們這個字段就確認了,即int HelloWorld.a。這不就是咱們在HelloWorld.java文件裏定義的變量a嘛。而後咱們依次把咱們全部的3個字段都列出來:

〇int HelloWorld.a , ①java.lang.String HelloWorld.b ,②java.io.PrintStream java.lang.System.out

ok,先告一段落。繼續分析下一個

13. methodIdsSizemethodIdsOff字段。這倆字段指明瞭方法所在的類、方法的聲明以及方法名。咱們看看



先是,methodIdsSize,爲Ah,即十進制的10,說明共有10個方法。methodIdsOff,爲170h,說明它們的位置偏移在170h。咱們看到這裏


對對對,又是新的數據結構,不過這個和上個同樣簡單,請看DexMethodId

struct DexMethodId{
	u2 classIdx;		/*類的類型,指向DexTypeId列表的索引*/
	u2 protoIdx;		/*聲明類型,指向DexProtoId列表的索引*/
	u4 nameIdx;		/*方法名,指向DexStringId列表的索引*/
}
對吧,這個也簡單,三個數據也都是指向對應的結構的索引值。咱們直接分析一下第一個數據,「01 00, 04 00, 00 00 00 00」,首先,classIdx,爲1,對應DexTypeId列表的索引1,也就是HelloWorld;其次,protoIdx,爲4,對應DexProtoId列表中的索引4,也就是void();最後,nameIdx,爲0,對應DexStringId列表中的索引0,也就是<clinit>。所以,第一個數據就出來了,即void HelloWorld.<clinit>() 。後面的不進行分析了,咱們依舊是把其他的9個方法列出來

好了,這個就算分析完了。下面真正開始咱們的重頭戲了。先緩一緩再繼續吧。

14. classDefsSizeclassDefsOff字段。這兩個字段指明的是dex文件中類的定義的相關信息。咱們先找到它們的位置。


classDefsSize字段,爲1,也就是隻有一個類定義,classDefsOff,爲1C0h,咱們找到它的偏移位置。


這裏就是了,到了這裏,你如今應該也知道又有新的數據結構了。對的,接下來的數據結構是DexClassDef,請看

struct DexClassDef{
	u4 classIdx;		/*類的類型,指向DexTypeId列表的索引*/
	u4 accessFlags;		/*訪問標誌*/
	u4 superclassIdx;	/*父類類型,指向DexTypeId列表的索引*/
	u4 interfacesOff;	/*接口,指向DexTypeList的偏移*/
	u4 sourceFileIdx;	/*源文件名,指向DexStringId列表的索引*/
	u4 annotationsOff;	/*註解,指向DexAnnotationsDirectoryItem結構*/
	u4 classDataOff;	/*指向DexClassData結構的偏移*/
	u4 staticValuesOff;	/*指向DexEncodedArray結構的偏移*/
}
很少說了,咱們直接根據結構開始分析吧,反正就只有一個類定義。classIdx爲1,對應DexTypeId列表的索引1,找到是HelloWorld,確實是咱們源程序中的類的類型。accessFlags爲1,它是類的訪問標誌,對應的值是一個以ACC_開頭的枚舉值,1對應的是 ACC_PUBLIC,你能夠在010Editor中看一下,說明咱們的類是public的。superclassIdx的值爲3,找到DexTypeId列表中的索引3,對應的是java.lang.object,說明咱們的類的父類類型是Object的。interfaceOff指向的是DexTypeList結構,咱們這裏是0說明沒有接口。若是有接口的話直接對應到DexTypeList,就和以前咱們分析的同樣了,這裏很少解釋,有興趣的能夠寫一個有接口的類驗證下。再下來sourceFileIdx指向的是DexStringId列表的索引,表明源文件名,咱們這裏位4,找一下對應到了字符串"HelloWorld.java",說明咱們類程序的源文件名爲HelloWorld.java。annotationsOff字段指向註解目錄接口,根據類型不一樣會有註解類、註解方法、註解字段與註解參數,咱們這裏的值爲0,說明沒有註解,這裏也不過多解釋,有興趣能夠本身試試。

接下來是classDataOff了,它指向的是DexClassData結構的位置偏移,DexClassData中存儲的是類的數據部分,咱們開始詳細分析一下它,首先,仍是先找到偏移位置3F8h


接着,咱們看看DexClassData數據結構

struct DexClassData{
	DexClassDataHeader	        header;			/*指定字段與方法的個數*/
	DexField* 			staticFields;		/*靜態字段,DexField結構*/
	DexField*			instanceFields;	/*實例字段,DexField結構*/
	DexMethod*			directMethods;		/*直接方法,DexMethod結構*/
	DexMethod*			virtualMethods;		/*虛方法,DexMethod結構*/
}

能夠看到,在DexClassData結構中又引入了三種結構,咱們一塊兒寫出來看一下吧

struct DexClassDataHeader{
	u4 staticFieldsSize;	/*靜態字段個數*/
	u4 instanceFieldsSize;	/*實例字段個數*/
	u4 directMethodsSize;	/*直接方法個數*/
	u4 virtualMethodsSize;  /*虛方法個數*/
}

struct DexField{
	u4 fieldIdx;	 /*指向DexFieldId的索引*/
	u4 accessFlags;	 /*訪問標誌*/
}

struct DexMethod{
	u4 methodIdx;	 /*指向DexMethodId的索引*/
	u4 accessFlags;	 /*訪問標誌*/
	u4 codeOff;		/*指向DexCode結構的偏移*/
}
代碼中的註釋寫的也都很清楚了,咱們就很少說了。可是請注意,在這些結構中的u4不是指的佔用4個字節,而是指它們是uleb128類型(佔用1~5個字節)的數據。關於uleb128仍是很少說,想了解的能夠本身查查看。

好,接下來開始分析,對於DexClassData,第一個爲DexClassDataHeader,咱們找到相應的位置,第一個staticFieldsSize其實只佔用了一個字節,即01h就是它的值,也就是說共有一個靜態字段,接下來instanceFieldsSize,directMethodsSize,virtualMethodsSize也都是隻佔用了一個字節,即實例字段的個數爲1,直接方法的個數爲3,虛方法的個數爲1。(這裏只是湊巧它們幾個都佔用一個字節,並不必定是隻佔用一個字節,這關於到uleb128數據類型,具體能夠本身瞭解下)。

而後接下來就是staticFields了,它對應的數據結構爲DexField,能夠看到,第一個fieldIdx,是指向DexFieldId的索引,值爲1,找到對應的索引值爲java.lang.String HelloWorld.b。第二個accessFlags,值爲8,對應的ACC_開頭的數據爲ACC_STATIC(能夠在010Editor中對應查看一下),說明咱們這個靜態字段爲:static java.lang.String HelloWorld.b。能夠對應咱們的源代碼看一下,咱們確實定義了一個static的b變量。

接着看instanceFields,它和staticFields對應的數據結構是同樣的,咱們直接分析,第一個fieldIdx,值爲0,對應的DexField的索引值爲int HelloWorld.a。第二個accessFlags,值爲0,對應的ACC_開頭的數據爲空,就是什麼也沒有。說明咱們這個實例字段爲:int HelloWorld.a。能夠對應咱們的源碼 看看,咱們確實定義了一個a實例變量。

再接着,根據directMethodsSize,有3個直接方法,咱們先看第一個,它對應的數據結構是DexMethod,首先methodIdx指向的是DexMethodId的索引,值爲0,找到對應的索引值爲void HelloWorld.<clinit>()。而後accessFlages爲......爲......爲....個人個天!我覺得就這樣能矇混過關了,沒想到還真碰到一個uleb128數據不是佔用一個字節的,這個accessFlags對應的值佔用了三個字節,「88 80 04」,爲何?由於是按照uleb128格式的數據讀出來的(仍是本身去查查吧,這個坑先不填了,其實這種數據也不麻煩,就是前面字節上的最高位指定了是否須要下一個字節上的內容)。「88 80 04」對應的ACC_開頭的數據爲 ACC_STATIC ACC_CONSTRUCTOR,代表這個方法是靜態的,而且是構造方法。最後,看看codeOff,它對應了DexCode結構的偏移,DexCode中存放了方法的指令集等信息,也就是真正的代碼了。咱們暫且不分析DexCode,就先看看它的偏移位置爲「E0 03」,這個等於多少呢?uleb128轉化爲16進制數結果爲:1E0h。也就是DexCode存放在偏移位置1E0h的位置上。


具體的DexCode咱們就先不分析了,由於它裏面存放的一些指令局須要根據相關資料一一查找,有興趣的本身能夠找資料看看。剩下的兩個直接方法咱們也不分析了。

接下來,咱們看根據virtualMethodsSize,有1個虛方法,咱們直接看。首先methodIdx的值爲2,對應的DexMethodId的索引值爲int HelloWorld.getNumber(int, int)。而後accessFlags爲1,對應的值爲ACC_PUBLIC,代表這是一個public類。codeOff爲「FC 04」,對應的位置爲27Ch,這裏就不上圖了,本身找找吧。

好了,咱們整個DEX文件的結構就這樣從DexHeader開始基本分析完了,好累啊,不過這樣分析一遍,對DEX文件的格式會有更深入的認識。老是看別人的真不如本身來一遍來的實在!


0x03■  參考資料

參考資料:

《Android軟件安全與逆向分析》.非蟲

相關文章
相關標籤/搜索