Android攔截AMS請(qǐng)求實(shí)戰(zhàn)
概述
接著上次后臺(tái)啟動(dòng) Activity 的需求,依照 實(shí)戰(zhàn)|Android后臺(tái)啟動(dòng)Activity實(shí)踐之路 中的一些方法處理后,雖然在 Android Q 版本上還是有一些問(wèn)題,但后臺(tái)啟動(dòng)的能力算是基本完成了,后來(lái)我又解開(kāi)了小米 ROM 的源碼,找到了他們后臺(tái)啟動(dòng)這一項(xiàng)權(quán)限的實(shí)現(xiàn)方式以及怎么繞開(kāi)這項(xiàng)權(quán)限的方法,發(fā)現(xiàn)結(jié)果意外的簡(jiǎn)單..(這部分以后有機(jī)會(huì)單獨(dú)寫(xiě)一篇文章)。
這篇文章發(fā)生在后臺(tái)啟動(dòng)的調(diào)研之后,如果我們要后臺(tái)啟動(dòng)的 Activity 頁(yè)面在第三方 SDK 里,且啟動(dòng)該頁(yè)面的動(dòng)作(startActivity)也發(fā)生在第三方 SDK 中,那么它們直接 startActivity 的方式是不具備后臺(tái)啟動(dòng)的能力的,因?yàn)橐恍┰蛭覀儾荒芤?SDK 方修改啟動(dòng) Activity 的方法,因此需要找個(gè)方法能夠在不修改第三方 SDK 調(diào)用 startActivity 代碼的情況下,讓其具備后臺(tái)啟動(dòng)的能力。第一個(gè)反應(yīng)就是攔截 startActivity 的請(qǐng)求,參考 Android之system_server進(jìn)程 和 Android-Activity啟動(dòng)流程,我們知道 AMS 是 system_server 進(jìn)程中的一個(gè)線程,它負(fù)責(zé)啟動(dòng) Activity 的具體工作,在它的工作完成之后,會(huì)通過(guò) Binder 調(diào)用回調(diào) APP 進(jìn)程中 Activity 實(shí)例的生命周期方法。當(dāng) APP 進(jìn)程調(diào)用 startActivity 時(shí),會(huì)由 Instrumentation 獲取到 AMS 的 Binder 代理,然后通過(guò)它來(lái)跨進(jìn)程調(diào)用 AMS 的相關(guān)方法,我們能做 Hook 攔截的地方就是這個(gè) Binder 代理對(duì)象!
下面從各個(gè) Android 版本看一下系統(tǒng)這個(gè)過(guò)程的實(shí)現(xiàn)方法以及我們是怎么攔截的,主要看一下 Android P 的源碼,其它版本的雖然過(guò)程不一樣,但是 Hook 的方式是類(lèi)似的。
Android P
Android 8 到 Android 9 版本的 AOSP 獲取 AMS 代理的方式是一樣的,APP 進(jìn)程在調(diào)用 context.startActivity 后,會(huì)來(lái)到 Instrumentation 中的相關(guān)方法里調(diào)用如下代碼:
- int result = ActivityManager.getService().startActivity(whoThread, who.getBasePackageName(), intent, ...);
這里通過(guò) Binder 跨進(jìn)程調(diào)用到 AMS 中的相關(guān)方法,看一下 ActivityManager.getService() 的實(shí)現(xiàn):
- /** @hide */
- public static IActivityManager getService() {
- return IActivityManagerSingleton.get();
- }
- private static final Singleton<IActivityManager> IActivityManagerSingleton = new Singleton<IActivityManager>() {
- protected IActivityManager create() {
- // 1...
- }
- };
可以看到 IActivityManagerSingleton 是 Singleton 類(lèi)型的實(shí)例,很顯然這個(gè) Singleton 是一個(gè)懶加載的單例模板類(lèi):
- public abstract class Singleton<T> {
- private T mInstance;
- protected abstract T create();
- public final T get() {
- synchronized (this) {
- if (mInstance == null) {
- mInstance = create();
- }
- return mInstance;
- }
- }
- }
于是可以知道 IActivityManagerSingleton.get() 返回的便是 create 方法中的實(shí)例,給出上面 1 處省略的 create 方法代碼:
- final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
- final IActivityManager am = IActivityManager.Stub.asInterface(b);
- return am;
熟悉 Binder 的同學(xué)一眼就能看出這里的 am 是一個(gè) Binder 代理對(duì)象,存在 ServiceManager.getService 方法就肯定存在 ServiceManager.addService 方法,一個(gè)是從 ServiceManager 中查詢 Binder 服務(wù),一個(gè)是往 ServiceManager 中注冊(cè)服務(wù),注冊(cè)的時(shí)機(jī)在系統(tǒng)啟動(dòng) system_server 進(jìn)程的時(shí)候,參考 AMS啟動(dòng)流程,這里就不深入描述了。
所以 ActivityManager.getService() 方法其實(shí)就是返回了 AMS 的一個(gè) Binder 代理對(duì)象,用來(lái)跨進(jìn)程調(diào)用 AMS 相關(guān)方法,因此可以通過(guò) JDK 動(dòng)態(tài)代理的方式,通過(guò) Proxy.newProxyInstance 方法創(chuàng)建 am 的代理 Proxy 對(duì)象,并通過(guò)反射的方式將 ActivityManager.getService() 方法返回的 am 對(duì)象替換成我們的 Proxy 對(duì)象,那么在 App 進(jìn)程調(diào)用 ActivityManager.getService().XXX 方法時(shí)都會(huì)被我們的 Proxy 攔截到,進(jìn)而做一些處理。JDK 動(dòng)態(tài)代理也是 Java 常用的設(shè)計(jì)模式之一,不太熟悉的同學(xué)可以參考 Jdk動(dòng)態(tài)代理 的使用。
這個(gè)過(guò)程可以分成三個(gè)步驟:
- 反射獲取 am 對(duì)象,由于 ActivityManager.getService() 是一個(gè)隱藏方法,因此可以通過(guò)反射調(diào)用它拿到原 am 對(duì)象;
- 創(chuàng)建代理對(duì)象Proxy;
- 通過(guò)反射用 Proxy 替換 am 對(duì)象;
我們看到 am 對(duì)象其實(shí)就是 Singleton(其實(shí)例是IActivityManagerSingleton) 中的 mInstance 屬性,因此第三步只需通過(guò)反射將 mInstance 屬性設(shè)置為我們的 Proxy 對(duì)象即可,下面的 AmsHooker 是一個(gè)抽象類(lèi),在不同的 Android 平臺(tái)上有不同的實(shí)現(xiàn),主要用來(lái)獲取不同 Android 平臺(tái)的 am 對(duì)象及通過(guò)反射替換 am 對(duì)象:
- abstract class AmsHooker {
- // 通過(guò)反射,將am替換成proxy
- fun hookAms(proxy: Any?) {
- try {
- val hookObj = getHookObj()
- val hookField = getHookField()
- if (hookObj != null && hookField != null && proxy != null) {
- hookField.set(hookObj, proxy)
- }
- } catch (e: Exception) {
- e.printStackTrace()
- }
- }
- // 即IActivityManagerSingleton實(shí)例
- protected abstract fun getHookObj(): Any?
- // 即mInstance
- protected abstract fun getHookField(): Field?
- // 即am
- abstract fun getTarget(): Any?
- // 接口,用來(lái)創(chuàng)建Proxy
- abstract fun getInterfaces(): Array<Class<*>>
- }
在 Android P 平臺(tái)上的實(shí)現(xiàn)如下,具體看注釋?zhuān)?/p>
- class AmsPHooker : AmsHooker() {
- override fun getHookObj(): Any? {
- val amClass = ReflectUtils.getClass("android.app.ActivityManager")
- // 拿到 IActivityManagerSingleton 屬性
- return ReflectUtils.readStaticField(amClass, "IActivityManagerSingleton")
- }
- override fun getHookField(): Field? {
- // 獲取 mInstance Field
- return ReflectUtils.getField(ReflectUtils.getClass("android.util.Singleton"), "mInstance")
- }
- override fun getTarget(): Any? {
- // ActivityManager.getService()返回 am
- return ReflectUtils.getClass("android.app.ActivityManager").getDeclaredMethod("getService").invoke(null)
- }
- // 獲取interfaces,用來(lái)創(chuàng)建動(dòng)態(tài)代理
- override fun getInterfaces(): Array<Class<*>> {
- return arrayOf(ReflectUtils.getClass("android.app.IActivityManager"))
- }
- }
接下來(lái)創(chuàng)建代理類(lèi)(代碼有刪減):
- public class AMSProxy implements InvocationHandler {
- private AmsHooker hooker; // 根據(jù)不同 Android 平臺(tái)返回不同實(shí)現(xiàn)
- private Object origAm; // 原有 am 對(duì)象
- private boolean ensureInit() {
- // ...
- hooker = getHooker();
- origAm = hooker.getTarget();
- }
- private AmsHooker getHooker() {
- if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
- return new AmsQHooker();
- } else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) {
- return new AmsPHooker();
- } else {
- return new AmsNHooker();
- }
- }
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- // ...
- }
- // 創(chuàng)建代理
- Object proxy = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
- hooker.getInterfaces(), this);
- // 替換系統(tǒng)am對(duì)象
- hooker.hookAms(proxy);
- }
上面以 AMSProxy 實(shí)例為參數(shù)創(chuàng)建了一個(gè)代理對(duì)象 Proxy,并用這個(gè) Proxy 對(duì)象通過(guò) hookAms 方法替換掉了 am 對(duì)象,這樣在本進(jìn)程通過(guò) ActivityManager.getService() 來(lái)調(diào)用相關(guān)方法時(shí),會(huì)調(diào)用到上述的 invoke 方法,在這可以做攔截:
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- try {
- if (callback.canIntercept(method, args)) {
- if (callback.autoRemove()) {
- // 將am對(duì)象還原
- // ...
- }
- // 攔截am的請(qǐng)求,做自己的業(yè)務(wù)處理
- return callback.intercept(origAm, method, args);
- }
- return method.invoke(origAm, args);
- } catch (Exception e) {
- e.printStackTrace();
- }
- return null;
- }
當(dāng)本進(jìn)程中有代碼嘗試通過(guò) am 來(lái)調(diào)用相關(guān)方法(比如說(shuō)startActivity等)時(shí),都會(huì)被 invoke 方法所攔截,然后通過(guò)我們?cè)O(shè)置的攔截條件(canIntercept)去選擇是否攔截。建議每次完成了攔截的業(yè)務(wù)需求后,就把原 am 對(duì)象通過(guò) hookAms 方法還原,防止的本次進(jìn)程中持續(xù)攔截系統(tǒng)請(qǐng)求。這里一直強(qiáng)調(diào)是本次進(jìn)程,顯而易見(jiàn),通過(guò)反射去替換 am 對(duì)象的方式,只會(huì)針對(duì)本進(jìn)程起作用。
Android Q
在 Android Q 上,上述 Instrumentation 中的調(diào)用變成如下:
- int result = ActivityTaskManager.getService().startActivity(whoThread, who.getBasePackageName(), intent, ...);
這變成了 ActivityTaskManager.getService():
- /** @hide */
- public static IActivityTaskManager getService() {
- return IActivityTaskManagerSingleton.get();
- }
- private static final Singleton<IActivityTaskManager> IActivityTaskManagerSingleton = new Singleton<IActivityTaskManager>() {
- protected IActivityTaskManager create() {
- final IBinder b = ServiceManager.getService(Context.ACTIVITY_TASK_SERVICE);
- return IActivityTaskManager.Stub.asInterface(b);
- }
- };
可以看到在 Android Q 上從 ActivityManager 變成了 ActivityTaskManager 系列的類(lèi),于是我們的 AmsQHooker 實(shí)現(xiàn)如下:
- class AmsQHooker : AmsHooker() {
- override fun getHookObj(): Any? {
- val amClass = ReflectUtils.getClass("android.app.ActivityTaskManager")
- // 拿到 IActivityTaskManagerSingleton 屬性
- return ReflectUtils.readStaticField(amClass, "IActivityTaskManagerSingleton")
- }
- override fun getHookField(): Field? {
- return ReflectUtils.getField(ReflectUtils.getClass("android.util.Singleton"), "mInstance")
- }
- override fun getTarget(): Any? {
- // Reflective access to getService is forbidden when targeting API 29 and above
- // val getServiceMethod = amClass.getDeclaredMethod("getService")
- return ReflectUtils.getClass("android.util.Singleton").getDeclaredMethod("get").invoke(getHookObj())
- }
- override fun getInterfaces(): Array<Class<*>> {
- return arrayOf(ReflectUtils.getClass("android.app.IActivityTaskManager"))
- }
- }
其它的步驟跟 Android P 是一樣的。
Android N
在 Android 7.1 及以下,Instrumentation 的調(diào)用又不一樣:
- int result = ActivityManagerNative.getDefault().startActivity(whoThread, who.getBasePackageName(), intent, ...);
這變成了 ActivityManagerNative.getDefault():
- static public IActivityManager getDefault() {
- return gDefault.get();
- }
- private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
- protected IActivityManager create() {
- IBinder b = ServiceManager.getService("activity");
- IActivityManager am = asInterface(b);
- return am;
- }
- };
可以看到雖然類(lèi)名和方法有所變化,但還是借助了 Singleton 類(lèi),所以只需要繼承 AmsHooker 重寫(xiě)相關(guān)方法即可:
- class AmsNHooker : AmsHooker() {
- override fun getHookObj(): Any? {
- val amNativeClass = ReflectUtils.getClass("android.app.ActivityManagerNative")
- // 獲取gDefault實(shí)例
- return ReflectUtils.readStaticField(amNativeClass, "gDefault")
- }
- override fun getHookField(): Field? {
- return ReflectUtils.getField(ReflectUtils.getClass("android.util.Singleton"), "mInstance")
- }
- override fun getTarget(): Any? {
- return getHookField()?.get(getHookObj())
- }
- override fun getInterfaces(): Array<Class<*>> {
- return arrayOf(ReflectUtils.getClass("android.app.IActivityManager"))
- }
- }
其它的也是重用 Android P 上的邏輯。
總結(jié)
通過(guò)上面的方式,可以實(shí)現(xiàn) 在本進(jìn)程內(nèi)攔截通過(guò) AMS 的 Binder 代理調(diào)用的相關(guān)方法,可以用來(lái)實(shí)現(xiàn)一些非常規(guī)的功能,雖然最近做的需求都比較非常規(guī)(liumang),不過(guò)拋開(kāi)需求,對(duì)于開(kāi)發(fā)而言去調(diào)研這些技術(shù),還是挺有意思的..哈~
寫(xiě)博客是一件有意思有收獲也有難度的事情,需要爭(zhēng)取把文章的脈絡(luò)和邏輯梳理清楚,怎么寫(xiě)才能把文章寫(xiě)得更加清晰易懂,又有好久沒(méi)更新了,最近太忙了都沒(méi)有時(shí)間做這些事情,想到自己寫(xiě)的文章(可)能被點(diǎn)贊,瞬間又有了動(dòng)力,于是忙里偷魚(yú)寫(xiě)了一篇,內(nèi)容沒(méi)啥深度,就當(dāng)平時(shí)的開(kāi)發(fā)筆記吧。