前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【Android 插件化】Hook 插件化框架 ( Hook Activity 启动流程 | AMS 启动前使用动态代理替换掉插件 Activity 类 )

【Android 插件化】Hook 插件化框架 ( Hook Activity 启动流程 | AMS 启动前使用动态代理替换掉插件 Activity 类 )

作者头像
韩曙亮
发布2023-03-29 15:22:45
5640
发布2023-03-29 15:22:45
举报
文章被收录于专栏:韩曙亮的移动开发专栏

Android 插件化系列文章目录

【Android 插件化】插件化简介 ( 组件化与插件化 )

【Android 插件化】插件化原理 ( JVM 内存数据 | 类加载流程 )

【Android 插件化】插件化原理 ( 类加载器 )

【Android 插件化】“ 插桩式 “ 插件化框架 ( 原理与实现思路 )

【Android 插件化】“ 插桩式 “ 插件化框架 ( 类加载器创建 | 资源加载 )

【Android 插件化】“ 插桩式 “ 插件化框架 ( 注入上下文的使用 )

【Android 插件化】“ 插桩式 “ 插件化框架 ( 获取插件入口 Activity 组件 | 加载插件 Resources 资源 )

【Android 插件化】“ 插桩式 “ 插件化框架 ( 运行应用 | 代码整理 )

【Android 插件化】Hook 插件化框架 ( Hook 技术 | 代理模式 | 静态代理 | 动态代理 )

【Android 插件化】Hook 插件化框架 ( Hook 实现思路 | Hook 按钮点击事件 )

【Android 插件化】Hook 插件化框架 ( Hook Activity 启动过程 | 静态代理 )

【Android 插件化】Hook 插件化框架 ( 从 Hook 应用角度分析 Activity 启动流程 一 | Activity 进程相关源码 )

【Android 插件化】Hook 插件化框架 ( 从 Hook 应用角度分析 Activity 启动流程 二 | AMS 进程相关源码 | 主进程相关源码 )

【Android 插件化】Hook 插件化框架 ( hook 插件化原理 | 插件包管理 )

【Android 插件化】Hook 插件化框架 ( 通过反射获取 “插件包“ 中的 Element[] dexElements )

【Android 插件化】Hook 插件化框架 ( 通过反射获取 “宿主“ 应用中的 Element[] dexElements )

【Android 插件化】Hook 插件化框架 ( 合并 “插件包“ 与 “宿主“ 中的 Element[] dexElements | 设置合并后的 Element[] 数组 )

【Android 插件化】Hook 插件化框架 ( 创建插件应用 | 拷贝插件 APK | 初始化插件包 | 测试插件 DEX 字节码 )

【Android 插件化】Hook 插件化框架 ( Hook Activity 启动流程 | Hook 点分析 )

【Android 插件化】Hook 插件化框架 ( Hook Activity 启动流程 | 反射获取 IActivityManager 对象 )

【Android 插件化】Hook 插件化框架 ( Hook Activity 启动流程 | AMS 启动前使用动态代理替换掉插件 Activity 类 )


文章目录


一、插件包 Activity 启动原理


使用动态代理 , 替换 android.app.ActivityManager 中的 private static final Singleton IActivityManagerSingleton 成员的 mInstance 成员 ;

注意 : 该操作一定要在 AMS 启动之前将原来的 Intent 替换掉 , 使用占坑的 Activity 替换插件包中的 Activity , 之后 AMS 执行完毕 , 执行到主线程 实例化 Activity 对象之前 , 还要替换回去 ;

插件包组件启动方式 : 使用 Intent 启动插件包时 , 一般都使用隐式启动 ; 调用 Intent 的 setComponent , 通过包名和类名创建 Component , 这样操作 , 即使没有获得 Activity 引用 , 也不会报错

该插件包中的 Activity 没有在 “宿主” 应用中注册 , 因此启动报错 ;

AMS 会干掉没有注册过的 Activity , 这里先 在启动 AMS 之前 , 设置一个已经 注册过的 占坑 Activity ( StubActivity ) 执行启动流程 , 在主线程生成 Activity 实例对象时 , 还需要恢复插件包中的 Activity

二、需要反射的相关类


1、Instrumentation

在 Instrumentation 中的 execStartActivity 方法 , 最终调用 ActivityManager.getService() 的 startActivity 方法 , 其中传入的参数就有 Intent , 这里我们需要将该 Intent 替换掉 ;

代码语言:javascript
复制
public class Instrumentation {
    public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {
            
        try {
        
            int result = ActivityManager.getService()
                .startActivity(whoThread, who.getBasePackageName(), intent,
                        intent.resolveTypeIfNeeded(who.getContentResolver()),
                        token, target != null ? target.mEmbeddedID : null,
                        requestCode, 0, null, options);
                        
        } catch (RemoteException e) {
        }
        
        return null;
    }
}

2、IActivityManager

IActivityManager 是接口 , 由 IActivityManager.aidl 生成 ;

源码路径 : /frameworks/base/core/java/android/app/IActivityManager.aidl

3、ActivityManager

要替换的是 ActivityManager 中的 Singleton<IActivityManager> IActivityManagerSingleton 成员的 mInstance 成员 ;

代码语言:javascript
复制
public class ActivityManager {
    /**
     * @hide
     */
    public static IActivityManager getService() {
        return IActivityManagerSingleton.get();
    }

    private static final Singleton<IActivityManager> IActivityManagerSingleton =
            new Singleton<IActivityManager>() {
                @Override
                protected IActivityManager create() {
                    final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
                    final IActivityManager am = IActivityManager.Stub.asInterface(b);
                    return am;
                }
            };
}

源码路径 : /frameworks/base/core/java/android/app/ActivityManager.java

4、Singleton

Singleton 类是单例的实现 , 注意该类只能由系统使用 , 应用开发者不能调用 ;

代码语言:javascript
复制
package android.util;

/**
 * Singleton helper class for lazily initialization.
 *
 * Modeled after frameworks/base/include/utils/Singleton.h
 *
 * @hide
 */
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;
        }
    }
}

源码路径 : /frameworks/base/core/java/android/util/Singleton.java

三、动态代理类


该类持有 mIActivityManager 接口对象 , 当检测到调用 startActivity 方法时 , 拦截该方法 , 在该 startActivity 方法中替换方法中的 Intent 参数 ;

代码语言:javascript
复制
package kim.hsl.plugin;

import android.content.Context;
import android.content.Intent;
import android.util.Log;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * 动态代理的代理类
 */
public class AmsInvocationHandler implements InvocationHandler {

    /**
     * 上下文对象
     */
    private final Context mContext;

    /**
     * 持有被代理的原对象
     */
    private final Object mIActivityManager;

    public AmsInvocationHandler(Context context, Object iActivityManager) {
        this.mContext = context;
        this.mIActivityManager = iActivityManager;
    }

    /**
     * 代理 IActivityManager 的 startActivity 方法
     * 替换要启动的 Activity 的 Intent
     *
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 检测到方法名称是 startActivity
        // 要使用自己的方法 , 替换被代理的方法
        // 主要进行替换要启动的 Activity 的 Intent 操作
        if("startActivity".equals(method.getName())){
            Intent intent = null;

            // Intent 会通过参数传入
            // 遍历方法的参数即可
            for (int i= 0; i < args.length; i++){
                // 获取参数对象
                Object arg = args[i];

                // 方法参数类型是 Intent
                if(arg instanceof Intent){
                    // 将原来的传入的 Intent 参数 , 改成自己的 Intent , 启动自己的类
                    intent = (Intent) arg;

                    // 新的 Intent , 用于替换原有的 Intent
                    Intent exchangeIntent = new Intent(mContext, StubActivity.class);

                    // 原来的 Intent 不能丢 , 里面包含了很多信息 , 如实际的跳转信息
                    // 最终还要替换回去
                    exchangeIntent.putExtra("actionIntent", intent);

                    // 替换原来的 Intent 参数值
                    args[i] = exchangeIntent;
                    break;
                }
            }
        }

        // 继续向后执行 , 这里要避免截断方法
        return method.invoke(mIActivityManager, args);
    }

}

四、使用动态代理替换 IActivityManagerSingleton 的 mInstance 成员


1、通过反射获取 IActivityManagerInterface 接口

IActivityManager 是接口 , 这是一个 AIDL 文件生成的 , 由 IActivityManager.aidl 生成 ;

通过反射获取 android.app.IActivityManager 接口 ;

代码语言:javascript
复制
// IActivityManager 是接口
// 这是一个 AIDL 文件生成的 , 由 IActivityManager.aidl 生成
Class<?> IActivityManagerInterface = null;
try {
    IActivityManagerInterface = Class.forName("android.app.IActivityManager");
} catch (ClassNotFoundException e) {
    e.printStackTrace();
}

2、动态代理

创建 InvocationHandler 对象 ;

然后创建动态代理对象 ;

代码语言:javascript
复制
// 动态代理的实际代理类
AmsInvocationHandler amsInvocationHandler =
        new AmsInvocationHandler(context, mInstanceObject);
        
// 动态代理过程
Object proxy = Proxy.newProxyInstance(
        Thread.currentThread().getContextClassLoader(), // 类加载器
        new Class[]{IActivityManagerInterface},         // 接口
        amsInvocationHandler);                          // 代理的对象

3、替换 mInstance 成员

使用动态代理类 , 替换原来的 ActivityManager 中的 IActivityManagerSingleton 成员 的 Singleton 类中的 mInstance 成员 ;

代码语言:javascript
复制
// 使用动态代理类 , 替换原来的 ActivityManager 中的 IActivityManagerSingleton 成员
//      的 Singleton 类中的 mInstance 成员
try {
    mInstanceField.set(iActivityManagerSingletonObject, proxy);
} catch (IllegalAccessException e) {
    e.printStackTrace();
}

4、代码示例

完整代码示例 :

代码语言:javascript
复制
// IActivityManager 是接口
// 这是一个 AIDL 文件生成的 , 由 IActivityManager.aidl 生成
Class<?> IActivityManagerInterface = null;
try {
    IActivityManagerInterface = Class.forName("android.app.IActivityManager");
} catch (ClassNotFoundException e) {
    e.printStackTrace();
}

// 动态代理的实际代理类
AmsInvocationHandler amsInvocationHandler =
        new AmsInvocationHandler(context, mInstanceObject);
        
// 动态代理过程
Object proxy = Proxy.newProxyInstance(
        Thread.currentThread().getContextClassLoader(), // 类加载器
        new Class[]{IActivityManagerInterface},         // 接口
        amsInvocationHandler);                          // 代理的对象
        
// 使用动态代理类 , 替换原来的 ActivityManager 中的 IActivityManagerSingleton 成员
//      的 Singleton 类中的 mInstance 成员
try {
    mInstanceField.set(iActivityManagerSingletonObject, proxy);
} catch (IllegalAccessException e) {
    e.printStackTrace();
}

五、完整代码


代码语言:javascript
复制
package kim.hsl.plugin;

import android.content.Context;

import java.lang.reflect.Field;
import java.lang.reflect.Proxy;

/**
 * 主要职责 : Hook Activity 的启动过程
 * 本工具类只针对 API Level 28 实现 , 如果是完整插件化框架 , 需要实现所有版本的 Hook 过程
 * 不同的版本 , Activity 的启动过程是不同的 , 需要逐个根据 Activity 启动源码进行 Hook 适配
 */
public class HookUtils {

    /**
     * 最终目的是劫持 ActivityManagerService 的 startActivity 方法 ,
     *      修改 Intent 中药启动的 Activity 类
     */
    public static void hookAms(Context context){
        // 获取 android.app.ActivityManager 类
        Class<?> activityManagerClass = null;
        try {
            activityManagerClass = Class.forName("android.app.ActivityManager");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        // 获取 android.app.ActivityManager 类 中的 IActivityManagerSingleton 属性
        // private static final Singleton<IActivityManager> IActivityManagerSingleton 成员变量
        Field iActivityManagerSingletonField = null;
        try {
            iActivityManagerSingletonField =
                    activityManagerClass.getDeclaredField("IActivityManagerSingleton");
            // 设置成员字段的可访问性
            iActivityManagerSingletonField.setAccessible(true);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }

        // 获取 android.app.ActivityManager 类的静态成员变量
        // private static final Singleton<IActivityManager> IActivityManagerSingleton
        // 直接调用 Field 字段 iActivityManagerSingletonField 的 get 方法 , 传入 null 即可获取
        Object iActivityManagerSingletonObject = null;
        try {
            iActivityManagerSingletonObject = iActivityManagerSingletonField.get(null);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        // 获取 Singleton 类
        // ActivityManager 中的 IActivityManagerSingleton 成员是 Singleton<IActivityManager> 类型的
        Class<?> singletonClass = null;
        try {
            singletonClass = Class.forName("android.util.Singleton");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        // 反射获取 Singleton 类中的 mInstance 字段
        Field mInstanceField = null;
        try {
            mInstanceField = singletonClass.getDeclaredField("mInstance");
            // 设置字段的可访问性
            mInstanceField.setAccessible(true);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }

        // 反射获取 Singleton 类中的 mInstance 成员对象
        // 该 mInstanceObject 成员对象就是 IActivityManager
        // private static final Singleton<IActivityManager> IActivityManagerSingleton
        Object mInstanceObject = null;
        try {
            mInstanceObject = mInstanceField.get(iActivityManagerSingletonObject);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        // 使用动态代理 , 替换 android.app.ActivityManager 中的
        // private static final Singleton<IActivityManager> IActivityManagerSingleton 成员的
        // mInstance 成员
        // 注意 : 该操作一定要在 AMS 启动之前将原来的 Intent 替换掉
        //          之后还要替换回去
        // 使用 Intent 启动插件包时 , 一般都使用隐式启动
        // 调用 Intent 的 setComponent , 通过包名和类名创建 Component ,
        //      这样操作 , 即使没有获得 Activity 引用 , 也不会报错
        // 该插件包中的 Activity 没有在 "宿主" 应用中注册 , 因此启动报错
        //      AMS 会干掉没有注册过的 Activity
        //      这里先在启动 AMS 之前 , 设置一个已经 注册过的 占坑 Activity ( StubActivity ) 执行启动流程
        //      在主线程生成 Activity 实例对象时 , 还需要恢复插件包中的 Activity

        // IActivityManager 是接口
        // 这是一个 AIDL 文件生成的 , 由 IActivityManager.aidl 生成
        Class<?> IActivityManagerInterface = null;
        try {
            IActivityManagerInterface = Class.forName("android.app.IActivityManager");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        // 动态代理的实际代理类
        AmsInvocationHandler amsInvocationHandler =
                new AmsInvocationHandler(context, mInstanceObject);

        // 动态代理过程
        Object proxy = Proxy.newProxyInstance(
                Thread.currentThread().getContextClassLoader(), // 类加载器
                new Class[]{IActivityManagerInterface},         // 接口
                amsInvocationHandler);                          // 代理的对象

        // 使用动态代理类 , 替换原来的 ActivityManager 中的 IActivityManagerSingleton 成员
        //      的 Singleton 类中的 mInstance 成员
        try {
            mInstanceField.set(iActivityManagerSingletonObject, proxy);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

    }

}

六、博客资源

博客资源 :

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

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Android 插件化系列文章目录
    • 文章目录
    • 一、插件包 Activity 启动原理
    • 二、需要反射的相关类
      • 1、Instrumentation
        • 2、IActivityManager
          • 3、ActivityManager
            • 4、Singleton
            • 三、动态代理类
            • 四、使用动态代理替换 IActivityManagerSingleton 的 mInstance 成员
              • 1、通过反射获取 IActivityManagerInterface 接口
                • 2、动态代理
                  • 3、替换 mInstance 成员
                    • 4、代码示例
                    • 五、完整代码
                    • 六、博客资源
                    领券
                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档