Android插件化探索(二)hook式插件化详解

·  阅读 2288

1.前言

上一篇跟大家介绍了占位式插件化的实现方式,如果没有看过的小伙伴可以点击这个链接: Android插件化探索(一)占位式(插桩式)插件化详解

今天就跟大家来讲一下hook式插件化的实现方式。今天会用到比较多的反射,而且也会设计到比较多的源码。请小伙伴们耐心阅读完本篇文章。

上一篇说的占位式插件化有个缺点:就是要依赖于宿主的环境,比如context 上下文这些。本篇所讲的hook式插件化不会有这种问题。可以直接使用里面的环境,并不需要重写像 setContentViewfindViewById 这类依赖环境的方法。下面我们开始进入正题。

startActivity源码解析

我们上篇提到,调用 startActivity 启动一个没有在 Manifest.xml 文件中注册的 Activity 会报异常,那么我们今天从 startActivity 开始分析为什么会报异常。

startActivity 的流程大概是上面所示,最后执行到 AMS.startActivity 后如果没有在 Manifest.xml 中注册,软件就会发生崩溃, 那么怎么样才能避免这样呢。其实非常简单,我们只要把我们未注册的 Activity 替换成我们已经在 Manifest.xml 注册不就行了吗。那么问题又来了,我们应该如何实现替换操作呢?说到替换,我们不得不借助动态代理的方式来实现了。

从上图可以看出,创建一个动态代理对象需要传递三个参数,下面介绍一下这些参数

ClassLoader loader: 类加载器

Class<?>[] interfaces: 需要监听的接口

InvocationHandler h: 监听的回调

说到这里,我们又延伸到另一个问题,我们应该监听哪一个接口呢?,其实从上面我们已经分析过了,我们应该要监听 AMS.startActivity 这一步,所以我们要监听的接口就是 IActivityTaskManager(9.0及以下是IActivityManager) 这个接口。接下来我们就对这个接口实现动态代理。

2.动态代理的实现

第一步:

我们可以通过 Class.forName() 得到 IActivityTaskManager 或者 IActivityManager 这个类,如下面代码所示:

Class<?> mIActivityManagerClass;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
    mIActivityManagerClass = Class.forName("android.app.IActivityTaskManager");
} else {
    mIActivityManagerClass = Class.forName("android.app.IActivityManager");
}
复制代码

有了类之后,我们可以创建动态代理对象了,但是 IActivityTaskManager 或者 IActivityManager 接口的方法那么多,我们没必要全部监听,我们只需要监听我们关注的 "startActivity" 这个方法就好了,如下代码所示:

//创建动态代理
Object mActivityManagerProxy = Proxy.newProxyInstance(
    getClassLoader(),//类加载器
    new Class[]{mIActivityManagerClass},//要监听的回调接口
    new InvocationHandler() {//回调的监听
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        if ("startActivity".equals(method.getName())) {
            //做自己的业务逻辑
            //换成可以通过AMS检测的Activity
           
        }
        //为了程序能够正确执行,我们需要系统的IActivityManager实例
        return method.invoke(需要系统的IActivityManager实例, args);
    }
);
复制代码

从上述代码中可以看出,要使动态代理生效,我们还需要一个 IActivityManager,查阅源码 ActivityTaskManager(8.0~9.0以下看 ActivityManager,8.0以下看 ActivityManagerNative) 可发现:

ActivityTaskManager.java:(Androdid10.0)

/** @hide */
public static IActivityTaskManager getService() {
    return IActivityTaskManagerSingleton.get();
}
复制代码

ActivityManager.java:(Android 8.0 ~ Android9.0)

/**
* @hide
*/
public static IActivityManager getService() {
    return IActivityManagerSingleton.get();
}
复制代码

ActivityManagerNative.java:(Android 7.0及以下)

/**
* Retrieve the system's default/global activity manager.
*/
static public IActivityManager getDefault() {
    return gDefault.get();
}
复制代码

通过 getService() 或者 getDefault() 可以返回一个我们需要的对象实例,我们接下来可以反射来执行该方法来获取 IActivityTaskManager 或者 IActivityManager 实例对象。代码如下:

//获取 ActivityManager 或 ActivityManagerNative 或 ActivityTaskManager
Class<?> mActivityManagerClass;
Method getActivityManagerMethod;
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1) {
    mActivityManagerClass = Class.forName("android.app.ActivityManagerNative");
    getActivityManagerMethod = mActivityManagerClass.getDeclaredMethod("getDefault");
} else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1 && Build.VERSION.SDK_INT < Build.VERSION_CODES.Q){
    mActivityManagerClass = Class.forName("android.app.ActivityManager");
    getActivityManagerMethod = mActivityManagerClass.getDeclaredMethod("getService");
} else {
    mActivityManagerClass = Class.forName("android.app.ActivityTaskManager");
    getActivityManagerMethod = mActivityManagerClass.getDeclaredMethod("getService");
}
getActivityManagerMethod.setAccessible(true);
//这个实例本质是 IActivityManager或者IActivityTaskManager
final Object IActivityManager = getActivityManagerMethod.invoke(null);
复制代码

现在有了 IActivityTaskManager 或者 IActivityManager 实例对象我们就可以让程序继续能够执行下去了。

第二步: 既然我们自己创建了 IActivityTaskManager 或者 IActivityManager 的动态代理,我们就要把原来系统的 IActivityTaskManager 或者 IActivityManager 实例对象给替换掉。还是通过上面的 getService() 或者 getDefault() 方法入手,我们继续跟踪代码发现: ActivityManager 或 ActivityManagerNative 或 ActivityTaskManager 都有一个 Singleton 共同的属性,我们查看一下这个类的源码:

public abstract class Singleton<T> {
    private T mInstance;

    protected abstract T create();

    public final T get() {
        synchronized (this) {
            if (mInstance == null) {
                mInstance = create();
            }
            return mInstance;
        }
    }
}
复制代码

里面的 mInstance 属性正好是 IActivityTaskManager 或者 IActivityManager 实例,所以我们直接替换掉 mInstance 值就可以了。代码如下:

 //获取 IActivityTaskManagerSingleton 或者 IActivityManagerSingleton 或者 gDefault 属性
Field mSingletonField;
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1) {
    mSingletonField = mActivityManagerClass.getDeclaredField("gDefault");
} else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1 && Build.VERSION.SDK_INT < Build.VERSION_CODES.Q){
    mSingletonField = mActivityManagerClass.getDeclaredField("IActivityManagerSingleton");
} else {
    mSingletonField = mActivityManagerClass.getDeclaredField("IActivityTaskManagerSingleton");
}
mSingletonField.setAccessible(true);
Object mSingleton = mSingletonField.get(null);

//替换点
Class<?> mSingletonClass = Class.forName("android.util.Singleton");
Field mInstanceField = mSingletonClass.getDeclaredField("mInstance");
mInstanceField.setAccessible(true);
//将我们创建的动态代理设置到 mInstance 属性当中
mInstanceField.set(mSingleton, mActivityManagerProxy);
复制代码

到这里我们的动态代理算是实现好了,完整的代码如下:

private void hookAMSAction() throws Exception {
        //动态代理
        Class<?> mIActivityManagerClass;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            mIActivityManagerClass = Class.forName("android.app.IActivityTaskManager");
        } else {
            mIActivityManagerClass = Class.forName("android.app.IActivityManager");
        }
        //获取 ActivityManager 或 ActivityManagerNative 或 ActivityTaskManager
        Class<?> mActivityManagerClass;
        Method getActivityManagerMethod;
        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1) {
            mActivityManagerClass = Class.forName("android.app.ActivityManagerNative");
            getActivityManagerMethod = mActivityManagerClass.getDeclaredMethod("getDefault");
        } else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1 && Build.VERSION.SDK_INT < Build.VERSION_CODES.Q){
            mActivityManagerClass = Class.forName("android.app.ActivityManager");
            getActivityManagerMethod = mActivityManagerClass.getDeclaredMethod("getService");
        } else {
            mActivityManagerClass = Class.forName("android.app.ActivityTaskManager");
            getActivityManagerMethod = mActivityManagerClass.getDeclaredMethod("getService");
        }
        getActivityManagerMethod.setAccessible(true);
        //这个实例本质是 IActivityManager或者IActivityTaskManager
        final Object IActivityManager = getActivityManagerMethod.invoke(null);

        //创建动态代理
        Object mActivityManagerProxy = Proxy.newProxyInstance(
                getClassLoader(),
                new Class[]{mIActivityManagerClass},//要监听的回调接口
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                        if ("startActivity".equals(method.getName())) {
                            //做自己的业务逻辑
                            //换成可以通过AMS检测的Activity
                            Intent intent = new Intent(MyApplication.this, ProxyActivity.class);
                            intent.putExtra("actonIntent", (Intent)args[2]);
                            args[2] = intent;
                        }
                        //让程序继续能够执行下去
                        return method.invoke(IActivityManager, args);
                    }
                }
        );

        //获取 IActivityTaskManagerSingleton 或者 IActivityManagerSingleton 或者 gDefault 属性
        Field mSingletonField;
        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1) {
            mSingletonField = mActivityManagerClass.getDeclaredField("gDefault");
        } else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1 && Build.VERSION.SDK_INT < Build.VERSION_CODES.Q){
            mSingletonField = mActivityManagerClass.getDeclaredField("IActivityManagerSingleton");
        } else {
            mSingletonField = mActivityManagerClass.getDeclaredField("IActivityTaskManagerSingleton");
        }
        mSingletonField.setAccessible(true);
        Object mSingleton = mSingletonField.get(null);

        //替换点
        Class<?> mSingletonClass = Class.forName(Constans.SINGLETON);
        Field mInstanceField = mSingletonClass.getDeclaredField("mInstance");
        mInstanceField.setAccessible(true);
        //将我们创建的动态代理设置到 mInstance 属性当中
        mInstanceField.set(mSingleton, mActivityManagerProxy);
    }
复制代码

3.hook ActivityThread LaunchActivity

实现了动态代理,我们现在就要hook ActivityThread 了,为什么要 hook ActivityThread 这个类呢,因为我们的 Activity 启动的时候就是这个类给调用的,我们要把之前替换成代理 Activity 的目标 Activity 给替换回来,所以我们现在分析一下 ActivityThread 是如何启动一个 Activity 的。

ActivityThread.class:(Android 9.0 ~ 10.0)

ActivityThread.class:(Android 8.0及以下)

我们发现,Android 9.0 ~ 10.0 的代码比较绕,绕了一大圈最后执行了 handleLaunchActivity() 方法来启动一个 Activity,不管怎样,我们都找到了 hook点,那就是在 handleLaunchActivity() 执行之前把我们的目标 Activity 替换回来就可以了。在 handleLaunchActivity() 执行之前也就需要在 Handler 回调 handleMessage() 之前把它替换回来,那么 handleMessage() 是如何回调的呢,我们看一下 Handler 的源码发现,它是通过调用 **dispatchMessage() ** 来回调的,从代码里看到,有一个属性 "mCallback",如果我们设置了这个属性的值是不是就走我们自己的逻辑了?答案是yes。所以接下来我们就要找到 ActivityThreadHandler 对象。

查看 ActivityThread 源码发现,“mH” 属性就是我们需要的对象,所以我们先要得到这个对象,再把我们自己的 CallBack 设置到这里就可以执行我们的流程了。

得到 “mH” 对象之前我们先要获取到 ActivityThread 类和实例对象,通过 currentActivityThread() 可以获取到

所以我们的代码如下:

private void hookLaunchActivity() throws Exception{
        //获取 ActivityThread 类
        Class<?> mActivityThreadClass = Class.forName("android.app.ActivityThread");

        //获取 ActivityThread 的 currentActivityThread() 方法
        Method currentActivityThread = mActivityThreadClass.getDeclaredMethod("currentActivityThread");
        currentActivityThread.setAccessible(true);
        //获取 ActivityThread 实例
        Object mActivityThread = currentActivityThread.invoke(null);

        //获取 ActivityThread 的 mH 属性
        Field mHField = mActivityThreadClass.getDeclaredField("mH");
        mHField.setAccessible(true);
        Handler mH = (Handler) mHField.get(mActivityThread);

        //获取 Handler 的 mCallback 属性
        Field mCallbackField = Handler.class.getDeclaredField("mCallback");
        mCallbackField.setAccessible(true);
        //设置我们自定义的 CallBack
        mCallbackField.set(mH, new MyCallBack());
}

class MyCallBack implements Handler.Callback{
        @Override
        public boolean handleMessage(@NonNull Message msg) {
            return false;
        }
    }
复制代码

执行了上面的代码,启动的时候就会先进入我们自定义的 handleMessage(),我们只需要在这方法里面把我们的目标的 Activity给替换回来就可以了。我们再次分析 handleLaunchActivity() 前的代码:

LaunchActivityItem.class:

ActivityThread.class:

我们可以发现,在调用 handleLaunchActivity() 之前都有一个 ActivityClientRecord 的属性,我们继续跟这个类:

发现了这个就是我们 startActivity(intent) 里面的 intent 对象,我们现在只需要替换掉这个对象就可以了。

注意: Android 9.0及以上版本我们可以只替换 LaunchActivityItem 里面的 mIntent 属性就可以了。所以代码实现如下:

class MyCallBack implements Handler.Callback{

        @Override
        public boolean handleMessage(@NonNull Message msg) {
            //159 对应的是 EXECUTE_TRANSACTION
            if (msg.what == 159) {
                try {
                    Field mActivityCallbacksField = msg.obj.getClass().getDeclaredField("mActivityCallbacks");
                    mActivityCallbacksField.setAccessible(true);
                    List<Object> mActivityCallbacks = (List<Object>) mActivityCallbacksField.get(msg.obj);
                    if (mActivityCallbacks != null && mActivityCallbacks.size() > 0) {
                        Object mClientTransactionItem = mActivityCallbacks.get(0);
                        Class<?> mLaunchActivityItemClass = Class.forName("android.app.servertransaction.LaunchActivityItem");
                        if (mLaunchActivityItemClass.isInstance(mClientTransactionItem)) {
                            //获取 LaunchActivityItem 的 mIntent 属性
                            Field mIntentField = mClientTransactionItem.getClass().getDeclaredField("mIntent");
                            mIntentField.setAccessible(true);
                            Intent intent = (Intent) mIntentField.get(mClientTransactionItem);
                            //取出我们传递的值
                            Intent actonIntent = intent.getParcelableExtra("actonIntent");
                            if (actonIntent != null) {
                                //替换掉原来的intent属性的值
                                mIntentField.set(mClientTransactionItem, actonIntent);
                            }
                        }
                    }

                } catch (Exception e) {
                    e.printStackTrace();
                }
            //100 对应的是 LAUNCH_ACTIVITY
            } else if (msg.what == 100) {
                try {
                    //获取 ActivityClientRecord 的 intent 属性
                    Field intentField = msg.obj.getClass().getDeclaredField("intent");
                    intentField.setAccessible(true);
                    Intent intent = (Intent) intentField.get(msg.obj);
                    //取出我们传递的值
                    Intent actonIntent = intent.getParcelableExtra("actonIntent");
                    if (actonIntent != null) {
                        //替换掉原来的intent属性的值
                        intentField.set(msg.obj, actonIntent);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            return false;
        }
    }
复制代码

到此我们就把我们的目标的 Activity 给替换回来了,这时候我们可以启动一个未在 Manifest.xml 注册的 Activity了。

4.加载插件apk的class文件

完成了动态代理及hook住了launchActivity 后我们就要加载插件中的Activity了,那么我们的Activity的类文件是如何被加载到内存当中的呢,我们还是从 startActivity() 开始看起吧。

根据源码一步步分析,我们可以得到 加载一个 Activity 需要非常复杂的步骤,由于步骤太多,我们直接进入主题,从 BaseDexClassLoader.findClass() 说起:

看上图,pathList.findClass() 返回了一个类,我们继续跟 PathList 这个类:

我们发现,PathList.findClass() 遍历了 dexElements 数组来查找类, 那这个 Element 是什么类型呢?其实这个类型就是我们apk里面的 dex 文件,dexElements就是我们 dex 文件数组。那么下面有一个问题需要我们考虑,我们这个 dexElements 数组只有宿主的 dex文件,如果加载的插件的类的话必定是找不到的。那么我们该如何加载插件中的类呢?其实原理很简单,如果宿主有 dexElements这个数组,那么我们的插件apk 也应该有这个数组,我们只需要把插件的 dexElements数组融合到宿主里面的,那么我们的宿主APP就可以加载到插件的类文件了。

4.1 融合dexElements数组

要融合 dexElements数组,我们分五个步骤来进行。

第一步:找到宿主的dexElements 对象

我们首先要拿到 BaseDexClassLoader 中的 pathList 属性,然后再拿 PathList 里面的 dexElements 属性

//第一步:找到宿主的 dexElements 对象
//获取 BaseDexClassLoader 类
Class<?> mainBaseDexClassLoaderClass = Class.forName("dalvik.system.BaseDexClassLoader");
//获取 pathList 属性
Field mainPathListField = mainBaseDexClassLoaderClass.getDeclaredField("pathList");
mainPathListField.setAccessible(true);
//获取 PathClassLoader
PathClassLoader mainClassLoader = (PathClassLoader) context.getClassLoader();
//获取 pathList 属性值
Object mainPathList = mainPathListField.get(mainClassLoader);
//获取 dexElements
Field mainDexElementsField = mainPathList.getClass().getDeclaredField("dexElements");
mainDexElementsField.setAccessible(true);
Object mainDexElements = mainDexElementsField.get(mainPathList);
复制代码

第二步:找到插件的dexElements 对象

第二步跟第一步差不多的,这是用的 ClassLoader不一样,插件用的是 DexClassLoader,而宿主的是用 PathClassLoader

//获取 BaseDexClassLoader 类
Class<?> pluginBaseDexClassLoaderClass = Class.forName("dalvik.system.BaseDexClassLoader");
//获取 pathList 属性
Field pluginPathListField = pluginBaseDexClassLoaderClass.getDeclaredField("pathList");
pluginPathListField.setAccessible(true);
//获取 DexClassLoader
File file = new File(path);
if (!file.exists()) {
    Log.e(TAG, "插件不存在");
    return;
}
File pluginDir = context.getDir("plugin", Context.MODE_PRIVATE);
//加载插件的class
dexClassLoader = new DexClassLoader(path, pluginDir.getAbsolutePath(), null, context.getClassLoader());
//获取 pathList 属性值
Object pluginPathList = pluginPathListField.get(dexClassLoader);
//获取 dexElements
Field pluginDexElementsField = pluginPathList.getClass().getDeclaredField("dexElements");
pluginDexElementsField.setAccessible(true);
Object pluginDexElements = pluginDexElementsField.get(pluginPathList);
复制代码

第三步:创建出新的 dexElements 对象

//获取宿主 dexElements 跟插件 dexElements 的长度
int mainLength = Array.getLength(mainDexElements);
int pluginLength = Array.getLength(pluginDexElements);
int newLength = mainLength + pluginLength;
//创建新的数组
Object newDexElements = Array.newInstance(mainDexElements.getClass().getComponentType(), newLength);
复制代码

第四步:新的 dexElements = 宿主的dexElements + 插件的dexElements

 for (int i = 0; i < newLength; i++) {
    if (i < mainLength) {
        Array.set(newDexElements, i, Array.get(mainDexElements, i));
    } else {
        Array.set(newDexElements, i, Array.get(pluginDexElements, i - mainLength));
    }
}
复制代码

第五步:新的 dexElements设置到宿主当中去

mainDexElementsField.set(mainPathList, newDexElements);
复制代码

通过上面五个步骤,我们就把宿主和插件的 dexElements 数组给融合好了,这时候我们宿主APP就可以加载插件apk的class文件了。

4.2加载插件的资源

融合好 dexElements数组后,我们开始加载插件的资源了,加载资源比较简单,上一篇文章中也有提到,这里就直接贴出代码:

public void loadPluginResource(final Handler handler, final String path) {
        try {
            //加载插件的资源文件
            //1、获取插件的AssetManager
            assetManager = AssetManager.class.newInstance();
            Method addAssetPath = AssetManager.class.getMethod("addAssetPath", String.class);
            addAssetPath.setAccessible(true);
            addAssetPath.invoke(assetManager, path);
            //2、获取宿主的Resources
            Resources appResources = context.getResources();
            //实例化插件的Resources
            pluginResource = new Resources(assetManager, appResources.getDisplayMetrics(), appResources.getConfiguration());
            if (dexClassLoader != null && pluginResource != null) {
                handler.sendEmptyMessage(666);
            } else {
                handler.sendEmptyMessage(0);
            }
        } catch (Exception e) {
            e.printStackTrace();
            handler.sendEmptyMessage(0);
        }
}
复制代码

到这里我们的hook式插件化框架已经写完了,我们看一下运行效果:

这里贴上部分代码给大家参考:项目地址再最底部。

HookManager.class

public class HookManager {
    private Context context;
    private static HookManager instance;

    private HookManager(Context context) {
        this.context = context;
    }

    public static HookManager getInstance(Context context) {
        if (instance == null) {
            synchronized (PluginManager.class) {
                if (instance == null) {
                    instance = new HookManager(context);
                }
            }
        }
        return instance;
    }


    public void hookAMSAction() throws Exception {
        //动态代理
        Class<?> mIActivityManagerClass;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            mIActivityManagerClass = Class.forName("android.app.IActivityTaskManager");
        } else {
            mIActivityManagerClass = Class.forName("android.app.IActivityManager");
        }
        //获取 ActivityManager 或 ActivityManagerNative 或 ActivityTaskManager
        Class<?> mActivityManagerClass;
        Method getActivityManagerMethod;
        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1) {
            mActivityManagerClass = Class.forName("android.app.ActivityManagerNative");
            getActivityManagerMethod = mActivityManagerClass.getDeclaredMethod("getDefault");
        } else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1 && Build.VERSION.SDK_INT < Build.VERSION_CODES.Q){
            mActivityManagerClass = Class.forName("android.app.ActivityManager");
            getActivityManagerMethod = mActivityManagerClass.getDeclaredMethod("getService");
        } else {
            mActivityManagerClass = Class.forName("android.app.ActivityTaskManager");
            getActivityManagerMethod = mActivityManagerClass.getDeclaredMethod("getService");
        }
        getActivityManagerMethod.setAccessible(true);
        //这个实例本质是 IActivityManager或者IActivityTaskManager
        final Object IActivityManager = getActivityManagerMethod.invoke(null);

        //创建动态代理
        Object mActivityManagerProxy = Proxy.newProxyInstance(
                context.getClassLoader(),
                new Class[]{mIActivityManagerClass},//要监听的回调接口
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                        if ("startActivity".equals(method.getName())) {
                            //做自己的业务逻辑
                            //换成可以通过AMS检测的Activity
                            Intent intent = new Intent(context, ProxyActivity.class);
                            intent.putExtra("actonIntent", (Intent)args[2]);
                            args[2] = intent;
                        }
                        //让程序继续能够执行下去
                        return method.invoke(IActivityManager, args);
                    }
                }
        );

        //获取 IActivityTaskManagerSingleton 或者 IActivityManagerSingleton 或者 gDefault 属性
        Field mSingletonField;
        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1) {
            mSingletonField = mActivityManagerClass.getDeclaredField("gDefault");
        } else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1 && Build.VERSION.SDK_INT < Build.VERSION_CODES.Q){
            mSingletonField = mActivityManagerClass.getDeclaredField("IActivityManagerSingleton");
        } else {
            mSingletonField = mActivityManagerClass.getDeclaredField("IActivityTaskManagerSingleton");
        }
        mSingletonField.setAccessible(true);
        Object mSingleton = mSingletonField.get(null);

        //替换点
        Class<?> mSingletonClass = Class.forName(Constans.SINGLETON);
        Field mInstanceField = mSingletonClass.getDeclaredField("mInstance");
        mInstanceField.setAccessible(true);
        //将我们创建的动态代理设置到 mInstance 属性当中
        mInstanceField.set(mSingleton, mActivityManagerProxy);
    }

    public void hookLaunchActivity() throws Exception{
        //获取 ActivityThread 类
        Class<?> mActivityThreadClass = Class.forName("android.app.ActivityThread");

        //获取 ActivityThread 的 currentActivityThread() 方法
        Method currentActivityThread = mActivityThreadClass.getDeclaredMethod("currentActivityThread");
        currentActivityThread.setAccessible(true);
        //获取 ActivityThread 实例
        Object mActivityThread = currentActivityThread.invoke(null);

        //获取 ActivityThread 的 mH 属性
        Field mHField = mActivityThreadClass.getDeclaredField("mH");
        mHField.setAccessible(true);
        Handler mH = (Handler) mHField.get(mActivityThread);

        //获取 Handler 的 mCallback 属性
        Field mCallbackField = Handler.class.getDeclaredField("mCallback");
        mCallbackField.setAccessible(true);
        //设置我们自定义的 CallBack
        mCallbackField.set(mH, new MyCallBack());
    }

    class MyCallBack implements Handler.Callback{

        @Override
        public boolean handleMessage(@NonNull Message msg) {
            if (msg.what == Constans.EXECUTE_TRANSACTION) {
                try {
                    Field mActivityCallbacksField = msg.obj.getClass().getDeclaredField("mActivityCallbacks");
                    mActivityCallbacksField.setAccessible(true);
                    List<Object> mActivityCallbacks = (List<Object>) mActivityCallbacksField.get(msg.obj);
                    if (mActivityCallbacks != null && mActivityCallbacks.size() > 0) {
                        Object mClientTransactionItem = mActivityCallbacks.get(0);
                        Class<?> mLaunchActivityItemClass = Class.forName("android.app.servertransaction.LaunchActivityItem");
                        if (mLaunchActivityItemClass.isInstance(mClientTransactionItem)) {
                            //获取 LaunchActivityItem 的 mIntent 属性
                            Field mIntentField = mClientTransactionItem.getClass().getDeclaredField("mIntent");
                            mIntentField.setAccessible(true);
                            Intent intent = (Intent) mIntentField.get(mClientTransactionItem);
                            //取出我们传递的值
                            Intent actonIntent = intent.getParcelableExtra("actonIntent");
                            if (actonIntent != null) {
                                //替换掉原来的intent属性的值
                                mIntentField.set(mClientTransactionItem, actonIntent);
                            }
                        }
                    }

                } catch (Exception e) {
                    e.printStackTrace();
                }

            } else if (msg.what == Constans.LAUNCH_ACTIVITY) {
                /*
                    7.0以下代码
                     case LAUNCH_ACTIVITY: {
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
                    final ActivityClientRecord r = (ActivityClientRecord) msg.obj;

                    r.packageInfo = getPackageInfoNoCheck(
                            r.activityInfo.applicationInfo, r.compatInfo);
                    handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                } break;

                 */
                try {
                    //获取 ActivityClientRecord 的 intent 属性
                    Field intentField = msg.obj.getClass().getDeclaredField("intent");
                    intentField.setAccessible(true);
                    Intent intent = (Intent) intentField.get(msg.obj);
                    //取出我们传递的值
                    Intent actonIntent = intent.getParcelableExtra("actonIntent");
                    if (actonIntent != null) {
                        //替换掉原来的intent属性的值
                        intentField.set(msg.obj, actonIntent);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            return false;
        }
    }
}
复制代码

PluginManager.class

public class PluginManager {
    private static final String TAG = PluginManager.class.getSimpleName();
    private static PluginManager instance;
    private Context context;
    private DexClassLoader dexClassLoader;
    private AssetManager assetManager;
    private Resources pluginResource;

    private PluginManager(Context context) {
        this.context = context;
    }

    public static PluginManager getInstance(Context context) {
        if (instance == null) {
            synchronized (PluginManager.class) {
                if (instance == null) {
                    instance = new PluginManager(context);
                }
            }
        }
        return instance;
    }

    public void pluginToApp(final Handler handler, final String path) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //第一步:找到宿主的 dexElements 对象
                    //获取 BaseDexClassLoader 类
                    Class<?> mainBaseDexClassLoaderClass = Class.forName("dalvik.system.BaseDexClassLoader");
                    //获取 pathList 属性
                    Field mainPathListField = mainBaseDexClassLoaderClass.getDeclaredField("pathList");
                    mainPathListField.setAccessible(true);
                    //获取 PathClassLoader
                    PathClassLoader mainClassLoader = (PathClassLoader) context.getClassLoader();
                    //获取 pathList 属性值
                    Object mainPathList = mainPathListField.get(mainClassLoader);
                    //获取 dexElements
                    Field mainDexElementsField = mainPathList.getClass().getDeclaredField("dexElements");
                    mainDexElementsField.setAccessible(true);
                    Object mainDexElements = mainDexElementsField.get(mainPathList);


                    //第二步:找到插件的dexElements 对象
                    //获取 BaseDexClassLoader 类
                    Class<?> pluginBaseDexClassLoaderClass = Class.forName("dalvik.system.BaseDexClassLoader");
                    //获取 pathList 属性
                    Field pluginPathListField = pluginBaseDexClassLoaderClass.getDeclaredField("pathList");
                    pluginPathListField.setAccessible(true);
                    //获取 DexClassLoader
                    File file = new File(path);
                    if (!file.exists()) {
                        Log.e(TAG, "插件不存在");
                        return;
                    }
                    File pluginDir = context.getDir("plugin", Context.MODE_PRIVATE);
                    //加载插件的class
                    dexClassLoader = new DexClassLoader(path, pluginDir.getAbsolutePath(), null, context.getClassLoader());
                    //获取 pathList 属性值
                    Object pluginPathList = pluginPathListField.get(dexClassLoader);
                    //获取 dexElements
                    Field pluginDexElementsField = pluginPathList.getClass().getDeclaredField("dexElements");
                    pluginDexElementsField.setAccessible(true);
                    Object pluginDexElements = pluginDexElementsField.get(pluginPathList);

                    //第三步:创建出新的 dexElements 对象
                    //获取宿主 dexElements 跟插件 dexElements 的长度
                    int mainLength = Array.getLength(mainDexElements);
                    int pluginLength = Array.getLength(pluginDexElements);
                    int newLength = mainLength + pluginLength;
                    //创建新的数组
                    Object newDexElements = Array.newInstance(mainDexElements.getClass().getComponentType(), newLength);

                    //第四步:新的 dexElements = 宿主的dexElements + 插件的dexElements
                    for (int i = 0; i < newLength; i++) {
                        if (i < mainLength) {
                            Array.set(newDexElements, i, Array.get(mainDexElements, i));
                        } else {
                            Array.set(newDexElements, i, Array.get(pluginDexElements, i - mainLength));
                        }
                    }

                    //第五步:新的 dexElements设置到宿主当中去
                    mainDexElementsField.set(mainPathList, newDexElements);

                    //加载插件的资源
                    loadPluginResource(handler, path);


                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    public void loadPluginResource(final Handler handler, final String path) {
        try {
            //加载插件的资源文件
            //1、获取插件的AssetManager
            assetManager = AssetManager.class.newInstance();
            Method addAssetPath = AssetManager.class.getMethod("addAssetPath", String.class);
            addAssetPath.setAccessible(true);
            addAssetPath.invoke(assetManager, path);
            //2、获取宿主的Resources
            Resources appResources = context.getResources();
            //实例化插件的Resources
            pluginResource = new Resources(assetManager, appResources.getDisplayMetrics(), appResources.getConfiguration());
            if (dexClassLoader != null && pluginResource != null) {
                handler.sendEmptyMessage(666);
            } else {
                handler.sendEmptyMessage(0);
            }
        } catch (Exception e) {
            e.printStackTrace();
            handler.sendEmptyMessage(0);
        }
    }

    public Resources getResource() {
        return pluginResource;
    }

    public DexClassLoader getClassLoader() {
        return dexClassLoader;
    }

    public AssetManager getAssetManager() {
        return assetManager;
    }
}

复制代码

MyApplication.class

public class MyApplication extends Application {

    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        Reflection.unseal(base);
    }

    @Override
    public void onCreate() {
        super.onCreate();

        try {
            //先hook AMS检查
            HookManager.getInstance(this).hookAMSAction();
            //hook ActivityThread
            HookManager.getInstance(this).hookLaunchActivity();
        } catch (Exception e) {

        }
    }



    @Override
    public Resources getResources() {
        return PluginManager.getInstance(this).getResource() == null ? super.getResources() : PluginManager.getInstance(this).getResource();
    }

    @Override
    public AssetManager getAssets() {
        return PluginManager.getInstance(this).getAssetManager() == null ? super.getAssets() : PluginManager.getInstance(this).getAssetManager();
    }
}
复制代码

MainActivity.class

public class MainActivity extends AppCompatActivity {
    private ProgressDialog dialog;
    //是否加载完成
    private boolean isLoadSuccess = false;

    private String path = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "plugin2.apk";
    @SuppressLint("HandlerLeak")
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            if (msg.what == 666) {
                isLoadSuccess = true;
                if (dialog != null && dialog.isShowing()) {
                    dialog.dismiss();
                }
                Toast.makeText(MainActivity.this, "加载插件成功!", Toast.LENGTH_SHORT).show();
            } else if (msg.what == 0) {
                if (dialog != null && dialog.isShowing()) {
                    dialog.dismiss();
                }
                Toast.makeText(MainActivity.this, "加载插件失败,请检查插件是否存在!", Toast.LENGTH_SHORT).show();
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @RequiresApi(api = Build.VERSION_CODES.M)
    public void loadPlugin(View view) {
        //判断是否已经赋予权限
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (ContextCompat.checkSelfPermission(this,
                    Manifest.permission.WRITE_EXTERNAL_STORAGE)
                    != PackageManager.PERMISSION_GRANTED) {
                requestPermissions(new String[]{ Manifest.permission.WRITE_EXTERNAL_STORAGE}, 666);
            } else {
                showProgress();
                PluginManager.getInstance(this).pluginToApp(handler, path);
            }
        } else {
            showProgress();
            PluginManager.getInstance(this).pluginToApp(handler, path);
        }

    }

    public void jumpPlugin(View view) {
        if (!isLoadSuccess) {
            Toast.makeText(this, "请先加载插件", Toast.LENGTH_SHORT).show();
        } else {
            Intent intent = new Intent();
            intent.setComponent(new ComponentName("com.yuong.plugin", "com.yuong.plugin.PluginActivity"));
            startActivity(intent);
        }
    }

    private void showProgress() {
        if (dialog == null) {
            dialog = new ProgressDialog(this);
        }
        dialog.setTitle("加载插件中,请稍后。。。");
        dialog.setCancelable(false);
        dialog.show();
    }

}

复制代码

插件的BaseActivity.class

public abstract class BaseActivity extends Activity {

    @Override
    public Resources getResources() {
        if (getApplication() != null && getApplication().getResources() != null) {
            //如果不为空,就说明已经被添加到了宿主当中
            return getApplication().getResources();
        }
        return super.getResources();
    }

    @Override
    public AssetManager getAssets() {
        if (getApplication() != null && getApplication().getAssets() !=  null) {
            //如果不为空,就说明已经被添加到了宿主当中
            return getApplication().getAssets();
        }
        return super.getAssets();
    }

    @Override
    public Resources.Theme getTheme() {
        if (getApplication() != null && getApplication().getTheme() !=  null) {
            //如果不为空,就说明已经被添加到了宿主当中
            return getApplication().getTheme();
        }
        return super.getTheme();
    }
}
复制代码

项目地址:hook式插件化

分类:
Android
标签:
分类:
Android
标签: