最近在读包建强老师的《Android插件化开发指南》一书,在读到第4章对于ActivityManager hook时,有点小启发。先看hook的代码(基于Android9.0源码,兼容Android7.0之前的版本代码需改动):
public class HookHelper {
public static void hookActivityManager() {
try {
Class<?> amClass = Class.forName("android.app.ActivityManager");
Field gDefaultField = amClass.getDeclaredField("IActivityManagerSingleton");
gDefaultField.setAccessible(true);
// 这个iActivityManagerSingleton对象实际上就是一个Singleton对象
Object iActivityManagerSingleton = gDefaultField.get(null);
// 反射Singleton类
@SuppressLint("PrivateApi")
Class<?> singleClass = Class.forName("android.util.Singleton");
Field instanceField = singleClass.getDeclaredField("mInstance");
instanceField.setAccessible(true);
Object rawIActivityManager = instanceField.get(iActivityManagerSingleton);
// 注释留空处
@SuppressLint("PrivateApi")
Class<?> iActivityManagerInterface = Class.forName("android.app.IActivityManager");
Object newProxyInstance = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[]{iActivityManagerInterface},
new HookHandler(rawIActivityManager));
// 将IActivityManagerSingleton中的mInstance字段替换成proxy对象
instanceField.set(iActivityManagerSingleton, newProxyInstance);
} catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
}
}
public class HookHandler implements InvocationHandler {
private Object mBase;
public HookHandler(Object base){
this.mBase = base;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Log.e("TAG", "-> before invoke....");
Object result = method.invoke(mBase, args);
Log.e("TAG", "-> method name -> " + method.getName());
Log.e("TAG", "-> after invoke....");
return result;
}
}
基础知识点就是反射和动态代理(不熟悉的童鞋可参看我的之前一篇文章)的使用。紧接着,我在MainActivity中调用了hookActivityManager方法,然后运行app,查看Log:
哇塞,hook成功了,好强大!但是当我第二次再打开app时,发现了一个问题:
发现会调用两次。第三次打开app呢?
会调用三次。
这是怎么回事呢?因为我们是在MainActivity中调用的,每次打开app都会进行一次hook,这样当第二次hook时,实际上是对第一次hook的代理对象又一次进行代理封装,此时相当于有两层代理对象了。同样地,第三次打开app时就会有三层代理对象了。所以,InvocationHandler的invoke方法会输出多次。我们可以试图来解决这个问题。
在前面代码的注释留空处添加:
if (Proxy.isProxyClass(rawIActivityManager.getClass())) {
return;
}
这样就会hook一次。(这里建议将我们的demo卸载掉,重新安装)
##### 2020.06.12更新
评论区有小伙伴问到:instanceField.get(iActivityManagerSingleton);这个地方为什么要传入iActivityManagerSingleton。本来在评论区已经回复了,但是有些字打错,然后一不小心就把评论删了。这里直接在文章中解释了:实际上我们这里好像剥洋葱一样,先拿到ActivityManager中的IActivityManagerSingleton静态属性:
private static final Singleton<IActivityManager> IActivityManagerSingleton =
new Singleton<IActivityManager>() {
@Override
protected IActivityManager create() {
final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
final IActivityManager am = IActivityManager.Stub.asInterface(b);
return am;
}
};
由于IActivityManagerSingleton是Singleton类型对象,而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;
}
}
}
所以instanceField.get(iActivityManagerSingleton); 这行代码依据就在这里,就是为了拿到Singleton中的mInstance指向的对象。