插件化知识点——启动未注册Activity

195 阅读1分钟

代码详情可见:
github.com/stewForAni/…

顺便给练习项目求个Star🌟🌟🌟,万分感谢🙏🙏🙏:
github.com/stewForAni/…

WechatIMG382.jpeg

技术方案

采用预埋ProxyActivity的方案(以Android11为例)

  • 启动PluginActivity后,在进入AMS之前替换为ProxyActivity,达到骗过AMS检测的目的;
  • 回到ActivityThread的Handler时,再替换回PluginActivity

关键代码

//PluginActvity 替换成 ProxyActivity(binder传递消息到AMS之前)
hookIActivityTaskManager()
//ProxyActivity 还原成 PluginActvity(handler处理消息之前,关键点:intent初始化于LaunchActivityItem中)
hookActivityThreadH()

btn.setOnClickListener {
    startActivity(Intent(this, PluginActivity::class.java))
}
//ActivityTaskManager.getService().startActivity(...intent...)
private fun hookIActivityTaskManager() {

    //获取IActivityTaskManagerSingleton实例
    val field1 = Class.forName("android.app.ActivityTaskManager")
        .getDeclaredField("IActivityTaskManagerSingleton")
    field1.isAccessible = true
    val obj1 = field1.get(null)

    //获取IActivityTaskManager实例
    val field2 = Class.forName("android.util.Singleton")
        .getDeclaredField("mInstance")
    field2.isAccessible = true
    val iatm = field2.get(obj1)

    val proxyObj = Proxy.newProxyInstance(
        Thread.currentThread().contextClassLoader,
        arrayOf(Class.forName("android.app.IActivityTaskManager"))
    ) { _, method, args ->

        if (method.name.equals("startActivity")) {
            for (i in args.indices) {
                if (args[i] is Intent) {
                    val pluginIntent = args[i] as Intent //plugin activity intent
                    val newIntent = Intent()
                    newIntent.component = ComponentName("com.stew.kotlinbox", ProxyActivity::class.java.name)
                    newIntent.putExtra("DPTEST", pluginIntent)
                    args[i] = newIntent
                    break
                }
            }
        }

        val a = args ?: emptyArray()
        val r = method?.invoke(iatm, *(a))
        return@newProxyInstance r
    }

    field2.set(obj1, proxyObj)
}
private fun hookActivityThreadH() {
    val atField =
        Class.forName("android.app.ActivityThread").getDeclaredField("sCurrentActivityThread")
    atField.isAccessible = true
    val at = atField.get(null)

    val hField = Class.forName("android.app.ActivityThread").getDeclaredField("mH")
    hField.isAccessible = true
    val handler: Handler = hField.get(at) as Handler

    val callbackField = Class.forName("android.os.Handler").getDeclaredField("mCallback")
    callbackField.isAccessible = true

    val myCallBack = Callback {
        when (it.what) {
            159 -> {
                val mActivityCallbacksField: Field =
                    it.obj.javaClass.getDeclaredField("mActivityCallbacks")
                mActivityCallbacksField.isAccessible = true
                val mActivityCallbacks: List<Any> =
                    mActivityCallbacksField.get(it.obj) as List<Any>
                for (i in mActivityCallbacks.indices) {
                    if (mActivityCallbacks[i].javaClass.name.equals("android.app.servertransaction.LaunchActivityItem")) {
                        val launchItem = mActivityCallbacks[i]
                        val intentFiled = launchItem.javaClass.getDeclaredField("mIntent")
                        intentFiled.isAccessible = true
                        val intent: Intent = intentFiled.get(launchItem) as Intent
                        val pluginIntent: Intent? = intent.getParcelableExtra("DPTEST")
                        if (pluginIntent != null) {
                            intentFiled.set(launchItem, pluginIntent)
                        }
                        break
                    }
                }
            }
        }
        return@Callback false
    }

    callbackField.set(handler, myCallBack)

}