接下来就是对dexFileParse函数进行分析。这个函数的代码如下:
/* * 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. * * 分析一个优化或者未优化的dex文件。这个函数在字节序确认与结构粒度对齐完成之后调用。 */ 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结构 pDexFile = (DexFile*) malloc(sizeof(DexFile)); if (pDexFile == NULL) goto bail; /* alloc failure */ memset(pDexFile, 0, sizeof(DexFile)); /* * Peel off the optimized header. */ // 比对是否是一个优化后的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; } // 取出优化后的文件头 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 */ // 分析优化dex文件表 if (!dexParseOptData(data, length, pDexFile)) goto bail; /* ignore the opt header and appended data from here on out */ // 忽略掉优化文件头与附加数据 data += pDexFile->pOptHeader->dexOffset;// 指向未优化的文件头 length -= pDexFile->pOptHeader->dexOffset;// 从整个文件长度中排除优化文件头的长度 // 检查长度是否合法 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指向原始文件头,设置DexFile结构 dexFileSetupBasicPointers(pDexFile, data); pHeader = pDexFile->pHeader; // 确定有效的magic if (!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. * * 验证校验和,基础的校验和已经在字节序交换与DEX优化中得到改变 */ 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); } // 如果是优化后的DEX文件则通过优化后的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"); } } // 文件头中保存的大小与预期大小不一致 if (pHeader->fileSize != length) { LOGE("ERROR: stored file size (%d) != expected (%d)", (int) pHeader->fileSize, (int) length); if (!(flags & kDexParseContinueOnError)) goto bail; } // 没有类信息在DEX文件内 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; }
其实这里也没有做什么文件结构分析,只是分优化与未优化后的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"); }
代码简单易懂
这里有一个我比较关注的选项,gOptions.showFileHeaders.这里调用了dumpFileHeader,dumpOptDirectory两个函数。前者只是打印一些头信息而已。后者是与优化DEX文件有关。如果不是优化后的DEX文件则直接退出。这里先研究原始的DEX文件结构。关于优化以后在研究。
随后则循环打印类信息。代码如下:
for (i = 0; i < (int) pDexFile->pHeader->classDefsSize; i++) { if (gOptions.showSectionHeaders) dumpClassDef(pDexFile, i); dumpClass(pDexFile, i, &package); }
调用dumpClassDef打印类定义信息,调用dumpClass打印类信息。
以下是dumpClassDef的代码
/* * Dump a class_def_item. * * dump 一个 class_def_item结构 */ 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); }
这个函数连续调用了dexGetClassDef,dexGetClassData,dexReadAndVerifyd
三个函数。
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结构指针。
dexGetClassData是通过给定的类信息获取类的实际数据。DEX文件存储数据是使用LEB128编码的。这个编码以后在进行分析。随后使用dexReadAndVerifyClassData进行解码操作。得到类数据
/* 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; // 文件基地址 + 类数据偏移 return (const u1*) (pDexFile->baseAddr + pClassDef->classDataOff); }
从代码可以看出是通过是通过类定义信息结构的classDataOff这个字段的所保存的文件偏移取得。返回一个DexClassData结构,但此时这里的数据是经过LEB128进行编码的。
下面列出了DexClassDef与DexClassData两个结构:
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 */ };
这个结构就是表明类的属性。
在libdex/DexClass.h中定义了DexClassData结构
/* 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. * * 类数据结构 */ struct DexClassData { DexClassDataHeader header;// 类信息头 DexField* staticFields;// 静态对象 DexField* instanceFields;// 实例对象 DexMethod* directMethods;// 类方方 DexMethod* virtualMethods;// 类的虚方法 };
这里的结构一看就明白了。储存了一个描述类的头以及4个关于变量与函数的队列。
其余相关的结构定义如下:
/* 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数并验证合法性 if (! verifyUlebs(*pData, pLimit, 4)) { return false; } // 读取类头信息 dexReadClassDataHeader(pData, pHeader); return true; }
verifyUlebs的作用是验证leb128编码。这里不做分析了。随后调用了dexReadClassDataHeader
进行读取并转码的操作。这个函数定义在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进行读取。
到这里一个类头数据就完整的读取完毕了。 接下来就是打印这个类本身的基础信息了。
调用了DexDump.cpp/dumpClass函数。
打印类信息比较复杂。下一篇完整分析。