Android 逆向整体加固脱壳 ( DEX 优化流程分析 | DexPrepare.cpp 中 dvmOptimizeDexFile() 方法分析 | /bin/dexopt 源码分析 )(代码片段
Posted 韩曙亮
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 逆向整体加固脱壳 ( DEX 优化流程分析 | DexPrepare.cpp 中 dvmOptimizeDexFile() 方法分析 | /bin/dexopt 源码分析 )(代码片段相关的知识,希望对你有一定的参考价值。
前言
上一篇博客 【Android 逆向】整体加固脱壳 ( DexClassLoader 加载 dex 流程分析 | RawDexFile.cpp 分析 | dvmRawDexFileOpen函数读取 DEX 文件 ) 中 , 在 RawDexFile.cpp 中的 dvmRawDexFileOpen() 方法中 , 调用了 DexPrepare.cpp 的 dvmOptimizeDexFile() 函数 , 对 DEX 文件进行了优化 ;
一、DexPrepare.cpp 中 dvmOptimizeDexFile() 方法分析
dvmOptimizeDexFile 函数的参数说明 : int fd
是打开的 dex 文件标识符 , long dexLength
是打开的 dex 文件大小 ;
在该函数中 , 调用 /bin/dexopt 程序 , 优化 dex 文件 , 最终产生 odex 文件 ;
/*
* 给定包含DEX数据的文件的描述符,生成
* 优化版本。
*
* “fd”指向的文件应为锁定的共享资源
* (或私人);我们不努力实施多进程正确性
* 在这里。
*
* “文件名”仅用于调试输出。存储“modWhen”和“crc”
* 在依赖项集中。
*
* “isBootstrap”标志确定优化器和验证器如何处理
* 包范围访问检查。优化时,我们只加载引导
* 类DEX文件和目标DEX,因此该标志确定
* 给目标DEX类一个(合成的)非空类加载器指针。
* 只有当目标DEX包含声明
* 与引导类位于同一个包中。
*
* 优化器需要加载目标DEX文件中的每个类。
* 这通常是不可取的,因此我们启动一个子流程来执行
* 工作并等待它完成。
*
* 成功时返回“true”。所有数据均已写入“fd”。
*/
bool dvmOptimizeDexFile(int fd, off_t dexOffset, long dexLength,
const char* fileName, u4 modWhen, u4 crc, bool isBootstrap)
const char* lastPart = strrchr(fileName, '/');
if (lastPart != NULL)
lastPart++;
else
lastPart = fileName;
ALOGD("DexOpt: --- BEGIN '%s' (bootstrap=%d) ---", lastPart, isBootstrap);
pid_t pid;
/*
* 如果我们的bootclasspath中出现了我们认为
* 都优化了,被拒绝了。
*/
if (gDvm.optimizing)
ALOGW("Rejecting recursive optimization attempt on '%s'", fileName);
return false;
pid = fork();
if (pid == 0)
static const int kUseValgrind = 0;
// 调用 /bin/dexopt 程序 , 优化 dex 文件 , 最终产生 odex 文件
static const char* kDexOptBin = "/bin/dexopt";
static const char* kValgrinder = "/usr/bin/valgrind";
static const int kFixedArgCount = 10;
static const int kValgrindArgCount = 5;
static const int kMaxIntLen = 12; // '-'+10dig+'\\0' -OR- 0x+8dig
int bcpSize = dvmGetBootPathSize();
int argc = kFixedArgCount + bcpSize
+ (kValgrindArgCount * kUseValgrind);
const char* argv[argc+1]; // last entry is NULL
char values[argc][kMaxIntLen];
char* execFile;
const char* androidRoot;
int flags;
/* change process groups, so we don't clash with ProcessManager */
setpgid(0, 0);
/* full path to optimizer */
androidRoot = getenv("ANDROID_ROOT");
if (androidRoot == NULL)
ALOGW("ANDROID_ROOT not set, defaulting to /system");
androidRoot = "/system";
execFile = (char*)alloca(strlen(androidRoot) + strlen(kDexOptBin) + 1);
strcpy(execFile, androidRoot);
strcat(execFile, kDexOptBin);
/*
* Create arg vector.
*/
int curArg = 0;
if (kUseValgrind)
/* probably shouldn't ship the hard-coded path */
argv[curArg++] = (char*)kValgrinder;
argv[curArg++] = "--tool=memcheck";
argv[curArg++] = "--leak-check=yes"; // check for leaks too
argv[curArg++] = "--leak-resolution=med"; // increase from 2 to 4
argv[curArg++] = "--num-callers=16"; // default is 12
assert(curArg == kValgrindArgCount);
argv[curArg++] = execFile;
argv[curArg++] = "--dex";
sprintf(values[2], "%d", DALVIK_VM_BUILD);
argv[curArg++] = values[2];
sprintf(values[3], "%d", fd);
argv[curArg++] = values[3];
sprintf(values[4], "%d", (int) dexOffset);
argv[curArg++] = values[4];
sprintf(values[5], "%d", (int) dexLength);
argv[curArg++] = values[5];
argv[curArg++] = (char*)fileName;
sprintf(values[7], "%d", (int) modWhen);
argv[curArg++] = values[7];
sprintf(values[8], "%d", (int) crc);
argv[curArg++] = values[8];
flags = 0;
if (gDvm.dexOptMode != OPTIMIZE_MODE_NONE)
flags |= DEXOPT_OPT_ENABLED;
if (gDvm.dexOptMode == OPTIMIZE_MODE_ALL)
flags |= DEXOPT_OPT_ALL;
if (gDvm.classVerifyMode != VERIFY_MODE_NONE)
flags |= DEXOPT_VERIFY_ENABLED;
if (gDvm.classVerifyMode == VERIFY_MODE_ALL)
flags |= DEXOPT_VERIFY_ALL;
if (isBootstrap)
flags |= DEXOPT_IS_BOOTSTRAP;
if (gDvm.generateRegisterMaps)
flags |= DEXOPT_GEN_REGISTER_MAPS;
sprintf(values[9], "%d", flags);
argv[curArg++] = values[9];
assert(((!kUseValgrind && curArg == kFixedArgCount) ||
((kUseValgrind && curArg == kFixedArgCount+kValgrindArgCount))));
ClassPathEntry* cpe;
for (cpe = gDvm.bootClassPath; cpe->ptr != NULL; cpe++)
argv[curArg++] = cpe->fileName;
assert(curArg == argc);
argv[curArg] = NULL;
if (kUseValgrind)
execv(kValgrinder, const_cast<char**>(argv));
else
execv(execFile, const_cast<char**>(argv));
ALOGE("execv '%s'%s failed: %s", execFile,
kUseValgrind ? " [valgrind]" : "", strerror(errno));
exit(1);
else
ALOGV("DexOpt: waiting for verify+opt, pid=%d", (int) pid);
int status;
pid_t gotPid;
/*
* 等待优化过程完成。我们进入VMI等待
* 模式,这样GC暂停就不必等待我们了。
*/
ThreadStatus oldStatus = dvmChangeStatus(NULL, THREAD_VMWAIT);
while (true)
gotPid = waitpid(pid, &status, 0);
if (gotPid == -1 && errno == EINTR)
ALOGD("waitpid interrupted, retrying");
else
break;
dvmChangeStatus(NULL, oldStatus);
if (gotPid != pid)
ALOGE("waitpid failed: wanted %d, got %d: %s",
(int) pid, (int) gotPid, strerror(errno));
return false;
if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
ALOGD("DexOpt: --- END '%s' (success) ---", lastPart);
return true;
else
ALOGW("DexOpt: --- END '%s' --- status=0x%04x, process failed",
lastPart, status);
return false;
二、/bin/dexopt 源码分析
dex 文件优化 , 主要是调用 /bin/dexopt 程序 , 最终产生 odex 文件 ;
其源码路径是 /dalvik/dexopt/ 路径 ,
该 OptMain.cpp 源码是一个有 main 函数 , 可以独立执行的 C++ 程序 , 可以在 Android 命令中执行 ;
加载 dex 文件时 , 执行 fromDex 函数 ;
return fromDex(argc, argv);
在 fromfromDex 函数中 , 先准备优化环境 ,
if (dvmPrepForDexOpt(bootClassPath, dexOptMode, verifyMode, flags) != 0)
ALOGE("VM init failed");
goto bail;
然后进行正式优化 ;
/* do the optimization */
if (!dvmContinueOptimization(fd, offset, length, debugFileName,
modWhen, crc, (flags & DEXOPT_IS_BOOTSTRAP) != 0))
ALOGE("Optimization failed");
goto bail;
真正的优化操作 , 在 dvmContinueOptimization
函数中执行的 ;
核心源码如下 : 源码路径 /dalvik/dexopt/OptMain.cpp
/*
* Copyright (C) 2008 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.
*/
/*
* 命令行DEX优化和验证入口点。
*
* 有三种方法可以启动此功能:
* (1)来自虚拟机。这需要十几个参数,其中一个是文件
* 同时作为输入和输出的描述符。这使我们能够
* 仍然不知道DEX数据最初来自何处。
* (2)来自installd或其他本机应用程序。传入文件
* 用于zip文件的描述符、用于输出的文件描述符,以及
* 调试消息的文件名。关于这一点,人们做了许多假设
* 发生了什么(验证+优化已启用,启动
* 类路径位于BOOTCLASSPATH中,等等)。
* (3)在构建过程中在主机上进行预优化。这种行为
* 与(2)几乎相同,只是它采用文件名而不是
* 文件描述符。
*
* bootclasspath条目存在一些脆弱的方面,原因如下
* 很大程度上是由于虚拟机在它认为需要的时候进行工作的历史
* 而不是严格按照要求去做。如果优化引导类路径
* 条目,始终按照它们在路径中出现的顺序执行。
*/
#include "Dalvik.h"
#include "libdex/OptInvocation.h"
#include "cutils/log.h"
#include "cutils/process_name.h"
#include <fcntl.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
static const char* kClassesDex = "classes.dex";
/*
*将zipFd中的“classes.dex”提取到“cacheFd”中,留下一点空间
*用于DEX优化收割台的前端。
*/
static int extractAndProcessZip(int zipFd, int cacheFd,
const char* debugFileName, bool isBootstrap, const char* bootClassPath,
const char* dexoptFlagStr)
ZipArchive zippy;
ZipEntry zipEntry;
size_t uncompLen;
long modWhen, crc32;
off_t dexOffset;
int err;
int result = -1;
int dexoptFlags = 0; /* bit flags, from enum DexoptFlags */
DexClassVerifyMode verifyMode = VERIFY_MODE_ALL;
DexOptimizerMode dexOptMode = OPTIMIZE_MODE_VERIFIED;
memset(&zippy, 0, sizeof(zippy));
/* make sure we're still at the start of an empty file */
if (lseek(cacheFd, 0, SEEK_END) != 0)
ALOGE("DexOptZ: new cache file '%s' is not empty", debugFileName);
goto bail;
/*
*编写骨架索引优化标头。我们要上课。指数
*紧跟其后。
*/
err = dexOptCreateEmptyHeader(cacheFd);
if (err != 0)
goto bail;
/* record the file position so we can get back here later */
dexOffset = lseek(cacheFd, 0, SEEK_CUR);
if (dexOffset < 0)
goto bail;
/*
*打开zip存档,找到DEX条目。
*/
if (dexZipPrepArchive(zipFd, debugFileName, &zippy) != 0)
ALOGW("DexOptZ: unable to open zip archive '%s'", debugFileName);
goto bail;
zipEntry = dexZipFindEntry(&zippy, kClassesDex);
if (zipEntry == NULL)
ALOGW("DexOptZ: zip archive '%s' does not include %s",
debugFileName, kClassesDex);
goto bail;
/*
*提取一些关于zip条目的信息。
*/
if (dexZipGetEntryInfo(&zippy, zipEntry, NULL, &uncompLen, NULL, NULL,
&modWhen, &crc32) != 0)
ALOGW("DexOptZ: zip archive GetEntryInfo failed on %s", debugFileName);
goto bail;
uncompLen = uncompLen;
modWhen = modWhen;
crc32 = crc32;
/*
*以当前偏移量将DEX数据提取到缓存文件中。
*/
if (dexZipExtractEntryToFile(&zippy, zipEntry, cacheFd) != 0)
ALOGW("DexOptZ: extraction of %s from %s failed",
kClassesDex, debugFileName);
goto bail;
/* Parse the options. */
if (dexoptFlagStr[0] != '\\0')
const char* opc;
const char* val;
opc = strstr(dexoptFlagStr, "v="); /* verification */
if (opc != NULL)
switch (*(opc+2))
case 'n': verifyMode = VERIFY_MODE_NONE; break;
case 'r': verifyMode = VERIFY_MODE_REMOTE; break;
case 'a': verifyMode = VERIFY_MODE_ALL; break;
default: break;
opc = strstr(dexoptFlagStr, "o="); /* optimization */
if (opc != NULL)
switch (*(opc+2))
case 'n': dexOptMode = OPTIMIZE_MODE_NONE; break;
case 'v': dexOptMode = OPTIMIZE_MODE_VERIFIED; break;
case 'a': dexOptMode = OPTIMIZE_MODE_ALL; break;
case 'f': dexOptMode = OPTIMIZE_MODE_FULL; break;
default: break;
opc = strstr(dexoptFlagStr, "m=y"); /* register map */
if (opc != NULL)
dexoptFlags |= DEXOPT_GEN_REGISTER_MAPS;
opc = strstr(dexoptFlagStr, "u="); /* uniprocessor target */
if (opc != NULL)
switch (*(opc+2))
case 'y': dexoptFlags |= DEXOPT_UNIPROCESSOR; break;
case 'n': dexoptFlags |= DEXOPT_SMP; break;
default: break;
/*
*准备VM并执行优化。
*/
if (dvmPrepForDexOpt(bootClassPath, dexOptMode, verifyMode,
dexoptFlags) != 0)
ALOGE("DexOptZ: VM init failed");
goto bail;
//vmStarted = 1;
/* do the optimization */
if (!dvmContinueOptimization(cacheFd, dexOffset, uncompLen, debugFileName,
modWhen, crc32, isBootstrap))
ALOGE("Optimization failed");
goto bail;
/* we don't shut the VM down -- process is about to exit */
result = 0;
bail:
dexZipCloseArchive(&zippy);
return result;
/*
*普通设备端处理的通用功能以及
*预优化。
*/
static int processZipFile(int zipFd, int cacheFd, const char* zipName,
const char *dexoptFlags)
char* bcpCopy = NULL;
/*
* Check to see if this is a bootstrap class entry. If so, truncate
* the path.
*/
const以上是关于Android 逆向整体加固脱壳 ( DEX 优化流程分析 | DexPrepare.cpp 中 dvmOptimizeDexFile() 方法分析 | /bin/dexopt 源码分析 )(代码片段的主要内容,如果未能解决你的问题,请参考以下文章
Android 逆向整体加固脱壳 ( DEX 优化流程分析 | DexPrepare.cpp 中 dvmOptimizeDexFile() 方法分析 | /bin/dexopt 源码分析 )(代码片段
Android 逆向整体加固脱壳 ( 脱壳点简介 | 修改系统源码进行脱壳 )
Android 逆向整体加固脱壳 ( 脱壳起点 : 整体加固脱壳 | Dalvik 脱壳机制 : 利用 DexClassLoader 加载过程进行脱壳 | 相关源码分析 )
Android 逆向脱壳解决方案 ( DEX 整体加壳 | 函数抽取加壳 | VMP 加壳 | Dex2C 加壳 | Android 应用加固防护级别 )
Android 逆向整体加固脱壳 ( DexClassLoader 加载 dex 流程分析 | RawDexFile.cpp 分析 | dvmRawDexFileOpen函数读取 DEX 文件 )(代
Android 逆向整体加固脱壳 ( DexClassLoader 加载 dex 流程分析 | DexPathList 构造函数分析 | makeDexElements 函数分析 )