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

本文涉及的产品
密钥管理服务KMS,1000个密钥,100个凭据,1个月
简介: 【Android 安全】DEX 加密 ( 代理 Application 开发 | 加载 dex 文件 | 反射获取系统的 Element[] dexElements )

文章目录

一、dex 文件准备

二、加载 dex 文件流程

三、Element[] dexElements 分析

四、反射获取系统的 Element[] dexElements



参考博客 :


【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 加密 ( 支持多 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 文件解压操作 ;


本博客中主要讲解 dex 文件加载操作 ;






一、dex 文件准备


上一篇博客讲解的是 apk 文件解压 , 继续后面的步骤 ;



如果本次是第一次启动 , 则需要 解压 apk 文件 ,


解压后 , 将所有的 dex 文件放到 dexDir 中 , 解密该 dex 文件 ,


解密完成后 , 将文件路径存放在 var dexFiles : ArrayList<File> 集合中 ;



如果本次不是第一次启动 , 则直接从 dexDir 中获取 dex 文件 ,


将所有的 dex 文件路径放在 var dexFiles : ArrayList<File> 集合中 ;


   

// 遍历解压后的 apk 文件 , 将需要加载的 dex 放入如下集合中
        var dexFiles : ArrayList<File> = ArrayList<File>()
        // 如果该 dexDir 存在 , 并且该目录不为空 , 并进行 MD5 文件校验
        if( !dexDir.exists() || dexDir.list().size == 0){
            // 将 apk 中的文件解压到了 appDir 目录
            unZipApk(apkFile, appDir)
            // 获取 appDir 目录下的所有文件
            var files = appDir.listFiles()
            // 遍历文件名称集合
            for(i in files.indices){
                var file = files[i]
                // 如果文件后缀是 .dex , 并且不是 主 dex 文件 classes.dex
                // 符合上述两个条件的 dex 文件放入到 dexDir 中
                if(file.name.endsWith(".dex") &&
                    TextUtils.equals(file.name, "classes.dex")){
                    // 筛选出来的 dex 文件都是需要解密的
                    // 解密需要使用 OpenSSL 进行解密
                    // 获取该文件的二进制 Byte 数据
                    // 这些 Byte 数组就是加密后的 dex 数据
                    var bytes = Utils.getBytes(file)
                    // 解密该二进制数据, 并替换原来的加密 dex, 直接覆盖原来的文件即可
                    Utils.decrypt(bytes, file.absolutePath)
                    // 将解密完毕的 dex 文件放在需要加载的 dex 集合中
                    dexFiles.add(file)
                }// 判定是否是需要解密的 dex 文件
            }// 遍历 apk 解压后的文件
        }else{
            // 已经解密完成, 此时不需要解密, 直接获取 dexDir 中的文件即可
            for (file in dexDir.listFiles()) {
                dexFiles.add(file)
            }
        }





二、加载 dex 文件流程


加载上述 dex 文件集合 , 这些 dex 文件已经解密 ;



加载 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 数组 封装在 libcore/dalvik/src/main/java/dalvik/system/DexPathList.java 中


/*package*/ final class DexPathList {
    /**
     * dex/resource (class path) 元素集合.
     * 应该调用 pathElements , 但是 Facebook 应用通过反射修改 dexElements .
     */
    private final Element[] dexElements;
    public DexPathList(ClassLoader definingContext, String dexPath,
            String libraryPath, File optimizedDirectory) {
        // save dexPath for BaseDexClassLoader
        this.dexElements = makePathElements(splitDexPath(dexPath), optimizedDirectory,
                                            suppressedExceptions);
  }
}


参考源码地址 : 6.0.1_r16/xref/libcore/dalvik/src/main/java/dalvik/system/DexPathList.java



DexPathList 对象被封装在

libcore/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java 中 ;
public class BaseDexClassLoader extends ClassLoader {
    private final DexPathList pathList;
}


参考源码地址 : libcore/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java



PathClassLoader 是我们可以拿到的类加载器 , 该类是 BaseDexClassLoader 的子类 ,



         


参考源码地址 : libcore/dalvik/src/main/java/dalvik/system/PathClassLoader.java



在 Context 中调用 getClassLoader() 方法 , 可以拿到 PathClassLoader 后 , 需要从其父类 BaseDexClassLoader 中找到 DexPathList , 进而获取封装在 DexPathList 类中的 Element[] dexElements 数组 ;






四、反射获取系统的 Element[] dexElements


上述的 DexPathList 对象 是 BaseDexClassLoader 的 私有成员 , Element[] dexElements 数组 也是 DexPathList 的 私有成员 , 因此只能使用 反射 获取 Element[] dexElements 数组 ;



反射获取系统的 Element[] dexElements , 需要分三个阶段完成 ;


第一阶段 : 在 Context 中调用 getClassLoader() 方法 , 可以 拿到 PathClassLoader ;


classLoader


第二阶段 : 从 PathClassLoader 父类 BaseDexClassLoader 中 找到 DexPathList ;


   

// 阶段一二 : 调用 getClassLoader() 方法可以获取 PathClassLoader 对象
        // 从 PathClassLoader 对象中获取 private final DexPathList pathList 成员
        var pathListField = reflexField(classLoader, "DexPathList");
        // 获取 classLoader 对象对应的 DexPathList pathList 成员
        var pathList = pathListField.get(classLoader)


第三阶段 : 获取封装在 DexPathList 类中的 Element[] dexElements 数组 ;


   

/*
            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>



目录
相关文章
|
20天前
|
搜索推荐 前端开发 API
探索安卓开发中的自定义视图:打造个性化用户界面
在安卓应用开发的广阔天地中,自定义视图是一块神奇的画布,让开发者能够突破标准控件的限制,绘制出独一无二的用户界面。本文将带你走进自定义视图的世界,从基础概念到实战技巧,逐步揭示如何在安卓平台上创建和运用自定义视图来提升用户体验。无论你是初学者还是有一定经验的开发者,这篇文章都将为你打开新的视野,让你的应用在众多同质化产品中脱颖而出。
40 19
|
20天前
|
JSON Java API
探索安卓开发:打造你的首个天气应用
在这篇技术指南中,我们将一起潜入安卓开发的海洋,学习如何从零开始构建一个简单的天气应用。通过这个实践项目,你将掌握安卓开发的核心概念、界面设计、网络编程以及数据解析等技能。无论你是初学者还是有一定基础的开发者,这篇文章都将为你提供一个清晰的路线图和实用的代码示例,帮助你在安卓开发的道路上迈出坚实的一步。让我们一起开始这段旅程,打造属于你自己的第一个安卓应用吧!
45 14
|
20天前
|
开发框架 Android开发 iOS开发
安卓与iOS开发中的跨平台策略:一次编码,多平台部署
在移动应用开发的广阔天地中,安卓和iOS两大阵营各占一方。随着技术的发展,跨平台开发框架应运而生,它们承诺着“一次编码,到处运行”的便捷。本文将深入探讨跨平台开发的现状、挑战以及未来趋势,同时通过代码示例揭示跨平台工具的实际运用。
|
Java 调度 Android开发
android体系课-系统启动流程-之zygote进程启动过程源码分析
笔者刚开始学习Android的时候也和大部分同学一样,只会使用一些应用层面的知识,对于一些比较常见的开源框架如<mark>RxJava</mark>,<mark>OkHttp</mark>,<mark>Retrofit</mark>,以及后来谷歌推出的<mark>协程</mark>等,都只在使用层面,对于他们<mark>内部原理</mark>,基本没有去了解觉得够用就可以了,又比如Activity,Service等四大组件的使用原理,系统开机过程,Launcher启动过程等知之甚少,知其然而不知其所以然,结果就是出现某些问题,不知道从哪里找原因,只能依赖万能的百度,但是百度看多了,你会发现自己
|
Java 调度 Android开发
android体系课-系统启动流程-之SystemServer启动过程源码分析
笔者刚开始学习Android的时候也和大部分同学一样,只会使用一些应用层面的知识,对于一些比较常见的开源框架如<mark>RxJava</mark>,<mark>OkHttp</mark>,<mark>Retrofit</mark>,以及后来谷歌推出的<mark>协程</mark>等,都只在使用层面,对于他们<mark>内部原理</mark>,基本没有去了解觉得够用就可以了,又比如Activity,Service等四大组件的使用原理,系统开机过程,Launcher启动过程等知之甚少,知其然而不知其所以然,结果就是出现某些问题,不知道从哪里找原因,只能依赖万能的百度,但是百度看多了,你会发现自己