文章摘要
Android Hook技术通过拦截和修改对象行为实现功能扩展,主要有静态代理和动态代理两种方式。动态代理利用反射和InvocationHandler,在运行时替换系统对象,侵入性低且灵活。典型应用如点击事件Hook,通过反射获取View的ListenerInfo并替换OnClickListener,在不影响原逻辑的情况下插入自定义代码。Hook技术广泛应用于插件化、埋点统计等领域,但需注意Android版本限制、反射性能损耗和安全风险。其核心在于准确找到目标对象及其持有者,并安全完成替换操作。
一、Android Hook 原理简述
Hook,本质上就是在不修改原有代码的情况下,拦截并插入自己的逻辑。
在Android中,常见的Hook方式有两种:
- 静态代理:需要插件或目标类实现特定接口或继承特定父类,侵入性较强。例如插件Activity必须继承
PluginActivity
。 - 动态代理/反射Hook:通过反射、动态代理等手段,在运行时替换系统或第三方对象,插入自定义逻辑,侵入性低,灵活性高。
二、Hook的通用步骤
- 确定要Hook的对象(如View.OnClickListener、AMS、PMS等)
- 找到持有该对象的宿主(如View的ListenerInfo)
- 获取原始对象(通过反射拿到原OnClickListener等)
- 创建代理对象(静态代理或动态代理)
- 用代理对象替换原对象(反射set回去)
三、静态代理 vs 动态代理
- 静态代理:自己写一个代理类实现目标接口,手动转发方法调用。
侵入性强,代码量大,适合接口简单、方法少的场景。 - 动态代理:用
Proxy.newProxyInstance
动态生成代理对象,所有方法调用都走InvocationHandler
。
侵入性低,适合接口多、方法多的场景,代码更简洁。
四、点击事件Hook完整流程(以动态代理为例)
1. 目标:在不影响原有点击事件的情况下,插入自己的逻辑
2. 关键源码分析
View.setOnClickListener()
其实是把listener赋值给了ListenerInfo.mOnClickListener
ListenerInfo
是View的内部类,View持有它的引用
3. Hook代码流程
public static void hook(Context context, final View v) {
try {
// 1. 反射获取ListenerInfo对象
Method getListenerInfo = View.class.getDeclaredMethod("getListenerInfo");
getListenerInfo.setAccessible(true);
Object listenerInfo = getListenerInfo.invoke(v);
// 2. 反射获取mOnClickListener字段
Class<?> listenerInfoClz = Class.forName("android.view.View$ListenerInfo");
Field mOnClickListenerField = listenerInfoClz.getDeclaredField("mOnClickListener");
mOnClickListenerField.setAccessible(true);
// 3. 获取原始OnClickListener
final View.OnClickListener originalListener =
(View.OnClickListener) mOnClickListenerField.get(listenerInfo);
// 4. 创建动态代理
View.OnClickListener hookedListener = (View.OnClickListener) Proxy.newProxyInstance(
context.getClassLoader(),
new Class[]{View.OnClickListener.class},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 插入自定义逻辑
Log.d("Hook", "点击事件被hook到了!");
Toast.makeText(v.getContext(), "Hook逻辑", Toast.LENGTH_SHORT).show();
// 调用原有逻辑
if (originalListener != null) {
return method.invoke(originalListener, args);
}
return null;
}
});
// 5. 替换原有OnClickListener
mOnClickListenerField.set(listenerInfo, hookedListener);
} catch (Exception e) {
e.printStackTrace();
}
}
4. 使用方式
View v = findViewById(R.id.tv);
v.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this, "doing....", Toast.LENGTH_SHORT).show();
}
});
hook(this, v);
效果:点击时,先弹出“Hook逻辑”,再弹出“doing…”。
五、常见Hook场景
- 插件化框架:Hook AMS/PMS/Instrumentation,实现未注册Activity的启动
- 埋点/统计:Hook View.OnClickListener、Fragment/Activity生命周期
- 权限/安全:Hook系统服务,拦截敏感API调用
- 热修复:Hook ClassLoader、Method等
六、注意事项
- Android 9.0+对反射和非公开API有更严格限制,部分Hook方式可能失效
- 动态代理只适用于接口,不能代理普通类
- 反射性能有损耗,注意使用场景
- Hook属于“黑科技”,有兼容性和安全风险,生产环境需谨慎
七、总结
- Hook是Android插件化、热修复、埋点等技术的基础
- 动态代理+反射是最常用的Hook手段
- 关键在于找到目标对象、持有者,并安全地替换