WIN32钩子

摘译自Win32 Hooks
作者:Kyle Marsh
        本文描述钩子及其子MS Win32应用编程接口中的使用,讨论钩子函数、
过滤函数、以及以下类型的钩子:

  • WH_CALLWNDPROC
  • WH_CBT
  • WH_DEBUG
  • WH_FOREGROUNDIDLE
  • WH_GETMESSAGE
  • WH_JOURNALPLAYBACK
  • WH_JOURNALRECORD
  • WH_KEYBOARD
  • WH_MOUSE
  • WH_MSGFILTER
  • WH_SHELL
  • WH_SYSMSGFILTER

简介
        在Windows操作系统中,钩子是在事件(包括消息、鼠标动作、击键)到达应用程序之前在函数中将其截获的一种机制。截获的事件后函数就可以修改、甚至抛弃事件。这里,将接收事件的函数称为过滤函数(filter functions),过滤函数可以根据截获的事件类型进行分类,例如,接收所有键盘和鼠标事件的过滤函数。窗口在调用一个过滤函数之前,必须先将过滤函数安装为一个窗口钩子。将一个或多个过滤函数安装到一个钩子的过程,称为配置钩子。如果一个钩子有不止一个过滤函数,则Windows维护一个过滤函数链,最近安装的过滤函数在链的开端。
        当在钩子上安装了一个或多个过滤函数且触发钩子的事件发生了,Windows会调用过滤函数链中的第一个过滤函数,这个过程称为调用钩子。
        过滤函数的维护和访问,需要使用SetWindowsHookEx和UnhookWindowsHookEx函数。钩子为基于窗口的应用程序提供了强大的能力,包括:
(1)处理或修改应用程序中用于对话框、消息框、滚动条、菜单的所有消息(WH_MSGFILTER)
(2)处理或修改系统中用于对话框、消息框、滚动条、菜单的所有消息(WH_SYSMSGFILTER)
(3)在调用GetMessage或PeekMessage函数时处理或修改系统的所有消息(WH_GETMESSAGE)
(4)在调用SendMessage函数时处理或修改所有消息(WH_CALLWNDPROC)
(5)记录或重复键盘和鼠标事件(WH_JOURNALRECORD, WH_JOURNALPLAYBACK)
(6)处理、修改或移除键盘事件(WH_KEYBOARD)
(7)处理、修改或抛弃鼠标事件(WH_MOUSE)
(8)对特定的系统行为作出响应,这使得开发基于计算机的训练应用程序成为可能(WH_CBT).
(9)阻止将要调用的另一个过滤函数(WH_DEBUG)
        应用程序使用钩子可以:
(1)可以为菜单、对话框、消息框提供F1帮助支持(WH_MSGFILTER)
(2)提供鼠标和键盘记录和回放特性,经常成为宏录制功能。例如,Windows录音机附件程序使用钩子提供录音和回放功能(WH_JOURNALRECORD, WH_JOURNALPLAYBACK)
(3)监视消息以决定哪些消息将被发送到特定窗口或决定该消息将会产生哪些行为(WH_GETMESSAGE, WH_CALLWNDPROC)。Win32 SDK中的Spy工具就是使用钩子来执行这些任务的。
(4)模拟鼠标或键盘输入(WH_JOURNALPLAYBACK)。钩子提供唯一可靠的方式来模拟这些活动,如果你试图通过发送或寄送消息来模拟这些事件,Windows内核不会更新键盘或鼠标的状态,这可能导致不可预期的行为。如果钩子被用来回放键盘或鼠标事件,则这些事件就像真实的鼠标或键盘事件那样确确实实地被处理。微软Excel使用钩子来实现其SEND.KEYS宏函数。
(5)为Windows环境下的应用提供CBT支持(WH_CBT)。

如何使用钩子

要了解如何使用钩子,需要知道:
(1)如何使用Windows钩子函数将过滤函数加入一个钩子的过滤函数链中,以及如何从链中移除过滤函数;
(2)安装的过滤函数需要执行什么样的行为;
(3)钩子有哪些类型,能干什么,以及会给过滤函数传递什么样的信息/参数。
Windows钩子函数
        基于窗口的应用程序使用SetWindowsHookEx、UnhookWindowsHookEx、以及CallNextHookEx函数来管理钩子的过滤函数链。
        SetWindowsHookEx函数:向钩子增加一个过滤函数,四个参数:
HHOOK SetWindowsHookEx(
int idHook,        // type of hook to install
HOOKPROC lpfn,     // address of hook procedure
HINSTANCE hMod,    // handle to application instance
DWORD dwThreadId   // identity of thread to install hook for
);
(1)一个整数码:描述过滤函数将要安装到的钩子类型。
(2)过滤函数地址:过滤函数必须在应用程序或DLL的模块定义文件中被包含在EXPORTS语句中。
(3)包含过滤函数的模块的实例句柄。在Win32中,如果要安装线程特定的钩子时该值应为NULL;当安装系统范围或在另一个进程中安装线程特定的钩子时,必须使用过滤函数所在的DLL的实例句柄。
(4)过滤函数被安装的线程ID。如果线程ID非零,则过滤函数只有在线程特定的环境下会被调用;如果线程ID为零,则过滤函数具有系统范围的作用域,可以在系统的任意线程环境中调用。应用程序或库可以使用GetCurrentThreadId函数获得线程句柄来勾住当前线程。
        一些钩子只能设置为系统范围,而另一些钩子则只能设置为线程特定,其他的钩子则既可以设置为系统范围,也可以设置为线程范围。具体见下表:

Hook
Scope

WH_CALLWNDPROC
Thread or System

WH_CBT
Thread or System

WH_DEBUG
Thread or System

WH_GETMESSAGE
Thread or System

WH_JOURNALRECORD
System Only

WH_JOURNALPLAYBACK
System Only

WH_FOREGROUNDIDLE
Thread or System

WH_SHELL
Thread or System

WH_KEYBOARD
Thread or System

WH_MOUSE
Thread or System

WH_MSGFILTER
Thread or System

WH_SYSMSGFILTER
System Only

对于特定类型的钩子,线程范围的钩子首先被调用,然后是系统范围的钩子。通常如果使用线程范围的钩子能够满足要求,则尽量使用线程钩子,原因如下:
(1)应用程序不会导致系统范围的开销;
(2)不会引起所有的事件被序列化;例如,如何应用程序安装了一个系统范围的键盘钩子,则发生到所有应用程序的所有的键盘消息将会流经该应用程序的键盘过滤函数,导致系统的多个输入队列毫无用处。如果过滤函数停止处理键盘事件,则对用户而言系统似乎停滞了,然而实际上没有停。这时候,用户似乎需要使用CTRL+ALT+DEL组合键来解决问题喽。
(3)不需要将过滤函数打包为一个独立的DLL。因为所有的系统级钩子必须置于DLL中。
(4)不需要做DLL中共享数据。系统级过滤函数必须打包为一个DLL,还必须共享其它进程所需的数据。由于共享数据不是DLL的缺省行为,所以必须仔细的设计系统级过滤函数。如果一个过滤函数不能正确地实现数据共享,会导致进程的崩溃。
         SetWindowsHookEx函数返回一个到所安装钩子的句柄(HHOOK),应用程序或库在调用UnhookWindowsHookEx函数时必须使用该句柄来标识钩子。如果过滤函数不能安装到钩子,则SetWindowsHookEx函数返回NULL。此外,SetWindowsHookEx函数会将最后的错误设置为如下值之一,以说明函数调用失败的原因:
ERROR_INVALID_HOOK_FILTER:钩子码无效
ERROR_INVALID_FILTER_PROC:过滤函数无效
ERROR_HOOK_NEEDS_HMOD:一个全局钩子却使用NULL实例参数,或者线程级钩子设置为可用于其它应用程序的线程
ERROR_GLOBAL_ONLY_HOOK:一个系统钩子被安装到特定线程
ERROR_INVALID_PARAMETER:线程ID无效
ERROR_JOURNAL_HOOK_SET:已经存在一个journal类型的钩子。此外,如果在屏保运行时应用程序尝试设置journal钩子,也会设置该代码。
ERROR_MOD_NOT_FOUND:用于全局钩子的hInstance参数不是一个库,该代码说明用户在其模块列表中不能定位模块句柄。
其它值:安全不允许设置钩子,或系统内存溢出
        Windows在内部保存过滤函数链,且不依赖于过滤函数来存储下一个过滤函数的地址。此外,由于内部存储过滤函数链,使得性能得以很大提升。

UnhookWindowsHookEx函数:从钩子链中移除过滤函数,返回值表示是否钩子被移除。
过滤函数(Filter Functions)
        过滤函数是安装到钩子上的函数。由于过滤函数是被Windows而非应用程序调用的,所以通常认为过滤函数是回调函数(callback functions)。所有的过滤函数具有如下形式:
LRESULT CALLBACK FilterFunc( nCode, wParam, lParam )int nCode;
WORD wParam;
DWORD lParam;
        所有的过滤函数均应返回LONG型值,FilterFunc是实际过滤函数名的占位符。
过滤函数的参数
        过滤函数接受三个参数:ncode(钩子代码)、wParam、lParam。钩子代码是一个整数,用来通知过滤函数一些附加的信息,例如钩子代码能够说明什么样的行为会引起调用钩子。
        在Windows 3.1版本之前,钩子代码表示过滤函数是否应该处理事件或调用DEFHookProc。如果钩子代码是负数,过滤函数不应处理事件,而应调用DefHookProc,并直接将三个参数传递过去。Windows在应用程序的辅助下使用这些负数代码来维护过滤函数链。
        在Windows 3.1中,如果Windows发送一个负数钩子代码给过滤函数,则过滤函数必须以传递给过滤函数的参数为实参对CallNextHookEx函数进行调用,并返回CallNextHookEx的返回值。
        传递给过滤函数的第二个参数wParam是一个WPARAM,第三个参数lParam是一个LPARAM,这两个参数传递过滤函数需要的信息。每种钩子对wParam和lParam附加了不同的意义。例如,安装到WH_KEYBOARD钩子的过滤函数在wParam中接收一个虚拟键代码,而lParam包含一组位域(bit fields)来描述键盘事件发生时的键盘状态;安装到WH_MSGFILTER钩子的过滤函数在wParam中接收一个NULL值,在lParam中接收一个指向消息结构的指针;具体wParam和lParam的意义依赖于引起钩子调用的事件。完整的参数列表及每种类型钩子下的意义,需要参考Win32 SDK中如下的内容:

Hook
Filter function documentation

WH_CALLWNDPROC
CallWndProc

WH_CBT
CBTProc

WH_DEBUG
DebugProc

WH_GETMESSAGE
GetMsgProc

WH_JOURNALRECORD
JournalRecordProc

WH_JOURNALPLAYBACK
JournalPlaybackProc

WH_SHELL
ShellProc

WH_KEYBOARD
KeyboardProc

WH_MOUSE
MouseProc

WH_MSGFILTER
MessageProc

WH_SYSMSGFILTER
SysMsgProc

调用钩子链中的下一个函数

钩子设置完成后,Windows调用钩子链中的第一个过滤函数,然后Windows的工作结束,调用钩子链中下一个过滤函数的责任必须由过滤函数来保证。为此,Windows提供了CallNextHookEx来完成该项工作。函数CallNextHookEx有四个参数:
(1)第一个参数是SetWindowsHookEx调用的返回值,即当前钩子的句柄;
(2)其它参数分别是nCode、wParam、lParam

Windows在内部存储过滤函数,并跟踪哪个过滤函数被调用。因此在调用CallNextHookEx函数时,Windows确定钩子链中的下一个过滤函数,并对其进行调用。

有时候,过滤函数并不希望将事件传递给钩子链中的其它钩子函数,尤其在钩子允许过滤函数抛弃事件且过滤函数决定这样做时,一定不要调用CallNextHookEx。

由于过滤函数不会以任何特定的顺序被安装,因此在任何时刻都不能确定一个过滤函数在钩子链的什么地方,当然有例外,在过滤函数安装的时候,它是钩子链的第一个函数。同样,不要绝对地肯定你会获得所有出现的事件,因为在你之后安装的过滤函数可能不会将事件传递给你的过滤函数。

DLL中的过滤函数

系统级的过滤函数必须存在于一个DLL中。在Win16中,允许但不推荐在一个应用程序中将过滤函数安装到一个系统级钩子上,这在Win32中已不可行。WH_JOURNALRECORD和WH_JOURNALPLAYBACK对该规则例外。

系统级钩子的过滤函数运行在多个不同的进程中,因此必须准备好共享任何需要的数据。一个DLL被映射到其每个客户进程的地址空间,在DLL中的全局变量将是实例特定的,除非全局变量被放在共享数据段。例如,在钩子的例子程序中HOOKSDLL.DLL库需要共享两个数据项:

  • 显示消息的窗口句柄
  • 窗口中文本线段高度

要共享这些数据,HOOKSDLL将其放在一个共享数据段中。以下是HOOKSDLL共享数据的步骤:

  • 使用pragmas指令将数据放在一个命名数据段,注意数据必须初始化:

    // Shared DATA #pragma data_seg(".SHARDATA") static HWND   hwndMain = NULL;  // Main hwnd. We will get this from the app. static int    nLineHeight = 0;  // Height of lines in window. #pragma data_seg() 
  • 在DLL的定义文件中增加SECTIONS语句:
    SECTIONS    .SHARDATA   Read Write Shared 
  • 从.DEF文件创建一个.EXP文件:
    hooksdll.exp: hooksdll.obj hooksdll.def    $(implib) -machine:$(CPU)     \    -def:hooks.def      \    hooksdll.obj  \    -out:hooksdll.lib 
  • 链接HOOKSDLL.EXP文件:
    hooksdll.dll: hooksdll.obj hooksdll.def hooksdll.lib hooksdll.exp    $(link) $(linkdebug)     \    -base:0x1C000000  \    -dll       \    -entry:LibMain$(DLLENTRY)     \    -out:hooksdll.dll    \    hooksdll.exp hooksdll.obj hooksdll.rbj \    $(guilibsdll) 

win32 hook 详解相关推荐

  1. flask与js交互的示例代码_Frida Java Hook 详解(安卓9):代码及示例(上)

    Frida Java Hook 详解(安卓9):代码及示例(上) 前言 1.1 FRIDA SCRIPT的"hello world" 1.1.1 "hello world ...

  2. Windows 全局钩子 Hook 详解

    监控程序的实现       我们发现一些木马或其他病毒程序常常会将我们的键盘或鼠标的操作消息记录下来然后再将它发到他们指定的地方以实现监听.这种功能其他是利用了全局钩子将鼠标或键盘消息进行了截取,从而 ...

  3. python win32模块详解_python模块:win32com用法详解

    使用技巧 import win32com from win32com.client import Dispatch, constants w = win32com.client.Dispatch('W ...

  4. C++ HOOK 详解

    一.Hook的概念: 钩子(Hook),是Windows消息处理机制的一个平台,应用程序可以在上面设置子程以监视指定窗口的某种消息,而且所监视的窗口可以是其他进程所创建的.当消息到达后,在目标窗口处理 ...

  5. @kubernetes(k8s)pod服务探针(健康检查)及回调钩子HOOK详解

    文章目录 服务探针与回调hook(健康检查) 一.存活性探针(LivenessProbe) 1.存活型检查基本用法 2.存活性探针三种使用方式 [ExecAction] [TCPSocketActio ...

  6. 安卓黑科技之HOOK详解

    本文带大家进入到安卓另一个世界 互联网攻防大战 Xposed框架 : 它能够让Android设备在没有修改源码的情况下修改系统中的API运行结果 可实现对java层任意HOOK   比如 修改 IME ...

  7. Android逆向之旅---Native层的Hook神器Cydia Substrate使用详解

    一.前言 在之前已经介绍过了Android中一款hook神器Xposed,那个框架使用非常简单,方法也就那几个,其实最主要的是我们如何找到一个想要hook的应用的那个突破点.需要逆向分析app即可.不 ...

  8. linux内核中的hook函数详解,linux内核中的hook函数详解

    在编写linux内核中的网络模块时,用到了钩子函数也就是hook函数.现在来看看linux是如何实现hook函数的. 先介绍一个结构体: struct nf_hook_ops,这个结构体是实现钩子函数 ...

  9. linux hook 任意内核函数,linux内核中的hook函数详解

    在编写linux内核中的网络模块时,用到了钩子函数也就是hook函数.现在来看看linux是如何实现hook函数的. 先介绍一个结构体: struct nf_hook_ops,这个结构体是实现钩子函数 ...

最新文章

  1. A Way Of Leader
  2. 第 45 届国际大学生程序设计竞赛(ICPC)亚洲区域赛(昆明) Cities(区间dp)
  3. 【转载 | 笔记】IIS无法删除应该程序池 因为它包含X个应用程序
  4. Mysql 基础学习
  5. 想要导航提示页_如何优化网站导航呢?
  6. httpHandler实现.Net无后缀名Web访问
  7. python股票自动交易系统_怎样用 Python 写一个股票自动交易的程序
  8. 基于C语言+sqlite3的FTP爬虫和搜索引擎系统
  9. 多目标跟踪算法SORT
  10. PDF怎样免费转换成word?无须借助软件,网页就能轻松实现。
  11. mt6573的DSI 接口
  12. 使用迅雷9.1.48从ftp服务器下载文件
  13. day35 数据库的初步认识
  14. mysql workbench crows foot_一步一步设计你的数据库(三)
  15. carbonData使用文档
  16. JS解析JSON并生成下拉框
  17. Nginx入门使用教程
  18. [NOIP2014 普及组] 珠心算测验
  19. ubuntu lazarus 调用 c写的so库
  20. 数据库MySQL的应急处理

热门文章

  1. 2021最新 SpringBoot面试题精选(附刷题小程序)
  2. python批量提取word文档中的图片(含图片格式转换和GUI)
  3. 什么是(抖音获客达人)
  4. 为duilib的MenuDemo增加消息响应,优化代码和显示效果
  5. 前端网页emoji方案:twemoji简单使用(推特emoji)
  6. 解决戴尔电脑win10无法正常关机现象
  7. SSM框架整合之CRUD操作
  8. 网站建设对企业的有什么价值,为何需要做网站
  9. 99.3对情侣参加婚礼
  10. Android10.0,计步传感器(Sensor.TYPE_STEP_COUNTER) 无法计步