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

接下来就是对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函数。

打印类信息比较复杂。下一篇完整分析。

你可能感兴趣的:(android,dalvik)