前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【Android 逆向】类加载器 ClassLoader ( 使用 DexClassLoader 动态加载字节码文件 | 拷贝 DEX 文件到内置存储 | 加载并执行 DEX 字节码文件 )

【Android 逆向】类加载器 ClassLoader ( 使用 DexClassLoader 动态加载字节码文件 | 拷贝 DEX 文件到内置存储 | 加载并执行 DEX 字节码文件 )

作者头像
韩曙亮
发布2023-03-30 09:16:23
6580
发布2023-03-30 09:16:23
举报
文章被收录于专栏:韩曙亮的移动开发专栏

文章目录

一、拷贝 Assets 目录下的 classes.dex 字节码文件到内置存储区


【Android 逆向】类加载器 ClassLoader ( 使用 DexClassLoader 动态加载字节码文件 | 准备 DEX 字节码文件 ) 博客中 , 准备了 classes.dex 字节码文件 , 将字节码文件拷贝到了 将 app\src\main\assets\classes.dex 目录中 ;

解析字节码文件时 , 首先将该 DEX 字节码文件 从

代码语言:javascript
复制
app\src\main\assets\classes.dex

路径拷贝到

代码语言:javascript
复制
/data/user/0/com.example.classloader_demo/files/classes.dex

内置存储空间中 ;

下面的代码 , 是拷贝字节码文件的代码 ;

代码示例 :

代码语言:javascript
复制
    /**
     * 将 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", "文件拷贝完毕");
        }
        return dexPath;
    }

二、加载 DEX 文件并执行其中的方法


使用 DexClassLoader 加载字节码文件时 , 要准备几个参数

  • DEX 字节码文件路径 : 必须制定准确的 DEX 字节码文件目录 ;
代码语言:javascript
复制
		/data/user/0/com.example.classloader_demo/files/classes.dex
  • 优化目录 : 设置一个空的文件目录即可 ;
代码语言:javascript
复制
        // 优化目录
        File optFile = new File(getFilesDir(), "opt_dex");
  • 依赖库目录 : 可以为空 , 也可以设置一个文件目录 ;
代码语言:javascript
复制
        // 依赖库目录 , 用于存放 so 文件
        File libFile = new File(getFilesDir(), "lib_path");
  • 父节点类加载器 : 直接获取当前类的父类类加载器节点 ;
代码语言:javascript
复制
		context.getClassLoader()

从字节码文件中 , 加载的类时 Class 对象 , 通过反射调用其方法即可 ;

代码示例 :

代码语言:javascript
复制
    /**
     * 测试调用 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();
            }
        }
    }

三、MainActivity 及执行结果


完整代码示例 :

代码语言:javascript
复制
package com.example.classloader_demo;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Context;
import android.os.Bundle;
import android.util.Log;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
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 = copyFile();

        // 测试 DEX 文件中的方法
        testDex(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", "文件拷贝完毕");
        }
        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();
            }
        }
    }
}

打印的日志 :

代码语言:javascript
复制
2021-12-10 13:25:22.915 31065-31065/com.example.classloader_demo I/MainActivity: MainActivity ClassLoader : dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.example.classloader_demo-kqe1gP2jfD1FRwkny0hX1w==/base.apk"],nativeLibraryDirectories=[/data/app/com.example.classloader_demo-kqe1gP2jfD1FRwkny0hX1w==/lib/arm64, /system/lib64]]]
2021-12-10 13:25:22.915 31065-31065/com.example.classloader_demo I/MainActivity: MainActivity Parent ClassLoader : java.lang.BootClassLoader@6457c5
2021-12-10 13:25:22.916 31065-31065/com.example.classloader_demo I/MainActivity: 开始拷贝文件 dexPath : /data/user/0/com.example.classloader_demo/files/classes.dex
2021-12-10 13:25:22.980 31065-31065/com.example.classloader_demo I/HSL: 文件拷贝完毕
2021-12-10 13:25:23.951 31065-31065/com.example.classloader_demo I/lassloader_dem: The ClassLoaderContext is a special shared library.
2021-12-10 13:25:23.955 31065-31065/com.example.classloader_demo I/DexTest: DexTest : Hello World!!!

拷贝到 /data/user/0/com.example.classloader_demo/files/classes.dex 位置的文件 , 以及在 /data/user/0/com.example.classloader_demo/files/opt/ 目录生成的字节码优化相关目录 ;

四、博客资源

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

CSDN 下载地址 : https://download.csdn.net/download/han1202012/60180205

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021-12-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • 一、拷贝 Assets 目录下的 classes.dex 字节码文件到内置存储区
  • 二、加载 DEX 文件并执行其中的方法
  • 三、MainActivity 及执行结果
  • 四、博客资源
相关产品与服务
对象存储
对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档