文章目录
- 一、将系统的 dexElements 与 应用的 dexElements 合并
- 二、Element[] dexElements 替换操作
- 三、完整 dex 加载源码
一、将系统的 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 ;
代码示例 :
代码语言:javascript复制 /*
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 中 )
代码语言:javascript复制 /*
4 . 替换 ClassLoader 加载过程中的 Element[] dexElements 数组 ( 封装在 DexPathList 中 )
*/
dexElementsField.set(pathList, newElements)
该 Field 字段由之前反射获得 ;
代码语言:javascript复制 //阶段三 : 获取封装在 DexPathList 类中的 Element[] dexElements 数组
var dexElementsField = reflexField(pathList, "dexElements")
// 获取 pathList 对象对应的 Element[] dexElements 数组成员
var dexElements : Array<Any> = dexElementsField.get(pathList) as Array<Any>
三、完整 dex 加载源码
代码语言:javascript复制 /**
* 加载 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)
}
截至到此处 , dex 加载工作基本完成 ;