代码详情可见:
github.com/stewForAni/…
顺便给练习项目求个Star🌟🌟🌟,万分感谢🙏🙏🙏:
github.com/stewForAni/…
技术方案
采用预埋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)
}