解析Dex文件的工作應(yīng)該是自動(dòng)化的,,由工具去完成。本文通過(guò)VS2012來(lái)新建一個(gè)控制臺(tái)的工程,,然后完成一個(gè)Dex文件的解析工具,。
對(duì)于解析Dex文件而言,,需要準(zhǔn)備一些頭文件,,這些頭文件都可以從安卓系統(tǒng)的源代碼中獲取到,,首先要有common.h,、uleb128.h,因?yàn)閏ommon.h中存放了相應(yīng)的數(shù)據(jù)類型(這里所說(shuō)的數(shù)據(jù)類型是u1,、u2),,uleb128.h中存放了讀取uleb128數(shù)據(jù)類型的相關(guān)函數(shù)。接著要準(zhǔn)備的是DexFile.h,、DexFile.cpp,、DexClass.h和DexClass.cpp 4個(gè)文件。
為了使用方便,,將這4個(gè)文件中的代碼都復(fù)制到了DexParse.h中,為了能夠編譯通過(guò),,在函數(shù)的定義部分進(jìn)行了刪除,,或者對(duì)某些函數(shù)的參數(shù)進(jìn)行了修改,對(duì)函數(shù)體的一些內(nèi)容也進(jìn)行了刪減,。
在自己準(zhǔn)備相關(guān)內(nèi)容時(shí),,可以在編譯時(shí)通過(guò)報(bào)錯(cuò)信息自己進(jìn)行修改。在這里,,將DexParse.h文件添加到了新建的控制臺(tái)工程當(dāng)中,。
解析Dex文件也按照Dex的格式逐步進(jìn)行即可,當(dāng)然在解析文件前請(qǐng)不要忘記,,對(duì)文件的操作首先是要打開(kāi)文件,。
1. 打開(kāi)與關(guān)閉文件
打開(kāi)與關(guān)閉文件的代碼如下:
int _tmain(int argc, _TCHAR* argv[])
{
HANDLE hFile = CreateFile(DEX_FILE, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_
EXISTING, FILE_ACTION_ADDED, NULL);
HANDLE hMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL),;
LPVOID hView = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0),;
UnmapViewOfFile(hView);
CloseHandle(hMap),;
CloseHandle(hFile),;
return 0;
}
在上面的代碼中,首先要打開(kāi)文件,,然后創(chuàng)建文件映射,,在MapViewOfFile函數(shù)和UnmapViewOfFile函數(shù)之間,來(lái)添加關(guān)于解析DEX文件的代碼,。
2. Dex文件頭部
在解析Dex文件時(shí),,需要對(duì)Dex文件的頭部進(jìn)行解析,解析Dex文件的頭部時(shí),,安卓系統(tǒng)提供了一個(gè)函數(shù),,函數(shù)定義如下:
DexFile* dexFileParse(const u1* data, size_t length, int flags),;
該函數(shù)有3個(gè)參數(shù),第一個(gè)參數(shù)是Dex文件數(shù)據(jù)的起始位置,,第二個(gè)參數(shù)是Dex文件的長(zhǎng)度,,第三個(gè)參數(shù)是用來(lái)告訴dexFileParse函數(shù)是否需要進(jìn)行驗(yàn)證的。對(duì)于目前階段而言,,我們不需要第三個(gè)參數(shù),,因此將該函數(shù)進(jìn)行刪減后的代碼如下:
DexFile* dexFileParse(const u1* data, size_t length)
{
DexFile* pDexFile = NULL;
const DexHeader* pHeader;
const u1* magic;
int result = -1;
pDexFile = (DexFile*) malloc(sizeof(DexFile));
if (pDexFile == NULL)
goto bail;
memset(pDexFile, 0, sizeof(DexFile)),;
/*
* 去掉優(yōu)化的頭部
*/
if (memcmp(data, DEX_OPT_MAGIC, 4) == 0) {
magic = data;
if (memcmp(magic+4, DEX_OPT_MAGIC_VERS, 4) != 0) {
goto bail;
}
/* 忽略可選的頭部和在這里追加的數(shù)據(jù)
data += pDexFile->pOptHeader->dexOffset;
length -= pDexFile->pOptHeader->dexOffset;
if (pDexFile->pOptHeader->dexLength > length) {
goto bail;
}
length = pDexFile->pOptHeader->dexLength;
}
dexFileSetupBasicPointers(pDexFile, data),;
pHeader = pDexFile->pHeader;
/*
* Success!
*/
result = 0;
bail:
if (result != 0 && pDexFile != NULL) {
dexFileFree(pDexFile);
pDexFile = NULL;
}
return pDexFile;
}
該函數(shù)首先判斷Dex文件的合法性,,然后將Dex文件的一些基礎(chǔ)的指針進(jìn)行了初始化,,在dexFileParse函數(shù)中調(diào)用了另外一個(gè)函數(shù),即dexFileSetupBasicPointers函數(shù),,該函數(shù)的函數(shù)體如下:
void dexFileSetupBasicPointers(DexFile* pDexFile, const u1* data) {
DexHeader *pHeader = (DexHeader*) data;
pDexFile->baseAddr = data;
pDexFile->pHeader = pHeader;
pDexFile->pStringIds = (const DexStringId*) (data + pHeader->stringIdsOff),;
pDexFile->pTypeIds = (const DexTypeId*) (data + pHeader->typeIdsOff);
pDexFile->pFieldIds = (const DexFieldId*) (data + pHeader->fieldIdsOff),;
pDexFile->pMethodIds = (const DexMethodId*) (data + pHeader->methodIdsOff),;
pDexFile->pProtoIds = (const DexProtoId*) (data + pHeader->protoIdsOff);
pDexFile->pClassDefs = (const DexClassDef*) (data + pHeader->classDefsOff),;
pDexFile->pLinkData = (const DexLink*) (data + pHeader->linkOff),;
}
從dexFileSetupBasicPointers函數(shù)中可以看出,對(duì)于其他各個(gè)結(jié)構(gòu)體的索引及數(shù)量已經(jīng)在這里全部讀取出來(lái),,在后面具體解析其他數(shù)據(jù)結(jié)構(gòu)時(shí),,它會(huì)很方便地被使用。
在dexFileParse中使用malloc函數(shù)申請(qǐng)了一塊空間,,這塊空間在解析完成以后需要手動(dòng)地進(jìn)行釋放,,在安卓系統(tǒng)的源碼中也定義了一個(gè)函數(shù)以方便使用,函數(shù)名是dexFileFree,,函數(shù)的定義如下:
void dexFileFree(DexFile* pDexFile)
{
if (pDexFile == NULL)
return;
free(pDexFile),;
}
很簡(jiǎn)單的函數(shù),判斷指針是否為NULL,,不為NULL則直接調(diào)用free函數(shù)釋放空間,。
有了上面的代碼,那么就可以完成解析Dex文件的第一步了,,具體代碼如下:
DWORD dwSize = GetFileSize(hFile, NULL),;
DexFile *pDexFile = dexFileParse((const u1 *)hView, (size_t)dwSize);
dexFileFree(pDexFile);
這樣就得到了指向DexFile結(jié)構(gòu)體的指針pDexFile,,DexFile結(jié)構(gòu)體的定義如下:
struct DexFile {
/* 直接映射的“opt”頭部 */
const DexOptHeader* pOptHeader;
/* 指向基礎(chǔ) 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;
/*
* 這些不映射到“auxillary”部分,,可能不包含在該文件中
*/
const DexClassLookup* pClassLookup;
const void* pRegisterMapPool; // RegisterMapClassPool
/* 指向 DEX 文件開(kāi)始的指針 */
const u1* baseAddr;
/* 跟蹤輔助結(jié)構(gòu)的內(nèi)存開(kāi)銷(xiāo) */
int overhead;
/* 與 DEX 相關(guān)聯(lián)的其他數(shù)據(jù)結(jié)構(gòu) */
//void* auxData;
};
對(duì)于我們而言,在寫(xiě)程序時(shí)只需要關(guān)心結(jié)構(gòu)體中DexHeader到DexClassDef之間的字段即可,。
之后解析的代碼中都會(huì)使用到返回的pDexFile指針,,因此之后縮寫(xiě)的代碼都必須寫(xiě)在調(diào)用dexFileFree函數(shù)之前。
3. 解析DexMapList相關(guān)數(shù)據(jù)
DexMapList是在DexHeader的mapOff給出的,,不過(guò)在程序中不用直接從DexHeader結(jié)構(gòu)體中去取,,因?yàn)樵诎沧肯到y(tǒng)中已經(jīng)給出了相關(guān)的函數(shù),函數(shù)代碼如下:
DEX_INLINE const DexMapList* dexGetMap(const DexFile* pDexFile) {
u4 mapOff = pDexFile->pHeader->mapOff;
if (mapOff == 0) {
return NULL;
} else {
return (const DexMapList*) (pDexFile->baseAddr + mapOff),;
}
}
dexGetMap函數(shù)通過(guò)前面返回的DexFile指針來(lái)定位DexMapList在文件中的偏移位置,。
在實(shí)際的代碼中,我們需要將DEX_INLINE宏刪掉,,或者按照安卓系統(tǒng)的源代碼中的定義去定義一下,。
通過(guò)dexGetMap函數(shù)獲得了DexMapList的指針,那么接下來(lái)就可以對(duì)DexMapList進(jìn)行遍歷了,,這里定義一個(gè)自定義函數(shù)來(lái)進(jìn)行遍歷,,代碼如下:
void PrintDexMapList(DexFile *pDexFile)
{
const DexMapList *pDexMapList = dexGetMap(pDexFile);
printf(“DexMapList:\r\n”),;
printf(“TypeDesc\t\t type unused size offset\r\n”);
for ( u4 i = 0; i < pDexMapList->size; i ++ )
{
switch (pDexMapList->list[i].type)
{
case 0x0000:printf(“kDexTypeHeaderItem”),;break;
case 0x0001:printf(“kDexTypeStringIdItem”),;break;
case 0x0002:printf(“kDexTypeTypeIdItem”);break;
case 0x0003:printf(“kDexTypeProtoIdItem”),;break;
case 0x0004:printf(“kDexTypeFieldIdItem”),;break;
case 0x0005:printf(“kDexTypeMethodIdItem”);break;
case 0x0006:printf(“kDexTypeClassDefItem”),;break;
case 0x1000:printf(“kDexTypeMapList”),;break;
case 0x1001:printf(“kDexTypeTypeList”);break;
case 0x1002:printf(“kDexTypeAnnotationSetRefList”),;break;
case 0x1003:printf(“kDexTypeAnnotationSetItem”),;break;
case 0x2000:printf(“kDexTypeClassDataItem”);break;
case 0x2001:printf(“kDexTypeCodeItem”),;break;
case 0x2002:printf(“kDexTypeStringDataItem”),;break;
case 0x2003:printf(“kDexTypeDebugInfoItem”);break;
case 0x2004:printf(“kDexTypeAnnotationItem”),;break;
case 0x2005:printf(“kDexTypeEncodedArrayItem”),;break;
case 0x2006:printf(“kDexTypeAnnotationsDirectoryItem”);break;
}
printf(“\t %04X %04X %08X %08X\r\n”,
pDexMapList->list[i].type,
pDexMapList->list[i].unused,
pDexMapList->list[i].size,
pDexMapList->list[i].offset);
}
}
在main函數(shù)中調(diào)用該函數(shù)時(shí),,只要將前面得到的指向DexFile結(jié)構(gòu)體的指針傳給該函數(shù)即可,。查看該部分解析的輸出,如圖1所示,。
圖1 DexMapList解析后的輸出
4. 解析StringIds相關(guān)數(shù)據(jù)
對(duì)于StringIds的解析也非常簡(jiǎn)單,,這里直接給出一個(gè)自定義函數(shù),代碼如下:
void PrintStringIds(DexFile *pDexFile)
{
printf(“DexStringIds:\r\n”),;
for ( u4 i = 0; i < pDexFile->pHeader->stringIdsSize; i ++ )
{
printf(“%d.%s \r\n”, i, dexStringById(pDexFile, i)),;
}
}
在該自定義函數(shù)中,它調(diào)用了dexStringById函數(shù),,也就是通過(guò)索引值來(lái)得到字符串,,該函數(shù)的定義如下:
/* 通過(guò)特定的 string_id index 返回 UIF-8 編碼的字符串 */
DEX_INLINE const char* dexStringById(const DexFile* pDexFile, u4 idx) {
const DexStringId* pStringId = dexGetStringId(pDexFile, idx);
return dexGetStringData(pDexFile, pStringId),;
}
在dexStringById函數(shù)中又調(diào)用了兩個(gè)其他的函數(shù),,分別是dexGetStringId和dexGetStringData,大家可以自行查看,。
在main函數(shù)中調(diào)用筆者的自定義函數(shù),,輸出如圖2所示。
圖2 StringIds解析后的輸出
5. 解析TypeIds相關(guān)數(shù)據(jù)
解析TypeIds也是非常簡(jiǎn)單的,,直接上代碼即可,,代碼如下:
void PrintTypeIds(DexFile *pDexFile)
{
printf(“DexTypeIds:\r\n”);
for ( u4 i = 0; i < pDexFile->pHeader->typeIdsSize; i ++ )
{
printf(“%d %s \r\n”, i, dexStringByTypeIdx(pDexFile, i)),;
}
}
代碼中調(diào)用了一個(gè)關(guān)鍵的函數(shù)dexStringByTypeIdx,,該函數(shù)也是安卓系統(tǒng)源碼中提供的函數(shù),該函數(shù)的實(shí)現(xiàn)如下:
/*
* 獲取與指定的類型索引相關(guān)聯(lián)的描述符字符串
* 調(diào)用者不能釋放返回的字符串
*/
DEX_INLINE const char* dexStringByTypeIdx(const DexFile* pDexFile, u4 idx) {
const DexTypeId* typeId = dexGetTypeId(pDexFile, idx),;
return dexStringById(pDexFile, typeId->descriptorIdx),;
}
在dexStringByTypeIdx函數(shù)中調(diào)用了dexGetTypeId和dexStringById兩個(gè)函數(shù),請(qǐng)大家自行在源碼中查看,。
在main函數(shù)中調(diào)用自定義函數(shù),,輸出如圖3所示。
圖3 TypeIds解析后的輸出
6. 解析ProtoIds相關(guān)數(shù)據(jù)
Proto是方法的原型或方法的聲明,,也就是提供了方法的返回值類型,、參數(shù)個(gè)數(shù),以及參數(shù)的類型,。對(duì)于ProtoIds的解析,,首先是對(duì)原始數(shù)據(jù)的解析,然后再將它簡(jiǎn)單地還原為可以直接閱讀的方法原型,。
先來(lái)看一下代碼,,代碼如下:
void PrintProtoIds(DexFile *pDexFile)
{
printf(“DexProtoIds:\r\n”);
// 對(duì)數(shù)據(jù)的解析
for ( u4 i = 0; i < pDexFile->pHeader->protoIdsSize; i ++ )
{
const DexProtoId *pDexProtoId = dexGetProtoId(pDexFile, i);
// 輸出原始數(shù)據(jù)
printf(“%08X %08X %08X \r\n”, pDexProtoId->shortyIdx, pDexProtoId->returnTy
peIdx, pDexProtoId->parametersOff),;
// 輸出對(duì)應(yīng)的 TypeId
printf(“%s %s\r\n”,
dexStringById(pDexFile, pDexProtoId->shortyIdx),,
dexStringByTypeIdx(pDexFile, pDexProtoId->returnTypeIdx));
// 獲得參數(shù)列表
const DexTypeList *pDexTypeList = dexGetProtoParameters(pDexFile, pDexProtoId),;
u4 num = pDexTypeList != NULL ? pDexTypeList->size : 0;
// 輸出參數(shù)
for ( u4 j = 0; j < num; j ++ )
{
printf(“%s ”, dexStringByTypeIdx(pDexFile, pDexTypeList->list[j].typeIdx)),;
}
printf(“\r\n”);
}
printf(“\r\n”),;
// 對(duì)解析數(shù)據(jù)的簡(jiǎn)單還原
for ( u4 i = 0; i < pDexFile->pHeader->protoIdsSize; i ++ )
{
const DexProtoId *pDexProtoId = dexGetProtoId(pDexFile, i),;
printf(“%s”, dexStringByTypeIdx(pDexFile, pDexProtoId->returnTypeIdx));
printf(“(”),;
// 獲得參數(shù)列表
const DexTypeList *pDexTypeList = dexGetProtoParameters(pDexFile, pDexProtoId),;
u4 num = pDexTypeList != NULL ? pDexTypeList->size : 0;
// 輸出參數(shù)
for ( u4 j = 0; j < num; j ++ )
{
printf(“%s\b, ”, dexStringByTypeIdx(pDexFile, pDexTypeList->list[j].typeIdx));
}
if ( num == 0 )
{
printf(“),;\r\n”),;
}
else
{
printf(“\b\b);\r\n”),;
}
}
}
在該自定義函數(shù)中有兩個(gè)for循環(huán),,其內(nèi)容基本一致。第一個(gè)循環(huán)完成了數(shù)據(jù)的解析,,第二個(gè)循環(huán)是將數(shù)據(jù)簡(jiǎn)單地解析成了方法的原型,。
這里只對(duì)第一個(gè)for循環(huán)進(jìn)行說(shuō)明。ProtoIds是方法的原型,,看一下DexProtoId的定義,,定義如下:
/*
* Direct-mapped “proto_id_item”.
*/
struct DexProtoId {
u4 shortyIdx; /* index into stringIds for shorty descriptor */
u4 returnTypeIdx; /* index into typeIds list for return type */
u4 parametersOff; /* file offset to type_list for parameter types */
};
第一個(gè)字段是方法原型的短描述,第二個(gè)字段是方法原型的返回值,,第三個(gè)字段是指向參數(shù)列表的。因此,,可以看到,,在兩個(gè)for循環(huán)中,仍然嵌套著一個(gè)for循環(huán),,外層的循環(huán)是用來(lái)解析方法原型的,,內(nèi)層的循環(huán)是用來(lái)解析方法原型中的參數(shù)的。
首先,,通過(guò)dexGetProtoId函數(shù)來(lái)獲得ProtoIds,,然后通過(guò)dexGetProtoParameters函數(shù)來(lái)得到相應(yīng)ProtoIds的參數(shù)。
在main函數(shù)中調(diào)用自定義函數(shù),,輸出如圖4所示,。
圖4 ProtoIds解析后的輸出
從圖4中可以看出,該Dex文件中有3個(gè)方法原型,這里來(lái)說(shuō)一下ProtoIds中的shortyIdx這個(gè)簡(jiǎn)短描述的意思,,用第二個(gè)方法原型來(lái)說(shuō)明,。
第二個(gè)方法原型是V(Ljava/lang/String);這種形式,,它的簡(jiǎn)短描述是VL,。V表示返回值類型,就是V,,而L就是第一個(gè)參數(shù)的類型,。再舉個(gè)例子,如果簡(jiǎn)短描述是VII,,那么返回值類型是V,,然后有兩個(gè)參數(shù),第一個(gè)參數(shù)是I類型,,第二個(gè)參數(shù)也是I類型,。
7. 解析FieldIds相關(guān)數(shù)據(jù)
FieldIds的解析相對(duì)于ProtoIds的解析就簡(jiǎn)單了,直接上代碼:
void PrintFieldIds(DexFile *pDexFile)
{
printf(“DexFieldIds:\r\n”),;
for ( u4 i = 0; i < pDexFile->pHeader->fieldIdsSize; i ++ )
{
const DexFieldId *pDexFieldId = dexGetFieldId(pDexFile, i),;
printf(“%04X %04X %08X \r\n”, pDexFieldId->classIdx, pDexFieldId->typeIdx,
pDexFieldId->nameIdx);
printf(“%s %s %s\r\n”,
dexStringByTypeIdx(pDexFile, pDexFieldId->classIdx),,
dexStringByTypeIdx(pDexFile, pDexFieldId->typeIdx),,
dexStringById(pDexFile, pDexFieldId->nameIdx));
}
}
Field是類中的屬性,,在DexFieldId中對(duì)于類屬性有3個(gè)字段,,分別是屬性所屬的類、屬性的類型和屬性的名稱,。
在main函數(shù)中調(diào)用自定義函數(shù),,輸出如圖5所示。
圖5 FieldIds解析后的輸出
8. 解析MethodIds相關(guān)數(shù)據(jù)
MethodIds的解析也分為兩部分,,第一部分是解析數(shù)據(jù),,第二部分是簡(jiǎn)單的還原方法。在DexMethodId中給出了方法所屬的類,、方法對(duì)應(yīng)的原型,,以及方法的名稱。在解析ProtoIds的時(shí)候,,只是方法的原型,,并沒(méi)有給出方法的所屬的類,還有方法的名稱,。在還原方法時(shí),,就要借助ProtoIds才能完整地還原方法,。
解析MethodIds的代碼如下:
void PrintMethodIds(DexFile *pDexFile)
{
printf(“DexMethodIds:\r\n”);
// 對(duì)數(shù)據(jù)的解析
for ( u4 i = 0; i < pDexFile->pHeader->methodIdsSize; i ++ )
{
const DexMethodId *pDexMethodId = dexGetMethodId(pDexFile, i),;
printf(“%04X %04X %08X \r\n”, pDexMethodId->classIdx, pDexMethodId->protoIdx,
pDexMethodId->nameIdx),;
printf(“%s %s \r\n”,
dexStringByTypeIdx(pDexFile, pDexMethodId->classIdx),
dexStringById(pDexFile, pDexMethodId->nameIdx)),;
}
printf(“\r\n”),;
// 根據(jù) protoIds 來(lái)簡(jiǎn)單還原方法
for ( u4 i = 0; i < pDexFile->pHeader->methodIdsSize; i ++ )
{
const DexMethodId *pDexMethodId = dexGetMethodId(pDexFile, i);
const DexProtoId *pDexProtoId = dexGetProtoId(pDexFile, pDexMethodId->protoIdx),;
printf(“%s ”, dexStringByTypeIdx(pDexFile, pDexProtoId->returnTypeIdx)),;
printf(“%s\b.”, dexStringByTypeIdx(pDexFile, pDexMethodId->classIdx));
printf(“%s”, dexStringById(pDexFile, pDexMethodId->nameIdx)),;
printf(“(”),;
// 獲得參數(shù)列表
const DexTypeList *pDexTypeList = dexGetProtoParameters(pDexFile, pDexProtoId);
u4 num = pDexTypeList != NULL ? pDexTypeList->size : 0;
// 輸出參數(shù)
for ( u4 j = 0; j < num; j ++ )
{
printf(“%s\b, ”, dexStringByTypeIdx(pDexFile, pDexTypeList->list[j].typeIdx)),;
}
if ( num == 0 )
{
printf(“),;”);
}
else
{
printf(“\b\b),;”),;
}
printf(“\r\n”);
}
}
在解析數(shù)據(jù)時(shí),,只是將數(shù)據(jù)對(duì)應(yīng)的字符串進(jìn)行了輸出,,而還原方法時(shí),則是借助ProtoIds來(lái)完整地還原了方法,。
同樣,,在main函數(shù)中調(diào)用自定義函數(shù),輸出如圖6所示,。
圖6 MethodIds解析后的輸出
在解析ProtoIds的時(shí)候是有3個(gè)方法原型,,在解析方法時(shí)是4個(gè)方法,第一個(gè)方法與第四個(gè)方法的方法原型是相同的,。
用第二個(gè)方法來(lái)進(jìn)行一個(gè)簡(jiǎn)單說(shuō)明,,V LHelloWorld.main([Ljava/lang/String]);,。V表示方法的返回值類型,LHelloWorld是方法所在的類,,main是方法的名稱,,Ljava/lang/String是該方法參數(shù)的類型。
9. 解析DexClassDef相關(guān)數(shù)據(jù)
解析DexClassDef是最復(fù)雜的部分了,,因?yàn)樗鼤?huì)先解析類相關(guān)的內(nèi)容,,類相關(guān)的內(nèi)容包含類所屬的文件,、類中的屬性、類中的方法,、方法中的字節(jié)碼等內(nèi)容,。雖然復(fù)雜,但是它只是前面每個(gè)部分和其余部分的組成,,因此只是代碼比較多,,沒(méi)有什么特別難的地方,具體代碼如下:
void PrintClassDef(DexFile *pDexFile)
{
for ( u4 i =0; i < pDexFile->pHeader->classDefsSize; i ++ )
{
const DexClassDef *pDexClassDef = dexGetClassDef(pDexFile, i),;
// 類所屬的源文件
printf(“SourceFile : %s\r\n”, dexGetSourceFile(pDexFile, pDexClassDef)),;
// 類和父類
// 因?yàn)槲覀兊?Dex 文件沒(méi)有接口所以這里就沒(méi)寫(xiě)
// 具體解析的時(shí)候需要根據(jù)實(shí)際情況而定
printf(“class %s\b externs %s\b { \r\n”,
dexGetClassDescriptor(pDexFile, pDexClassDef),
dexGetSuperClassDescriptor(pDexFile, pDexClassDef)),;
const u1 *pu1 = dexGetClassData(pDexFile, pDexClassDef),;
DexClassData *pDexClassData = dexReadAndVerifyClassData(&pu1, NULL);
// 類中的屬性
for ( u4 z = 0; z < pDexClassData->header.instanceFieldsSize; z ++ )
{
const DexFieldId *pDexField = dexGetFieldId(pDexFile, pDexClassData->
instanceFields[z].fieldIdx),;
printf(“%s %s\r\n”,
dexStringByTypeIdx(pDexFile, pDexField->typeIdx),,
dexStringById(pDexFile, pDexField->nameIdx));
}
// 類中的方法
for ( u4 z = 0; z < pDexClassData->header.directMethodsSize; z ++ )
{
const DexMethodId *pDexMethod = dexGetMethodId(pDexFile, pDexClassData->
directMethods[z].methodIdx),;
const DexProtoId *pDexProtoId = dexGetProtoId(pDexFile, pDexMethod->
protoIdx),;
printf(“\t%s ”, dexStringByTypeIdx(pDexFile, pDexProtoId->returnTypeIdx));
printf(“%s\b.”, dexStringByTypeIdx(pDexFile, pDexMethod->classIdx)),;
printf(“%s”, dexStringById(pDexFile, pDexMethod->nameIdx)),;
printf(“(”);
// 獲得參數(shù)列表
const DexTypeList *pDexTypeList = dexGetProtoParameters(pDexFile, pDexProtoId),;
u4 num = pDexTypeList != NULL ? pDexTypeList->size : 0;
// 輸出參數(shù)
for ( u4 k = 0; k < num; k ++ )
{
printf(“%s\b v%d, ”, dexStringByTypeIdx(pDexFile, pDexTypeList->
list[k].typeIdx),, k);
}
if ( num == 0 )
{
printf(“)”),;
}
else
{
printf(“\b\b)”),;
}
printf(“{\r\n”);
// 方法中具體的數(shù)據(jù)
const DexCode *pDexCode = dexGetCode(pDexFile, (const DexMethod *)&pDex
ClassData->directMethods[z]),;
printf(“\t\tregister:%d \r\n”, pDexCode->registersSize),;
printf(“\t\tinsnsSize:%d \r\n”, pDexCode->insSize);
printf(“\t\tinsSize:%d \r\n”, pDexCode->outsSize),;
// 方法的字節(jié)碼
printf(“\t\t// ByteCode …\r\n\r\n”),;
printf(“\t\t//”);
for ( u2 x = 0; x < pDexCode->insnsSize; x ++ )
{
printf(“%04X ”, pDexCode->insns[x]),;
}
printf(“\r\n”),;
printf(“\t}\r\n\r\n”);
}
printf(“}\r\n”),;
}
}
在代碼中逐步地對(duì)類進(jìn)行了解析,,從類所屬的源文件,、類的名稱、類的父類,、類的屬性,,到類的方法以及類的字節(jié)碼。除了方法中的數(shù)據(jù)在前面的代碼中沒(méi)有,,其余的代碼在前面都有過(guò)介紹了,。對(duì)于類方法中的數(shù)據(jù)只要按照DexCode進(jìn)行解析即可,這里請(qǐng)參考前面給出的DexCode結(jié)構(gòu)體即可,。
最后,,在main函數(shù)中調(diào)用自定義函數(shù),輸出如圖7所示,。
圖7 DexClassDef解析后的輸出