唯一插件化Replugin源码及原理深度剖析--唯一Hook点原理

本文深入剖析Replugin如何通过Hook系统ClassLoader实现类加载拦截,详细解释了Hook原理和流程,揭示其破坏双亲委派模型的思想,以确保插件与宿主、系统类的精确加载。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

*本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布

上一篇唯一插件化Replugin源码及原理深度剖析–初始化之框架核心,我们说了Replugin的整体框架的初始化,但是因为篇幅的缘故还有Hook系统的ClassLoader和插件的加载没有说,那么我们这一篇就来详解的来分析一下Hook这块,本章我们讲从Hook系统ClassLoader的思想和原理进行剖析,如果没有看过上一篇建议先看上一篇

提示:请不要忽略代码注释,由于通畅上下逻辑思维,不太重要的部分跳转代码不会全部进去一行行的看,但是会将注释写出来,所以请务必不要忽略注释,而且最好是跟着文章一起看源码。

概要:
一、关于ClassLoader的知识回顾和Replugin中ClassLoader

二、Hook系统ClassLoader的原理分析

三、Hook系统ClassLoader的思想及总结

一、关于ClassLoader的知识回顾和Replugin中ClassLoader

ClassLoader是什么?

ClassLoader是类加载器,它是用来形容将一个类的二进制流加载到虚拟机中的过程,一个类的唯一性要由它的类加载器和它本身来确定,也就是说一个Class文件如果使用不同的类加载器来加载,那么加载出来的类也是不相等的,而在Java中为了保证一个类的唯一性使用了双亲委派模型,也就是说如果要加载一个类首先会委托给自己的父加载器去完成,父加载器会再向上委托,直到最顶层的类加载器,如果父加载器没有找个要加载的类,子类才会尝试自己去加载,这样就保证了加载的类都是一个类,例如Object都是一个类。

Android中的ClassLoader:

1、BootClassLoader:

它是Android中最顶层的ClassLoader,创建一个ClassLoader需要传入一个parent,而android中所有的ClassLoader的最终parent都是BootClassLoader,它也继承自ClassLoader,但是继承的这个ClassLoader也不同于Java本身的ClassLoader,是android经过修改后的ClassLoader,它是ClassLoader的内部类,可以通过ClassLoader.getSystemClassLoader().getParent()得到。

    //BootClassLoader
class BootClassLoader extends ClassLoader {

    private static BootClassLoader instance;

    @FindBugsSuppressWarnings("DP_CREATE_CLASSLOADER_INSIDE_DO_PRIVILEGED")
    public static synchronized BootClassLoader getInstance() {
        if (instance == null) {
            instance = new BootClassLoader();
        }

        return instance;
    }

    public BootClassLoader() {
        super(null, true);
    }

    。。。。
}   

2、PathClassLoader:

继承自BaseDexClassLoader ,它是我们apk的默认加载器,它是用来加载系统类和主dex文件中的类的,但是系统类是由BootClassLoader加载的,如果apk中有多个dex文件,只会加载主dex

//PathClassLoader
public class PathClassLoader extends BaseDexClassLoader {

    public PathClassLoader(String dexPath, ClassLoader parent) {
        super(dexPath, null, null, parent);
    }


    public PathClassLoader(String dexPath, String libraryPath,
            ClassLoader parent) {
        super(dexPath, null, libraryPath, parent);
    }
}

3、DexClassLoader:
继承自BaseDexClassLoader ,可以用来加载外置的dex文件或者apk等

//DexClassLoader
public class DexClassLoader extends BaseDexClassLoader {

    public DexClassLoader(String dexPath, String optimizedDirectory,
            String libraryPath, ClassLoader parent) {
        super(dexPath, new File(optimizedDirectory), libraryPath, parent);
    }
}

Android中主要使用的ClassLoader有PathClassLoader和DexClassLoader,它们都继承自BaseDexClassLoader,BaseDexClassLoader中维护了一个DexPathList,PathClassLoader和DexClassLoader查找类的操作直接调用BaseClassLoader的findClass方法,而BaseClassLoader的findClass中又通过内部维护的DexPathList来查找,DexPathList中又维护这一个Element数组,这个数组中Element元素其实就是Dex文件。

PathClassLoader和DexClassLoader最大的区别就是DexClassLoader可以加载外置dex文件,这是因为PathClassLoader构造方法中像上传递时第二个参数传了null,这个参数代表的是dex优化后的路径,DexPathList在生成Element数组时会判断这个参数是否为null,如果为null就使用系统默认路径/data/dalvik-cache,这也是导致如果要加载外置dex文件只能使用DexClassLoader的原因。

PathClassLoader只会加载apk中的主dex文件,其他的dex文件是使用DexClassloader动态加载进来,然后通过反射获取到PathClassLoader中的DexPathList,然后再拿到DexPathList中的Element数组,最后将后加载进来的dex和反射拿到的数组进行合并后并重新设置回去,这也是Google的MultiDex的做法,在我之前写过的插件化的实现的博客中也采用了这种方式

Replugin中的ClassLoader:
在Replugin中有两个ClassLoader,一个用来代替宿主工作的RePluginClassLoader,一个用来加载插件apk类的PluginDexClassLoader,下面我们分别来看一下这两个类是怎么实现的

RePluginClassLoader:用来代替宿主工作的ClassLoader

源码位置:com.qihoo360.replugin.RePluginClassLoader

public class RePluginClassLoader extends PathClassLoader{

    。。。。

    public RePluginClassLoader(ClassLoader parent, ClassLoader orig) {

        // 由于PathClassLoader在初始化时会做一些Dir的处理,所以这里必须要传一些内容进来
        // 但我们最终不用它,而是拷贝所有的Fields
        super("", "", parent);
        mOrig = orig;

        // 将原来宿主里的关键字段,拷贝到这个对象上,这样骗系统以为用的还是以前的东西(尤其是DexPathList)
        // 注意,这里用的是“浅拷贝”
        // Added by Jiongxuan Zhang
        copyFromOriginal(orig);
        //反射获取原ClassLoader中的重要方法用来重写这些方法
        initMethods(orig);
    }

    //反射获取原ClassLoader中的方法
     private void initMethods(ClassLoader cl) {
        Class<?> c = cl.getClass();
        findResourceMethod = ReflectUtils.getMethod(c, "findResource", String.class);
        findResourceMethod.setAccessible(true);
        findResourcesMethod = ReflectUtils.getMethod(c, "findResources", String.class);
        findResourcesMethod.setAccessible(true);
        findLibraryMethod = ReflectUtils.getMethod(c, "findLibrary", String.class);
        findLibraryMethod.setAccessible(true);
        getPackageMethod = ReflectUtils.getMethod(c, "getPackage", String.class);
        getPackageMethod.setAccessible(true);
    }

     //拷贝原ClassLoader中的字段到本对象中
     private void copyFromOriginal(ClassLoader orig) {
        if (LOG && IPC.isPersistentProcess()) {
            LogDebug.d(TAG, "copyFromOriginal: Fields=" + StringUtils.toStringWithLines(ReflectUtils.getAllFieldsList(orig.getClass())));
        }

        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1) {
            // Android 2.2 - 2.3.7,有一堆字段,需要逐一复制
            // 以下方法在较慢的手机上用时:8ms左右
            copyFieldValue("libPath", orig);
            copyFieldValue("
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值