【Android 插件化】Hook 插件化框架 ( 通过反射获取 “插件包“ 中的 Element[] dexElements )
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 插件化系列文章目錄
- 前言
- 一、通過反射獲取 "插件包" 中的 Element[] dexElements
- 1、反射獲取 BaseDexClassLoader.class
- 2、反射獲取 DexPathList pathList 字段
- 3、反射獲取 DexPathList pathList 對象
- 4、獲取 DexPathList pathList 對象
- 5、反射獲取 Element[] dexElements 字段
- 6、反射獲取 Element[] dexElements 對象
- 二、完整代碼示例
- 三、博客資源
前言
在上一篇博客 【Android 插件化】Hook 插件化框架 ( hook 插件化原理 | 插件包管理 ) 中簡要介紹了 hook 插件化原理 , 并開始開發插件化管理類 , 本博客中開始加載插件包中的 Element[] dexElements ;
Android 中的類加載器 DexClassLoader , PathClassLoader 的父類是 BaseDexClassLoader ,
BaseDexClassLoader 中有 private final DexPathList pathList 成員變量 ,
DexPathList 中有 private Element[] dexElements 成員變量 ,
Element[] dexElements 就是最終存放 Dex 字節碼數據的內存變量 , 最終將 " 插件包 " 中讀取的 dexElements 合并到 " 宿主 " 應用的 dexElements 中 ;
一、通過反射獲取 “插件包” 中的 Element[] dexElements
反射 " 插件包 " 應用的 dexElement 執行步驟 :
① 反射獲取 BaseDexClassLoader.class
② 反射獲取 BaseDexClassLoader.calss 中的 private final DexPathList pathList 成員字段
③ 反射獲取 plugin_dexClassLoader 類加載器中的 DexPathList pathList 成員對象
④ 獲取 DexPathList.class
⑤ 反射獲取 DexPathList.class 中的 private Element[] dexElements 成員變量的 Field 字段對象
⑥ 反射獲取 DexPathList 對象中的 private Element[] dexElements 成員變量對象
1、反射獲取 BaseDexClassLoader.class
反射獲取 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.java
代碼示例 :
// ① 反射獲取 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.java Class<?> baseDexClassLoaderClass = null; try {baseDexClassLoaderClass = Class.forName("dalvik.system.BaseDexClassLoader"); } catch (ClassNotFoundException e) {e.printStackTrace(); }2、反射獲取 DexPathList pathList 字段
// ② 反射獲取 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(); }
3、反射獲取 DexPathList pathList 對象
根據 Field 字段獲取 成員變量 , DexClassLoader 繼承了 BaseDexClassLoader, 因此其內部肯定有 private final DexPathList pathList 成員變量 ;
// ③ 反射獲取 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(); }4、獲取 DexPathList pathList 對象
獲取 DexPathList.class , 之前已經通過反射獲取了 DexPathList pathList 對象 , 這里直接通過調用該對象的 getClass 方法 , 獲取該類對象 ;
參考 : https://www.androidos.net.cn/android/9.0.0_r8/xref/libcore/dalvik/src/main/java/dalvik/system/DexPathList.java
// ④ 獲取 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.class Class<?> plugin_dexPathListClass = plugin_pathListObject.getClass();5、反射獲取 Element[] dexElements 字段
// ⑤ 反射獲取 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(); }
6、反射獲取 Element[] dexElements 對象
// ⑥ 反射獲取 DexPathList 對象中的 private Element[] dexElements 成員變量對象 Object plugin_dexElementsObject = null; try {plugin_dexElementsObject = plugin_dexElementsField.get(plugin_pathListObject); } catch (IllegalAccessException e) {e.printStackTrace(); }
二、完整代碼示例
package kim.hsl.plugin;import android.content.Context;import java.lang.reflect.Field;import dalvik.system.DexClassLoader;/*** 使用 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 成員字段// ③ 反射獲取 plugin_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();}}}
三、博客資源
博客資源 :
- GitHub : https://github.com/han1202012/Plugin_Hook
總結
以上是生活随笔為你收集整理的【Android 插件化】Hook 插件化框架 ( 通过反射获取 “插件包“ 中的 Element[] dexElements )的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Android 插件化】Hook 插件
- 下一篇: 【Android 插件化】Hook 插件