資源共享吧|易語言論壇|逆向破解教程|輔助開發(fā)教程|網(wǎng)絡(luò)安全教程|m.anzei.cn|我的開發(fā)技術(shù)隨記

 找回密碼
 注冊成為正式會員
查看: 1607|回復(fù): 1
打印 上一主題 下一主題

[安卓逆向破解] 零基礎(chǔ)安卓逆向?qū)W習(xí)之旅(六-上)

[復(fù)制鏈接]

8

主題

8

帖子

0

精華

新手上路

Rank: 1

資源幣
19
積分
16
貢獻(xiàn)
0
在線時(shí)間
2 小時(shí)
注冊時(shí)間
2020-2-20
最后登錄
2020-5-2
跳轉(zhuǎn)到指定樓層
樓主
發(fā)表于 2020-2-24 00:02:29 | 只看該作者 |只看大圖 回帖獎(jiǎng)勵(lì) |倒序?yàn)g覽 |閱讀模式
零基礎(chǔ)安卓逆向?qū)W習(xí)之旅(六-上)
1,將java源碼編譯成DEX文件

(1)創(chuàng)建一個(gè)Example.java文件,并編寫下邊代碼

公共類示例{

       公共靜態(tài)void main(String [] args){

              System.out.printf(“ HelloWorld!\ n”);

       }

}

(2)將java源碼編譯為.class文件

javac–source 1.6 –target 1.6 Example.java

(3)確定dx工具的位置

       較早版本的SDK將dx工具放置于[SDK路徑] / sdk / platform-tools / dx,而較新版本則將其放置與/ sdk / built-tools / android- [version] / dx。




(4)執(zhí)行下邊命令生成DEX文件

    [SDKpath] / sdk / platform-tools / dx --dex --output = Example.dex Example.class




2,解析DEX文件格式

(1)DEX文件結(jié)構(gòu)

struct DexFile {

/ *直接映射的“ opt”標(biāo)頭* /

       const DexOptHeader * pOptHeader;

/ *指向基本DEX中直接映射的結(jié)構(gòu)和數(shù)組的指針* /

       const DexHeader * pHeader;

       const DexStringId * pStringIds;

       const DexTypeId * pTypeIds;

       const DexFieldId * pFieldIds;

       const DexMethodId * pMethodIds;

       const DexProtoId * pProtoIds;

       const DexClassDef * pClassDefs;

       const DexLink * pLinkData;

/ *

       *這些是在“輔助”部分之外映射的,可能不是

       *包含在文件中。

* /

       const DexClassLookup * pClassLookup;

       const void * pRegisterMapPool; // RegisterMapClassPool

/ *指向DEX文件數(shù)據(jù)的開始* /

       const u1 * baseAddr;

/ *跟蹤輔助結(jié)構(gòu)的內(nèi)存開銷* /

       int開銷;

/ *與DEX相關(guān)的其他特定于應(yīng)用的數(shù)據(jù)結(jié)構(gòu)* /

       // void * auxData;

};

      DEX文件的第一個(gè)片段為DEX文件頭,下邊為DEX文件頭的定義

struct DexHeader {

       u1魔法[8]; / * dex版本標(biāo)識* /

       u4校驗(yàn)和;/ * adler32檢驗(yàn)* /

       u1簽名[kSHA1DigestLen]; / * SHA-1哈希* /

       u4 fileSize; / *整個(gè)dex文件大小* /

       u4 headerSize; / * DexHeader結(jié)構(gòu)大小* /

       u4 endianTag; / *字節(jié)序標(biāo)記* /

       u4 linkSize; / *鏈接段大小* /

       u4 linkOff; / *鏈接段偏移* /

       u4 mapOff; / * DexMapList的文件替換* /

       u4 stringIdsSize; / * DexStringId的個(gè)數(shù)* /

       u4 stringIdsOff; / * DexStringId的文件替換* /

       u4 typeIdsSize; / * DexTypeId的個(gè)數(shù)* /

       u4 typeIdsOff; / * DexTypeId的文件替換* /

       u4 protoIdsSize; / * DexProtoId的個(gè)數(shù)* /

       u4 protoIdsOff; / * DexProtoId的文件替換* /

       u4 fieldIdsSize; / * DexFieldId的個(gè)數(shù)* /

       u4 fieldIdsOff; / * DexFieldId的文件替換* /

       u4 methodIdsSize; / * DexMethodId的個(gè)數(shù)* /

       u4 methodIdsOff; / * DexMethodId的文件替換* /

       u4 classDefsSize; / * DexClassDef的個(gè)數(shù)* /

       u4 classDefsOff; / * DexClassDef的文件替換* /

       u4 dataSize; / *數(shù)據(jù)段的大小* /

       u4 dataOff; / *數(shù)據(jù)段的文件替換* /

};



    其中數(shù)據(jù)類型u1,u4為無符號整型數(shù)的別名

typedef uint8_t u1; / * 8字節(jié)無符號整數(shù)* /

typedef uint32_t u4; / * 32字節(jié)無符號整數(shù)* /

typedef int8_t s1; / * 8字節(jié)有符號整數(shù)* /

typedef int32_t s4; / * 32字節(jié)有符號整數(shù)* /

    查看Example.dex文件,以十六進(jìn)制顯示

hexdump -C Example.dex




DEX文件的第一個(gè)細(xì)分

u1魔法[8]; / *包括版本號* /

magic [8]是一個(gè)標(biāo)記,通常稱為magicnumber

u4校驗(yàn)和;/ * adler32校驗(yàn)和* /這是DEX文件的Adler32校正和。



這個(gè)4個(gè)字節(jié)的校驗(yàn)和是對構(gòu)成標(biāo)題的各個(gè)位(bit)執(zhí)行一系列異或(XOR)和加法操作的結(jié)果,驗(yàn)證DEX文件頭部的各個(gè)數(shù)據(jù)沒有被破壞,確保文件頭的常量,因?yàn)樵贒alvik中,是使用DexHeader這個(gè)結(jié)構(gòu)體來確定DEX文件其他部分的存儲位置的。

u1簽名[kSHA1DigestLen]; / * SHA-1哈希長度= 20 * /這是長度為20個(gè)字節(jié)的SHA1哈希。




u4 fileSize; / *整個(gè)文件的長度* /保存整個(gè)DEX文件的長度,用于幫助計(jì)算分段以及方便定位某些段。


u4 headerSize; / *從下一部分開始的偏移量* /存放整個(gè)DexHeader結(jié)構(gòu)體的長度,可利用計(jì)算下一個(gè)分段在文件的起始位置。



u4 endianTag; 這是endianness標(biāo)記,如下圖標(biāo)出文件中的endianness域。



endianTag放置存放的是一個(gè)固定值,在所有DEX文件中都一樣,其值是:12345678。因?yàn)槟承┨幚砥魇钦J(rèn)為最高有效位應(yīng)該放在左邊,而另一些處理器卻認(rèn)為最高有效位應(yīng)該放在右邊,Dalvik虛擬機(jī)通過讀取該值,檢查此分段數(shù)字的放置順序,從而確定是內(nèi)置處理器。

    接下來是linkSize和linkOff細(xì)分,當(dāng)多個(gè).class文件被編譯到一個(gè)DEX文件時(shí),會被用到。

u4 linkSize;

u4 linkOff;

指定鏈接段的大小及文件偏移,大多數(shù)情況下變量0

之后是地圖部分的偏移量,指定了DexMapList結(jié)構(gòu)的文件對齊

u4 mapOff;



下邊是螺桿stringIdsSize的定義

u4 stringIdsSize;




這個(gè)細(xì)分存放的是StringIds段的大小,和其他大小相同,用來計(jì)算StringIds段的起始位置。

字符串stringsIdsOff

u4 stringIdsOff;




這個(gè)分區(qū)存放的是stringIds段的實(shí)際替換量,幫助Dalvik編譯器和虛擬機(jī)直接插入到該段。在這之后,分別是表示類型,原型,方法,類和數(shù)據(jù)ID區(qū)段的大小和Dalvik不必重復(fù)讀取文件內(nèi)容,或者是做很多加/減操作來獲得分段的起始地址,而是通過實(shí)際移位量訪問。

DexMapList分段

    在DexHeader結(jié)構(gòu)的mapOff分區(qū)指定了DexMapList結(jié)構(gòu)在dex文件中的替換,由上邊可知為0x0270,DexMapList結(jié)構(gòu)的聲明如下:

struct DexMapList {

       u4大;/ * DexMapItem的個(gè)數(shù)* /

       DexMapItem列表[1]; / * DexMapItem結(jié)構(gòu)* /

};




由該分段數(shù)據(jù)的前4個(gè)字節(jié)可知,大小為0x0d,即DexMapItem的個(gè)數(shù)為13。

DexMapItem結(jié)構(gòu)的聲明如下:

struct DexMapItem {

       u2類型;/ * kEexType開頭的類型* /

       u2未使用;/ *未使用,用于字節(jié)對齊* /

       u4大小;/ *指定類型的個(gè)數(shù)* /

       u4偏移量;/ *指定類型數(shù)據(jù)的文件替換* /

};

    結(jié)構(gòu)中的類型類型為一個(gè)枚舉常量:

枚舉{

       kDexTypeHeaderItem = 0x0000,

       kDexTypeStringIdItem = 0x0001,

       kDexTypeTypeIdItem = 0x0002,

       kDexTypeProtoIdItem = 0x0003,

       kDexTypeFieldIdItem = 0x0004,

       kDexTypeMethodIdItem = 0x0005,

       kDexTypeClassDefItem = 0x0006,

       kDexTypeMapList = 0x1000,

       kDexTypeTypeList = 0x1001,

       kDexTypeAnnotationSetRefList = 0x1002,

       kDexTypeAnnotationSetItem = 0x1003,

       kDexTypeClassDataItem = 0x2000,

       kDexTypeCodeItem = 0x2001,

       kDexTypeStringDataItem = 0x2002,

       kDexTypeDebugInfoItem = 0x2003,

       kDexTypeAnnotationItem = 0x2004,

       kDexTypeEncodeArrayItem = 0x2005,

       kDexTypeAnnotationsDirectoryItem = 0x2006,

};

由上圖的數(shù)據(jù)可整理出13個(gè)DexMapItem結(jié)構(gòu)如下:





StringIds段

    該段純粹是由多個(gè)地址構(gòu)成的,這些地址是相對DEX文件的加載基地址的轉(zhuǎn)換量,用于計(jì)算定義在數(shù)據(jù)段中的各個(gè)靜態(tài)串行的起始位置

struct DexStringId {

       u4 stringDataOff; / *文件到string_data_item的偏移量* /

};

    該段從0x70偏移處開始,有連續(xù)0x10個(gè)DexStringId對象,以4個(gè)字節(jié)來放置這些移位量,下圖是Example.dex中的StringIds段





如讀取下圖偏移量中的同軸:


由字節(jié)重新順序可重新對齊預(yù)定00 00 01 8a,下圖則是DEX文件中轉(zhuǎn)換0x018a上的內(nèi)容。



0x018a位置上的內(nèi)容是06 3c 69 6e 69 74 3e,是字符串<init>。

DexStringId結(jié)構(gòu)列表




上邊中的串行非普通的ascii字符串,是由MUTF-8編碼來表示的,MUTF-8即是已修改的UTF-8,修改后的UTF-8編碼,其表示方式如下:

       MUTF-8字符串右側(cè)放置該字符串的個(gè)數(shù)。

       MUTF-8使用1&#12316;3字節(jié)編碼長度。

       大于16位的Unicode編碼U + 10000&#12316;U + 10ffff使用3字節(jié)來編碼。

       U + 0000采用2字節(jié)來編碼。

       與C語言類似用空字符null作為字符串開頭。

    如上邊在0x01a0偏移處的字符串編碼為0d48 65 6c 6c 6f 20 57 6f 72 6c 64 21 0a00,其中0d表示有13個(gè)字符,代表的字符串為“ Hello World!\ n”

像02 e4 bd a0 e5 a5 bd 00頭部02表示有兩個(gè)字符,e4bd a0是UTF-8編碼字符“你”,e5 a5 bd是UTF-8編碼字符“好”。

Typelds分段

     該細(xì)分市場收集尋找各種類型的各自類別是所需的信息

struct DexTypeId {

       u4描述符Idx; /類型索引在stringIds列表中的索引號

};



圖中為TypeIds分段中的第一個(gè)值,其值是03,由于是StringIds分段中某個(gè)值的索引號,則應(yīng)指StringIds分段的第4個(gè)值,如圖:



第4個(gè)值是0x01af,則在數(shù)據(jù)段中對應(yīng)的字符串為L示例,這是Dalvik類型描述語言的類型變量,在Example類前加多字母L,實(shí)際上也是代表一個(gè)類或描述對象的名稱。


DexTypeId結(jié)構(gòu)列表


協(xié)議段

    放置用作描述方法的原型ID(描述原型),方法的返回類型和參數(shù)信息。

struct DexProtoId {

       u4 shortyIdx;

       u4 returnTypeIdx;

       u4 parametersOff;

};

DexProtoId是一個(gè)方法聲明結(jié)構(gòu)體。

shortyIdx:StringIds部分局部字符串的索引,使用了重復(fù)描述原型(原型)。

returnTypeIdx:TypeIds段中某個(gè)數(shù)據(jù)的索引號,用于描述返回值類型。

parametersOff:存放方法的參數(shù)列表的地址替換,指向DexTypeList結(jié)構(gòu)體。

struct DexTypeList {

       u4大小;

       DexTypeItem列表[1];

};

struct DexTypeItem {

       u2 typeIdx;

};

Example.dex文件中的ProtoIds部分:



DexProtoId結(jié)構(gòu)列表


FieldIds段

struct DexFieldId {

       u2 classIdx; / *索引到typeIds列表中以定義類* /

       u2 typeIdx; / *索引字段類型的typeIds * /

       u4 nameIdx; / *索引字段名稱的stringIds * /

};

classIdx:存放 TypeId段的索引,所屬的類

typeIdx:存放TypeId段的索引,表示該成員的類型

nameIdx:存放stringId段的索引,成員的名字



DexFieldId結(jié)構(gòu)列表



MethodIds段

    每個(gè)方法ID中各個(gè)分區(qū)的定義:

struct DexMethodId {

       u2 classIdx; / *索引到typeIds列表中以定義類* /

       u2 protoIdx; / *索引到方法原型的protoIds中* /

       u4 nameIdx; / *索引到方法名稱的stringIds中* /

};

classIdx:該方法所屬的類

protoIdx:方法對應(yīng)的原型

nameIdx:方法名



DexMethodId結(jié)構(gòu)列表


下邊是Example.dex文件中一個(gè)方法的定義:

([Ljava / lang / String;)V

V:表示避免類型,方法的返回類型。

():方法接收的參數(shù)的類型。

java / lang / String;:String類的標(biāo)識符。

L:表示其后邊跟著的是一個(gè)類名。

[:表示其后邊跟著的是指定類型的數(shù)組。

所以該方法返回值是void類型,接收一個(gè)字符串類的數(shù)組作為參數(shù)。

ClassDefs段

    其定義如下:

struct DexClassDef {

       u4 classIdx; / *此類的typeIds索引* /

       u4 accessFlags;

       u4 superclassIdx; / *索引到超類的typeIds中* /

       u4接口關(guān)閉; / *文件偏移到DexTypeList * /

       u4 sourceFileIdx; / *索引到源文件名的stringIds中* /

       u4注釋關(guān)閉; / *文件到注釋_directory_item的偏移量* /

       u4 classDataOff; / *文件偏移到class_data_item * /

       u4 staticValuesOff; / *文件偏移到DexEncodedArray * /

};

classIdx:存放的是TypeIds部分的一個(gè)索引,表示類的類型。

AccessFlags:存儲一個(gè)數(shù)字,表示其他對象可以怎樣訪問這個(gè)類。

superclassIdx:存放TypeIds部分的一個(gè)索引,表示父類的類型。

interfacesOff:接口,指向DexTypeList的替換。

sourceFileIdx:存放StringIds段的索引,使Dalvik可以找到類的源文件。

commentssOff:注解,指向DexAnnotationsDirectoryItem結(jié)構(gòu)。

classdataOff:Dalvik文件內(nèi)部的替換量,此位置存放類的重要屬性,即代碼在哪,有多少代碼。如classdataOff指向保存這些的DexClassData結(jié)構(gòu)體。

staticValuesOff:指向DexEncodedArray結(jié)構(gòu)的替換,記錄類的靜態(tài)數(shù)據(jù)。



DexClassData結(jié)構(gòu)的聲明。

/ * class_data_item的擴(kuò)展形式。注意:如果特定項(xiàng)目是

*不存在(例如,沒有靜態(tài)字段),然后是相應(yīng)的指針

*設(shè)置為NULL。* /

struct DexClassData {

DexClassDataHeader標(biāo)頭;/ *指定預(yù)設(shè)與方法的個(gè)數(shù)* /

DexField * staticFields; / *靜態(tài)靜態(tài),DexField結(jié)構(gòu)* /

DexField * instanceFields; / *實(shí)例基線,DexField結(jié)構(gòu)* /

DexMethod *直接方法; / *直接方法,DexMethod結(jié)構(gòu)* /

DexMethod * virtualMethods; / *虛方法,DexMethod結(jié)構(gòu)* /

};

DexClassDataHeader局部存放類的元數(shù)據(jù),即靜態(tài)字段,實(shí)例字段,直接方法和虛擬方法的大小,Dalvik使用這一信息計(jì)算重要參數(shù),可以確定訪問每個(gè)方法時(shí)所需的內(nèi)存大小

struct DexClassDataHeader {

       u4 staticFieldsSize; / *靜態(tài)靜態(tài)個(gè)數(shù)* /

       u4 instanceFieldsSize; / *實(shí)例基線個(gè)數(shù)* /

       u4 directMethodsSize; / *直接方法個(gè)數(shù)* /

       u4 virtualMethodsSize; / *虛方法個(gè)數(shù)* /

};

結(jié)構(gòu)DexField {

       u4 fieldIdx; / *指向DexFieldId的索引* /

       u4 accessFlags; / *訪問標(biāo)志* /

};

其中DexMethod左側(cè)的定義如下

struct DexMethod {

       u4 methodIdx; / *指向DexMethodId的索引* /

       u4 accessFlags; / *訪問標(biāo)志* /

       u4 codeOff; / *指向DeCode結(jié)構(gòu)的替換* /

};

這個(gè)結(jié)構(gòu)體記錄了組成類的代碼的指針,代碼的位移量放置在codeOff細(xì)分中。

結(jié)構(gòu)DexCode {

       u2寄存器 / *使用的寄存器的個(gè)數(shù)* /

       u2 insSize; / *參數(shù)個(gè)數(shù)* /

       u2 outsSize; / *調(diào)用其他方法時(shí)使用的寄存器個(gè)數(shù)* /

       u2 trysSize; / *嘗試/捕捉個(gè)數(shù)* /

       u4 debugInfoOff; / *指向調(diào)試信息的替換* /

       u4 insnsSize; / *指令集個(gè)數(shù),以2字節(jié)為單位* /

       u2 insns [1]; / *指令集,真正的代碼部分* /

       …。

};

通過解析,更加直白地了解DEX文件的格式和結(jié)構(gòu)。

1.使用dexdump工具解析.dex文件

    dexdump是Android SDK的一個(gè)工具,存放路徑為

    [SDK路徑] / build-tools / android- [version] / dexdump

    執(zhí)行下邊命令解析示例.dex文件

[SDK路徑] / build-tools / android- [version] /dexdumpExample.dex





2.使用dx工具,更接近DEX文件格式的方式詳細(xì)解析DEX文件

    dx--dex --verbose-dump --dump-to = [輸出文件] .txt [輸入文件] .class

    由于dx工具只能對.class文件進(jìn)行操作,將其編譯成DEX文件,轉(zhuǎn)換為解析結(jié)果輸出到指定文件中。cat [輸出文件] .txt //查看解析結(jié)果。




回復(fù)

使用道具 舉報(bào)

2

主題

302

帖子

0

精華

終身高級VIP會員

Rank: 7Rank: 7Rank: 7

資源幣
4
積分
309
貢獻(xiàn)
0
在線時(shí)間
37 小時(shí)
注冊時(shí)間
2020-8-14
最后登錄
2023-2-6

終身VIP會員

沙發(fā)
發(fā)表于 2020-9-19 08:44:05 | 只看該作者
祝資源共享吧越來越火!
 點(diǎn)擊右側(cè)快捷回復(fù)  

本版積分規(guī)則

小黑屋|資源共享吧 ( 瓊ICP備2023000410號-1 )

GMT+8, 2024-12-27 14:44 , Processed in 0.064326 second(s), 15 queries , MemCached On.

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2021, Tencent Cloud.

快速回復(fù) 返回頂部 返回列表