PathClassLoader用于加载三方包中的类列如supper-v4或者supper-v7包中的类
BootClassLoader加载的时SDK中的类
HookAMS及Handler启动插件Activity
HookAMS
启动Activity需要注意的是Activity需要通过AMS去清单文件中去查找如果查找不到就不能启动,插件化apk中的Activity不能再宿主apk中清单文件中注册所以需要去欺骗AMS启动插件中的Activity,这个过程主要是找到几个Hook点!!!Hook在使用过程中尽量找好获取不易改变变量。
首先想要对AMS进行欺骗要知道启动Activity是通过什么进行启动的,在启动Activity的时候使用的都是Intent进行启动,所以想要欺骗AMS就需要修改Intent,那么需要找到应该在那个点将启动插件的Intent替换为清单文件中占坑Activity的Intent,通过查看Activity启动流程源码可以发现HookAMS是比较好的Hook点。Hook位置是Instrumentation中的execStartActivity这个方法,具体位置如下图
这个位置如果不进行Hook那么就会执行到AMS中进行Activity启动了,因此这个位置是Hook的最终位置,这里只需要劫持AMS中startActivity这个方法在执行这个方法之前替换掉Intent就可以实现对AMS的欺骗,从而不会出现Activity找不到的异常。具体Hook代码如下:
/**
* 因为startActivity的时候使用的ActivityManager.getService()这个方法获取的IActivityManager对象,这个对象
* 存放在IActivityManagerSingleton的mInstance里面,要保证原有的启动流程不变的情况下那么需要获取原有的IActivityManager对象
* 在使用动态代理的时最后继续使用原对象执行流程
*/
fun hookAMS() {
//获取ActivityManager中的IActivityManagerSingleton
val activityManagerClazz = ActivityManager::class.java
val singletonFiled = activityManagerClazz.getDeclaredField("IActivityManagerSingleton")
singletonFiled.isAccessible = true
val activityManagerSingleton = singletonFiled.get(null)
//获取Singleton的mInstance
val singletonClazz = Class.forName("android.util.Singleton")
val mInstanceField = singletonClazz.getDeclaredField("mInstance")
mInstanceField.isAccessible = true
val mInstance = mInstanceField.get(activityManagerSingleton)
//获取IActivityManager的Class对象
val clazzIActivityManager = Class.forName("android.app.IActivityManager")
val proxyIActivityManager = Proxy.newProxyInstance(
Thread.currentThread().contextClassLoader,
arrayOf(clazzIActivityManager)
) { proxy, method, args ->
//执行Intent替换操作
method?.invoke(mInstance, *args)
}
//替换系统获取IActivityManager的对象为代理对象
singletonFiled.set(activityManagerSingleton, proxyIActivityManager)
}
这样插件化启动代码的第一步就基本已经完成。
但是上面的代码只能在Android R(10)以下的手机上运行,如果版本大于R是不能实现动态代理替换startActivity中的Intent,因为Google原生将ActivityTaskManager中的Singleton属性屏蔽掉不能被外部反射获取。需要使用其他方式实现插件Activity目前本人还没有实现希望各位大大们有什么好的方案提供一下。
HookHandler
上面欺骗AMS已经完成,那么这个时候再去启动插件中的Activity就不会抛出因为没有在清单文件中注册的异常了,但是只是替换那么还是不能启动插件的Activity,还需要将占坑的Activity替换为插件的Activity才能会启动插件的Activity,查看系统Activity处理流程会发现最终会通过ActivityThread中的Handler处理Activity启动的最终创建过程,所以可以HookActivityThread中的Handler实现占坑替换。代码如下
fun hookHandler(){
//先获取ActivityThread对象
val activityThreadClazz = Class.forName("android.app.ActivityThread")
val sCurrentActivityThreadField = activityThreadClazz.getDeclaredField("sCurrentActivityThread")
sCurrentActivityThreadField.isAccessible = true
val sCurrentActivityThread = sCurrentActivityThreadField.get(null)
//获取ActivityThread中的Handler对象
val mHField = activityThreadClazz.getDeclaredField("mH")
mHField.isAccessible = true
val mH = mHField.get(sCurrentActivityThread)
//创建HookHandler对象
val hookHandler = HookHandler(mH as android.os.Handler)
//获取Handler的CallBack属性对象
val handlerClazz = Class.forName("android.os.Handler")
val mCallBackField = handlerClazz.getDeclaredField("mCallback")
mCallBackField.isAccessible = true
mCallBackField.set(mH,hookHandler)
}
class HookHandler(handler:android.os.Handler):android.os.Handler.Callback{
val mHandler = handler
val LAUNCH_ACTIVITY = 100
override fun handleMessage(p0: Message?): Boolean {
if(p0.what == LAUNCH_ACTIVITY){
val intentField = p0?.obj?.javaClass?.getDeclaredField("intent")
val intent: Intent = intentField?.get(p0.obj) as Intent
val pluginIntent = intent.getParcelableExtra<Intent>("TAG")
intent.setComponent(pluginIntent.component)
}
Log.e("HookAMS","HookHandler handleMessage run ${p0?.what}")
mHandler.handleMessage(p0)
return true
}
}
这样就可以实现插件Activity的Intent的还原,之后就会开启插件Activity了。