文章目录

  • 前言
  • 一、DexPrepare.cpp 中 rewriteDex() 方法分析
  • 二、DvmDex.cpp 中 dvmDexFileOpenPartial() 方法分析 ( 脱壳点 )
  • 三、DexFile.cpp 中 dexFileParse() 方法分析 ( 脱壳点 )

前言


上一篇博客 【Android 逆向】整体加固脱壳 ( DEX 优化流程分析 | DexPrepare.cpp 中 dvmContinueOptimizati() 函数分析 ) 中 , 分析了 DexPrepare.cpp 中 dvmContinueOptimizati() 方法 , 在其中调用了 rewriteDex() 方法 , 重写 DEX 文件 ;

本篇博客继续分析 DexPrepare.cpp 中 rewriteDex() 方法 ;

一、DexPrepare.cpp 中 rewriteDex() 方法分析


第一个参数 u1* addr 是加载到内存中的 dex 文件的首地址 ;

第二个参数 int len 是内存中的 dex 文件的字节长度 ;

static bool rewriteDex(u1* addr, int len, bool doVerify, bool doOpt,DexClassLookup** ppClassLookup, DvmDex** ppDvmDex)

dvmDexFileOpenPartial 函数是 脱壳点 函数 , 通过该函数定位脱壳点 , 然后进行脱壳操作 ;

 /** 既然可以直接读取DEX文件,那么创建一个DexFile结构* 为了它。*/if (dvmDexFileOpenPartial(addr, len, &pDvmDex) != 0) {ALOGE("Unable to create DexFile");goto bail;}

DexPrepare.cpp 中 rewriteDex() 方法源码 :

/** 对内存映射的DEX文件执行就地重写。* * 如果这是从短期子进程(dexopt)调用的,我们可以* 疯狂地加载类和分配内存。当天气好的时候* 调用以准备字节数组中提供的类,我们可能需要* 要保守一点。* * 如果“ppClassLookup”为非空,则为指向新分配的* DexClassLookup将在成功时返回。* * 如果“ppDvmDex”为非空,则将创建新分配的DvmDex结构* 成功返回。*/
static bool rewriteDex(u1* addr, int len, bool doVerify, bool doOpt,DexClassLookup** ppClassLookup, DvmDex** ppDvmDex)
{DexClassLookup* pClassLookup = NULL;u8 prepWhen, loadWhen, verifyOptWhen;DvmDex* pDvmDex = NULL;bool result = false;const char* msgStr = "???";/* 如果索引的字节顺序错误,请立即交换它 */if (dexSwapAndVerify(addr, len) != 0)goto bail;/** 既然可以直接读取DEX文件,那么创建一个DexFile结构* 为了它。*/if (dvmDexFileOpenPartial(addr, len, &pDvmDex) != 0) {ALOGE("Unable to create DexFile");goto bail;}/** 创建类查找表。这最终将被追加* 直到最后。奥德克斯。* * 我们从DexFile创建一个临时链接,以便* 类加载,如下所示。*/pClassLookup = dexCreateClassLookup(pDvmDex->pDexFile);if (pClassLookup == NULL)goto bail;pDvmDex->pDexFile->pClassLookup = pClassLookup;/** 如果我们不打算验证或优化这些类,* 加载它们没有任何价值,所以尽早退出。*/if (!doVerify && !doOpt) {result = true;goto bail;}prepWhen = dvmGetRelativeTimeUsec();/** 加载在此DEX文件中找到的所有类。如果它们无法加载* 由于某些原因,它们不会得到验证(这是应该的)。*/if (!loadAllClasses(pDvmDex))goto bail;loadWhen = dvmGetRelativeTimeUsec();/** 创建字节码优化器使用的数据结构。* 我们需要在几个类中查找方法,因此这可能会导致* 一点类加载。我们通常在VM初始化期间执行此操作,但是* 对于dexopt on core。jar操作的顺序变得有点棘手,* 所以我们把它推迟到这里。*/if (!dvmCreateInlineSubsTable())goto bail;/** 验证并优化DEX文件(命令行)中的所有类* (如果允许的话)。* * 这是最大的努力,所以dexopt真的没有办法* 在这一点上失败。*/verifyAndOptimizeClasses(pDvmDex->pDexFile, doVerify, doOpt);verifyOptWhen = dvmGetRelativeTimeUsec();if (doVerify && doOpt)msgStr = "verify+opt";else if (doVerify)msgStr = "verify";else if (doOpt)msgStr = "opt";ALOGD("DexOpt: load %dms, %s %dms, %d bytes",(int) (loadWhen - prepWhen) / 1000,msgStr,(int) (verifyOptWhen - loadWhen) / 1000,gDvm.pBootLoaderAlloc->curOffset);result = true;bail:/** 成功后,归还来电者要求的物品。*/if (pDvmDex != NULL) {/* break link between the two */pDvmDex->pDexFile->pClassLookup = NULL;}if (ppDvmDex == NULL || !result) {dvmDexFileFree(pDvmDex);} else {*ppDvmDex = pDvmDex;}if (ppClassLookup == NULL || !result) {free(pClassLookup);} else {*ppClassLookup = pClassLookup;}return result;
}

源码路径 : /dalvik/vm/analysis/DexPrepare.cpp

二、DvmDex.cpp 中 dvmDexFileOpenPartial() 方法分析 ( 脱壳点 )


该函数中的 参数 const void* addr 是 dex 文件在内存中的起始地址 ;

在调用的 dexFileParse 函数中 , 也可以获取到 dex 文件在内存中的首地址 ;

DvmDex.cpp 中 dvmDexFileOpenPartial() 方法源码 :

/** 为“部分”DEX创建DexFile结构。这是一个在* 被优化的过程。优化标头未完成* 我们没有任何辅助数据表,所以我们必须这样做* 初始化过程略有不同。* * 错误时返回非零。*/
int dvmDexFileOpenPartial(const void* addr, int len, DvmDex** ppDvmDex)
{DvmDex* pDvmDex;DexFile* pDexFile;int parseFlags = kDexParseDefault;int result = -1;/* -- 文件不完整,尚未计算新校验和if (gDvm.verifyDexChecksum)parseFlags |= kDexParseVerifyChecksum;*/pDexFile = dexFileParse((u1*)addr, len, parseFlags);if (pDexFile == NULL) {ALOGE("DEX parse failed");goto bail;}pDvmDex = allocateAuxStructures(pDexFile);if (pDvmDex == NULL) {dexFileFree(pDexFile);goto bail;}pDvmDex->isMappedReadOnly = false;*ppDvmDex = pDvmDex;result = 0;bail:return result;
}

源码路径 : /dalvik/vm/DvmDex.cpp

三、DexFile.cpp 中 dexFileParse() 方法分析 ( 脱壳点 )


/** 解析优化或未优化的。存储在内存中的dex文件。这是* 在字节排序和结构对齐修复后调用。* * 成功后,返回新分配的文件。*/
DexFile* dexFileParse(const u1* data, size_t length, int flags)
{DexFile* pDexFile = NULL;const DexHeader* pHeader;const u1* magic;int result = -1;if (length < sizeof(DexHeader)) {ALOGE("too short to be a valid .dex");goto bail;      /* bad file format */}pDexFile = (DexFile*) malloc(sizeof(DexFile));if (pDexFile == NULL)goto bail;      /* alloc failure */memset(pDexFile, 0, sizeof(DexFile));/** Peel off the optimized header.*/if (memcmp(data, DEX_OPT_MAGIC, 4) == 0) {magic = data;if (memcmp(magic+4, DEX_OPT_MAGIC_VERS, 4) != 0) {ALOGE("bad opt version (0x%02x %02x %02x %02x)",magic[4], magic[5], magic[6], magic[7]);goto bail;}pDexFile->pOptHeader = (const DexOptHeader*) data;ALOGV("Good opt header, DEX offset is %d, flags=0x%02x",pDexFile->pOptHeader->dexOffset, pDexFile->pOptHeader->flags);/* parse the optimized dex file tables */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) {ALOGE("File truncated? stored len=%d, rem len=%d",pDexFile->pOptHeader->dexLength, (int) length);goto bail;}length = pDexFile->pOptHeader->dexLength;}dexFileSetupBasicPointers(pDexFile, data);pHeader = pDexFile->pHeader;if (!dexHasValidMagic(pHeader)) {goto bail;}/** 验证校验和。这相当快,但确实需要* 触摸DEX文件中的每个字节。基本校验和在* 字节交换和索引优化。*/if (flags & kDexParseVerifyChecksum) {u4 adler = dexComputeChecksum(pHeader);if (adler != pHeader->checksum) {ALOGE("ERROR: bad checksum (%08x vs %08x)",adler, pHeader->checksum);if (!(flags & kDexParseContinueOnError))goto bail;} else {ALOGV("+++ adler32 checksum (%08x) verified", adler);}const DexOptHeader* pOptHeader = pDexFile->pOptHeader;if (pOptHeader != NULL) {adler = dexComputeOptChecksum(pOptHeader);if (adler != pOptHeader->checksum) {ALOGE("ERROR: bad opt checksum (%08x vs %08x)",adler, pOptHeader->checksum);if (!(flags & kDexParseContinueOnError))goto bail;} else {ALOGV("+++ adler32 opt checksum (%08x) verified", adler);}}}/** 验证SHA-1摘要。(通常我们不想这样做--* 摘要用于唯一标识原始DEX文件,以及* 无法在索引被字节交换后计算以进行验证* (并进行了优化。)*/if (kVerifySignature) {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];ALOGE("ERROR: bad SHA1 digest (%s vs %s)",dexSHA1DigestToStr(sha1Digest, tmpBuf1),dexSHA1DigestToStr(pHeader->signature, tmpBuf2));if (!(flags & kDexParseContinueOnError))goto bail;} else {ALOGV("+++ sha1 digest verified");}}if (pHeader->fileSize != length) {ALOGE("ERROR: stored file size (%d) != expected (%d)",(int) pHeader->fileSize, (int) length);if (!(flags & kDexParseContinueOnError))goto bail;}if (pHeader->classDefsSize == 0) {ALOGE("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;
}

源码路径 : /dalvik/libdex/DexFile.cpp

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

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

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

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

    文章目录 前言 一.DexPrepare.cpp 中 dvmOptimizeDexFile() 方法分析 二./bin/dexopt 源码分析 前言 上一篇博客 [Android 逆向]整体加固脱壳 ...

  3. 高通Android智能平台环境搭建_编译流程分析

    高通Android智能平台环境搭建_编译流程分析 高通平台环境搭建,编译,系统引导流程分析 TOC \o \h \z \u 1. 高通平台android开发总结. 7 1.1 搭建高通平台环境开发环境 ...

  4. 【Android 逆向】使用 Python 编写 APK 批处理分析工具

    文章目录 一.涉及到的工具和脚本 二.使用 Python 编写 APK重打包工具 三.博客源码 一.涉及到的工具和脚本 apktool.jar : 反编译 APK 文件使用到的工具 ; 参考 [And ...

  5. Android 4.0按键事件以及系统流程分析

    Android 4.0中按键的处理流程 按键在Android系统中,有着不同的代表意义.以前的全键盘的手机代码没有阅读过,所以也不是很了解.本人介绍的是在触摸屏的手机上的按键消息的处理流程. 在现在触 ...

  6. Android逆向之旅---抖音短视频的Native注册混淆函数获取方法

    一.静态分析 最近在小密圈中有很多同学都在咨询有时候有些应用的动态注册Native函数,在分析so之后发现找不到真的实现函数功能地方,我们知道有时候为了安全考虑会动态注册Native函数,但是如果只是 ...

  7. 高通android智能平台环境搭建_编译流程分析,高通平台环境搭建,编译,系统引导流程分析参考...

    高通有两个cpu,他们分别跑不同的系统,应用程序(ap)端是android系统,modem 端是高通自己的系统. 要编译出可供烧写使用的镜像文件需要三部分代码: 1) 获取经过高通打补丁的 andro ...

  8. Android 5.1 长按power键流程分析

    安全模式简述 android平台,在长按power / menu键时会快速进入一个模式选择,部分定制的平台是直接进入安装模式,也可以定制成公司需要的一些特定功能模式,比如报警 ... power 也属 ...

  9. Android系统的心脏-Zygote进程启动流程分析

    简介: Android中,Zygote是整个Android系统的核心进程,是Android系统的心脏.所有的Android应用程序,包括Android框架层所在的进程system_server,都是由 ...

最新文章

  1. 算法:程序设计之并查集
  2. Netflix:我们是如何评估Codec性能的?
  3. diff 比较两个文件的差异
  4. sketch里的ios控件_30个让你眼前一亮的iOS Swift UI控件!
  5. ashx连接mysql_对C#中的web访问mysql数据库的一些知识点进行了整理归纳总结
  6. XMLHttpRequest+WebForm模式(接口IHttpHandler)实现ajax
  7. python 函数中所有print保存csv_python for循环print怎样才能输出csv呢
  8. 《Cortex-M0权威指南》之体系结构---系统模型
  9. 男27,想转行互联网,是学习软件测试好,还是前端编程?
  10. QDebug输出彩色消息
  11. 强悍的 ubuntu —— 窗口界面管理与设置
  12. 如何打造一款可靠的WAF(Web应用防火墙)
  13. vue脚手架实现选项卡_从零一步步实现一个前端脚手架
  14. 计算机专业所需的职业道德,浅议计算机职业道德
  15. aloha协议c语言实现,任务ALOHA协议的OPNET仿真.doc
  16. EnterpriseArchitect画图工具-活动图使用(一)
  17. 7-1 电话聊天狂人
  18. na5tr1 测距芯片调试小结
  19. Mysql(三)索引、视图、存储过程、触发器、分区表
  20. PHP preg_match(): Unknown modifier '/'

热门文章

  1. Ember.js 入门指南——handlebars属性绑定
  2. 从字符串指定位置删除指定个数的字符
  3. 实验楼项目课学习笔记-jQuery翻转拼图游戏
  4. 证明并推导汉诺塔(河内之塔)问题公式
  5. C# 对象深拷贝、浅铐贝、直接拷贝(转)
  6. PHP 设计模式系列 —— 资源库模式(Repository)
  7. 【sping揭秘】9、容器内部事件发布(二)
  8. 跨站请求伪造(CSRF)-简述
  9. rtems的GNU(GCC)编译环境配置
  10. 数据降维(特征提取)和特征选择有什么区别?