【Android 逆向】启动 DEX 字节码中的 Activity 组件 ( 替换 LoadedApk 中的类加载器 | 加载 DEX 文件中的 Activity 类并启动成功 )(二)

简介: 【Android 逆向】启动 DEX 字节码中的 Activity 组件 ( 替换 LoadedApk 中的类加载器 | 加载 DEX 文件中的 Activity 类并启动成功 )(二)

二、完整代码示例


下面代码中


   

// 替换 LoadedApk 中的 类加载器 ClassLoader
        // 然后使用替换的类加载器加载 DEX 字节码文件中的 Activity 组件
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            startDexActivityWithoutReplacedClassLoader(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;
    @Override
    protected 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) {
            startDexActivityWithoutReplacedClassLoader(this, mDexPath);
        }
    }
    /**
     * 打印当前的类加载器及父节点
     */
    private void classloaderLog() {
        // 获取当前 Activity 的 类加载器 ClassLoader
        ClassLoader 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");
        // 初始化 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.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");
        // 初始化 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 中的 类加载器 ClassLoader
     *
     * @param context
     * @param dexFilePath
     */
    @RequiresApi(api = Build.VERSION_CODES.KITKAT)
    private void startDexActivityWithoutReplacedClassLoader(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()        // 父节点类加载器
        );
        //------------------------------------------------------------------------------------------
        // 下面开始替换 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() 方法 , 传入参数 null
        Object 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));
        }
    }
}





三、执行结果


执行结果 :


2021-12-12 11:49:06.816 26145-26145/com.example.classloader_demo I/MainActivity: MainActivity ClassLoader : dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.example.classloader_demo-Ij2qh4a32d4VghdA5YWXdA==/base.apk"],nativeLibraryDirectories=[/data/app/com.example.classloader_demo-Ij2qh4a32d4VghdA5YWXdA==/lib/arm64, /system/lib64]]]
2021-12-12 11:49:06.816 26145-26145/com.example.classloader_demo I/MainActivity: MainActivity Parent ClassLoader : java.lang.BootClassLoader@6457c5
2021-12-12 11:49:06.817 26145-26145/com.example.classloader_demo I/MainActivity: 开始拷贝文件 dexPath : /data/user/0/com.example.classloader_demo/files/classes2.dex
2021-12-12 11:49:06.879 26145-26145/com.example.classloader_demo I/HSL: classes2.dex 文件拷贝完毕
2021-12-12 11:49:07.939 26145-26145/com.example.classloader_demo I/lassloader_dem: The ClassLoaderContext is a special shared library.
2021-12-12 11:49:07.942 26145-26145/com.example.classloader_demo I/DexTest: DexTest : Hello World!!!
2021-12-12 11:49:07.943 26145-26145/com.example.classloader_demo I/lassloader_dem: The ClassLoaderContext is a special shared library.
2021-12-12 11:49:07.945 26145-26145/com.example.classloader_demo W/lassloader_dem: Accessing hidden method Landroid/app/ActivityThread;->currentActivityThread()Landroid/app/ActivityThread; (light greylist, reflection)
2021-12-12 11:49:07.945 26145-26145/com.example.classloader_demo W/lassloader_dem: Accessing hidden field Landroid/app/ActivityThread;->mPackages:Landroid/util/ArrayMap; (light greylist, reflection)
2021-12-12 11:49:07.945 26145-26145/com.example.classloader_demo W/lassloader_dem: Accessing hidden field Landroid/app/LoadedApk;->mClassLoader:Ljava/lang/ClassLoader; (light greylist, reflection)
2021-12-12 11:49:07.995 26145-26145/com.example.classloader_demo W/lassloader_dem: Accessing hidden method Landroid/graphics/Insets;->of(IIII)Landroid/graphics/Insets; (light greylist, linking)
2021-12-12 11:49:08.008 26145-26220/com.example.classloader_demo I/Adreno: QUALCOMM build                   : 6fb5a5b, Ife855c4895
    Build Date                       : 08/21/18
    OpenGL ES Shader Compiler Version: EV031.25.00.00
    Local Branch                     : Googledrop_0815
    Remote Branch                    : 
    Remote Branch                    : 
    Reconstruct Branch               : 
2021-12-12 11:49:08.008 26145-26220/com.example.classloader_demo I/Adreno: Build Config                     : S L 4.0.10 AArch64
2021-12-12 11:49:08.011 26145-26220/com.example.classloader_demo I/Adreno: PFP: 0x005ff110, ME: 0x005ff066
2021-12-12 11:49:08.013 26145-26220/com.example.classloader_demo I/ConfigStore: android::hardware::configstore::V1_0::ISurfaceFlingerConfigs::hasWideColorDisplay retrieved: 1
2021-12-12 11:49:08.014 26145-26220/com.example.classloader_demo I/ConfigStore: android::hardware::configstore::V1_0::ISurfaceFlingerConfigs::hasHDRDisplay retrieved: 0
2021-12-12 11:49:08.014 26145-26220/com.example.classloader_demo I/OpenGLRenderer: Initialized EGL, version 1.4
2021-12-12 11:49:08.015 26145-26145/com.example.classloader_demo W/ActivityThread: handleWindowVisibility: no activity for token android.os.BinderProxy@463dd0
2021-12-12 11:49:08.036 26145-26145/com.example.classloader_demo I/MainActivity2: com.example.dex_demo.MainActivity2 onCreate 执行成功 !!!



启动的 DEX 中的 Activity 组件如下 :


package com.example.dex_demo;
import android.os.Bundle;
import android.util.Log;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity2 extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.i("MainActivity2", "com.example.dex_demo.MainActivity2 onCreate 执行成功 !!!");
    }
}


在上面的日志中 , 成功打印出了 com.example.dex_demo.MainActivity2 onCreate 执行成功 !!! 内容 , 说明 com.example.dex_demo.MainActivity2 执行成功 ;



目录
相关文章
|
1月前
|
ARouter Android开发
Android不同module布局文件重名被覆盖
Android不同module布局文件重名被覆盖
|
3月前
|
Java Android开发 C++
Android Studio JNI 使用模板:c/cpp源文件的集成编译,快速上手
本文提供了一个Android Studio中JNI使用的模板,包括创建C/C++源文件、编辑CMakeLists.txt、编写JNI接口代码、配置build.gradle以及编译生成.so库的详细步骤,以帮助开发者快速上手Android平台的JNI开发和编译过程。
256 1
|
1月前
|
JSON 调度 数据库
Android面试之5个Kotlin深度面试题:协程、密封类和高阶函数
本文首发于公众号“AntDream”,欢迎微信搜索“AntDream”或扫描文章底部二维码关注,和我一起每天进步一点点。文章详细解析了Kotlin中的协程、扩展函数、高阶函数、密封类及`inline`和`reified`关键字在Android开发中的应用,帮助读者更好地理解和使用这些特性。
22 1
|
18天前
|
前端开发 Java 测试技术
android MVP契约类架构模式与MVVM架构模式,哪种架构模式更好?
android MVP契约类架构模式与MVVM架构模式,哪种架构模式更好?
28 0
|
1月前
|
前端开发 Java 测试技术
android MVP契约类架构模式与MVVM架构模式,哪种架构模式更好?
android MVP契约类架构模式与MVVM架构模式,哪种架构模式更好?
21 2
|
1月前
|
ARouter Android开发
Android不同module布局文件重名被覆盖
Android不同module布局文件重名被覆盖
113 0
|
3月前
|
开发工具 git 索引
repo sync 更新源码 android-12.0.0_r34, fatal: 不能重置索引文件至版本 ‘v2.27^0‘。
本文描述了在更新AOSP 12源码时遇到的repo同步错误,并提供了通过手动git pull更新repo工具来解决这一问题的方法。
125 1
|
3月前
|
存储 监控 数据库
Android经典实战之OkDownload的文件分段下载及合成原理
本文介绍了 OkDownload,一个高效的 Android 下载引擎,支持多线程下载、断点续传等功能。文章详细描述了文件分段下载及合成原理,包括任务创建、断点续传、并行下载等步骤,并展示了如何通过多种机制保证下载的稳定性和完整性。
110 0
|
3月前
|
Java 测试技术 Android开发
Android项目架构设计问题之使用反射调用类的私有方法如何解决
Android项目架构设计问题之使用反射调用类的私有方法如何解决
29 0
下一篇
无影云桌面