Android hook式插件化详解

news/2024/5/19 4:41:11 标签: android, 插件化, 反射, Hook

引言

Android插件化是一种将应用程序的功能模块化为独立的插件,并动态加载到主应用程序中的技术。通过插件化,开发者可以将应用程序的功能分解成独立的模块,每个模块可以作为一个插件单独开发、测试和维护,然后通过动态加载的方式集成到主应用程序中,实现功能的动态扩展和更新。

Android插件化通常涉及到动态加载、组件化、插件生命周期管理、插件间通信等技术,开发者需要使用相关的框架和工具来实现插件化功能。插件化可以帮助开发者更好地管理应用程序的复杂性,提高开发效率,同时也能够实现应用程序的功能动态更新和扩展,为用户提供更好的体验。

1.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 = mActivityMa

http://www.niftyadmin.cn/n/5255297.html

相关文章

物联网平台:网络调试助手+HTTP+上传数据到onenet

目录 onenet设备初始化 获取设备id和密钥key 尝试上传数据&#xff0c;实例 onenet设备初始化 获取设备id和密钥key 进入官网 中移坤灵 - 中国移动物联网开放平台 (10086.cn)https://open.iot.10086.cn/ 创建登录账号后&#xff0c;点击左上角的开发者中心 点击左上角的全…

软考06总结(干货)

文章目录 前言一、刷题二、思路分析总结 前言 2023下半年开始准备的软件设计师考试&#xff0c;有幸赶上了改革升级&#xff0c;考试由原来的纸质改为了机考&#xff0c;有很多东西我看过了&#xff0c;但是没有总结下来&#xff0c;确实没有足够的时间&#xff0c;因为做题的…

windows MYSQL解决中文乱码问题

1.首先确保你已经把mysql配置了环境变量 2.打开window终端 3.输入mysql -u root -p 4.输入密码&#xff0c;就是安装的时候设置的root超级管理员权限密码 5.输入&#xff1a; SHOW VARIABLES LIKE ‘character%’; 出现上图&#xff0c;说明就会出现中文乱码问题。 6.该怎么办…

成功解决git clone遇到的error: RPC failed; curl 16 Error in the HTTP2 framing layer fatal: expected flush af

成功解决git clone遇到的error: RPC failed; curl 16 Error in the HTTP2 framing layer fatal: expected flush af 问题描述解决方案 问题描述 用git的时候可能会遇到这个问题&#xff1a; (base) zhouzikang7443-8x4090-120:~/project$ git clone https://github.com/123/12…

软件测试之缺陷管理

一、软件缺陷的基本概念 1、软件缺陷的基本概念主要分为&#xff1a;缺陷、故障、失效这三种。 &#xff08;1&#xff09;缺陷&#xff08;defect&#xff09;&#xff1a;存在于软件之中的偏差&#xff0c;可被激活&#xff0c;以静态的形式存在于软件内部&#xff0c;相当…

开关量防抖滤波器(梯形图和SCL源代码)

模拟量防抖超限报警功能块请查看下面文章链接: https://rxxw-control.blog.csdn.net/article/details/133969425https://rxxw-control.blog.csdn.net/article/details/133969425 1、开关量防抖滤波器 2、防抖滤波 3、梯形图代码

低代码开发平台通常提供哪些功能?发挥什么作用?

目录 一、低代码助力开发 二、低代码核心功能 1.业务建模&#xff1a; 2.表单建模&#xff1a; 3.页面建模&#xff1a; 4.流程建模&#xff1a; 5.报表建模&#xff1a; 6.门户建模&#xff1a; 7.大屏建模&#xff1a; 8.移动建模&#xff1a; 三、解放程序员的双…

【ensp实践】eNSP实战篇(4)用eNSP实验来认识什么是OSPF及OSPF配置?

OSPF目录 写在前面涉及知识一、什么是OSPF&#xff1f;二、OSPF特性&#xff08;优缺点&#xff09;2.1 OSPF优点2.2 OSPF缺点 三、OSPF实验3.1 打开ensp&#xff0c;添加设备3.2 建立连线3.3 配置及ospf命令【核心】3.3.1 配置PC机3.3.2 设置命令 3.4 验证效果3.4.1、验证OSPF…