文章目录

  • 前言
  • 一、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++;elselastPart = 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+8digint bcpSize = dvmGetBootPathSize();int argc = kFixedArgCount + bcpSize+ (kValgrindArgCount * kUseValgrind);const char* argv[argc+1];             // last entry is NULLchar 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 tooargv[curArg++] = "--leak-resolution=med";   // increase from 2 to 4argv[curArg++] = "--num-callers=16";        // default is 12assert(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));elseexecv(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 char* bcp = getenv("BOOTCLASSPATH");if (bcp == NULL) {ALOGE("DexOptZ: BOOTCLASSPATH not set");return -1;}bool isBootstrap = false;const char* match = strstr(bcp, zipName);if (match != NULL) {/**TODO:我们有一个部分字符串匹配,但这并不意味着*我们已经匹配了整个路径组件。我们应该确保*我们正在匹配完整的zipName,如果不是*应从(匹配+1)开始重新执行strstr。**该场景将是一个bootclasspath,具有以下内容*“/system/framework/core.jar”,而我们正在尝试优化*“/framework/core.jar”。不太可能,因为所有路径都是*绝对,以“.jar”结尾,但并非不可能。*/int matchOffset = match - bcp;if (matchOffset > 0 && bcp[matchOffset-1] == ':')matchOffset--;ALOGV("DexOptZ: found '%s' in bootclasspath, cutting off at %d",zipName, matchOffset);bcpCopy = strdup(bcp);bcpCopy[matchOffset] = '\0';bcp = bcpCopy;ALOGD("DexOptZ: truncated BOOTCLASSPATH to '%s'", bcp);isBootstrap = true;}int result = extractAndProcessZip(zipFd, cacheFd, zipName, isBootstrap,bcp, dexoptFlags);free(bcpCopy);return result;
}/* advance to the next arg and extract it */
#define GET_ARG(_var, _func, _msg)                                          \{                                                                       \char* endp;                                                         \(_var) = _func(*++argv, &endp, 0);                                  \if (*endp != '\0') {                                                \ALOGE("%s '%s'", _msg, *argv);                                   \goto bail;                                                      \}                                                                   \--argc;                                                             \}/*
*解析参数。我们希望:
*   0. (dexopt命令的名称--已忽略)
*   1. “--zip”
*   2. zip fd(输入,只读)
*   3. 缓存fd(输出、读写、用群集锁定)
*   4. 正在优化的zipfile的文件名(用于调试消息和
*用于与BOOTCLASSPATH进行比较;不需要
*可访问或甚至存在)
*   5. dexopt标志
*
*假定BOOTCLASSPATH环境变量包含正确的
*引导类路径。如果提供的文件名出现在引导类中
*路径,路径将在该条目之前被截断(因此,如果
*如果您选择dexopt“core.jar”,您的引导类路径将为空)。
*
*这不会尝试规范化引导类路径名,因此
*如果你有创意,文件名测试不会抓住你。
*/
static int fromZip(int argc, char* const argv[])
{int result = -1;int zipFd, cacheFd;const char* zipName;char* bcpCopy = NULL;const char* dexoptFlags;if (argc != 6) {ALOGE("Wrong number of args for --zip (found %d)", argc);goto bail;}/* skip "--zip" */argc--;argv++;GET_ARG(zipFd, strtol, "bad zip fd");GET_ARG(cacheFd, strtol, "bad cache fd");zipName = *++argv;--argc;dexoptFlags = *++argv;--argc;result = processZipFile(zipFd, cacheFd, zipName, dexoptFlags);bail:return result;
}/*
*分析预优化运行的参数。这是dalvikvm运行的时间
*在主机上优化dex文件,以便最终在主机上运行(不同)
*装置。我们希望:
*   0. (dexopt命令的名称--已忽略)
*   1. “--preopt”
*   2. zipfile名称
*   3. 输出文件名
*   4. dexopt标志
*
*假定BOOTCLASSPATH环境变量包含正确的
*引导类路径。如果提供的文件名出现在引导类中
*路径,路径将在该条目之前被截断(因此,如果
*如果您选择dexopt“core.jar”,您的引导类路径将为空)。
*
*这不会尝试规范化引导类路径名,因此
*如果你有创意,文件名测试不会抓住你。
*/
static int preopt(int argc, char* const argv[])
{int zipFd = -1;int outFd = -1;int result = -1;if (argc != 5) {/** Use stderr here, since this variant is meant to be called on* the host side.*/fprintf(stderr, "Wrong number of args for --preopt (found %d)\n",argc);return -1;}const char* zipName = argv[2];const char* outName = argv[3];const char* dexoptFlags = argv[4];if (strstr(dexoptFlags, "u=y") == NULL &&strstr(dexoptFlags, "u=n") == NULL){fprintf(stderr, "Either 'u=y' or 'u=n' must be specified\n");return -1;}zipFd = open(zipName, O_RDONLY);if (zipFd < 0) {perror(argv[0]);return -1;}outFd = open(outName, O_RDWR | O_EXCL | O_CREAT, 0666);if (outFd < 0) {perror(argv[0]);goto bail;}result = processZipFile(zipFd, outFd, zipName, dexoptFlags);bail:if (zipFd >= 0) {close(zipFd);}if (outFd >= 0) {close(outFd);}return result;
}/*
*直接从VM解析“旧式”调用的参数。
*
*以下是我们想要的:
*   0. (dexopt命令的名称--已忽略)
*   1. “--dex”
*   2. DALVIK_VM_构建值,作为一种健全性检查
*   3. 文件描述符,用flock锁定,用于正在优化的DEX文件
*   4. 文件内的索引偏移量
*   5. 指数长度
*   6. 正在优化的文件的文件名(仅适用于调试消息)
*   7. 源的修改日期(进入依赖项部分)
*   8. 源的CRC(进入依赖项部分)
*   9. 标志(优化级别,isBootstrap)
*  10. bootclasspath条目#1
*  11. bootclasspath条目#2
*   ...
*
*dalvik/vm/analysis/DexOptimize中的dvmOptimizeDexFile()。c构建
*参数列表并调用此可执行文件。
*
*bootclasspath条目将成为此DEX文件的依赖项。
*
*打开的文件描述符不能用于任何bootclasspath文件。
*父项已锁定描述符,我们将尝试再次将其锁定
*处理引导类路径的一部分。(我们可以抓住这个然后回来
*比较文件名或打开bootclasspath文件时出错
*并统计它们的索引节点编号)。
*/
static int fromDex(int argc, char* const argv[])
{int result = -1;bool vmStarted = false;char* bootClassPath = NULL;int fd, flags, vmBuildVersion;long offset, length;const char* debugFileName;u4 crc, modWhen;char* endp;bool onlyOptVerifiedDex = false;DexClassVerifyMode verifyMode;DexOptimizerMode dexOptMode;if (argc < 10) {/* don't have all mandatory args */ALOGE("Not enough arguments for --dex (found %d)", argc);goto bail;}/* skip "--dex" */argc--;argv++;/** Extract the args.*/GET_ARG(vmBuildVersion, strtol, "bad vm build");if (vmBuildVersion != DALVIK_VM_BUILD) {ALOGE("DexOpt: build rev does not match VM: %d vs %d",vmBuildVersion, DALVIK_VM_BUILD);goto bail;}GET_ARG(fd, strtol, "bad fd");GET_ARG(offset, strtol, "bad offset");GET_ARG(length, strtol, "bad length");debugFileName = *++argv;--argc;GET_ARG(modWhen, strtoul, "bad modWhen");GET_ARG(crc, strtoul, "bad crc");GET_ARG(flags, strtol, "bad flags");ALOGV("Args: fd=%d off=%ld len=%ld name='%s' mod=%#x crc=%#x flg=%d (argc=%d)",fd, offset, length, debugFileName, modWhen, crc, flags, argc);assert(argc > 0);if (--argc == 0) {bootClassPath = strdup("");} else {int i, bcpLen;char* const* argp;char* cp;bcpLen = 0;for (i = 0, argp = argv; i < argc; i++) {++argp;ALOGV("DEP: '%s'", *argp);bcpLen += strlen(*argp) + 1;}cp = bootClassPath = (char*) malloc(bcpLen +1);for (i = 0, argp = argv; i < argc; i++) {int strLen;++argp;strLen = strlen(*argp);if (i != 0)*cp++ = ':';memcpy(cp, *argp, strLen);cp += strLen;}*cp = '\0';assert((int) strlen(bootClassPath) == bcpLen-1);}ALOGV("  bootclasspath is '%s'", bootClassPath);/* start the VM partway *//* ugh -- upgrade these to a bit field if they get any more complex */if ((flags & DEXOPT_VERIFY_ENABLED) != 0) {if ((flags & DEXOPT_VERIFY_ALL) != 0)verifyMode = VERIFY_MODE_ALL;elseverifyMode = VERIFY_MODE_REMOTE;} else {verifyMode = VERIFY_MODE_NONE;}if ((flags & DEXOPT_OPT_ENABLED) != 0) {if ((flags & DEXOPT_OPT_ALL) != 0)dexOptMode = OPTIMIZE_MODE_ALL;elsedexOptMode = OPTIMIZE_MODE_VERIFIED;} else {dexOptMode = OPTIMIZE_MODE_NONE;}// 准备优化环境 if (dvmPrepForDexOpt(bootClassPath, dexOptMode, verifyMode, flags) != 0) {ALOGE("VM init failed");goto bail;}vmStarted = true;/* 正式进行优化 */if (!dvmContinueOptimization(fd, offset, length, debugFileName,modWhen, crc, (flags & DEXOPT_IS_BOOTSTRAP) != 0)){ALOGE("Optimization failed");goto bail;}result = 0;bail:/**理论上,此时我们应该优雅地关闭VM。在里面*只有当我们使用检查内存泄漏时,这才有意义*valgrind——简单地退出要快得多。**事实证明,DEX优化器有点快,有点松*使用类加载。我们从一个部分-*形成的DEX文件,完成后将取消映射。如果我们想*在这里进行清洁关机,可能是为了使用valgrind进行测试,我们需要*要跳过那里的munmap调用。*/
#if 0if (vmStarted) {ALOGI("DexOpt shutting down, result=%d", result);dvmShutdown();}
#endiffree(bootClassPath);ALOGV("DexOpt command complete (result=%d)", result);return result;
}/*
*主要入口点。决定去哪里。
*/
int main(int argc, char* const argv[])
{set_process_name("dexopt");setvbuf(stdout, NULL, _IONBF, 0);if (argc > 1) {if (strcmp(argv[1], "--zip") == 0)return fromZip(argc, argv);else if (strcmp(argv[1], "--dex") == 0)// 加载 dex 文件时 , 执行 fromDex 函数return fromDex(argc, argv);else if (strcmp(argv[1], "--preopt") == 0)return preopt(argc, argv);}fprintf(stderr,"Usage:\n\n""Short version: Don't use this.\n\n""Slightly longer version: This system-internal tool is used to\n""produce optimized dex files. See the source code for details.\n");return 1;
}

源码路径 : /dalvik/dexopt/OptMain.cpp

【Android 逆向】整体加固脱壳 ( DEX 优化流程分析 | DexPrepare.cpp 中 dvmOptimizeDexFile() 方法分析 | /bin/dexopt 源码分析 )相关推荐

  1. 【Android 逆向】整体加固脱壳 ( DEX 优化流程分析 | dvmDexFileOpenPartial | dexFileParse | 脱壳点 | 获取 dex 文件在内存中的首地址 )

    文章目录 前言 一.DexPrepare.cpp 中 rewriteDex() 方法分析 二.DvmDex.cpp 中 dvmDexFileOpenPartial() 方法分析 ( 脱壳点 ) 三.D ...

  2. 【Android 逆向】整体加固脱壳 ( DEX 优化流程分析 | DexPrepare.cpp 中 dvmContinueOptimizati() 函数分析 )

    文章目录 前言 一.DexPrepare.cpp 中 dvmContinueOptimizati() 方法分析 前言 上一篇博客 [Android 逆向]整体加固脱壳 ( DEX 优化流程分析 | D ...

  3. 【Android 安全】DEX 加密 ( 多 DEX 加载 | 65535 方法数限制和 MultiDex 配置 | PathClassLoader 类加载源码分析 | DexPathList )

    文章目录 一.65535 方法数限制和 MultiDex 配置 二.多 DEX 加载引入 三.PathClassLoader 类加载源码分析 四.BaseDexClassLoader 类加载源码分析 ...

  4. Android研发中对String的思考(源码分析)

    1.常用创建方式思考: String text = "this is a test text "; 上面这一句话实际上是执行了三件事  1.声明变量 String text; 2. ...

  5. 【Android 安全】DEX 加密 ( Application 替换 | 获取 ContextImpl、ActivityThread、LoadedApk 类型对象 | 源码分析 )

    文章目录 一.获取对象类型分析 二.获取 ContextImpl 类型对象 三.获取 ActivityThread.LoadedApk 类型对象 dex 解密时 , 需要将 代理 Applicatio ...

  6. Android开发必会技术!Flutter中网络图片加载和缓存源码分析,完整PDF

    起因 事情是这样的. 4年前毕业那会,呆在公司的短视频项目,做 视频.那会做得比抖音还早,但是由于短视频太烧钱了,项目被公司关掉了.当时需要开发横竖屏直播/异步视频的场景,就研究下了市场上的 app, ...

  7. Clothoid回旋曲线在APA路径优化中的工程应用实例及其C++源码分析与下载

    回旋曲线的定义 回旋曲线是缓和曲线的一种,而缓和曲线的线型多种多样,如回旋线.三次抛物线.七次四项式型.半波正弦型.一波正弦型.双纽线.多心复曲线--,公路中推荐使用的是回旋曲线,因为回旋曲线的曲率变 ...

  8. 【Android 逆向】整体加固脱壳 ( 脱壳点简介 | 修改系统源码进行脱壳 )

    文章目录 一.脱壳点简介 二.修改系统源码进行脱壳 一.脱壳点简介 在上一篇博客 [Android 逆向]整体加固脱壳 ( DEX 优化流程分析 | DexPrepare.cpp 中 rewriteD ...

  9. 【Android】Retrofit基础源码分析

    文章目录 流程图 基本使用 1. 创建服务端ApiInterface 2.配置BaseUrl生成Retrofit对象 3.生成服务端ApiInterface对象 4.调用服务端ApiInterface ...

最新文章

  1. linux net.ipv4.ip_forward 数据包转发
  2. udp tcp ip 校验和对比
  3. ionic@2.0 beta版本安装指南
  4. 现代网络管理员必备技能
  5. matlab实验题目,MATLAB实验题目
  6. 2017.3.25 SJY摆石子 思考记录
  7. CentOS 7.0编译安装Nginx1.6.0+MySQL5.6.19+PHP5.5.14
  8. mac mysql sequel_苹果系统Sequel Pro—MySQL客户端工具一个大坑
  9. perl的几个小tips
  10. signature=0727ee8cc38ba70036807ebbc0b018d6,NMSSM+
  11. gRPC传输协议使用(python教程)
  12. 《给你一个团队,你能怎么管?》读书笔记
  13. 公文中的六角括号怎么打?
  14. c#如何实现叫号操作_C#银行排队叫号系统
  15. python中string什么意思_Python:string是什么意思
  16. DevIL的学习笔记
  17. JQuery读书笔记---很全面的教程
  18. Codeforces 950C-Zebras(模拟构造)
  19. 下载visual studio 2022 Professional 离线包
  20. 【Druid】(四)Apache Druid 部署和配置(单机版 / Docker 容器版 / Kubernetes 集群版)

热门文章

  1. 【原创】.NET读写Excel工具Spire.Xls使用(4)对数据操作与控制
  2. 如何有效地记录 Java SQL 日志(转)
  3. 敏捷个人手机应用:如何使用时中法目标
  4. 网管日志-06.09.08
  5. poj2456 Aggressive cows(二分查找)
  6. MFC接收命令行参数的三种方法
  7. 编写高质量代码改善C#程序的157个建议——建议87:区分WPF和WinForm的线程模型...
  8. 如何解决Contacts中的多音字排序错误问题
  9. 杭电2855 Fibonacci Check-up
  10. Oracle PL/SQL语言入门