文章目录
前言
一、替换 LoadedApk 中的类加载器
1、获取 ActivityThread 实例对象
2、获取 LoadedApk 实例对象
3、替换 LoadedApk 实例对象中的 mClassLoader 类加载器
二、完整代码示例
三、执行结果
前言
在 上一篇博客 【Android 逆向】启动 DEX 字节码中的 Activity 组件 ( DEX 文件准备 | 拷贝资源目录下的文件到内置存储区 | 配置清单文件 | 启动 DEX 文件中的组件 | 执行结果 ) 的代码基础上 , 使用类加载器加载 com.example.dex_demo.MainActivity2 组件前 , 先替换 LoadedApk 的类加载器 , 就可以成功加载 DEX 文件了 , 该操作类似于热修复 ;
/** * 不修改类加载器的前提下 , 运行 Dex 字节码文件中的组件 * * @param context * @param dexFilePath */ private void startDexActivityWithoutClassLoader(Context context, String dexFilePath) { // 优化目录 File optFile = new File(getFilesDir(), "opt_dex"); // 依赖库目录 , 用于存放 so 文件 File libFile = new File(getFilesDir(), "lib_path"); // 初始化 DexClassLoader DexClassLoader dexClassLoader = new DexClassLoader( dexFilePath, // Dex 字节码文件路径 optFile.getAbsolutePath(), // 优化目录 libFile.getAbsolutePath(), // 依赖库目录 context.getClassLoader() // 父节点类加载器 ); // 加载 com.example.dex_demo.DexTest 类 // 该类中有可执行方法 test() Class<?> clazz = null; try { clazz = dexClassLoader.loadClass("com.example.dex_demo.MainActivity2"); } catch (ClassNotFoundException e) { e.printStackTrace(); } // 启动 com.example.dex_demo.MainActivity2 组件 if (clazz != null) { context.startActivity(new Intent(context, clazz)); } }
一、替换 LoadedApk 中的类加载器
参考 【Android 逆向】加壳的 Android 应用启动流程 | 使用反射替换 LoadedApk 中的类加载器流程 二、使用反射替换 LoadedApk 中的类加载器流程 博客章节 , 使用反射替换 LoadedApk 的类加载器 ;
1、获取 ActivityThread 实例对象
首先 , 获取 ActivityThread 实例对象 , 该类全局单例 , 获取其类 , 就可以获取实例对象 ;
// I. 获取 ActivityThread 实例对象 // 获取 ActivityThread 字节码类 , 这里可以使用自定义的类加载器加载 // 原因是 基于 双亲委派机制 , 自定义的 DexClassLoader 无法加载 , 但是其父类可以加载 // 即使父类不可加载 , 父类的父类也可以加载 Class<?> ActivityThreadClass = null; try { ActivityThreadClass = dexClassLoader.loadClass( "android.app.ActivityThread"); } catch (ClassNotFoundException e) { e.printStackTrace(); } // 获取 ActivityThread 中的 sCurrentActivityThread 成员 // 获取的字段如下 : // private static volatile ActivityThread sCurrentActivityThread; // 获取字段的方法如下 : // public static ActivityThread currentActivityThread() {return sCurrentActivityThread;} Method currentActivityThreadMethod = null; try { currentActivityThreadMethod = ActivityThreadClass.getDeclaredMethod( "currentActivityThread"); // 设置可访问性 , 所有的 方法 , 字段 反射 , 都要设置可访问性 currentActivityThreadMethod.setAccessible(true); } catch (NoSuchMethodException e) { e.printStackTrace(); } // 执行 ActivityThread 的 currentActivityThread() 方法 , 传入参数 null Object activityThreadObject = null; try { activityThreadObject = currentActivityThreadMethod.invoke(null); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); }
2、获取 LoadedApk 实例对象
然后 , 从 ActivityThread 实例对象中 , 获取 LoadedApk 成员 ;
// II. 获取 LoadedApk 实例对象 // 获取 ActivityThread 实例对象的 mPackages 成员 // final ArrayMap<String, WeakReference<LoadedApk>> mPackages = new ArrayMap<>(); Field mPackagesField = null; try { mPackagesField = ActivityThreadClass.getDeclaredField("mPackages"); // 设置可访问性 , 所有的 方法 , 字段 反射 , 都要设置可访问性 mPackagesField.setAccessible(true); } catch (NoSuchFieldException e) { e.printStackTrace(); } // 从 ActivityThread 实例对象 activityThreadObject 中 // 获取 mPackages 成员 ArrayMap mPackagesObject = null; try { mPackagesObject = (ArrayMap) mPackagesField.get(activityThreadObject); } catch (IllegalAccessException e) { e.printStackTrace(); } // 获取 WeakReference<LoadedApk> 弱引用对象 WeakReference weakReference = (WeakReference) mPackagesObject.get(this.getPackageName()); // 获取 LoadedApk 实例对象 Object loadedApkObject = weakReference.get();
3、替换 LoadedApk 实例对象中的 mClassLoader 类加载器
最后 , 替换 LoadedApk 实例对象中的 mClassLoader 类加载器 ;
// III. 替换 LoadedApk 实例对象中的 mClassLoader 类加载器 // 加载 android.app.LoadedApk 类 Class LoadedApkClass = null; try { LoadedApkClass = dexClassLoader.loadClass("android.app.LoadedApk"); } catch (ClassNotFoundException e) { e.printStackTrace(); } // 通过反射获取 private ClassLoader mClassLoader; 类加载器对象 Field mClassLoaderField = null; try { mClassLoaderField = LoadedApkClass.getDeclaredField("mClassLoader"); // 设置可访问性 mClassLoaderField.setAccessible(true); } catch (NoSuchFieldException e) { e.printStackTrace(); } // 替换 mClassLoader 成员 try { mClassLoaderField.set(loadedApkObject, dexClassLoader); } catch (IllegalAccessException e) { e.printStackTrace(); }