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

2023-03-29 15:22:45 浏览数 (1)

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 类 )


文章目录

  • Android 插件化系列文章目录
  • 一、插件包 Activity 启动原理
  • 二、需要反射的相关类
    • 1、Instrumentation
    • 2、IActivityManager
    • 3、ActivityManager
    • 4、Singleton
  • 三、动态代理类
  • 四、使用动态代理替换 IActivityManagerSingleton 的 mInstance 成员
    • 1、通过反射获取 IActivityManagerInterface 接口
    • 2、动态代理
    • 3、替换 mInstance 成员
    • 4、代码示例
  • 五、完整代码
  • 六、博客资源

一、插件包 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();
        }

    }

}

六、博客资源

博客资源 :

  • GitHub : https://github.com/han1202012/Plugin_Hook

0 人点赞