【Android 插件化】Hook 插件化框架 ( 合并 “插件包“ 与 “宿主“ 中的 Element[] dexElements | 设置合并后的 Element[] 数组 )
Android 插件化系列文章目錄
【Android 插件化】插件化簡介 ( 組件化與插件化 )
【Android 插件化】插件化原理 ( JVM 內存數據 | 類加載流程 )
【Android 插件化】插件化原理 ( 類加載器 )
【Android 插件化】“ 插樁式 “ 插件化框架 ( 原理與實現思路 )
【Android 插件化】“ 插樁式 “ 插件化框架 ( 類加載器創建 | 資源加載 )
【Android 插件化】“ 插樁式 “ 插件化框架 ( 注入上下文的使用 )
【Android 插件化】“ 插樁式 “ 插件化框架 ( 獲取插件入口 Activity 組件 | 加載插件 Resources 資源 )
【Android 插件化】“ 插樁式 “ 插件化框架 ( 運行應用 | 代碼整理 )
【Android 插件化】Hook 插件化框架 ( Hook 技術 | 代理模式 | 靜態代理 | 動態代理 )
【Android 插件化】Hook 插件化框架 ( Hook 實現思路 | Hook 按鈕點擊事件 )
【Android 插件化】Hook 插件化框架 ( Hook Activity 啟動過程 | 靜態代理 )
【Android 插件化】Hook 插件化框架 ( 從 Hook 應用角度分析 Activity 啟動流程 一 | Activity 進程相關源碼 )
【Android 插件化】Hook 插件化框架 ( 從 Hook 應用角度分析 Activity 啟動流程 二 | AMS 進程相關源碼 | 主進程相關源碼 )
【Android 插件化】Hook 插件化框架 ( hook 插件化原理 | 插件包管理 )
【Android 插件化】Hook 插件化框架 ( 通過反射獲取 “插件包“ 中的 Element[] dexElements )
【Android 插件化】Hook 插件化框架 ( 通過反射獲取 “宿主“ 應用中的 Element[] dexElements )
【Android 插件化】Hook 插件化框架 ( 合并 “插件包“ 與 “宿主“ 中的 Element[] dexElements | 設置合并后的 Element[] 數組 )
文章目錄
- Android 插件化系列文章目錄
- 前言
- 一、合并 “插件包“ 與 “宿主“ 中的 Element[] dexElements
- 1、獲取 “插件包“ 與 “宿主“ 中的 Element[] dexElements 數組長度
- 2、獲取數組元素類型 Element
- 3、計算合并后的 Element[] dexElements 數組長度
- 4、創建 Element[] 數組
- 5、拷貝 Element[] 數組元素
- 6、完整代碼
- 二、設置 “宿主“ 中的 Element[] dexElements
- 三、完整代碼示例
- 四、博客資源
前言
在 【Android 插件化】Hook 插件化框架 ( 通過反射獲取 “插件包“ 中的 Element[] dexElements ) 博客中介紹了從 " 插件包 " APK 文件中獲取 Element[] dexElements 流程 ;
在上一篇博客 【Android 插件化】Hook 插件化框架 ( 通過反射獲取 “宿主“ 應用中的 Element[] dexElements ) 介紹了從 " 宿主 " 應用中獲取 Element[] dexElements 流程 ;
本篇博客中開始將 " 插件包 " APK 中的 Element[] dexElements 和 “宿主“ 應用中的 Element[] dexElements 合并 ;
一、合并 “插件包“ 與 “宿主“ 中的 Element[] dexElements
將兩個 Element[] dexElements 數組合并 , 合并完成后 , 設置到 PathClassLoader 中的 DexPathList pathList 成員的 Element[] dexElements 成員中 ;
1、獲取 “插件包“ 與 “宿主“ 中的 Element[] dexElements 數組長度
調用 Java 提供的 Array API 中的 getLength 方法 , 獲取數組長度 ;
// 獲取 “宿主“ 中的 Element[] dexElements 數組長度 int host_dexElementsLength = Array.getLength(host_dexElementsObject); // 獲取 “插件包“ 中的 Element[] dexElements 數組長度 int plugin_dexElementsLength = Array.getLength(plugin_dexElementsObject);2、獲取數組元素類型 Element
獲取 Element[] dexElements 數組中的 , 數組元素的 Element 類型 , 獲取的是 Element.class ;
// 獲取 Element[] dexElements 數組中的 , 數組元素的 Element 類型 // 獲取的是 Element.class Class<?> elementClazz = host_dexElementsObject.getClass().getComponentType();3、計算合并后的 Element[] dexElements 數組長度
// 合并后的 Element[] dexElements 數組長度 int new_dexElementsLength = plugin_dexElementsLength + host_dexElementsLength;
4、創建 Element[] 數組
創建 Element[] 數組 , elementClazz 是 Element.class 數組元素類型 ;
// 創建 Element[] 數組 , elementClazz 是 Element.class 數組元素類型 Object newElementsArray = Array.newInstance(elementClazz, new_dexElementsLength);5、拷貝 Element[] 數組元素
為新的 Element[] newElementsArray 數組賦值 , 先將 “插件包“ 中的 Element[] dexElements 數組放入到新數組中 , 然后將 “宿主“ 中的 Element[] dexElements 數組放入到新數組中 ;
// 為新的 Element[] newElementsArray 數組賦值 // 先將 “插件包“ 中的 Element[] dexElements 數組放入到新數組中 // 然后將 “宿主“ 中的 Element[] dexElements 數組放入到新數組中 for (int i = 0; i < new_dexElementsLength; i++) {if (i < plugin_dexElementsLength) {// “插件包“ 中的 Element[] dexElements 數組放入到新數組中Array.set(newElementsArray, i, Array.get(plugin_dexElementsObject, i));} else {// “宿主“ 中的 Element[] dexElements 數組放入到新數組中Array.set(newElementsArray, i, Array.get(host_dexElementsObject, i - plugin_dexElementsLength));} }6、完整代碼
完整代碼 :
// 3. 合并 “插件包“ 與 “宿主“ 中的 Element[] dexElements// 將兩個 Element[] dexElements 數組合并 ,// 合并完成后 , 設置到 PathClassLoader 中的// DexPathList pathList 成員的 Element[] dexElements 成員中// 獲取 “宿主“ 中的 Element[] dexElements 數組長度int host_dexElementsLength = Array.getLength(host_dexElementsObject);// 獲取 “插件包“ 中的 Element[] dexElements 數組長度int plugin_dexElementsLength = Array.getLength(plugin_dexElementsObject);// 獲取 Element[] dexElements 數組中的 , 數組元素的 Element 類型// 獲取的是 Element.classClass<?> elementClazz = host_dexElementsObject.getClass().getComponentType();// 合并后的 Element[] dexElements 數組長度int new_dexElementsLength = plugin_dexElementsLength + host_dexElementsLength;// 創建 Element[] 數組 , elementClazz 是 Element.class 數組元素類型Object newElementsArray = Array.newInstance(elementClazz, new_dexElementsLength);// 為新的 Element[] newElementsArray 數組賦值// 先將 “插件包“ 中的 Element[] dexElements 數組放入到新數組中// 然后將 “宿主“ 中的 Element[] dexElements 數組放入到新數組中for (int i = 0; i < new_dexElementsLength; i++) {if (i < plugin_dexElementsLength) {// “插件包“ 中的 Element[] dexElements 數組放入到新數組中Array.set(newElementsArray, i, Array.get(plugin_dexElementsObject, i));} else {// “宿主“ 中的 Element[] dexElements 數組放入到新數組中Array.set(newElementsArray, i, Array.get(host_dexElementsObject, i - plugin_dexElementsLength));}}二、設置 “宿主“ 中的 Element[] dexElements
將之前 合并 “插件包“ 與 “宿主“ 中的 Element[] dexElements 數組 , 設置到 宿主 PathClassLoader 中的 DexPathList pathList 成員的 Element[] dexElements 屬性值中 ;
先獲取 Element[] dexElements 字段 Field 對象 ;
Field elementsFiled = null; try {elementsFiled = host_pathListObject.getClass().getDeclaredField("dexElements"); } catch (NoSuchFieldException e) {e.printStackTrace(); } elementsFiled.setAccessible(true);然后調用 Field 的 set 方法 , 設置該屬性值 , 其中 host_pathListObject 是原來的屬性值 , newElementsArray 是新的合并后的 Element[] dexElements 數組 ;
elementsFiled.set(host_pathListObject, newElementsArray);完整代碼 :
// 4. 重新設置 PathClassLoader 中的 DexPathList pathList 成員的 Element[] dexElements 屬性值 Field elementsFiled = null; try {elementsFiled = host_pathListObject.getClass().getDeclaredField("dexElements"); } catch (NoSuchFieldException e) {e.printStackTrace(); } elementsFiled.setAccessible(true);// 設置 DexPathList pathList 的 Element[] dexElements 屬性值 // host_pathListObject 是原來的屬性值 // newElementsArray 是新的合并后的 Element[] dexElements 數組 // 注意 : 這里也可以使用 host_dexElementsField 字段進行設置 try {elementsFiled.set(host_pathListObject, newElementsArray); } catch (IllegalAccessException e) {e.printStackTrace(); }三、完整代碼示例
package kim.hsl.plugin;import android.content.Context;import java.lang.reflect.Array; import java.lang.reflect.Field;import dalvik.system.DexClassLoader; import dalvik.system.PathClassLoader;/*** 使用 Hook 實現的插件使用入口* 1. 加載插件包中的字節碼* 2. 直接通過 hook 技術, 鉤住系統的 Activity 啟動流程實現* ① Activity 對象創建之前 , 要做很多初始化操作 , 先在 ActivityRecord 中加載 Activity 信息* 如果修改了該信息 , 將要跳轉的 Activity 信息修改為插件包中的 Activity* 原來的 Activity 只用于占位 , 用于欺騙 Android 系統* ② 使用 hook 技術 , 加載插件包 apk 中的 Activity* ③ 實現跳轉的 Activity ( 插件包中的 )* 3. 解決 Resources 資源沖突問題* ( 使用上述 hook 插件化 , 可以不用考慮 Activity 的聲明周期問題 )** 插件包中的 Activity 是通過正規流程 , 由 AMS 進行創建并加載的* 但是該 Activity 并沒有在 AndroidManifest.xml 清單文件中注冊* 這里需要一個已經在清單文件注冊的 Activity 欺騙系統** 插裝式插件化 是通過代理 Activity , 將插件包加載的字節碼 Class 作為一個普通的 Java 類* 該普通的 Java 類有所有的 Activity 的業務邏輯* 該 Activity 的聲明周期 , 由代理 Activity 執行相關的生命周期方法* hook 插件化 : hook 插件化直接鉤住系統中 Activity 啟動流程的某個點* 使用插件包中的 Activity 替換占位的 Activity*/ public class PluginManager {/*** 上下文*/private Context mBase;/*** 單例*/private static PluginManager instance;public static PluginManager getInstance(Context context) {if (instance == null) {instance = new PluginManager(context);}return instance;}private PluginManager(Context context) {this.mBase = context;}/*** Application 啟動后 , 調用該方法初始化插件化環境* 加載插件包中的字節碼*/private void init() {// 加載 apk 文件loadApk();}private void loadApk() {// 插件包的絕對路徑 , /data/data/< package name >/files/String apkPath = mBase.getFilesDir().getAbsolutePath() + "plugin.apk";// 加載插件包后產生的緩存文件路徑// /data/data/< package name >/app_plugin_cache/String cachePath =mBase.getDir("plugin_cache", Context.MODE_PRIVATE).getAbsolutePath();// 創建類加載器DexClassLoader plugin_dexClassLoader =new DexClassLoader(apkPath, // 插件包路徑cachePath, // 插件包加載時產生的緩存路徑null, // 庫的搜索路徑, 可以設置為空mBase.getClassLoader() // 父加載器, PathClassLoader);// 1. 反射 " 插件包 " 應用的 dexElement// 執行步驟 :// ① 反射獲取 BaseDexClassLoader.class// ② 反射獲取 BaseDexClassLoader.calss 中的 private final DexPathList pathList 成員字段// ③ 反射獲取 DexClassLoader 類加載器中的 DexPathList pathList 成員對象// ④ 反射獲取 DexPathList.class// ⑤ 反射獲取 DexPathList.class 中的 private Element[] dexElements 成員變量的 Field 字段對象// ⑥ 反射獲取 DexPathList 對象中的 private Element[] dexElements 成員變量對象// ① 反射獲取 BaseDexClassLoader.class// 通過反射獲取插件包中的 dexElements// 這種類加載是合并類加載 , 將所有的 Dex 文件 , 加入到應用的 dex 文件集合中// 可參考 dex 加固 , 熱修復 , 插裝式插件化 的實現步驟// 反射出 BaseDexClassLoader 類 , PathClassLoader 和 DexClassLoader// 都是 BaseDexClassLoader 的子類// 參考 https://www.androidos.net.cn/android/9.0.0_r8/xref/libcore/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.javaClass<?> baseDexClassLoaderClass = null;try {baseDexClassLoaderClass = Class.forName("dalvik.system.BaseDexClassLoader");} catch (ClassNotFoundException e) {e.printStackTrace();}// ② 反射獲取 BaseDexClassLoader.calss 中的 private final DexPathList pathList 成員字段Field plugin_pathListField = null;try {plugin_pathListField = baseDexClassLoaderClass.getDeclaredField("pathList");// 設置屬性的可見性plugin_pathListField.setAccessible(true);} catch (NoSuchFieldException e) {e.printStackTrace();}// ③ 反射獲取 plugin_dexClassLoader 類加載器中的 DexPathList pathList 成員對象// 根據 Field 字段獲取 成員變量// DexClassLoader 繼承了 BaseDexClassLoader, 因此其內部肯定有// private final DexPathList pathList 成員變量Object plugin_pathListObject = null;try {plugin_pathListObject = plugin_pathListField.get(plugin_dexClassLoader);} catch (IllegalAccessException e) {e.printStackTrace();}// ④ 獲取 DexPathList.class// DexPathList 類中有 private Element[] dexElements 成員變量// 通過反射獲取該成員變量// 參考 https://www.androidos.net.cn/android/9.0.0_r8/xref/libcore/dalvik/src/main/java/dalvik/system/DexPathList.java// 獲取 DexPathList pathList 成員變量的字節碼類型 ( 也可以通過反射獲得 )// 獲取的是 DexPathList.classClass<?> plugin_dexPathListClass = plugin_pathListObject.getClass();// ⑤ 反射獲取 DexPathList.class 中的 private Element[] dexElements 成員變量的 Field 字段對象Field plugin_dexElementsField = null;try {plugin_dexElementsField = plugin_dexPathListClass.getDeclaredField("dexElements");// 設置屬性的可見性plugin_dexElementsField.setAccessible(true);} catch (NoSuchFieldException e) {e.printStackTrace();}// ⑥ 反射獲取 DexPathList 對象中的 private Element[] dexElements 成員變量對象Object plugin_dexElementsObject = null;try {plugin_dexElementsObject = plugin_dexElementsField.get(plugin_pathListObject);} catch (IllegalAccessException e) {e.printStackTrace();}// 2. 反射 " 宿主 " 應用的 dexElement// 執行步驟 :// ① 反射獲取 BaseDexClassLoader.class// ② 反射獲取 BaseDexClassLoader.calss 中的 private final DexPathList pathList 成員字段// ③ 反射獲取 PathClassLoader 類加載器中的 DexPathList pathList 成員對象// ④ 反射獲取 DexPathList.class// ⑤ 反射獲取 DexPathList.class 中的 private Element[] dexElements 成員變量的 Field 字段對象// ⑥ 反射獲取 DexPathList 對象中的 private Element[] dexElements 成員變量對象// ① 反射獲取 BaseDexClassLoader.classClass<?> host_baseDexClassLoaderClass = null;try {host_baseDexClassLoaderClass = Class.forName("dalvik.system.BaseDexClassLoader");} catch (ClassNotFoundException e) {e.printStackTrace();}// ② 反射獲取 BaseDexClassLoader.calss 中的 private final DexPathList pathList 成員字段Field host_pathListField = null;try {host_pathListField = host_baseDexClassLoaderClass.getDeclaredField("pathList");// 設置屬性的可見性host_pathListField.setAccessible(true);} catch (NoSuchFieldException e) {e.printStackTrace();}// ③ 反射獲取 DexClassLoader 類加載器中的 DexPathList pathList 成員對象// 根據 Field 字段獲取 成員變量// DexClassLoader 繼承了 BaseDexClassLoader, 因此其內部肯定有// private final DexPathList pathList 成員變量PathClassLoader host_pathClassLoader = (PathClassLoader) mBase.getClassLoader();Object host_pathListObject = null;try {host_pathListObject = host_pathListField.get(host_pathClassLoader);} catch (IllegalAccessException e) {e.printStackTrace();}// ④ 獲取 DexPathList.class// DexPathList 類中有 private Element[] dexElements 成員變量// 通過反射獲取該成員變量// 參考 https://www.androidos.net.cn/android/9.0.0_r8/xref/libcore/dalvik/src/main/java/dalvik/system/DexPathList.java// 獲取 DexPathList pathList 成員變量的字節碼類型 ( 也可以通過反射獲得 )// 獲取的是 DexPathList.classClass<?> host_dexPathListClass = host_pathListObject.getClass();// ⑤ 反射獲取 DexPathList.class 中的 private Element[] dexElements 成員變量的 Field 字段對象Field host_dexElementsField = null;try {host_dexElementsField = host_dexPathListClass.getDeclaredField("dexElements");// 設置屬性的可見性host_dexElementsField.setAccessible(true);} catch (NoSuchFieldException e) {e.printStackTrace();}// ⑥ 反射獲取 DexPathList 對象中的 private Element[] dexElements 成員變量對象Object host_dexElementsObject = null;try {host_dexElementsObject = host_dexElementsField.get(host_pathListObject);} catch (IllegalAccessException e) {e.printStackTrace();}// 3. 合并 “插件包“ 與 “宿主“ 中的 Element[] dexElements// 將兩個 Element[] dexElements 數組合并 ,// 合并完成后 , 設置到 PathClassLoader 中的// DexPathList pathList 成員的 Element[] dexElements 成員中// 獲取 “宿主“ 中的 Element[] dexElements 數組長度int host_dexElementsLength = Array.getLength(host_dexElementsObject);// 獲取 “插件包“ 中的 Element[] dexElements 數組長度int plugin_dexElementsLength = Array.getLength(plugin_dexElementsObject);// 獲取 Element[] dexElements 數組中的 , 數組元素的 Element 類型// 獲取的是 Element.classClass<?> elementClazz = host_dexElementsObject.getClass().getComponentType();// 合并后的 Element[] dexElements 數組長度int new_dexElementsLength = plugin_dexElementsLength + host_dexElementsLength;// 創建 Element[] 數組 , elementClazz 是 Element.class 數組元素類型Object newElementsArray = Array.newInstance(elementClazz, new_dexElementsLength);// 為新的 Element[] newElementsArray 數組賦值// 先將 “插件包“ 中的 Element[] dexElements 數組放入到新數組中// 然后將 “宿主“ 中的 Element[] dexElements 數組放入到新數組中for (int i = 0; i < new_dexElementsLength; i++) {if (i < plugin_dexElementsLength) {// “插件包“ 中的 Element[] dexElements 數組放入到新數組中Array.set(newElementsArray, i, Array.get(plugin_dexElementsObject, i));} else {// “宿主“ 中的 Element[] dexElements 數組放入到新數組中Array.set(newElementsArray, i, Array.get(host_dexElementsObject, i - plugin_dexElementsLength));}}// 4. 重新設置 PathClassLoader 中的 DexPathList pathList 成員的 Element[] dexElements 屬性值Field elementsFiled = null;try {elementsFiled = host_pathListObject.getClass().getDeclaredField("dexElements");} catch (NoSuchFieldException e) {e.printStackTrace();}elementsFiled.setAccessible(true);// 設置 DexPathList pathList 的 Element[] dexElements 屬性值// host_pathListObject 是原來的屬性值// newElementsArray 是新的合并后的 Element[] dexElements 數組// 注意 : 這里也可以使用 host_dexElementsField 字段進行設置try {elementsFiled.set(host_pathListObject, newElementsArray);} catch (IllegalAccessException e) {e.printStackTrace();}}}
四、博客資源
博客資源 :
- GitHub : https://github.com/han1202012/Plugin_Hook
總結
以上是生活随笔為你收集整理的【Android 插件化】Hook 插件化框架 ( 合并 “插件包“ 与 “宿主“ 中的 Element[] dexElements | 设置合并后的 Element[] 数组 )的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【错误记录】Android Studio
- 下一篇: 【Google Play】IARC 年龄