Android Hook原理与动态代理实战

文章摘要

Android Hook技术通过拦截和修改对象行为实现功能扩展,主要有静态代理和动态代理两种方式。动态代理利用反射和InvocationHandler,在运行时替换系统对象,侵入性低且灵活。典型应用如点击事件Hook,通过反射获取View的ListenerInfo并替换OnClickListener,在不影响原逻辑的情况下插入自定义代码。Hook技术广泛应用于插件化、埋点统计等领域,但需注意Android版本限制、反射性能损耗和安全风险。其核心在于准确找到目标对象及其持有者,并安全完成替换操作。


一、Android Hook 原理简述

Hook,本质上就是在不修改原有代码的情况下,拦截并插入自己的逻辑
在Android中,常见的Hook方式有两种:

  1. 静态代理:需要插件或目标类实现特定接口或继承特定父类,侵入性较强。例如插件Activity必须继承PluginActivity
  2. 动态代理/反射Hook:通过反射、动态代理等手段,在运行时替换系统或第三方对象,插入自定义逻辑,侵入性低,灵活性高。

二、Hook的通用步骤

  1. 确定要Hook的对象(如View.OnClickListener、AMS、PMS等)
  2. 找到持有该对象的宿主(如View的ListenerInfo)
  3. 获取原始对象(通过反射拿到原OnClickListener等)
  4. 创建代理对象(静态代理或动态代理)
  5. 用代理对象替换原对象(反射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手段
  • 关键在于找到目标对象、持有者,并安全地替换

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

你一身傲骨怎能输

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值