Android Hook入门

Hook 英文意思是钩子,可以把一段执行着的代码钩下来,然后加入我们自己的逻辑,最后在放回去。比如我们可以Hook住一段系统代码,在执行系统代码之前加入我们自己的逻辑。

Hook技术主要用到java反射和java动态代理两个知识点,下面来个简单的例子,我们来Hook一个按钮的点击事件

 Button button = findViewById(R.id.btn_click);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(getApplicationContext(),((Button) v).getText(),Toast.LENGTH_SHORT).show();
            }
        });

点击按钮的时候,我们如何能在不改变上面代码的前提下,在Toast弹出之前执行一些别的逻辑呢?比如现在Toast弹出的是按钮上的字,我们动态的把子给它改了

思路就是我们通过反射拿到系统中的OnClickListener的对象,通过动态代理,创建一个该对象的代理对象,这个代理对象中就可以写一些别的逻辑啦,最后把这个代理对象通过反射设置回系统中来个偷梁换柱就可以啦。

OK下面开始按照步骤来

第一步

反射拿到设置的OnClickListener对象,上面的代码中,我们进入setOnClickListener方法可以看到

 public void setOnClickListener(@Nullable OnClickListener l) {
        if (!isClickable()) {
            setClickable(true);
        }
        getListenerInfo().mOnClickListener = l;
    }

这里把我们传进来的回调对象设置给了getListenerInfo().mOnClickListener。

  ListenerInfo getListenerInfo() {
        if (mListenerInfo != null) {
            return mListenerInfo;
        }
        mListenerInfo = new ListenerInfo();
        return mListenerInfo;
    }

getListenerInfo()方法返回一个ListenerInfo对象,它内部就保存了我们传过来的mOnClickListener回调对象。我们功过反射执行getListenerInfo方法就能拿到ListenerInfo对象了如下

        Class<?> viewClass = Class.forName("android.view.View");
        //需要拿到setOnClickListener方法set过去的对象
        Method getListenerInfoMethod = viewClass.getDeclaredMethod("getListenerInfo");
        getListenerInfoMethod.setAccessible(true);
        //本质是ListenerInfo对象
        Object listenerInfo = getListenerInfoMethod.invoke(view);

拿到了ListenerInfo对象,在通过它拿到它的成员变量mOnClickListener

     Class<?> listenerInfoClass = Class.forName("android.view.View$ListenerInfo");
     Field onClickListenerField = listenerInfoClass.getField("mOnClickListener");
     final Object onClickListener = onClickListenerField.get(listenerInfo);

第二步

使用动态代理定义一个代理的onClickListener,在回调函数中添加一些别的逻辑

   Object proxyClickListener = Proxy.newProxyInstance(MainActivity.class.getClassLoader(),
                    //需要将监听的方法
                    new Class[]{View.OnClickListener.class}, new InvocationHandler() {
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            Button button = new Button(MainActivity.this);
                            button.setText("我就点你");
                            return method.invoke(onClickListener, button);
                        }
                    });

在InvocationHandler方法中就可以添加别的逻辑了,这里添加了Toast用来测试,最后返回一个代理的OnClickListener对象。

第三步

把代理的OnClickListener对象设置回系统中,在第一步中已经拿到了mOnClickListener对象,最后把它给改了就好了。

 onClickListenerField.set(listenerInfo,proxyClickListener);

OK Hook完成,效果
在这里插入图片描述
完整代码:

/**
     * hook
     * @param view 需要hook的view
     */
    private void hook(View view) {
        try {
            Class<?> viewClass = Class.forName("android.view.View");
            //需要拿到setOnClickListener方法set过去的对象
            Method getListenerInfoMethod = viewClass.getDeclaredMethod("getListenerInfo");
            getListenerInfoMethod.setAccessible(true);
            Object listenerInfo = getListenerInfoMethod.invoke(view);

            Class<?> listenerInfoClass = Class.forName("android.view.View$ListenerInfo");
            Field onClickListenerField = listenerInfoClass.getField("mOnClickListener");
            final Object onClickListener = onClickListenerField.get(listenerInfo);

            Object proxyClickListener = Proxy.newProxyInstance(MainActivity.class.getClassLoader(),
                    //需要将监听的方法
                    new Class[]{View.OnClickListener.class}, new InvocationHandler() {
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            Button button = new Button(MainActivity.this);
                            button.setText("我就点你");
                            return method.invoke(onClickListener, button);
                        }
                    });
            onClickListenerField.set(listenerInfo,proxyClickListener);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值