带你了解Hook技术

  • Post author:
  • Post category:其他




什么是Hook

在这里插入图片描述

Hook 是“钩子”的意思,可以在事件传送的过程中截获并监控事件的传输,可以将我们的代码与系统方法进行融合。当这些系统方法被调用时,也会执行我们的代码。面向切面编程的思想(AOP)也是利用这个原理。



Hook 分类

Android有基于Android SDK的Java语言开发和基于Android NDK的Native C/C++语言开发,因此有:

  • Java层级的Hook
  • Native层级的Hook

根据Hook到的对象与Hook后处理事件方式的不同可以分为:

  • 消息Hook
  • API Hook

根据Hook到的进程的不同分为:

  • 全局Hook
  • 单个进程Hook

Hook的原理就是改变目标函数的指向。Hook技术无论对安全软件还是恶意软件都是十分关键的一项技术,其本质就是劫持函数调用。


Hook技术的难点是如何找到函数的入口点、替换函数,这就涉及了理解函数的连接与加载机制。

对于Android的开发来说,

hook Native层关键是要理解好ELF文件

(linux平台上的可执行文件),hook Java层关键是要了解虚拟机的特性和Java反射技术。



Hook例子


Hook 过程


  • 寻找 Hook 点

    ,原则是尽可能是静态变量或者单例对象,因为它们容易定位,其次是尽量 Hook public 的对象和方法。

  • 选择合适的代理方式

    ,如果是接口可以用动态代理。

  • 用代理对象替换原始对象

我们现在来拦截android控件的点击事件:



第一步:寻找 Hook 点

分析View.java代码:

public class View implements Drawable.Callback, KeyEvent.Callback,
        AccessibilityEventSource {
	...
    static class ListenerInfo {
          /**
         * Listener used to dispatch click events.
         * This field should be made private, so it is hidden from the SDK.
         * {@hide}
         */
        public OnClickListener mOnClickListener;

      	...
    }

    ListenerInfo mListenerInfo;

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

    public void setOnClickListener(@Nullable OnClickListener l) {
        if (!isClickable()) {
            setClickable(true);
        }
        getListenerInfo().mOnClickListener = l;
    }
     /**
     * Call this view's OnClickListener, if it is defined.  Performs all normal
     * actions associated with clicking: reporting accessibility event, playing
     * a sound, etc.
     *
     * @return True there was an assigned OnClickListener that was called, false
     *         otherwise is returned.
     */
    // NOTE: other methods on View should not call this method directly, but performClickInternal()
    // instead, to guarantee that the autofill manager is notified when necessary (as subclasses
    // could extend this method without calling super.performClick()).
    public boolean performClick() {
        // We still need to call this method to handle the cases where performClick() was called
        // externally, instead of through performClickInternal()
        notifyAutofillManagerOnClick();

        final boolean result;
        final ListenerInfo li = mListenerInfo;
        if (li != null && li.mOnClickListener != null) {
            playSoundEffect(SoundEffectConstants.CLICK);
            li.mOnClickListener.onClick(this);
            result = true;
        } else {
            result = false;
        }

        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);

        notifyEnterOrExitForAutoFillIfNeeded(true);

        return result;
    }
    /**
     * Register a callback to be invoked when this view is clicked. If this view is not
     * clickable, it becomes clickable.
     *
     * @param l The callback that will run
     *
     * @see #setClickable(boolean)
     */
    public void setOnClickListener(@Nullable OnClickListener l) {
        if (!isClickable()) {
            setClickable(true);
        }
        getListenerInfo().mOnClickListener = l;
    }
        /**
     * Interface definition for a callback to be invoked when a view is clicked.
     */
    public interface OnClickListener {
        /**
         * Called when a view has been clicked.
         *
         * @param v The view that was clicked.
         */
        void onClick(View v);
    }
}

通过分析 View.java的代码,我们可以知道setOnClickListener注册的OnClickListener 对象被保存在ListenerInfo内部类对象mListenerInfo里。ListenerInfo 里面保存了 View 的各种监听事件。因此,我们要劫持点击事件,就要hook得到 ListenerInfo对象里的mOnClickListener ,hook得到以后,用 Hook代理类替换原始的 OnClickListener,就可以实现我们的目的了。



第二步:选择代理方式

public class HookOnClickListenerProxy implements View.OnClickListener {
    private View.OnClickListener origin;
    public HookOnClickListenerProxy(View.OnClickListener origin){
        this.origin = origin;
    }
    @Override
    public void onClick(View v) {
        Toast.makeText(v.getContext(), "Hook Click Listener", Toast.LENGTH_SHORT).show();

        if(origin != null){
            origin.onClick(v);
        }
    }
}

可以看出我们的代理类做了一件事,就是先提示“Hook Click Listener”,然后再继续执行原先的东西。



第三步:用代理对象替换原始对象

		// 第一步:反射得到 ListenerInfo 对象
        Method getListenerInfo = View.class.getDeclaredMethod("getListenerInfo"); 
        getListenerInfo.setAccessible(true);
        Object listenerInfo = getListenerInfo.invoke(view);
        
        // 第二步:得到原始的 OnClickListener事件方法
        Class<?> listenerInfoClz = Class.forName("android.view.View$ListenerInfo");
        Field mOnClickListener = listenerInfoClz.getDeclaredField("mOnClickListener");
        mOnClickListener.setAccessible(true);
        View.OnClickListener originOnClickListener = (View.OnClickListener)mOnClickListener.get(listenerInfo);
        
        // 第三步:用 Hook代理类替换原始的 OnClickListener
        View.OnClickListener hookedOnClickListener = new HookOnClickListenerProxy(originOnClickListener);
        mOnClickListener.set(listenerInfo,hookedOnClickListener);

Hook的过程,我们就演示了一遍了。

demo示例


Hook通知栏,劫持通知栏的消息的demo




Hook剪贴板demo



Hook 框架

  • Xposed
  • Cydia Substrate

  • Legend


    Legend 是 Android 免 Root 环境下的一个 Apk Hook 框架,该框架代码设计简洁,通用性高,适合逆向工程时一些 Hook 场景。大部分的功能都放到了 Java 层,兼容性非常好。

    原理是直接构造出新旧方法对应的虚拟机数据结构,然后替换信息写到内存中即可。

谢谢阅读!



版权声明:本文为weixin_40763897原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。