插件化(二)如何启动Activity

插件化(二)如何启动Activity

Android小彩虹2021-07-17 9:06:0670A+A-

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了。

点击这里复制本文地址 以上内容由权冠洲的博客整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!

支持Ctrl+Enter提交

联系我们| 本站介绍| 留言建议 | 交换友链 | 域名展示
本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除

权冠洲的博客 © All Rights Reserved.  Copyright quanguanzhou.top All Rights Reserved
苏公网安备 32030302000848号   苏ICP备20033101号-1

联系我们