文章目录

  • 前言
  • 一、在 PathClassLoader 和 BootClassLoader 之间插入 DexClassLoader
    • 1、创建 DexClassLoader
    • 2、使用 DexClassLoader 实例对象作为 PathClassLoader 的父节点
  • 二、完整代码示例
  • 三、执行结果
  • 四、博客资源

前言


在 上一篇博客 【Android 逆向】启动 DEX 字节码中的 Activity 组件 ( 替换 LoadedApk 中的类加载器 | 加载 DEX 文件中的 Activity 类并启动成功 ) 中 , 通过 替换 LoadedApk 中的类加载器可以成功加载 DEX 字节码文件中的 Activity 类 , 并成功启动 Activity ;

本篇博客中尝试使用 【Android 逆向】启动 DEX 字节码中的 Activity 组件 ( 使用 DexClassLoader 获取组件类失败 | 失败原因分析 | 自定义类加载器没有加载组件类的权限 ) 博客中 提出的 加载组件类的 第二种方案 ;

一、在 PathClassLoader 和 BootClassLoader 之间插入 DexClassLoader


1、创建 DexClassLoader

原来的逻辑是 PathClassLoader 用于加载组件类 , 其父节点是 BootClassLoader , 现在将 PathClassLoader 父节点设置为 DexClassLoader , DexClassLoader 父节点设置为 BootClassLoader , 相当于在 PathClassLoaderBootClassLoader 之间插入了一个 DexClassLoader ;

代码示例 :

        // I. 创建 DexClassLoader , 并设置其 父类节点为 BootClassLoader// 获取 PathClassLoaderClassLoader pathClassloader = MainActivity.class.getClassLoader();// 获取 BootClassLoaderClassLoader bootClassloader = MainActivity.class.getClassLoader().getParent();/*注意原来的逻辑是 PathClassLoader 用于加载组件类 , 其父节点是 BootClassLoader现在将 PathClassLoader 父节点设置为 DexClassLoader ,DexClassLoader 父节点设置为 BootClassLoader相当于在 PathClassLoader 和 BootClassLoader 之间插入了一个 DexClassLoader*/// 初始化 DexClassLoaderDexClassLoader dexClassLoader = new DexClassLoader(dexFilePath,                    // Dex 字节码文件路径optFile.getAbsolutePath(),      // 优化目录libFile.getAbsolutePath(),      // 依赖库目录bootClassloader                 // 父节点类加载器);

2、使用 DexClassLoader 实例对象作为 PathClassLoader 的父节点

首先 , 获取 ClassLoader 的 private final ClassLoader parent; 成员 ;

然后 , 设置 PathClassLoader 的 parent 字段为 自定义的 DexClassLoader 实例对象 ;

代码示例 :

        // II. 使用 DexClassLoader 实例对象作为 PathClassLoader 的父节点// 获取 ClassLoader 的 private final ClassLoader parent; 成员Field parentField = null;try {parentField = ClassLoader.class.getDeclaredField("parent");// 设置可访问性parentField.setAccessible(true);} catch (NoSuchFieldException e) {e.printStackTrace();}// 设置 PathClassLoader 的 parent 字段为 自定义的 DexClassLoader 实例对象try {parentField.set(pathClassloader, dexClassLoader);} catch (IllegalAccessException e) {e.printStackTrace();}

二、完整代码示例


下面代码中

        // 在类加载器的双亲委派机制中的 PathClassLoader 和 BootClassLoader 之间// 插入 DexClassLoaderif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {startDexActivityWithInsertedClassLoader(this, mDexPath);}

就是先替换 LoadedApk 中的 类加载器 ClassLoader , 然后使用替换的类加载器加载 DEX 字节码文件中的 Activity 组件 ;

完整代码示例 :

package com.example.classloader_demo;import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.util.ArrayMap;
import android.util.Log;import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;import dalvik.system.DexClassLoader;public class MainActivity extends AppCompatActivity {private static final String TAG = "MainActivity";/*** Dex 文件路径*/private String mDexPath;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);// 打印类加载器及父节点classloaderLog();// 拷贝 dex 文件mDexPath = copyFile2();// 测试 DEX 文件中的方法testDex(this, mDexPath);// 拷贝 dex2 文件//mDexPath = copyFile2();// 启动 DEX 中的 Activity 组件 , 此处启动会失败//startDexActivityWithoutClassLoader(this, mDexPath);// 替换 LoadedApk 中的 类加载器 ClassLoader// 然后使用替换的类加载器加载 DEX 字节码文件中的 Activity 组件if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {//startDexActivityWithReplacedClassLoader(this, mDexPath);}// 在类加载器的双亲委派机制中的 PathClassLoader 和 BootClassLoader 之间// 插入 DexClassLoaderif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {startDexActivityWithInsertedClassLoader(this, mDexPath);}}/*** 打印当前的类加载器及父节点*/private void classloaderLog() {// 获取当前 Activity 的 类加载器 ClassLoaderClassLoader classLoader = MainActivity.class.getClassLoader();// 打印当前 Activity 的 ClassLoader 类加载器Log.i(TAG, "MainActivity ClassLoader : " + classLoader);// 获取 类加载器 父类ClassLoader parentClassLoader = classLoader.getParent();// 打印当前 Activity 的 ClassLoader 类加载器 的父类Log.i(TAG, "MainActivity Parent ClassLoader : " + parentClassLoader);}/*** 将 app\src\main\assets\classes.dex 文件 ,* 拷贝到 /data/user/0/com.example.classloader_demo/files/classes.dex 位置*/private String copyFile() {// DEX 文件File dexFile = new File(getFilesDir(), "classes.dex");// DEX 文件路径String dexPath = dexFile.getAbsolutePath();Log.i(TAG, "开始拷贝文件 dexPath : " + dexPath);// 如果之前已经加载过 , 则退出if (dexFile.exists()) {Log.i(TAG, "文件已经拷贝 , 退出");return dexPath;}try {InputStream inputStream = getAssets().open("classes.dex");FileOutputStream fileOutputStream = new FileOutputStream(dexPath);byte[] buffer = new byte[1024 * 4];int readLen = 0;while ((readLen = inputStream.read(buffer)) != -1) {fileOutputStream.write(buffer, 0, readLen);}inputStream.close();fileOutputStream.close();} catch (IOException e) {e.printStackTrace();} finally {Log.i("HSL", "classes.dex 文件拷贝完毕");}return dexPath;}/*** 将 app\src\main\assets\classes2.dex 文件 ,* 拷贝到 /data/user/0/com.example.classloader_demo/files/classes2.dex 位置*/private String copyFile2() {// DEX 文件File dexFile = new File(getFilesDir(), "classes2.dex");// DEX 文件路径String dexPath = dexFile.getAbsolutePath();Log.i(TAG, "开始拷贝文件 dexPath : " + dexPath);// 如果之前已经加载过 , 则退出if (dexFile.exists()) {Log.i(TAG, "文件已经拷贝 , 退出");return dexPath;}try {InputStream inputStream = getAssets().open("classes2.dex");FileOutputStream fileOutputStream = new FileOutputStream(dexPath);byte[] buffer = new byte[1024 * 4];int readLen = 0;while ((readLen = inputStream.read(buffer)) != -1) {fileOutputStream.write(buffer, 0, readLen);}inputStream.close();fileOutputStream.close();} catch (IOException e) {e.printStackTrace();} finally {Log.i("HSL", "classes2.dex 文件拷贝完毕");}return dexPath;}/*** 测试调用 Dex 字节码文件中的方法** @param context* @param dexFilePath*/private void testDex(Context context, String dexFilePath) {// 优化目录File optFile = new File(getFilesDir(), "opt_dex");// 依赖库目录 , 用于存放 so 文件File libFile = new File(getFilesDir(), "lib_path");// 初始化 DexClassLoaderDexClassLoader 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.DexTest");} catch (ClassNotFoundException e) {e.printStackTrace();}// 获取 com.example.dex_demo.DexTest 类 中的 test() 方法if (clazz != null) {try {// 获取 test 方法Method method = clazz.getDeclaredMethod("test");// 获取 Object 对象Object object = clazz.newInstance();// 调用 test() 方法method.invoke(object);} catch (NoSuchMethodException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();}}}/*** 不修改类加载器的前提下 , 运行 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");// 初始化 DexClassLoaderDexClassLoader 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 中的 类加载器 ClassLoader** @param context* @param dexFilePath*/@RequiresApi(api = Build.VERSION_CODES.KITKAT)private void startDexActivityWithReplacedClassLoader(Context context, String dexFilePath) {// 优化目录File optFile = new File(getFilesDir(), "opt_dex");// 依赖库目录 , 用于存放 so 文件File libFile = new File(getFilesDir(), "lib_path");// 初始化 DexClassLoaderDexClassLoader dexClassLoader = new DexClassLoader(dexFilePath,                    // Dex 字节码文件路径optFile.getAbsolutePath(),      // 优化目录libFile.getAbsolutePath(),      // 依赖库目录context.getClassLoader()        // 父节点类加载器);//------------------------------------------------------------------------------------------// 下面开始替换 LoadedApk 中的 ClassLoader// 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() 方法 , 传入参数 nullObject activityThreadObject = null;try {activityThreadObject = currentActivityThreadMethod.invoke(null);} catch (IllegalAccessException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();}// 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();// 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();}//------------------------------------------------------------------------------------------// 加载 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));}}/*** 在类加载器的父类子类节点中 , 插入自定义 DexClassLoader* 基于双亲 委派机制的解决方案** @param context* @param dexFilePath*/@RequiresApi(api = Build.VERSION_CODES.KITKAT)private void startDexActivityWithInsertedClassLoader(Context context, String dexFilePath) {// 优化目录File optFile = new File(getFilesDir(), "opt_dex");// 依赖库目录 , 用于存放 so 文件File libFile = new File(getFilesDir(), "lib_path");//------------------------------------------------------------------------------------------// 下面开始 在 ClassLoader 的双亲委派体系中 , 插入自定义的 DexClassLoader// I. 创建 DexClassLoader , 并设置其 父类节点为 BootClassLoader// 获取 PathClassLoaderClassLoader pathClassloader = MainActivity.class.getClassLoader();// 获取 BootClassLoaderClassLoader bootClassloader = MainActivity.class.getClassLoader().getParent();/*注意原来的逻辑是 PathClassLoader 用于加载组件类 , 其父节点是 BootClassLoader现在将 PathClassLoader 父节点设置为 DexClassLoader ,DexClassLoader 父节点设置为 BootClassLoader相当于在 PathClassLoader 和 BootClassLoader 之间插入了一个 DexClassLoader*/// 初始化 DexClassLoaderDexClassLoader dexClassLoader = new DexClassLoader(dexFilePath,                    // Dex 字节码文件路径optFile.getAbsolutePath(),      // 优化目录libFile.getAbsolutePath(),      // 依赖库目录bootClassloader                 // 父节点类加载器);// II. 使用 DexClassLoader 实例对象作为 PathClassLoader 的父节点// 获取 ClassLoader 的 private final ClassLoader parent; 成员Field parentField = null;try {parentField = ClassLoader.class.getDeclaredField("parent");// 设置可访问性parentField.setAccessible(true);} catch (NoSuchFieldException e) {e.printStackTrace();}// 设置 PathClassLoader 的 parent 字段为 自定义的 DexClassLoader 实例对象try {parentField.set(pathClassloader, dexClassLoader);} catch (IllegalAccessException e) {e.printStackTrace();}//------------------------------------------------------------------------------------------// 加载 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));}}}

三、执行结果


执行结果 : 参考 【错误记录】Android 应用运行报错 ( java.lang.VerifyError: Verifier rejected class androidx. | 逆向中遇到的问题 ) 博客 , 启动 Activity 组件有报错 , 但是使用类加载器加载 Activity 组件是成功的 ;

在 启动 Activity 组件之前打上断点 , 可以发现 , dexClassLoader.loadClass 操作是成功的 , 加载 Activity 组件操作是成功的 ;

        // 启动 com.example.dex_demo.MainActivity2 组件if (clazz != null) {context.startActivity(new Intent(context, clazz));}

四、博客资源


GitHub 地址 : https://github.com/han1202012/ClassLoader_Demo

CSDN 下载 :

【Android 逆向】启动 DEX 字节码中的 Activity 组件 ( 在 PathClassLoader 和 BootClassLoader 之间插入 DexClassLoader )相关推荐

  1. 【Android 逆向】启动 DEX 字节码中的 Activity 组件 ( 使用 DexClassLoader 获取组件类失败 | 失败原因分析 | 自定义类加载器没有加载组件类的权限 )

    文章目录 一.使用 DexClassLoader 获取组件类失败报错 二.失败原因分析 一.使用 DexClassLoader 获取组件类失败报错 在上一篇博客 [Android 逆向]启动 DEX ...

  2. 【Android 逆向】启动 DEX 字节码中的 Activity 组件 ( DEX 文件准备 | 拷贝资源目录下的文件到内置存储区 | 配置清单文件 | 启动 DEX 文件中的组件 | 执行结果 )

    文章目录 一.DEX 字节码文件准备 二.拷贝 Assets 目录下的 classes2.dex 字节码文件到内置存储区 三.在 AndroidManifest.xml 清单文件中配置组件 四.启动 ...

  3. 【Android 安全】DEX 加密 ( Application 替换 | 分析 Activity 组件中获取的 Application | ActivityThread | LoadedApk )

    文章目录 一. Activity 中的 getApplication() 方法分析 二. ActivityThread 中的 H 处理 消息及 handleLaunchActivity 方法操作 三. ...

  4. 【Android Gradle 插件】DexOptions 配置 ⑤ ( additionalParameters 属性配置 | --minimal-main-dex 参数最小化主 dex 字节码 )

    文章目录 一.dx --minimal-main-dex 参数 1.参数简介 2.配置 main dex 保留文件 3.配置 android#buildTypes 4.配置 android#dexOp ...

  5. 【Android 插件化】Hook 插件化框架 ( 创建插件应用 | 拷贝插件 APK | 初始化插件包 | 测试插件 DEX 字节码 )

    Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...

  6. 【Android 安全】DEX 加密 ( Application 替换 | 分析 ContentProvider 组件中调用 getApplication() 获取的 Application 二 )

    文章目录 一. ActivityThread 中的 installProvider 方法 ( 创建 ContentProvider 内容提供者 ) 二. installProvider 方法的第三分支 ...

  7. 欧尼酱讲JVM(08)——字节码中方法内部结构的剖析

    用到的工具--Jclasslib IDEA提供了插件,很方便. 首先 先看一段代码: public class LocalVariablesTest {private int count = 0;pu ...

  8. 【Android 逆向】加壳技术简介 ( 动态加载 | 第一代加壳技术 - DEX 整体加固 | 第二代加壳技术 - 函数抽取 | 第三代加壳技术 - VMP / Dex2C | 动态库加壳技术 )

    文章目录 一.动态加载 二.第一代加壳技术 ( DEX 整体加固 ) 三.第二代加壳技术 ( 函数抽取 ) 四.第三代加壳技术 ( Java 函数 -> Native 函数 ) 五.so 动态库 ...

  9. 【Android 安全】DEX 加密 ( Application 替换 | 分析 BroadcastReceiver 组件中调用 getApplication() 获取的 Application )

    文章目录 一. Service 中的 getApplication() 方法分析 二. ActivityThread 中的 H 处理 RECEIVER 消息 三. ActivityThread 中的 ...

最新文章

  1. spring JdbcTemplate 在项目中的浅层(5个使用场景)封装
  2. mysql多租户schema复制,Asp.net core下利用EF core实现从数据实现多租户(3): 按Schema分离 附加:EF Migration 操作...
  3. Spring - Java/J2EE Application Framework 应用框架 第 6 章 集成AspectJ
  4. RDA8955的新版本SDK串口接收数据的问题记录
  5. 查阅文献时向原作者发邮件要文献的简单模板
  6. android+六边形布局,android – 带六边形触摸区域的六角形按钮
  7. c++里面的内联函数
  8. [译]理解Node.js事件驱动机制
  9. 理解URL以及如何区分相对URL和绝对URL
  10. mac 中的 zip 和 unzip 命令
  11. Python学习笔记(9):函数调用与定义
  12. 六轴UR机械臂正逆运动学求解_MATLAB代码(标准DH参数表)
  13. 打破第一范式的要求 (中英对照)Michael Rys 对 SQL Server 2005 中XML 的 评论——对微软SQL Server项目经理Michael Rys博士的采访
  14. ic卡信息保存在服务器,智能IC卡网络数据安全保密系统
  15. 详解如何在vue项目中使用lodop打印插件,以及样式打印预览缺失的问题(底部)
  16. 在谷歌上安装倍速播放的插件video-speed-controller
  17. fhq——treap
  18. Python(大蟒蛇)与云计算
  19. MySQL中单句实现无限层次父子关系查询
  20. SAT数学考试需要准备的物品

热门文章

  1. CPU性能监控之一------CPU架构
  2. python requests的安装与简单运用
  3. VIM编辑器使用技巧
  4. RAID,LVM创建
  5. 用Word 2010发布博文
  6. 买房猛于虎《功夫熊猫无家可归》
  7. 一个简单的Ajax开发框架
  8. Postman使用方法示例
  9. JSON WEB TOKEN(JWT)的分析
  10. 2018.08.04 cogs2633. [HZOI 2016]数列操作e(线段树)