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