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 验证之

Dalvik源码阅读笔记(一)的更多相关文章

  1. Dalvik源码阅读笔记(二)

    DVM 类加载原理: DEX 文件加载到内存中 DvmDex 结构后,还没有完成类的解析工作,我们将 DEX 中的类填充到 ClassObject 结构的过程称为类加载. ClassObject 用来 ...

  2. CI框架源码阅读笔记5 基准测试 BenchMark.php

    上一篇博客(CI框架源码阅读笔记4 引导文件CodeIgniter.php)中,我们已经看到:CI中核心流程的核心功能都是由不同的组件来完成的.这些组件类似于一个一个单独的模块,不同的模块完成不同的功 ...

  3. CI框架源码阅读笔记4 引导文件CodeIgniter.php

    到了这里,终于进入CI框架的核心了.既然是“引导”文件,那么就是对用户的请求.参数等做相应的导向,让用户请求和数据流按照正确的线路各就各位.例如,用户的请求url: http://you.host.c ...

  4. CI框架源码阅读笔记3 全局函数Common.php

    从本篇开始,将深入CI框架的内部,一步步去探索这个框架的实现.结构和设计. Common.php文件定义了一系列的全局函数(一般来说,全局函数具有最高的加载优先权,因此大多数的框架中BootStrap ...

  5. CI框架源码阅读笔记2 一切的入口 index.php

    上一节(CI框架源码阅读笔记1 - 环境准备.基本术语和框架流程)中,我们提到了CI框架的基本流程,这里再次贴出流程图,以备参考: 作为CI框架的入口文件,源码阅读,自然由此开始.在源码阅读的过程中, ...

  6. 源码阅读笔记 - 1 MSVC2015中的std::sort

    大约寒假开始的时候我就已经把std::sort的源码阅读完毕并理解其中的做法了,到了寒假结尾,姑且把它写出来 这是我的第一篇源码阅读笔记,以后会发更多的,包括算法和库实现,源码会按照我自己的代码风格格 ...

  7. Three.js源码阅读笔记-5

    Core::Ray 该类用来表示空间中的“射线”,主要用来进行碰撞检测. THREE.Ray = function ( origin, direction ) { this.origin = ( or ...

  8. PHP源码阅读笔记一(explode和implode函数分析)

    PHP源码阅读笔记一一.explode和implode函数array explode ( string separator, string string [, int limit] )此函数返回由字符 ...

  9. AQS源码阅读笔记(一)

    AQS源码阅读笔记 先看下这个类张非常重要的一个静态内部类Node.如下: static final class Node { //表示当前节点以共享模式等待锁 static final Node S ...

随机推荐

  1. HBase环境搭建、shell操作及Java API编程

    一. 1.掌握Hbase在Hadoop集群体系结构中发挥的作用和使过程. 2.掌握安装和配置HBase基本方法. 3.掌握HBase shell的常用命令. 4.使用HBase shell命令进行表的 ...

  2. C++ leetcode Longest Palindromic Substring

    明天就要上课了,再过几天又要见班主任汇报项目进程了,什么都没做的我竟然有一种迷之淡定,大概是想体验一波熬夜修仙的快乐了.不管怎么说,每天还是要水一篇博文,写一个LeetCode的题才圆满. 题目:Gi ...

  3. Linux NTP服务配置 for Oracle RAC

    安装Oracle 11g RAC时,我们需要配置ntp服务.在使用虚拟机的情况下对于时钟同步方式的配置有很多种方式,可以使用vmware自带的时钟同步功能,也可以直接将本地的一个节点用作时间服务器.本 ...

  4. Python print打印

    1.Python运算符: +:加 -:减 *:乘 /:除以 %:去除法后的余数 //:取整除 2.字符串center方法: a='111'print(a.center(4,'2')) #让字符串占位 ...

  5. Qt Widgets——菜单和菜单栏

    主窗口MainWindow需要菜单栏QMenuBar及菜单QMenu来组成自身,一般应用程序的所有功能都能在菜单中找到.接下来就来说说它们. QMenu 它添加了很多动作QAction,并用自身组成了 ...

  6. Python3+ssl实现加密通信

    一.说明 1. python标准库ssl可实现加密通信 2. ssl库底层使用openssl,做了面向对像化改造和简化,但还是可以明显看出openssl的痕迹 3. 本文先给出python实现的soc ...

  7. FileZilla Server隐藏版本号教程

    1.查看当前是否泄漏版本号 telnet FileZilla监听端口查看返回信息:telnet 192.168.220.130 21 2.自定义欢迎信息 登录FileZilla--点击“Edit”-- ...

  8. UVa 10795 - A Different Task 对称, 中间状态, 数位DP 难度: 3

    题目 https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&a ...

  9. Java反射《一》获取类

    package com.study.reflect; /** * 反射:java程序运行中,可以获得该类的所有属性和方法,对于任意一个对象可以 调用它的属性和方法,这种动态获得属性和方法,调用对象属性 ...

  10. tomcat 服务器线程问题

    http://blog.csdn.net/wtopps/article/details/71339295 http://blog.csdn.net/wtopps/article/details/713 ...