Dalvik源码阅读笔记(一)

dalvik 虚拟机启动入口在 JNI_CreateJavaVM(), 在进行完 JNIEnv 等环境设置后,调用 dvmStartup() 函数进行真正的 DVM 初始化。

jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
    --snip--
    std::string status = dvmStartup(argc, argv.get(),
        args->ignoreUnrecognized, (JNIEnv*)pEnv);
    --snip--
}

dvmStartup() 进行了一系列 DVM 子模块初始化工作,主要关注 dvmInternalNativeStartup() ,这个函数用来初始化一个内部 Native 函数集。

std::string dvmStartup(int argc, const char* const argv[],
        bool ignoreUnrecognized, JNIEnv* pEnv)
{
    --snip--
    if (!dvmInlineNativeStartup()) {
        return "dvmInlineNativeStartup";
    }
    --snip--
}

所有需要直接访问 Dalvik 虚拟机内部函数或者数据结构的 Native 函数都需要定义在这个集合中,dvmInlineNativeStartup() 创建了函数集的 HashTable 用于快速访问。

bool dvmInternalNativeStartup()
{
    DalvikNativeClass* classPtr = gDvmNativeMethodSet;
    --snip--
    gDvm.userDexFiles = dvmHashTableCreate(2, dvmFreeDexOrJar);
    --snip--
}

查看 gDvmNativeMethodSet 变量定义:

static DalvikNativeClass gDvmNativeMethodSet[] = {
    --snip--
    { "Ldalvik/system/DexFile;", dvm_dalvik_system_DexFile, 0 },
    --snip--
};

所有与 DEX 文件操作相关的 Native 函数都定义在 dvm_dalvik_system_DexFile() 中

const DalvikNativeMethod dvm_dalvik_system_DexFile[] = {
    { "openDexFileNative",  "(Ljava/lang/String;Ljava/lang/String;I)I",
        Dalvik_dalvik_system_DexFile_openDexFileNative },
    { "openDexFile",        "([B)I",
        Dalvik_dalvik_system_DexFile_openDexFile_bytearray },
    { "closeDexFile",       "(I)V",
        Dalvik_dalvik_system_DexFile_closeDexFile },
    { "defineClassNative",  "(Ljava/lang/String;Ljava/lang/ClassLoader;I)Ljava/lang/Class;",
        Dalvik_dalvik_system_DexFile_defineClassNative },
    { "getClassNameList",   "(I)[Ljava/lang/String;",
        Dalvik_dalvik_system_DexFile_getClassNameList },
    { "isDexOptNeeded",     "(Ljava/lang/String;)Z",
        Dalvik_dalvik_system_DexFile_isDexOptNeeded },
    { NULL, NULL, NULL },
};

openDexFileNative 与 openDexFile 函用于打开一个 DEX 文件,区别是 openDexFile 是 Opening in-memory DEX,而 openDexFileNative 读取的是磁盘文件。

Dalvik_dalvik_system_DexFile_openDexFileNative() 函数用于打开 jar 或 DEX 文件。

这里的 jar 也可以是 APK 文件,即用户安装的应用,他们本质都是 zip 压缩包。

DEX 文件走 dvmRawDexFileOpen() 函数分支。

jar 文件走 dvmJarFileOpen() 函数分支,这是最常见的方式。

--snip--
    } else if (dvmJarFileOpen(sourceName, outputName, &pJarFile, false) == 0) {
        ALOGV("Opening DEX file '%s' (Jar)", sourceName);

        pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar));
        pDexOrJar->isDex = false;
        pDexOrJar->pJarFile = pJarFile;
        pDexOrJar->pDexMemory = NULL;
    } else {
    --snip--

dvmJarFileOpen() 执行成功后,在 pJarFile 中保存了 zip 文件,dex(实际为 opt 过的 odex 文件) 文件加载到内存的地址:

struct JarFile {
    ZipArchive  archive;
    //MemMapping  map;
    char*       cacheFileName;
    DvmDex*     pDvmDex;
};

继续看 dvmJarFileOpen() 函数实现:

int dvmJarFileOpen(const char* fileName, const char* odexOutputName,
    JarFile** ppJarFile, bool isBootstrap)
{
    /* 将 zip 读入内存 archive 中 */
    if (dexZipOpenArchive(fileName, &archive) != 0) 
        goto bail;
        
    fd = openAlternateSuffix(fileName, "odex", O_RDONLY, &cachedName);
    if (fd >= 0) {
        /* 如果缓存中存在 fileName.odex 文件 */
        if (!dvmCheckOptHeaderAndDependencies(fd, false, 0, 0, true, true)) { /* 检查如果不是新的,跳转到 tryArchive */
            ALOGE("%s odex has stale dependencies", fileName);
            goto tryArchive;
        } else {
            ALOGV("%s odex has good dependencies", fileName);
        }
    } else { 
        ZipEntry entry;
tryArchive:
        /* 查找 zip 中的 DEX 文件 */
        entry = dexZipFindEntry(&archive, kDexInJarName);
        if (entry != NULL) {
            /* 优化为 odex */
            dvmOptimizeDexFile(...);
        }
    }
    /* 将 odex 文件映射到内存 pDvmDex */
    if (dvmDexFileOpenFromFd(fd, &pDvmDex) != 0) {  
        ALOGI("Unable to map %s in %s", kDexInJarName, fileName); 
        goto bail;
    }
    /* 初始化 pJarFile 结构体 */
    (*ppJarFile)->archive = archive;
    (*ppJarFile)->cacheFileName = cachedName;
    (*ppJarFile)->pDvmDex = pDvmDex;
}

DEX 在内存加载完成后,挂载到 HashTable 上:

addToDexFileTable(pDexOrJar);

dvmOptimizeDexFile() 是一个重要的函数,它通过 fork 执行 /bin/dexopt 程序进行 DEX 优化操作。代码在 OptMain.cpp 中

dvmOptimizeDexFile() 设置了“–dex”参数,它决定了 dexopt 函数中的分支:

argv[curArg++] = "--dex";
int main(int argc, char* const argv[])
{
    set_process_name("dexopt");
--snip--
    if (argc > 1) {
        if (strcmp(argv[1], "--zip") == 0)
            return fromZip(argc, argv);
        else if (strcmp(argv[1], "--dex") == 0)
            return fromDex(argc, argv);
        else if (strcmp(argv[1], "--preopt") == 0)
            return preopt(argc, argv);
    }
    --snip--
}

–dex 走 fromDex() 分支,主要优化在 dvmContinueOptimization() 函数完成:

/* do the optimization */
if (!dvmContinueOptimization(fd, offset, length, debugFileName,
        modWhen, crc, (flags & DEXOPT_IS_BOOTSTRAP) != 0))
{
    ALOGE("Optimization failed");
    goto bail;
}
bool dvmContinueOptimization(int fd, off_t dexOffset, long dexLength,
    const char* fileName, u4 modWhen, u4 crc, bool isBootstrap)
{
--snip--
    /* 映射整个文件到内存 */
    void* mapAddr;
    mapAddr = mmap(NULL, dexOffset + dexLength, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
    if (mapAddr == MAP_FAILED) {
        ALOGE("unable to mmap DEX cache: %s", strerror(errno));
        goto bail;
    }
    
        
    /* rewriteDex 主要做对齐,大小端转换等操作 */
    success = rewriteDex(((u1*) mapAddr) + dexOffset, dexLength, doVerify, doOpt, &pClassLookup, NULL);
    
    u1* dexAddr = ((u1*) mapAddr) + dexOffset;
    /* 经常在这个里下断或Hook脱壳,因为完成了rewriteDex
        完成由 DEX File 到内存 pDvmDex 结构的映射
    */
    if (dvmDexFileOpenPartial(dexAddr, dexLength, &pDvmDex) != 0) {
        ALOGE("Unable to create DexFile");
        success = false;
    }
    
    /* 最后生成 odex 文件 */
    --snip--
}
int dvmDexFileOpenPartial (const void* addr, int len, DvmDex** ppDvmDex)
{
    DvmDex* pDvmDex;
    DexFile* pDexFile;
--snip--
    pDexFile = dexFileParse((u1*)addr, len, parseFlags);
    
    pDvmDex = allocateAuxStructures(pDexFile);
    pDvmDex->isMappedReadOnly = false;
    *ppDvmDex = pDvmDex;
--snip--
}
/*
 * 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.
 */
DexFile* dexFileParse(const u1* data, size_t length, int flags)

疑问:
1.为什么没有直接opt前dump的文章?为什么要在rewriteDex后,Hook 验证之

转载于:https://www.cnblogs.com/gm-201705/p/9864046.html

Android虚拟机Dalvik完整源码,宝贵资源,欢迎下载! This directory contains the Dalvik virtual machine and core class library, as well as related tools, libraries, and tests. A note about the licenses and header comments --------------------------------------------- Much of the code under this directory originally came from the Apache Harmony project, and as such contains the standard Apache header comment. Some of the code was written originally for the Android project, and as such contains the standard Android header comment. Some files contain code from both projects. In these cases, the header comment is a combination of the other two, and the portions of the code from Harmony are identified as indicated in the comment. Here is the combined header comment: /* * Copyright (C) The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * ---------- * * Portions of the code surrounded by "// BEGIN Harmony code" and * "// END Harmony code" are copyrighted and licensed separately, as * follows: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ Native SH call bridge --------------------- Native SH call bridge is written by Shin-ichiro KAWASAKI and Contributed to Android by Hitachi, Ltd. and Renesas Solutions Corp.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值