C++ hook,windows钩子hook指的是
一.基本概念:
Hook是一个Windows消息处理机制的平台,应用程序可以在其上设置程序来监控指定窗口中的某些消息,并且被监控的窗口可以由其他进程创建。当消息到达时,它在目标窗口处理函数之前被处理。钩子机制允许应用程序拦截和处理窗口消息或特定事件。
钩子实际上是处理消息的程序段,通过系统调用挂入系统。每当一个特定的消息被发送出去,钩子程序就在消息到达目的窗口之前捕获它,也就是钩子函数先获得控制权。此时,钩子函数可以处理(改变)消息,继续传递消息而不进行处理,或者强制结束消息的传递。
二、运行机制:
1.挂钩链表和挂钩过程:
每个钩子都有一个与之相关联的指针列表,它被称为钩子链表,由系统维护。这个列表的指针指向应用程序定义的、钩子子程调用的指定回调函数,也就是钩子的每个处理程序。当与指定钩子类型相关联的消息出现时,系统将该消息传递给钩子子程。有些钩子子程只能监视消息,或修改消息,或阻止消息前进,以防止这些消息传递到下一个钩子子程或目的窗口。最新安装的钩子放在链的开头,最早安装的钩子放在最后,也就是后来加入的先获得控制权。
Windows不要求钩子子程的卸载顺序必须与安装顺序相反。每当一个钩子被卸载,Windows就释放它所占用的内存,并更新整个钩子链表。如果程序安装了钩子,但是在卸载钩子之前就结束了,那么系统会自动为它卸载钩子。
钩子子程是应用程序定义的回调函数,不能定义为类的成员函数,只能定义为普通的C函数。用于监视系统或特定类型的事件,这些事件可以与系统中的特定线程或所有线程的事件相关。
钩子子程必须遵循以下语法:[cpp] view plaincopyprint?
LRESULTCALLBACKHookProcintnCode,WPARAMwParam,LPARAMlParam);LRESULT回调钩子子程
int nCode,
WPARAM wParam,
第二个消息参数
);
HookProc是应用程序定义的名称。
nCode参数是钩子代码,钩子子程使用这个参数来确定任务。该参数的值取决于钩子类型,每个钩子都有自己的钩子代码特征字符集。
wParam和lParam参数的值取决于钩子代码,但是它们的典型值包含关于发送或接收消息的信息。
2.吊钩的安装和释放:
使用API函数SetWindowsHookEx()将应用程序定义的钩子子程安装到钩子链表中。SetWindowsHookEx函数总是在钩子链的开头安装钩子子程。当由指定类型的钩子监视的事件发生时,系统在与该钩子关联的钩子链的开始处调用钩子子程。每个钩子链中的钩子子程决定是否将这个事件传递给下一个钩子子程。钩子子程需要调用CallNextHookEx函数将事件传递给下一个钩子子程。
【cpp】查看plaincopyprint?
HHOOKSETWINDOWS HOOKEX(intid hook,//钩子的类型,即它处理的消息类型,HOOKPROClpfn,//钩子子程的地址指针。如果dwThreadId参数为0 //或者是另一个进程创建的线程的Id,//lpfn必须指向DLL中的钩子子程。//此外,lpfn可以指向当前进程的一段钩子子程代码。//钩子函数的入口地址,钩子钩住任何消息时调用。HINSTANCEhMod,///应用程序实例的句柄。标识lpfn指向的过程的DLL。//如果dwThreadId标识当前进程创建的线程,//并且过程代码在当前进程中,则hMod必须为NULL。//可以简单的设置为这个应用的实例句柄。DWORDdwThreadId///与已安装的钩子子程关联的线程的标识符。//如果是0,钩子子程关联所有线程,就是全局钩子。);函数成功时返回钩子子程的句柄,失败时返回NULL。HHOOK SetWindowsHookEx(
Inthook,//钩子的类型,也就是它处理的消息的类型。
HOOKPROC lpfn,//钩子子程的地址指针。如果dwThreadId参数为0
//或者由另一个进程创建的线程的ID,
//lpfn必须指向DLL中的钩子子程。
//此外,lpfn可以指向当前进程的一段钩子子程代码。
//钩子函数的入口地址,钩子钩住任何消息时调用。
HinceHMOD,//应用程序实例的句柄。标识lpfn指向的过程的DLL。
//如果dwThreadId标识由当前进程创建的线程,
//并且过程代码在当前进程中,hMod必须为空。
//可以简单的设置为这个应用的实例句柄。
orddwdwthreadid//与已安装的钩子子程相关联的线程的标识符。
//如果是0,钩子子程关联所有线程,就是全局钩子。
函数成功时返回钩子子程的句柄,失败时返回NULL。
上面提到的钩子子程关联一个线程,是指钩子链表中发送给线程的消息同时发送给钩子子程,由钩子子程先处理。
调用钩子函数来获得钩子子程的控制权。处理完消息后,如果想让消息继续传递,就必须调用SDK中的另一个API函数CallNextHookEx来传递,这样才能执行钩子链表指向的下一个钩子子程。这个函数在成功时返回钩子链中下一个钩子子程的返回值。返回值的类型取决于钩子的类型。这个函数的原型如下:【cpp】查看plaincopyprint?
LRESULTCallNextHookEx(hhookhk;intnCodeWPARAMwParamLPARAMlParam);LRESULT调用NextHookEx
HHOOK hhk
int nCode
WPARAM wParam
LPARAM lParam
);
Hhk是当前钩子的句柄,由SetWindowsHookEx()函数返回。
NCode是传递给钩子子程的事件代码。
wParam和lParam分别是传递给钩子子程的WParam值,它们的具体含义与钩子类型有关。
钩子函数也可以通过直接返回TRUE来丢弃消息,并阻止消息被传递。否则,其他带有钩子的应用程序将不会收到钩子的通知,并可能产生不正确的结果。
使用钩子后需要用UnHookWindowsHookEx()卸载,否则会造成麻烦。很容易松开钩子。UnHookWindowsHookEx()只有一个参数。功能原型如下:
卸载钩子
(
HHOOK hhk
);
该函数成功返回TRUE,否则返回FALSE。
3.一些运作机制:
在Win16环境中,DLL的全局数据对于加载它的每个进程都是相同的;然而,在Win32环境中,情况发生了变化。DLL函数中的代码创建的任何对象(包括变量)都归调用它的线程或进程所有。当进程加载DLL时,操作系统自动将DLL地址映射到进程的私有空间,即进程的虚拟地址空间,并且还将DLL的全局数据的副本复制到进程空间。也就是说,每个进程拥有的同一个DLL的全局数据名称相同,但值不一定相同,互不干扰。
因此,如果要在Win32环境中的多个进程之间共享数据,必须进行必要的设置。访问同一个Dll的进程之间共享内存是通过内存映射文件技术实现的。也可以将需要共享的数据分离出来,放在一个独立的数据段中,并将这个段的属性设置为共享。这些变量必须被赋予初始值,否则编译器会把没有初始值的变量放在一个名为未初始化的数据段中。
#pragma data_seg预处理指令用于设置共享数据段。例如:
# pragma data _ seg( shared dataname )
HHOOK hHook=NULL
#pragma data_seg()
#pragma data_seg( shared data name )和# pragma data _ seg()之间的所有变量都将被所有访问该Dll的进程看到和共享。
当进程隐式或显式调用动态库中的函数时,系统必须将动态库映射到进程的虚拟地址空间(以下简称为‘地址空间’)。这使得DLL成为进程的一部分,作为这个进程执行,使用这个进程的堆栈。
4.系统挂钩和线程挂钩:
SetWindowsHookEx()函数的最后一个参数决定了这个钩子是系统钩子还是线程钩子。
线程钩子用于监控指定线程的事件消息。线程钩子一般在当前线程或者从当前线程派生的线程中。
系统钩子监视系统中所有线程的事件消息。因为系统钩子会影响系统中的所有应用程序,所以钩子函数必须放在独立的动态链接库(DLL)中。系统自动将包含‘钩子回调函数’的DLL映射到所有受钩子函数影响的进程的地址空间,也就是将这个DLL注入到那些进程中。
一些注意事项:
(1)如果为同一个事件(如鼠标消息)同时安装了线程钩子和系统钩子,那么系统会自动先调用线程钩子,再调用系统钩子。
(2)同一个事件消息可以安装多个钩子进程,这些钩子进程形成一个钩子链。在当前钩子处理之后,钩子信息应该被传递给下一个钩子函数。
(3)钩子,尤其是系统钩子,会消耗消息处理时间,降低系统性能。只在必要时安装挂钩,使用后及时卸载。
第三,挂钩式
每种类型的钩子都可以让应用程序监控不同类型的系统消息处理机制。所有可用的挂钩类型如下所述。
1.WH_CALLWNDPROC和WH_CALLWNDPROCRET钩子
WH_CALLWNDPROC和WH_CALLWNDPROCRET钩子使你能够监视发送到窗口过程的消息。在系统消息发送到接收窗口过程之前,调用WH_CALLWNDPROC钩子子程,在窗口过程处理完消息之后,调用WH_CALLWNDPROCRET钩子子程。
WH_CALLWNDPROCRET钩子将指针传递给CWPRETSTRUCT结构,然后传递给钩子子程。CWPRETSTRUCT结构包含处理消息的窗口过程的返回值,还包含与此消息相关的消息参数。
2、WH_CBT挂钩
WH_CBT钩子子程将在以下事件之前被调用,包括:
1.激活、建立、销毁、最小化、最大化、移动、改变大小等窗口事件;
2.完成系统说明;
3.来自系统消息队列的移动鼠标和键盘事件;
4.设置输入焦点事件;
5.同步系统消息队列事件。
钩子子程的返回值决定了系统是允许还是阻止这些操作中的一个。
3、WH _调试钩子
在系统调用与系统中其他钩子关联的钩子子程之前,系统会调用WH _调试钩子子程。你可以使用这个钩子来决定是否允许系统调用与其他钩子相关联的钩子子程。
4、WH _前景闲置钩
当应用程序的前台线程空闲时,可以使用WH_FOREGROUNDIDLE钩子来执行低优先级的任务。当应用程序的前台线程即将空闲时,系统会调用WH _前台空闲钩子子程。
5、WH_GETMESSAGE挂钩
应用程序使用WH GETMESSAGE钩子来监视从GETMESSAGE或PeekMessage函数返回的消息。您可以使用WH_GETMESSAGE钩子来监视鼠标和键盘输入以及发送到消息队列的其他消息。
6、WH _日志回放挂钩
WH_JOURNALPLAYBACK钩子允许应用程序将消息插入到系统消息队列中。您可以使用这个钩子来回放通过使用WH _日志记录钩子记录的连续鼠标和键盘事件。只要安装了WH_JOURNALPLAYBACK钩子,正常的鼠标和键盘事件都是无效的。WH_JOURNALPLAYBACK钩子是一个全局钩子,它不能像特定于线程的钩子一样使用。WH_JOURNALPLAYBACK钩子返回超时值,该值告诉系统在处理来自播放钩子的当前消息之前要等待多长时间(以毫秒为单位)。这允许Hook控制实时事件的回放。WH_JOURNALPLAYBACK是系统范围的本地钩子,它们不会被注入到任何旅行地址空间。
7、WH _日志记录挂钩
WH _日志记录钩子用于监视和记录输入事件。通常,你可以用这个钩子来记录连续的鼠标和键盘事件,然后用WH_JOURNALPLAYBACK钩子来回放它们。WH日志记录钩子是一个全局钩子,它不能像特定于线程的钩子一样使用。WH _日志记录是系统范围的本地钩子,它们不会被注入到任何旅行地址空间。
8、WH _键盘挂钩
在应用程序中,使用WH _键盘钩子来监视由GetMessage或Peekmessage函数返回的WM_KEYDOWN和WM_KEYUP消息。您可以使用这个钩子来监视输入到消息队列中的键盘消息。
9、WH _键盘_LL钩子
WH _键盘_LL钩子监视输入到线程消息队列的键盘消息。
10、WH _鼠标钩
WH _鼠标钩子监视从GetMessage或PeekMessage函数返回的鼠标消息。使用这个钩子来监视输入到消息队列中的鼠标消息。
11、WH _鼠标_LL钩子
WH _鼠标_LL钩子监视输入到线程消息队列的鼠标消息。
12.WH _ msg过滤器和WH _ sysmsg过滤器挂钩
WH_MSGFILTER和WH_SYSMSGFILTER钩子使我们能够监视菜单、滚动条、消息框和对话框消息,发现用户使用ALT TAB或ALT ESC组合键切换窗口。WH_MSGFILTER钩子只能监视由安装了钩子子程的应用程序创建的传递到菜单、滚动条、消息框和对话框的消息。WH_SYSMSGFILTER钩子监视所有的应用程序消息。
WH_MSGFILTER和WH_SYSMSGFILTER钩子使我们能够在模式循环期间过滤消息,这相当于在主消息循环中过滤消息。
WH_MSGFILTER钩子可以通过调用msgfilterfunction直接调用。通过使用这个函数,应用程序可以使用相同的代码在模式循环中过滤消息,就像在主消息循环中一样。
13、WH _贝壳钩
外壳应用程序可以使用WH外壳钩子来接收重要的通知。当外壳应用程序处于活动状态并且顶层窗口被构建或销毁时,系统调用WH _外壳钩子子程。
WH壳牌公司有5分钟时间:
1.只要一个顶级且无主的窗口被生成、工作或销毁;
2.当任务栏需要重画按钮时;
3.当系统需要显示关于任务栏的程序的最小化形式时;
4.当当前键盘布局状态改变时;
5.当用户按Ctrl Esc执行任务管理器(或同级程序)时。
传统上,外壳应用程序不接收WH外壳消息。因此,在应用程序可以接收WH _外壳消息之前,应用程序必须调用SystemParametersInfo函数来注册自己。
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。