日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

.dex文件结构学习笔记(4)

發(fā)布時間:2025/3/19 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 .dex文件结构学习笔记(4) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

接下來就是對dexFileParse函數(shù)進行分析。這個函數(shù)的代碼如下:

/** Parse an optimized or unoptimized .dex file sitting in memory. This is* called after the byte-ordering and structure alignment has been fixed up.** On success, return a newly-allocated DexFile.** 分析一個優(yōu)化或者未優(yōu)化的dex文件。這個函數(shù)在字節(jié)序確認與結(jié)構(gòu)粒度對齊完成之后調(diào)用。*/ DexFile* dexFileParse(const u1* data, size_t length, int flags) {DexFile* pDexFile = NULL;const DexHeader* pHeader;const u1* magic;int result = -1;// 文件長度最小為一個DEX頭if (length < sizeof(DexHeader)) {LOGE("too short to be a valid .dex");goto bail; /* bad file format */}// 分配一個DexFile結(jié)構(gòu)pDexFile = (DexFile*) malloc(sizeof(DexFile));if (pDexFile == NULL)goto bail; /* alloc failure */memset(pDexFile, 0, sizeof(DexFile));/** Peel off the optimized header.*/// 比對是否是一個優(yōu)化后的dex文件if (memcmp(data, DEX_OPT_MAGIC, 4) == 0) {magic = data;// 確定版本if (memcmp(magic+4, DEX_OPT_MAGIC_VERS, 4) != 0) {LOGE("bad opt version (0x%02x %02x %02x %02x)",magic[4], magic[5], magic[6], magic[7]);goto bail;}// 取出優(yōu)化后的文件頭pDexFile->pOptHeader = (const DexOptHeader*) data;LOGV("Good opt header, DEX offset is %d, flags=0x%02x",pDexFile->pOptHeader->dexOffset, pDexFile->pOptHeader->flags);/* parse the optimized dex file tables */// 分析優(yōu)化dex文件表if (!dexParseOptData(data, length, pDexFile))goto bail;/* ignore the opt header and appended data from here on out */// 忽略掉優(yōu)化文件頭與附加數(shù)據(jù)data += pDexFile->pOptHeader->dexOffset;// 指向未優(yōu)化的文件頭length -= pDexFile->pOptHeader->dexOffset;// 從整個文件長度中排除優(yōu)化文件頭的長度// 檢查長度是否合法if (pDexFile->pOptHeader->dexLength > length) {LOGE("File truncated? stored len=%d, rem len=%d",pDexFile->pOptHeader->dexLength, (int) length);goto bail;}// 確定新的長度length = pDexFile->pOptHeader->dexLength;}// data指向原始文件頭,設(shè)置DexFile結(jié)構(gòu)dexFileSetupBasicPointers(pDexFile, data);pHeader = pDexFile->pHeader;// 確定有效的magicif (!dexHasValidMagic(pHeader)) {goto bail;}/** Verify the checksum(s). This is reasonably quick, but does require* touching every byte in the DEX file. The base checksum changes after* byte-swapping and DEX optimization.** 驗證校驗和,基礎(chǔ)的校驗和已經(jīng)在字節(jié)序交換與DEX優(yōu)化中得到改變*/if (flags & kDexParseVerifyChecksum) {// 重新計算校驗和u4 adler = dexComputeChecksum(pHeader);if (adler != pHeader->checksum) {LOGE("ERROR: bad checksum (%08x vs %08x)",adler, pHeader->checksum);if (!(flags & kDexParseContinueOnError))goto bail;} else {LOGV("+++ adler32 checksum (%08x) verified", adler);}// 如果是優(yōu)化后的DEX文件則通過優(yōu)化后的DEX頭進行計算文件校驗和const DexOptHeader* pOptHeader = pDexFile->pOptHeader;if (pOptHeader != NULL) {adler = dexComputeOptChecksum(pOptHeader);if (adler != pOptHeader->checksum) {LOGE("ERROR: bad opt checksum (%08x vs %08x)",adler, pOptHeader->checksum);if (!(flags & kDexParseContinueOnError))goto bail;} else {LOGV("+++ adler32 opt checksum (%08x) verified", adler);}}}/** Verify the SHA-1 digest. (Normally we don't want to do this --* the digest is used to uniquely identify the original DEX file, and* can't be computed for verification after the DEX is byte-swapped* and optimized.)** 使用SHA1驗證簽名*/if (kVerifySignature) {// 計算簽名也不包含magic與校驗和段unsigned char sha1Digest[kSHA1DigestLen];const int nonSum = sizeof(pHeader->magic) + sizeof(pHeader->checksum) +kSHA1DigestLen;dexComputeSHA1Digest(data + nonSum, length - nonSum, sha1Digest);// 對比簽名if (memcmp(sha1Digest, pHeader->signature, kSHA1DigestLen) != 0) {char tmpBuf1[kSHA1DigestOutputLen];char tmpBuf2[kSHA1DigestOutputLen];LOGE("ERROR: bad SHA1 digest (%s vs %s)",dexSHA1DigestToStr(sha1Digest, tmpBuf1),dexSHA1DigestToStr(pHeader->signature, tmpBuf2));if (!(flags & kDexParseContinueOnError))goto bail;} else {LOGV("+++ sha1 digest verified");}}// 文件頭中保存的大小與預(yù)期大小不一致if (pHeader->fileSize != length) {LOGE("ERROR: stored file size (%d) != expected (%d)",(int) pHeader->fileSize, (int) length);if (!(flags & kDexParseContinueOnError))goto bail;}// 沒有類信息在DEX文件內(nèi)if (pHeader->classDefsSize == 0) {LOGE("ERROR: DEX file has no classes in it, failing");goto bail;}/** Success!*/result = 0; bail:if (result != 0 && pDexFile != NULL) {dexFileFree(pDexFile);pDexFile = NULL;}return pDexFile; }

其實這里也沒有做什么文件結(jié)構(gòu)分析,只是分優(yōu)化與未優(yōu)化后的DEX文件做了校驗和計算與簽名匹配

隨后通過processDexFile對pDexFile進行打印輸出。其代碼如下:

void processDexFile(const char* fileName, DexFile* pDexFile) {char* package = NULL;int i;if (gOptions.verbose) {printf("Opened '%s', DEX version '%.3s'\n", fileName,pDexFile->pHeader->magic +4);}if (gOptions.dumpRegisterMaps) {dumpRegisterMaps(pDexFile);return;}if (gOptions.showFileHeaders) {dumpFileHeader(pDexFile);dumpOptDirectory(pDexFile);}if (gOptions.outputFormat == OUTPUT_XML)printf("<api>\n");for (i = 0; i < (int) pDexFile->pHeader->classDefsSize; i++) {if (gOptions.showSectionHeaders)dumpClassDef(pDexFile, i);dumpClass(pDexFile, i, &package);}/* free the last one allocated */if (package != NULL) {printf("</package>\n");free(package);}if (gOptions.outputFormat == OUTPUT_XML)printf("</api>\n"); }

代碼簡單易懂

這里有一個我比較關(guān)注的選項,gOptions.showFileHeaders.這里調(diào)用了dumpFileHeader,dumpOptDirectory兩個函數(shù)。前者只是打印一些頭信息而已。后者是與優(yōu)化DEX文件有關(guān)。如果不是優(yōu)化后的DEX文件則直接退出。這里先研究原始的DEX文件結(jié)構(gòu)。關(guān)于優(yōu)化以后在研究。

隨后則循環(huán)打印類信息。代碼如下:

for (i = 0; i < (int) pDexFile->pHeader->classDefsSize; i++) {if (gOptions.showSectionHeaders)dumpClassDef(pDexFile, i);dumpClass(pDexFile, i, &package); }

調(diào)用dumpClassDef打印類定義信息,調(diào)用dumpClass打印類信息。

以下是dumpClassDef的代碼

/** Dump a class_def_item.** dump 一個 class_def_item結(jié)構(gòu)*/ void dumpClassDef(DexFile* pDexFile, int idx) {const DexClassDef* pClassDef;const u1* pEncodedData;DexClassData* pClassData;pClassDef = dexGetClassDef(pDexFile, idx);pEncodedData = dexGetClassData(pDexFile, pClassDef);pClassData = dexReadAndVerifyClassData(&pEncodedData, NULL);if (pClassData == NULL) {fprintf(stderr, "Trouble reading class data\n");return;}printf("Class #%d header:\n", idx);printf("class_idx : %d\n", pClassDef->classIdx);printf("access_flags : %d (0x%04x)\n",pClassDef->accessFlags, pClassDef->accessFlags);printf("superclass_idx : %d\n", pClassDef->superclassIdx);printf("interfaces_off : %d (0x%06x)\n",pClassDef->interfacesOff, pClassDef->interfacesOff);printf("source_file_idx : %d\n", pClassDef->sourceFileIdx);printf("annotations_off : %d (0x%06x)\n",pClassDef->annotationsOff, pClassDef->annotationsOff);printf("class_data_off : %d (0x%06x)\n",pClassDef->classDataOff, pClassDef->classDataOff);printf("static_fields_size : %d\n", pClassData->header.staticFieldsSize);printf("instance_fields_size: %d\n",pClassData->header.instanceFieldsSize);printf("direct_methods_size : %d\n", pClassData->header.directMethodsSize);printf("virtual_methods_size: %d\n",pClassData->header.virtualMethodsSize);printf("\n");free(pClassData); }

這個函數(shù)連續(xù)調(diào)用了dexGetClassDef,dexGetClassData,dexReadAndVerifyd

三個函數(shù)。

dexGetClassDef的作用是獲取類的定義信息

/* return the ClassDef with the specified index */ /* 通過制定的索引值返回類定義 */ DEX_INLINE const DexClassDef* dexGetClassDef(const DexFile* pDexFile, u4 idx) {assert(idx < pDexFile->pHeader->classDefsSize);return &pDexFile->pClassDefs[idx]; }偶

從代碼可以看出它直接從文件頭的pClassDefs隊列中通過索引獲取DexClassDef結(jié)構(gòu)指針。

dexGetClassData是通過給定的類信息獲取類的實際數(shù)據(jù)。DEX文件存儲數(shù)據(jù)是使用LEB128編碼的。這個編碼以后在進行分析。隨后使用dexReadAndVerifyClassData進行解碼操作。得到類數(shù)據(jù)

/* DexClassDef convenience - get class_data_item pointer */ /* 獲取class_data_item指針 */ DEX_INLINE const u1* dexGetClassData(const DexFile* pDexFile,const DexClassDef* pClassDef) {if (pClassDef->classDataOff == 0)return NULL;// 文件基地址 + 類數(shù)據(jù)偏移return (const u1*) (pDexFile->baseAddr + pClassDef->classDataOff); }

從代碼可以看出是通過是通過類定義信息結(jié)構(gòu)的classDataOff這個字段的所保存的文件偏移取得。返回一個DexClassData結(jié)構(gòu),但此時這里的數(shù)據(jù)是經(jīng)過LEB128進行編碼的。

下面列出了DexClassDef與DexClassData兩個結(jié)構(gòu):

struct DexClassDef {u4 classIdx; /* index into typeIds for this class */u4 accessFlags;u4 superclassIdx; /* index into typeIds for superclass */u4 interfacesOff; /* file offset to DexTypeList */u4 sourceFileIdx; /* index into stringIds for source file name */u4 annotationsOff; /* file offset to annotations_directory_item */u4 classDataOff; /* file offset to class_data_item */u4 staticValuesOff; /* file offset to DexEncodedArray */ };

這個結(jié)構(gòu)就是表明類的屬性。

在libdex/DexClass.h中定義了DexClassData結(jié)構(gòu)

/* expanded form of class_data_item. Note: If a particular item is* absent (e.g., no static fields), then the corresponding pointer* is set to NULL.** 類數(shù)據(jù)結(jié)構(gòu)*/ struct DexClassData {DexClassDataHeader header;// 類信息頭DexField* staticFields;// 靜態(tài)對象DexField* instanceFields;// 實例對象DexMethod* directMethods;// 類方方DexMethod* virtualMethods;// 類的虛方法 };

這里的結(jié)構(gòu)一看就明白了。儲存了一個描述類的頭以及4個關(guān)于變量與函數(shù)的隊列。

其余相關(guān)的結(jié)構(gòu)定義如下:

/* expanded form of a class_data_item header */ struct DexClassDataHeader {u4 staticFieldsSize;u4 instanceFieldsSize;u4 directMethodsSize;u4 virtualMethodsSize; }; /* expanded form of encoded_field */ struct DexField {u4 fieldIdx; /* index to a field_id_item */u4 accessFlags; }; /* expanded form of encoded_method */ struct DexMethod {u4 methodIdx; /* index to a method_id_item */u4 accessFlags;u4 codeOff; /* file offset to a code_item */ };

dexReadAndVerifyClassData位于libdex/DexClass.cpp中

bool dexReadAndVerifyClassDataHeader(const u1** pData, const u1* pLimit,DexClassDataHeader *pHeader) {// 從pData中讀取一個leb128數(shù)并驗證合法性if (! verifyUlebs(*pData, pLimit, 4)) {return false;}// 讀取類頭信息dexReadClassDataHeader(pData, pHeader);return true; }

verifyUlebs的作用是驗證leb128編碼。這里不做分析了。隨后調(diào)用了dexReadClassDataHeader

進行讀取并轉(zhuǎn)碼的操作。這個函數(shù)定義在DexClass.h中

DEX_INLINE void dexReadClassDataHeader(const u1** pData,DexClassDataHeader *pHeader) {pHeader->staticFieldsSize = readUnsignedLeb128(pData);pHeader->instanceFieldsSize = readUnsignedLeb128(pData);pHeader->directMethodsSize = readUnsignedLeb128(pData);pHeader->virtualMethodsSize = readUnsignedLeb128(pData); }

使用readUnsignedLeb128進行讀取。

到這里一個類頭數(shù)據(jù)就完整的讀取完畢了。 接下來就是打印這個類本身的基礎(chǔ)信息了。

調(diào)用了DexDump.cpp/dumpClass函數(shù)。

打印類信息比較復(fù)雜。下一篇完整分析。

轉(zhuǎn)載于:https://blog.51cto.com/devilogic/1203135

總結(jié)

以上是生活随笔為你收集整理的.dex文件结构学习笔记(4)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。