【干货篇】Android各版本拦截进程对AMS的请求实战,墙裂建议收藏(1)

int result = ActivityManager.getService().startActivity(whoThread, who.getBasePackageName(), intent, …);

这里通过 Binder 跨进程调用到 AMS 中的相关方法,看一下 ActivityManager.getService() 的实现:

/** @hide */

public static IActivityManager getService() {

return IActivityManagerSingleton.get();

}

private static final Singleton IActivityManagerSingleton = new Singleton() {

@Override

protected IActivityManager create() {

// 1…

}

};

可以看到 IActivityManagerSingleton 是 Singleton 类型的实例,很显然这个 Singleton 是一个懒加载的单例模板类:

public abstract class Singleton {

private T mInstance;

protected abstract T create();

public final T get() {

synchronized (this) {

if (mInstance == null) {

mInstance = create();

}

return mInstance;

}

}

}

于是可以知道 IActivityManagerSingleton.get() 返回的便是 create 方法中的实例,给出上面 1 处省略的 create 方法代码:

final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);

final IActivityManager am = IActivityManager.Stub.asInterface(b);

return am;

熟悉 Binder 的同学一眼就能看出这里的 am 是一个 Binder 代理对象,存在 ServiceManager.getService 方法就肯定存在 ServiceManager.addService 方法,一个是从 ServiceManager 中查询 Binder 服务,一个是往 ServiceManager 中注册服务,注册的时机在系统启动 system_server 进程的时候,参考 AMS启动流程,这里就不深入描述了。

所以 ActivityManager.getService() 方法其实就是返回了 AMS 的一个 Binder 代理对象,用来跨进程调用 AMS 相关方法,因此可以通过 JDK 动态代理的方式,通过 Proxy.newProxyInstance 方法创建 am 的代理 Proxy 对象,并通过反射的方式将 ActivityManager.getService() 方法返回的 am 对象替换成我们的 Proxy 对象,那么在 App 进程调用 ActivityManager.getService().XXX 方法时都会被我们的 Proxy 拦截到,进而做一些处理。JDK 动态代理也是 Java 常用的设计模式之一,不太熟悉的同学可以参考 Jdk动态代理 的使用。

这个过程可以分成三个步骤:

  1. 反射获取 am 对象,由于 ActivityManager.getService() 是一个隐藏方法,因此可以通过反射调用它拿到原 am 对象;

  2. 创建代理对象Proxy;

  3. 通过反射用 Proxy 替换 am 对象;

我们看到 am 对象其实就是 Singleton(其实例是IActivityManagerSingleton) 中的 mInstance 属性,因此第三步只需通过反射将 mInstance 属性设置为我们的 Proxy 对象即可,下面的 AmsHooker 是一个抽象类,在不同的 Android 平台上有不同的实现,主要用来获取不同 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实例

protected abstract fun getHookObj(): Any?

// 即mInstance

protected abstract fun getHookField(): Field?

// 即am

abstract fun getTarget(): Any?

// 接口,用来创建Proxy

abstract fun getInterfaces(): Array<Class<*>>

}

在 Android 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,用来创建动态代理

override fun getInterfaces(): Array<Class<*>> {

return arrayOf(ReflectUtils.getClass(“android.app.IActivityManager”))

}

}

接下来创建代理类(代码有删减):

public class AMSProxy implements InvocationHandler {

private AmsHooker hooker; // 根据不同 Android 平台返回不同实现

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();

}

}

@Override

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

// …

}

// 创建代理

Object proxy = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),

hooker.getInterfaces(), this);

// 替换系统am对象

hooker.hookAms(proxy);

}

上面以 AMSProxy 实例为参数创建了一个代理对象 Proxy,并用这个 Proxy 对象通过 hookAms 方法替换掉了 am 对象,这样在本进程通过 ActivityManager.getService() 来调用相关方法时,会调用到上述的 invoke 方法,在这可以做拦截:

@Override

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

try {

if (callback.canIntercept(method, args)) {

if (callback.autoRemove()) {

// 将am对象还原

// …

}

// 拦截am的请求,做自己的业务处理

return callback.intercept(origAm, method, args);

}

return method.invoke(origAm, args);

} catch (Exception e) {

e.printStackTrace();

}

return null;

}

当本进程中有代码尝试通过 am 来调用相关方法(比如说startActivity等)时,都会被 invoke 方法所拦截,然后通过我们设置的拦截条件(canIntercept)去选择是否拦截。建议每次完成了拦截的业务需求后,就把原 am 对象通过 hookAms 方法还原,防止的本次进程中持续拦截系统请求。这里一直强调是本次进程,显而易见,通过反射去替换 am 对象的方式,只会针对本进程起作用。

Android Q

========================================================================

在 Android Q 上,上述 Instrumentation 中的调用变成如下:

int result = ActivityTaskManager.getService().startActivity(whoThread, who.getBasePackageName(), intent, …);

这变成了 ActivityTaskManager.getService():

/** @hide */

最后

我这里整理了一份完整的学习思维以及Android开发知识大全PDF。

当然实践出真知,即使有了学习线路也要注重实践,学习过的内容只有结合实操才算是真正的掌握。

====

在 Android Q 上,上述 Instrumentation 中的调用变成如下:

int result = ActivityTaskManager.getService().startActivity(whoThread, who.getBasePackageName(), intent, …);

这变成了 ActivityTaskManager.getService():

/** @hide */

最后

我这里整理了一份完整的学习思维以及Android开发知识大全PDF。

[外链图片转存中…(img-JP4C9gW9-1720110602250)]

当然实践出真知,即使有了学习线路也要注重实践,学习过的内容只有结合实操才算是真正的掌握。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值