四、完整代码示例
1、HookUtils 完整代码示例
package kim.hsl.plugin; import android.app.Instrumentation; import android.content.Context; import android.os.Handler; import android.util.Log; import java.lang.reflect.Field; import java.lang.reflect.Proxy; /** * 主要职责 : Hook Activity 的启动过程 * 本工具类只针对 API Level 28 实现 , 如果是完整插件化框架 , 需要实现所有版本的 Hook 过程 * 不同的版本 , Activity 的启动过程是不同的 , 需要逐个根据 Activity 启动源码进行 Hook 适配 */ public class HookUtils { private static final String TAG = "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(); } } /** * 劫持 Activity Thread 的 final H mH = new H(); 成员 * 该成员类型是 class H extends Handler ; * @param context */ public static void hookActivityThread(Context context) { // 反射获取 ActivityThread 类 Class<?> activityThreadClass = null; try { activityThreadClass = Class.forName("android.app.ActivityThread"); } catch (ClassNotFoundException e) { e.printStackTrace(); } // Activity Thread 是一个单例 , 内部的单例成员是 // private static volatile ActivityThread sCurrentActivityThread; // 可以直接通过 ActivityThread 类 , 获取该单例对象 // 这也是 Hook 点优先找静态变量的原因 , 静态变量对象容易拿到 , 通过反射即可获取 , 不涉及系统源码相关操作 Field sCurrentActivityThreadField = null; try { sCurrentActivityThreadField = activityThreadClass.getDeclaredField("sCurrentActivityThread"); // 反射获取的字段一般都要设置可见性 sCurrentActivityThreadField.setAccessible(true); } catch (NoSuchFieldException e) { e.printStackTrace(); } // 获取类的静态变量 , 使用 字段.get(null) 即可 Object activityThreadObject = null; try { activityThreadObject = sCurrentActivityThreadField.get(null); } catch (IllegalAccessException e) { e.printStackTrace(); } // 获取 Activity Thread 中的 final H mH = new H() 成员字段 ; Field mHField = null; try { mHField = activityThreadClass.getDeclaredField("mH"); // 设置该字段的可见性 mHField.setAccessible(true); } catch (NoSuchFieldException e) { e.printStackTrace(); } // 通过反射获取 Activity Thread 中的 final H mH = new H() 成员实例对象 Handler mHObject = null; try { mHObject = (Handler) mHField.get(activityThreadObject); } catch (IllegalAccessException e) { e.printStackTrace(); } Class<?> handlerClass = null; try { handlerClass = Class.forName("android.os.Handler"); } catch (ClassNotFoundException e) { e.printStackTrace(); } // 通过反射获取 final H mH = new H() 成员的 mCallback 成员字段 // Handler 中有成员变量 final Callback mCallback; Field mCallbackField = null; try { // 类可以直接获取到, 可以不用反射 mCallbackField = Handler.class.getDeclaredField("mCallback"); //mCallbackField = mHObject.getClass().getDeclaredField("mCallback"); // 设置字段的可见性 mCallbackField.setAccessible(true); } catch (NoSuchFieldException e) { e.printStackTrace(); } // 使用静态代理类 HandlerProxy , 替换 final H mH = new H() 成员实例对象中的 mCallback 成员 HandlerProxy proxy = new HandlerProxy(); try { Log.i(TAG, "mCallbackField : " + mCallbackField + " , mHObject : " + mHObject + " , proxy : " + proxy); mCallbackField.set(mHObject, proxy); } catch (Exception e) { e.printStackTrace(); } } /** * 主要用于 Resources 资源的加载 */ public static void hookInstrumentation() { // 反射 ActivityThread 类 // 反射获取 ActivityThread 类中的 sCurrentActivityThread 静态成员 // 这是单例类内部的静态成员 Object sCurrentActivityThreadObj = Reflector.on("android.app.ActivityThread") // 反射 ActivityThread 类 .field("sCurrentActivityThread") // 获取 sCurrentActivityThread 字段 .get(); // 获取 sCurrentActivityThread 对象 // 反射获取 ActivityThread 对象中的 mInstrumentation 成员变量 // 目的是替换 sCurrentActivityThread 中的 mInstrumentation 字段 Reflector reflector = Reflector.on("android.app.ActivityThread") // 反射 ActivityThread 类 .field("mInstrumentation") // 获取 mInstrumentation 字段 .with(sCurrentActivityThreadObj); // 设置 ActivityThread 实例对象 // 获取 ActivityThread 中的 mInstrumentationObj 成员, 创建 Instrumentation 静态代理时使用 Instrumentation mInstrumentationObj = (Instrumentation) reflector.get(); // 将 ActivityThread 对象中的 mInstrumentation 成员变量 // 替换成自己的代理类 reflector.set(new InstrumentationProxy(mInstrumentationObj)); } }
2、Instrumentation 代理类完整代码示例
package kim.hsl.plugin; import android.app.Activity; import android.app.Application; import android.app.Instrumentation; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.res.Resources; import android.os.Bundle; import android.os.IBinder; import java.lang.reflect.Field; public class InstrumentationProxy extends Instrumentation { private static final String TAG = "InstrumentationProxy"; /** * 持有被代理对象 * 有一些操作需要使用原来的 Instrumentation 进行操作 */ private final Instrumentation mBase; /** * 在构造方法中注入被代理对象 * @param mBase */ public InstrumentationProxy(Instrumentation mBase) { this.mBase = mBase; } public ActivityResult execStartActivity( Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) { ActivityResult result = null; // 反射调用 Instrumentation mBase 成员的 execStartActivity 方法 result = Reflector.on("android.app.Instrumentation") .method("execStartActivity", // 反射的方法名 Context.class, // 后续都是方法的参数类型 IBinder.class, IBinder.class, Activity.class, Intent.class, int.class, Bundle.class) .with(mBase) .call(who, // 后续都是传入 execStartActivity 方法的参数 contextThread, token, target, intent, requestCode, options); return result; } /** * 在该方法中 , 可以拿到 Activity , 通过反射修改 Activity 中的 Resources 成员变量 * @param cl * @param className * @param intent * @return * @throws ClassNotFoundException * @throws IllegalAccessException * @throws InstantiationException */ public Activity newActivity(ClassLoader cl, String className, Intent intent) throws ClassNotFoundException, IllegalAccessException, InstantiationException { Activity activity = mBase.newActivity(cl, className, intent); // 替换 Activity 中的 Resources exchangeResourcesOfActivity(activity, intent); return activity; } /** * 在该方法中 , 可以拿到 Activity , 通过反射修改 Activity 中的 Resources 成员变量 * @param clazz * @param context * @param token * @param application * @param intent * @param info * @param title * @param parent * @param id * @param lastNonConfigurationInstance * @return * @throws IllegalAccessException * @throws InstantiationException */ @Override public Activity newActivity(Class<?> clazz, Context context, IBinder token, Application application, Intent intent, ActivityInfo info, CharSequence title, Activity parent, String id, Object lastNonConfigurationInstance) throws IllegalAccessException, InstantiationException { Activity activity = mBase.newActivity(clazz, context, token, application, intent, info, title, parent, id, lastNonConfigurationInstance); // 替换 Activity 中的 Resources exchangeResourcesOfActivity(activity, intent); return activity; } /** * 反射 Activity , 并设置 Activity 中 Resources 成员变量 * @param activity * @param intent */ private void exchangeResourcesOfActivity(Activity activity, Intent intent) { // 这里注意 : 所有的 Activity 创建 , 都会过这个方法 , 这里只将插件包中的 Activity 的资源替换 // 这里要做一个判断 // 不能修改宿主应用的资源 // 只有插件包中的 Activity 才进行相应的修改 // 在调用插件包中的组件时 , 在 Intent 中传入一个 isPlugin 变量 , // 也可以传入插件的标志位 , 区分不同的插件包 // 这里只有一个插件包 , 只设置一个 Boolean 变量即可 if (!intent.getBooleanExtra("isPlugin", false)) return; try { // 获取插件资源 Resources pluginResources = PluginManager.getInstance(activity).getResources(); // 反射 ContextThemeWrapper 类 , Activity 是 ContextThemeWrapper 的子类 // Resources mResources 成员定义在 ContextThemeWrapper 中 Class<?> contextThemeWrapperClass = Class.forName("android.view.ContextThemeWrapper"); // 反射获取 AppCompatActivity 类的 mResources 字段 Field mResourcesField = contextThemeWrapperClass.getDeclaredField("mResources"); // 设置字段可见性 mResourcesField.setAccessible(true); // 将插件资源设置为 mResourcesField.set(activity, PluginManager.getInstance(activity).getResources()); } catch (Exception e) { e.printStackTrace(); } } }
五、为不同的插件包设置不同的资源
在 Hook Instrumentation 的时候 , 替换 Activity 的 mResources 成员前 , 要先进行判定 ;
启动插件包组件时 , 可以向 Intent 中添加各种数据标识 , 根据标识位判定应该加载哪些资源 ;
在本示例中 , 只进行了是否加载插件包的标识位 , 用于区分 宿主应用组件 和 插件应用组件 ;
如果有多个插件包 , 可以将插件包名称 , 序号作为标识位 , 为不同的插件包加载不同的插件资源 ;
// 这里注意 : 所有的 Activity 创建 , 都会过这个方法 , 这里只将插件包中的 Activity 的资源替换
// 这里要做一个判断 // 不能修改宿主应用的资源 // 只有插件包中的 Activity 才进行相应的修改 // 在调用插件包中的组件时 , 在 Intent 中传入一个 isPlugin 变量 , // 也可以传入插件的标志位 , 区分不同的插件包 // 这里只有一个插件包 , 只设置一个 Boolean 变量即可 if (!intent.getBooleanExtra("isPlugin", false)) return;