您的位置:

Windows Hook详解

一、概述

Windows是非常流行的操作系统,其提供了非常多的API来实现各种功能。其中,hook是Windows系统中的一个重要机制,可以用来监视和影响系统中发生的事件。

通俗地说,hook就是一种机制,通过这种机制可以将自己的函数挂载到指定的Windows事件上,从而实现对这些事件的监视和控制。这样一来,当Windows系统发生特定事件时,挂载在该事件上的函数就会被自动调用。hook机制广泛应用于Windows系统中各种安全软件、GUI界面程序等,是Windows开发过程中非常重要的机制之一。

二、hook的类型

Windows中提供了不同类型的hook,主要分为以下三类:

1. 系统级hook

系统级hook也称为全局hook,可以监视系统中的所有进程。系统级hook需要使用Win32 API中的SetWindowsHookEx函数来实现。系统级hook分为以下三种类型:

- WH_KEYBOARD

WH_KEYBOARD hook允许你监视键盘消息。

HHOOK SetWindowsHookEx(
  int       idHook,
  HOOKPROC  lpfn,
  HINSTANCE hMod,
  DWORD     dwThreadId
);

- WH_MOUSE

WH_MOUSE hook允许你监视鼠标消息。

HHOOK SetWindowsHookEx(
  int       idHook,
  HOOKPROC  lpfn,
  HINSTANCE hMod,
  DWORD     dwThreadId
);

- WH_CALLWNDPROC

WH_CALLWNDPROC hook允许你监视指定的窗口。

HHOOK SetWindowsHookEx(
  int       idHook,
  HOOKPROC  lpfn,
  HINSTANCE hMod,
  DWORD     dwThreadId
);

2. 线程级hook

线程级hook也称为本地hook,只能监视指定线程的事件。线程级hook需要使用Win32 API中的SetWindowsHookEx函数来实现。

3. 子类化钩子

子类化钩子是一种特殊的hook,可以替换窗口中的原有消息处理函数。子类化钩子需要使用Win32 API中的SetWindowLongPtrA函数来实现。

三、hook的实现

1. WH_KEYBOARD hook的实现

WH_KEYBOARD hook可以用来监视键盘消息。

LRESULT CALLBACK KeyboardProc(int code, WPARAM wParam, LPARAM lParam) {
    if (code < 0) {
        return CallNextHookEx(NULL, code, wParam, lParam);
    }

    switch (wParam) {
        case VK_F1:
            //按下F1键,执行一些操作
            break;

        case VK_F2:
            //按下F2键,执行一些操作
            break;

        default:
            break;
    }

    return CallNextHookEx(NULL, code, wParam, lParam);
}

void SetKeyboardHook() {
    HHOOK hook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, NULL, GetCurrentThreadId());
}

2. WH_MOUSE hook的实现

WH_MOUSE hook可以用来监视鼠标消息。

LRESULT CALLBACK MouseProc(int code, WPARAM wParam, LPARAM lParam) {
    if (code < 0) {
        return CallNextHookEx(NULL, code, wParam, lParam);
    }

    switch (wParam) {
        case WM_LBUTTONDOWN:
            //鼠标左键按下,执行一些操作
            break;

        case WM_RBUTTONDOWN:
            //鼠标右键按下,执行一些操作
            break;

        default:
            break;
    }

    return CallNextHookEx(NULL, code, wParam, lParam);
}

void SetMouseHook() {
    HHOOK hook = SetWindowsHookEx(WH_MOUSE, MouseProc, NULL, GetCurrentThreadId());
}

3. WH_CALLWNDPROC hook的实现

WH_CALLWNDPROC hook可以用来监视指定窗口的消息。

LRESULT CALLBACK CallWndProc(int code, WPARAM wParam, LPARAM lParam) {
    if (code < 0) {
        return CallNextHookEx(NULL, code, wParam, lParam);
    }

    LPCWPSTRUCT lpCwpStruct = (LPCWPSTRUCT)lParam;
    HWND hwnd = lpCwpStruct->hwnd;

    switch (lpCwpStruct->message) {
        case WM_COMMAND:
            //窗口中的菜单被点击
            break;

        case WM_CREATE:
            //窗口被创建
            break;

        case WM_DESTROY:
            //窗口被销毁
            break;

        default:
            break;
    }

    return CallNextHookEx(NULL, code, wParam, lParam);
}

void SetCallWndProcHook(HWND hwnd) {
    HHOOK hook = SetWindowsHookEx(WH_CALLWNDPROC, CallWndProc, NULL, GetCurrentThreadId());
}

4. 子类化钩子的实现

子类化钩子可以替换窗口中的原有消息处理函数。

LRESULT CALLBACK NewWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
    switch (message) {
        case WM_COMMAND:
            //窗口中的菜单被点击
            break;

        case WM_CREATE:
            //窗口被创建
            break;

        case WM_DESTROY:
            //窗口被销毁
            break;

        default:
            break;
    }

    return CallWindowProc(OldWndProc, hwnd, message, wParam, lParam);
}

void SubclassWnd(HWND hwnd) {
    OldWndProc = (WNDPROC)SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)NewWndProc);
}

四、hook的注意事项

在使用hook的过程中,需要注意以下几个问题:

1. 钩子函数的实现

钩子函数需要使用LRESULT CALLBACK声明,它的返回值可以是true或false。

2. 钩子的顺序

当多个hook挂载在同一事件上时,它们的执行顺序与挂载的顺序有关。因此,在使用多个hook的时候,需要注意它们的调用顺序。

3. 钩子的释放

使用完毕的hook需要及时释放,否则可能会导致系统错误。

4. 钩子的安全性

hook机制可以用来实现各种功能,但是也具有一定的安全风险。因此,在使用hook的过程中需要注意安全性问题。

5. 钩子的范围

不同类型的hook的监视范围不同,需要根据具体需求进行选择。

五、参考资料

1. MSDN官方文档:https://docs.microsoft.com/en-us/windows/win32/winmsg/about-hooks

2. 《Windows核心编程》第五版,陈硕著,机械工业出版社