《電子技術(shù)應(yīng)用》
您所在的位置:首頁(yè) > 通信與網(wǎng)絡(luò) > 業(yè)界動(dòng)態(tài) > 網(wǎng)絡(luò)安全編程:開(kāi)發(fā)Dex文件格式解析工具

網(wǎng)絡(luò)安全編程:開(kāi)發(fā)Dex文件格式解析工具

2021-07-30
來(lái)源:計(jì)算機(jī)與網(wǎng)絡(luò)安全
關(guān)鍵詞: 安全 編程 Dex文件

  解析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解析后的輸出




電子技術(shù)圖片.png

本站內(nèi)容除特別聲明的原創(chuàng)文章之外,,轉(zhuǎn)載內(nèi)容只為傳遞更多信息,并不代表本網(wǎng)站贊同其觀點(diǎn),。轉(zhuǎn)載的所有的文章,、圖片、音/視頻文件等資料的版權(quán)歸版權(quán)所有權(quán)人所有,。本站采用的非本站原創(chuàng)文章及圖片等內(nèi)容無(wú)法一一聯(lián)系確認(rèn)版權(quán)者,。如涉及作品內(nèi)容、版權(quán)和其它問(wèn)題,,請(qǐng)及時(shí)通過(guò)電子郵件或電話通知我們,,以便迅速采取適當(dāng)措施,避免給雙方造成不必要的經(jīng)濟(jì)損失,。聯(lián)系電話:010-82306118,;郵箱:[email protected]