Android Hook技术初探

原谅我见识短浅,Android竟然还能这么玩!!

什么是Hook

首先我们来了解一下,什么是Hook?Hook有什么用?

我们都知道有一种设计模式 - 模板方法模式,原理是父类定义多个方法,但是不实现任何内容,然后将这些方法组合起来,来实现某种功能。
其中的没有实现的方法我们就称之为“Hook”钩子方法,主要目的是留给子类定制自己的东西。

而这里的Hook有点不一样,运用反射技术和代理模式,以实现改变Android系统的API的功能,这里称为API Hook技术。

网上说的都是这么一个例子,因为比较容易记住,所以我也打算用这个例子。说是要在所有View的点击事件都要打印出日志。依我们正常的做法,挨个找出View的setOnClickListener方法,在设置的监听器中都加上打印日志。但是,项目中如果有很多很多个类,然后还有很多很多的View,估计程序不崩,你都崩了。

这里主要的解决办法是,统一处理OnClickListener监听器的onClick方法。
基本思路是创建一个监听器的代理HookOnClickListener,替换原来设置的OnClickListener监听器,然后在代理监听器HookOnClickListener的onClick方法去实现打印日志和原来OnClickListener监听器的onClick方法。如此一来,我们即使不改变原来OnClickListener监听器的onClick方法内容,也可以实现代码“入侵”到onClick方法中。

怎么Hook

具体怎么实现API Hook技术呢?

由于我们要改的是API的功能,所以得从源码开始了解。先看看View的setOnClickListener方法源码:

// View 源码
...
static class ListenerInfo {
    ...
    public OnClickListener mOnClickListener;
    ...
}
...
ListenerInfo mListenerInfo;
...
public void setOnClickListener(@Nullable OnClickListener l) {
    if (!isClickable()) {
        setClickable(true);
    }
    getListenerInfo().mOnClickListener = l;
}
...
ListenerInfo getListenerInfo() {
    if (mListenerInfo != null) {
        return mListenerInfo;
    }
    mListenerInfo = new ListenerInfo();
    return mListenerInfo;
}
...

从源码我们知道两个信息:

  1. setOnClickListener方法把我们设置的“l”赋给了ListenerInfo的mOnClickListener属性,好,我们只要把ListenerInfo的mOnClickListener替换为代理对象,就可以实现了。

  2. ListenerInfo对象时从getListenerInfo方法得到的,看到没有,mListenerInfo是在实例中是不会改变的。我们把ListenerInfo作为Hook对象。

知道了这两个东西,我们就可以开始撸代码了。

先定义代理对象,实现打印日志和执行原来监听器的onClick方法:

static class HookOnClickListener implements View.OnClickListener {
    private View.OnClickListener origin;
    public HookOnClickListener(View.OnClickListener origin) {
        this.origin = origin;
    }
    @Override
    public void onClick(View v) {
        System.err.println("before view click");
        origin.onClick(v);
        System.err.println("after view click");
    }
}

再来,我们创建代理对象,来代替原来的监听器:

private void hookOnClick(View view) {
    try {
        // 1 获取Hook对象,这里是ListenerInfo实例
        // 源码中,ListenerInfo实例是从getListenerInfo方法获取的,
        // 但是getListenerInfo方法不是公开的,所以我们通过反射调用getListenerInfo方法来获取ListenerInfo实例
        // 1.1 获取View的Class
        Class viewClass = Class.forName("android.view.View");
        // 1.2 获取View的getListenerInfo方法,getDeclaredMethod面向所有方法,包括private和protected
        Method getListenerInfoMethod = viewClass.getDeclaredMethod("getListenerInfo");
        // 1.3 改变getListenerInfo方法的可见性,设置为可访问
        getListenerInfoMethod.setAccessible(true);
        // 1.4 反射调用getListenerInfo方法得到ListenerInfo实例
        Object listenerInfoObject = getListenerInfoMethod.invoke(view);
        // 2 获取Hook对象中要被代理的对象,这里是ListenerInfo的mOnClickListener
        // 也就是我们设置的监听器,因为我们要在监听中改变行为
        // 2.1 获取ListenerInfo的class,由于ListenerInfo是View的内部类,所以需要这么写类名:android.view.View$ListenerInfo
        Class listenerInfoClass = Class.forName("android.view.View$ListenerInfo");
        // 2.2 获取ListenerInfo的mOnClickListener字段,getDeclaredField面向所有字段,包括private和protected
        Field onClickListenerField = listenerInfoClass.getDeclaredField("mOnClickListener");
        // 2.3 改变onClickListenerField字段的可见性,设置为可访问
        onClickListenerField.setAccessible(true);
        // 2.4 获取ListenerInfo的mOnClickListener字段的值
        View.OnClickListener originOnClickListener = (View.OnClickListener) onClickListenerField.get(listenerInfoObject);
        // 3 创建代理对象,并代替Hook对象需要改变的字段,这里我们要代替ListenerInfo原来的mOnClickListener
        // 3.1 创建代理对象
        View.OnClickListener hookOnClickListener = new HookOnClickListener(originOnClickListener);
        // 3.2 代理对象代替原来对象
        onClickListenerField.set(listenerInfoObject, hookOnClickListener);
    } catch (Exception exception) {
        exception.printStackTrace();
        System.err.println(exception.getMessage());
    }
}

相信代码注释足够解析整个代替流程,主要有3个步骤:

  1. 获取Hook对象。Hook对象的选取很重要,一般选单例对象或者静态变量。

  2. 获取Hook对象中要被代理的对象,也就是你想要改变的对象。

  3. 创建代理对象,并代替Hook对象中要被代理的对象。

使用:

Button clickButton = (Button) findViewById(R.id.click_button);
clickButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        System.err.println("view clicking");
    }
});
hookOnClick(clickButton);

打印结果:

W/System.err: before view click
W/System.err: view clicking
W/System.err: after view click

这只是我对Android Hook技术的初探,如有错误,请不吝赐教,谢谢!!

参考资料

理解 Android Hook 技术以及简单实战
Android Hook 全面入侵监听器

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值