最近看Android招聘信息,发现一个自己不太会的点 hook 机制,查了一下资料,现在整理一下。特别感谢acxingyun
同学提供了详细的姿势。点击地址
什么是 Hook
hook
翻译过来是钩子的意思。 目的就是在事件传送到终点前截获并监控事件的传输,像个钩子钩上事件一样,并且能够在钩上事件时,处理一些自己特定的事件。而代理模式正好可以做到这种效果。
代理对象
了解了hook
的目的,下面就是怎样勾上整个事件,来监测事件的每一步。
拿 Activity 来举例,Activity 的相关的启动方法在IActivityManager接口里面都已经定义好了,但正常情况下我们无法获取相关的对象的信息,更无法修改,所以在这里我们要借用反射来获取到相应的 Class 对象,将IActivityManager的实例替换为我们自己的代理对象,在代理对象里面去做我们想做的事情。
无图无真相。上图
ActivityManagerNative
的实例持有一个Singleton<IActivityManager>
类型的对象 gDefault
,gDefault
持有IActivityManager
的实例mInstance
,
正常状态持有的mInstance
实例 我们通过 Proxy.newProxyInstance()
做出一个代理对象来,再利用反射机制重新给 gDefault
对象,这样我们就能在InvocationHandler
中监听整个事件并处理。
具体实现
public class APIHook {
/**
* ActivityManager 替换为代理
* @throws ClassNotFoundException
* @throws NoSuchFieldException
* @throws IllegalAccessException
*/
public void hookAM() throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
Class<?> amnClass = Class.forName("android.app.ActivityManagerNative");
//获取单例对象 Singleton<IActivityManager> ,变量名 gDefault 私有
Field gDefaultField = amnClass.getDeclaredField("gDefault");
//禁止JAVA 进行语言访问检查,private 等修饰的就可以访问操作了
gDefaultField.setAccessible(true);
//If the underlying field is a static field, the obj argument is ignored; it may be null.
//静态属性,直接传入 Null。获取ActivityManagerNative 中的 gDefault
Object gDefault = gDefaultField.get(null);
Class<?> singletonClass = Class.forName("android.util.Singleton");
Field mInstanceField = singletonClass.getDeclaredField("mInstance");
mInstanceField.setAccessible(true);
//调用 Singleton 的 get方法 取出 instance 对象
//instance 对象即 ActivityManager
Object instance = mInstanceField.get(gDefault);
//创建代理 handler
ActivityManagerHandler handler = new ActivityManagerHandler(instance);
//反射 IActivityManager接口 Class 文件
Class<?> amClass = Class.forName("android.app.IActivityManager");
//创建代理对象
Object amProxy = Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{
amClass}, handler);
//将gDefault中的 Instance 置换为代理
mInstanceField.set(gDefault,amProxy);
}
/**
* 代理对象回调
*/
private class ActivityManagerHandler implements InvocationHandler {
private Object am;
public ActivityManagerHandler(Object am) {
this.am = am;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Log.e("APIHook","正在调用的方法--->"+method.getName());
return method.invoke(am,args);
}
}
}
知识点:对于 private 修饰的变量,我们需要调用
Field.setAccessible(true);
来禁止 JAVA 语法检查才能给变量赋值哟
这个代码是可以直接用的,把玩时在 Application 的 onCreate 中调用
new APIHook().hookAM();
就写到这里啦,权当又复习了一遍代理和反射,大家一起加油啊