接下来就是对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函数。
打印类信息比较复杂。下一篇完整分析。
转载于:https://blog.51cto.com/devilogic/1203135