【Android 安全】DEX 加密 ( 代理 Application 开发 | 加载 dex 文件 | 将系统的 dexElements 与 应用的 dexElements 合并 | 替换操作 )

本文涉及的产品
密钥管理服务KMS,1000个密钥,100个凭据,1个月
简介: 【Android 安全】DEX 加密 ( 代理 Application 开发 | 加载 dex 文件 | 将系统的 dexElements 与 应用的 dexElements 合并 | 替换操作 )

文章目录

一、将系统的 dexElements 与 应用的 dexElements 合并

二、Element[] dexElements 替换操作

三、完整 dex 加载源码



参考博客 :


【Android 安全】DEX 加密 ( 常用 Android 反编译工具 | apktool | dex2jar | enjarify | jd-gui | jadx )

【Android 安全】DEX 加密 ( Proguard 简介 | Proguard 相关网址 | Proguard 混淆配置 )

【Android 安全】DEX 加密 ( Proguard 简介 | 默认 ProGuard 分析 )

【Android 安全】DEX 加密 ( Proguard keep 用法 | Proguard 默认混淆结果 | 保留类及成员混淆结果 | 保留注解以及被注解修饰的类/成员/方法 )

【Android 安全】DEX 加密 ( Proguard 混淆 | 混淆后的报错信息 | Proguard 混淆映射文件 mapping.txt )

【Android 安全】DEX 加密 ( Proguard 混淆 | 将混淆后的报错信息转为原始报错信息 | retrace.bat 命令执行目录 | 暴露更少信息 )

【Android 安全】DEX 加密 ( DEX 加密原理 | DEX 加密简介 | APK 文件分析 | DEX 分割 )

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

【Android 安全】DEX 加密 ( 不同 Android 版本的 DEX 加载 | Android 8.0 版本 DEX 加载分析 | Android 5.0 版本 DEX 加载分析 )

【Android 安全】DEX 加密 ( DEX 加密使用到的相关工具 | dx 工具 | zipalign 对齐工具 | apksigner 签名工具 )

【Android 安全】DEX 加密 ( 支持多 DEX 的 Android 工程结构 )

【Android 安全】DEX 加密 ( 代理 Application 开发 | multiple-dex-core 依赖库开发 | 配置元数据 | 获取 apk 文件并准备相关目录 )

【Android 安全】DEX 加密 ( 代理 Application 开发 | 解压 apk 文件 | 判定是否是第一次启动 | 递归删除文件操作 | 解压 Zip 文件操作 )

【Android 安全】DEX 加密 ( 代理 Application 开发 | 加载 dex 文件 | 反射获取系统的 Element[] dexElements )

【Android 安全】DEX 加密 ( 代理 Application 开发 | 加载 dex 文件 | 使用反射获取方法创建本应用的 dexElements | 各版本创建 dex 数组源码对比 )


在 【Android 安全】DEX 加密 ( 支持多 DEX 的 Android 工程结构 ) 博客中介绍了 DEX 加密工程的基本结构 ,


app 是主应用 , 其 Module 类型是 “Phone & Tablet Module” ,


multiple-dex-core 是 Android 依赖库 , 其作用是解密并加载多 DEX 文件 , 其 Module 类型是 “Android Library” ,


multiple-dex-tools 是 Java 依赖库 , 其类型是 “Java or Kotlin Library” , 其作用是用于生成主 DEX ( 主 DEX 的作用就是用于解密与加载多 DEX ) , 并且还要为修改后的 APK 进行签名 ;



在 【Android 安全】DEX 加密 ( 代理 Application 开发 | multiple-dex-core 依赖库开发 | 配置元数据 | 获取 apk 文件并准备相关目录 ) 博客中讲解了 multiple-dex-core 依赖库开发 , 每次启动都要解密与加载 dex 文件 , 在该博客中讲解到了 获取 apk 文件 , 并准备解压目录 ;


在 【Android 安全】DEX 加密 ( 代理 Application 开发 | 解压 apk 文件 | 判定是否是第一次启动 | 递归删除文件操作 | 解压 Zip 文件操作 ) 博客中讲解了 apk 文件解压操作 ;


在 【Android 安全】DEX 加密 ( 代理 Application 开发 | 加载 dex 文件 | 反射获取系统的 Element[] dexElements )博客中讲解了 dex 文件加载第一阶段 , 获取系统中的 Element[] dexElements ;


在 【Android 安全】DEX 加密 ( 代理 Application 开发 | 加载 dex 文件 | 使用反射获取方法创建本应用的 dexElements | 各版本创建 dex 数组源码对比 ) 博客中讲解了讲解 dex 文件加载操作 第二阶段 , 创建本应用的 dex 文件数组 Element[] dexElements ;



本博客中主要讲解 剩余的两个操作 :


将 系统加载的 Element[] dexElements 数组 与 我们 自己的 Element[] dexElements 数组 进行合并操作 ;

替换 ClassLoader 加载过程中的 Element[] dexElements 数组 ( 封装在 DexPathList 中 )





一、将系统的 dexElements 与 应用的 dexElements 合并


加载 dex 文件 需要执行的步骤 :


1 . 获得系统 DexPathList 中的 Element[] dexElements 数组

( libcore/dalvik/src/main/java/dalvik/system/DexPathList.java )


2 . 在 本应用 中创建 Element[] dexElements 数组 , 用于存放解密后的 dex 文件


3 . 将 系统加载的 Element[] dexElements 数组 与 我们自己的 Element[] dexElements 数组进行 合并操作


4 . 替换 ClassLoader 加载过程中的 Element[] dexElements 数组 ( 封装在 DexPathList 中 )



之前的博客中已经讲解了前两个步骤的操作 , 开始执行第三个步骤 , 将 系统加载的 Element[] dexElements 数组 与 我们自己的 Element[] dexElements 数组进行合并操作 ;


首先 创建数组 , 数组类型与 dexElements 数组类型相同 ,


将 dexElements 数组中的元素拷贝到 newElements 前半部分, 拷贝元素个数是 dexElements.size ,


将 addElements 数组中的元素拷贝到 newElements 后半部分, 拷贝元素个数是 dexElements.size ;



代码示例 :


     

/*
            3 . 将 系统加载的 Element[] dexElements 数组
                与 我们自己的 Element[] dexElements 数组进行合并操作
            首先创建数组 , 数组类型与 dexElements 数组类型相同
            将 dexElements 数组中的元素拷贝到 newElements 前半部分, 拷贝元素个数是 dexElements.size
            将 addElements 数组中的元素拷贝到 newElements 后半部分, 拷贝元素个数是 dexElements.size
         */
        var newElements: Array<Any> = java.lang.reflect.Array.newInstance(dexElements.javaClass.componentType,
                dexElements.size + addElements.size) as Array<Any>
        // 将 dexElements 数组中的元素拷贝到 newElements 前半部分, 拷贝元素个数是 dexElements.size
        System.arraycopy(dexElements, 0, newElements, 0, dexElements.size)
        // 将 addElements 数组中的元素拷贝到 newElements 后半部分, 拷贝元素个数是 dexElements.size
        System.arraycopy(addElements, 0, newElements, dexElements.size, addElements.size)





二、Element[] dexElements 替换操作


替换 ClassLoader 加载过程中的 Element[] dexElements 数组 ( 封装在 DexPathList 中 )


     

/*
            4 . 替换 ClassLoader 加载过程中的 Element[] dexElements 数组 ( 封装在 DexPathList 中 )
         */
        dexElementsField.set(pathList, newElements)


该 Field 字段由之前反射获得 ;


   

//阶段三 : 获取封装在 DexPathList 类中的 Element[] dexElements 数组
        var dexElementsField = reflexField(pathList, "dexElements")
        // 获取 pathList 对象对应的 Element[] dexElements 数组成员
        var dexElements : Array<Any> = dexElementsField.get(pathList) as Array<Any>





三、完整 dex 加载源码


 

/**
     * 加载 dex 文件集合
     * 这些 dex 文件已经解密
     * 参考博客 : https://hanshuliang.blog.csdn.net/article/details/109608605
     *
     * 创建自己的 Element[] dexElements 数组
     * ( libcore/dalvik/src/main/java/dalvik/system/DexPathList.java )
     * 然后将 系统加载的 Element[] dexElements 数组 与 我们自己的 Element[] dexElements 数组进行合并操作
     * 00:17:07
     */
    fun loadDex(dexFiles : ArrayList<File>, optimizedDirectory : File) : Unit{
        /*
            需要执行的步骤
            1 . 获得系统 DexPathList 中的 Element[] dexElements 数组
                ( libcore/dalvik/src/main/java/dalvik/system/DexPathList.java )
            2 . 在本应用中创建 Element[] dexElements 数组 , 用于存放解密后的 dex 文件
            3 . 将 系统加载的 Element[] dexElements 数组
                与 我们自己的 Element[] dexElements 数组进行合并操作
            4 . 替换 ClassLoader 加载过程中的 Element[] dexElements 数组 ( 封装在 DexPathList 中 )
         */
        /*
            1 . 获得系统 DexPathList 中的 Element[] dexElements 数组
            第一阶段 : 在 Context 中调用 getClassLoader() 方法 , 可以拿到 PathClassLoader ;
            第二阶段 : 从 PathClassLoader 父类 BaseDexClassLoader 中找到 DexPathList ;
            第三阶段 : 获取封装在 DexPathList 类中的 Element[] dexElements 数组 ;
            上述的 DexPathList 对象 是 BaseDexClassLoader 的私有成员
            Element[] dexElements 数组 也是 DexPathList 的私有成员
            因此只能使用反射获取 Element[] dexElements 数组
         */
        // 阶段一二 : 调用 getClassLoader() 方法可以获取 PathClassLoader 对象
        // 从 PathClassLoader 对象中获取 private final DexPathList pathList 成员
        var pathListField = reflexField(classLoader, "DexPathList");
        // 获取 classLoader 对象对应的 DexPathList pathList 成员
        var pathList = pathListField.get(classLoader)
        //阶段三 : 获取封装在 DexPathList 类中的 Element[] dexElements 数组
        var dexElementsField = reflexField(pathList, "dexElements")
        // 获取 pathList 对象对应的 Element[] dexElements 数组成员
        var dexElements : Array<Any> = dexElementsField.get(pathList) as Array<Any>
        /*
            2 . 在本应用中创建 Element[] dexElements 数组 , 用于存放解密后的 dex 文件
                不同的 Android 版本中 , 创建 Element[] dexElements 数组的方法不同 , 这里需要做兼容
         */
        var makeDexElements: Method
        var addElements : Array<Any> = Array(0, {})
        if(Build.VERSION.SDK_INT <=
                Build.VERSION_CODES.LOLLIPOP_MR1){ // 5.0, 5.1  makeDexElements
            // 反射 5.0, 5.1, 6.0 版本的 DexPathList 中的 makeDexElements 方法
            makeDexElements = reflexMethod(pathList, "makeDexElements",
                    ArrayList::class.java, File::class.java, ArrayList::class.java)
            var suppressedExceptions: ArrayList<IOException> = ArrayList<IOException>()
            addElements = makeDexElements.invoke(pathList, dexFiles,
                    optimizedDirectory,
                    suppressedExceptions) as Array<Any>
        }else if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){   // 7.0 以上版本 makePathElements
            // 反射 7.0 以上版本的 DexPathList 中的 makeDexElements 方法
            makeDexElements = reflexMethod(pathList, "makePathElements",
                    ArrayList::class.java, File::class.java, ArrayList::class.java)
            var suppressedExceptions: ArrayList<IOException> = ArrayList<IOException>()
            addElements = makeDexElements.invoke(pathList, dexFiles,
                    optimizedDirectory,
                    suppressedExceptions) as Array<Any>
        }
        /*
            3 . 将 系统加载的 Element[] dexElements 数组
                与 我们自己的 Element[] dexElements 数组进行合并操作
            首先创建数组 , 数组类型与 dexElements 数组类型相同
            将 dexElements 数组中的元素拷贝到 newElements 前半部分, 拷贝元素个数是 dexElements.size
            将 addElements 数组中的元素拷贝到 newElements 后半部分, 拷贝元素个数是 dexElements.size
         */
        var newElements: Array<Any> = java.lang.reflect.Array.newInstance(dexElements.javaClass.componentType,
                dexElements.size + addElements.size) as Array<Any>
        // 将 dexElements 数组中的元素拷贝到 newElements 前半部分, 拷贝元素个数是 dexElements.size
        System.arraycopy(dexElements, 0, newElements, 0, dexElements.size)
        // 将 addElements 数组中的元素拷贝到 newElements 后半部分, 拷贝元素个数是 dexElements.size
        System.arraycopy(addElements, 0, newElements, dexElements.size, addElements.size)
        /*
            4 . 替换 ClassLoader 加载过程中的 Element[] dexElements 数组 ( 封装在 DexPathList 中 )
         */
        dexElementsField.set(pathList, newElements)
    }


目录
相关文章
|
4月前
|
人工智能 搜索推荐 物联网
Android系统版本演进与未来展望####
本文深入探讨了Android操作系统从诞生至今的发展历程,详细阐述了其关键版本迭代带来的创新特性、用户体验提升及对全球移动生态系统的影响。通过对Android历史版本的回顾与分析,本文旨在揭示其成功背后的驱动力,并展望未来Android可能的发展趋势与面临的挑战,为读者呈现一个既全面又具深度的技术视角。 ####
|
1月前
|
算法 安全 Java
即时通讯安全篇(一):正确地理解和使用Android端加密算法
本文主要讨论针对Android这样的移动端应用开发时,如何正确的理解目前常用的加密算法,为诸如即时通讯应用的实战开发,如何在合适的场景下选择适合的算法,提供一些参考。
46 0
|
4月前
|
IDE Java 开发工具
移动应用与系统:探索Android开发之旅
在这篇文章中,我们将深入探讨Android开发的各个方面,从基础知识到高级技术。我们将通过代码示例和案例分析,帮助读者更好地理解和掌握Android开发。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的信息和技巧。让我们一起开启Android开发的旅程吧!
|
3月前
|
监控 Java Android开发
深入探索Android系统的内存管理机制
本文旨在全面解析Android系统的内存管理机制,包括其工作原理、常见问题及其解决方案。通过对Android内存模型的深入分析,本文将帮助开发者更好地理解内存分配、回收以及优化策略,从而提高应用性能和用户体验。
|
3月前
|
存储 安全 Android开发
探索Android系统的最新安全特性
在数字时代,智能手机已成为我们生活中不可或缺的一部分。随着技术的不断进步,手机操作系统的安全性也越来越受到重视。本文将深入探讨Android系统最新的安全特性,包括其设计理念、实施方式以及对用户的影响。通过分析这些安全措施如何保护用户免受恶意软件和网络攻击的威胁,我们希望为读者提供对Android安全性的全面了解。
|
4月前
|
监控 Java Android开发
深入探讨Android系统的内存管理机制
本文将深入分析Android系统的内存管理机制,包括其内存分配、回收策略以及常见的内存泄漏问题。通过对这些方面的详细讨论,读者可以更好地理解Android系统如何高效地管理内存资源,从而提高应用程序的性能和稳定性。
143 16
|
4月前
|
安全 Android开发 iOS开发
深入探讨Android与iOS系统的差异及未来发展趋势
本文旨在深入分析Android和iOS两大移动操作系统的核心技术差异、用户体验以及各自的市场表现,进一步探讨它们在未来技术革新中可能的发展方向。通过对比两者的开放性、安全性、生态系统等方面,本文揭示了两大系统在移动设备市场中的竞争态势和潜在变革。
|
4月前
|
算法 JavaScript Android开发
|
4月前
|
安全 搜索推荐 程序员
深入探索Android系统的碎片化问题及其解决方案
在移动操作系统的世界中,Android以其开放性和灵活性赢得了广泛的市场份额。然而,这种开放性也带来了一个众所周知的问题——系统碎片化。本文旨在探讨Android系统碎片化的现状、成因以及可能的解决方案,为开发者和用户提供一种全新的视角来理解这一现象。通过分析不同版本的Android系统分布、硬件多样性以及更新机制的影响,我们提出了一系列针对性的策略,旨在减少碎片化带来的影响,提升用户体验。
|
10月前
|
移动开发 安全 Android开发
构建高效Android应用:Kotlin协程的实践与优化策略
【5月更文挑战第30天】 在移动开发领域,性能优化始终是关键议题之一。特别是对于Android开发者来说,如何在保证应用流畅性的同时,提升代码的执行效率,已成为不断探索的主题。近年来,Kotlin语言凭借其简洁、安全和实用的特性,在Android开发中得到了广泛的应用。其中,Kotlin协程作为一种新的并发处理机制,为编写异步、非阻塞性的代码提供了强大工具。本文将深入探讨Kotlin协程在Android开发中的应用实践,以及如何通过协程优化应用性能,帮助开发者构建更高效的Android应用。