PC辅导:WINDOWS钩子函数,第1张

PC辅导:WINDOWS钩子函数,第2张

在这节课中,我们将学习如何使用WINDOWS的钩子函数。WINDOWS钩子函数非常强大,用它可以检测其他进程并改变它们的行为。
理论:
WINDOWS的钩子功能可以认为是WINDOWS的主要特性之一。有了它们,您可以捕获自己的进程或其他进程的事件。通过挂钩,可以给WINDOWS一个回调函数来处理或过滤事件。这个函数也叫“钩子函数”,每次你感兴趣的事件发生时,WINDOWS都会调用这个函数。有两种类型的挂钩:本地和远程。
局部钩子只钩住你自己进程的事件。
远程挂钩也可以挂钩其他进程中发生的事件。远程钩子有两种:
基于线程,它会捕获其他进程中特定线程的事件。简而言之,它可以用来观察其他进程中特定线程的事件。
全系统将捕获系统中所有进程的事件消息。
安装hook功能会影响系统的性能。用于监控“系统范围事件”的系统钩子尤其明显。因为系统在处理所有相关事件时都会调用你的hook函数,所以你的系统会明显变慢。所以要慎用,用后立即卸下。还有,因为你可以提前拦截其他进程的消息,一旦你的钩子函数出了问题,肯定会影响到其他进程。记住:强大也意味着在使用时要负责任。
在正确使用钩子函数之前,我们先来解释一下钩子函数的工作原理。创建钩子时,WINDOWS会先在内存中创建一个数据结构,其中包含钩子的相关信息,然后将该结构添加到已有的钩子链表中。新挂钩将添加到旧挂钩上。当一个事件发生时,如果你安装了一个本地钩子,你的进程中的钩子函数将被调用。如果是远程钩子,系统必须将钩子函数插入到其他进程的地址空中。要做到这一点,钩子函数必须在动态链接库中,所以如果要使用远程钩子,必须把钩子函数放在动态链接库中。当然有两个例外:工作日志钩子和工作日志回放钩子。这两个挂钩的挂钩功能一定是在安装挂钩的螺纹里。原因是:这两个钩子是用来监控和比较底层硬件事件的。因为它们被记录和回放,所以所有事件当然是有序的。所以如果把回调函数放在DLL里,输入事件是分几个线程记录的,所以我们不能保证顺序正确。因此,解决方案是将钩子函数放在一个单独的线程中,比如安装钩子的线程。
有14种挂钩, 以下是它们被调用的次数:
WH_CALLWNDPROC调用SendMessage时
WH _ CALLWNDPROCRET SendMessage的调用返回时
WH_GetMessage调用GetMessage或PeekMessage时[/ Br/] WH_KEYBOARD调用GETMESSAGE或PeekMessage从消息队列中查询WM_KEYUP或WM_KEYDOWN消息时
WH_MOUSE调用GETMESSAGE或PeekMessage从消息队列中查询鼠标事件消息时[/Br/] 钩子是本地的。它是为拥有自己的消息处理过程的控制对象而设计的。
WH_SYSMSGFILTER和WH_MSGFILTER一样,只是一个系统范围的
WH_JOURNALRECORD当WINDOWS从硬件队列获得消息时
WH_JOURNALPLAYBACK当系统的硬件输入队列请求事件时
WH_SHELL当一个关于WINDOWS shell的事件发生时,比如任务栏需要重画它的按钮,
WH_CBT当基于计算机的训练(CBT)事件发生时
WH_FOREGROUNDIDLE是WINDOWS自己用的,普通应用很少用
WH_DEBUG调试钩子函数
现在我们知道了一些基本理论,下面开始解释如何
安装钩子,可以调用SetWindowHookEx函数,这个函数的原型如下:
SetWindowsHookex proto hook Type:DWORD,P Hookblock: DWORD,Hinstance: DWORD,ThreadID: DWORD
HookType是上面列出的值之一,比如WH _鼠标,WH _键盘
P Hookblock。如果使用的是远程钩子,必须放在DLL中,否则放在自己的代码
hin instance钩子函数所在DLL的实例句柄中。如果是本地钩子,则值为NULL
ThreadID是安装钩子函数后要监控的线程的ID号。该参数可以确定挂钩是本地的还是系统范围的。如果值为NULL,钩子将被解释为系统范围的,它可以监视所有进程及其线程。如果您在自己的进程中指定了一个线程ID号,那么这个钩子就是一个局部钩子。如果线程ID是另一个进程中一个线程的ID,那么这个钩子就是一个全局远程钩子。这里有两个特例:WH _日志记录和WH _日志回放总是代表本地系统范围的钩子。它们是本地的,因为它们不需要放在DLL中。WH_SYSMSGFILTER总是一个系统范围的远程钩子。实际上,它类似于WH_MSGFILTER钩子。如果参数ThreadID设置为0,则它们完全相同。
如果函数调用成功,则返回eax中钩子的句柄,否则返回NULL。您必须保存句柄,因为我们稍后将需要它来卸载挂钩。
卸载一个钩子,调用UnhookWidowHookEx函数,这个函数只有一个参数,就是要卸载的钩子的句柄。如果调用成功,它在eax中返回一个非零值,否则返回NULL。
现在你已经知道了如何安装和卸载钩子,接下来我们来看看钩子函数。。
每当你安装的钩子的消息事件类型发生时,WINDOWS都会调用钩子函数。例如,如果你安装的钩子是WH _鼠标类型,钩子函数将在鼠标事件发生时被调用。无论安装什么类型的钩子,钩子函数的原型总是一样的:
HookProc Protoncode: dword,WParam: dword,LPARAM: dword。

NCode指定是否需要处理消息。
wParam和lParam包含消息的附加消息。
HookProc可以看作是函数名的占位符。你可以给函数取任何名字,只要它的原型是一致的。至于以上参数和返回值的具体含义,各种挂钩都不一样。比如:
WH _调用WndProc
nCode只能是HC_ACTION,表示消息已经发送到窗口
WPRAM。如果不为0,则lparam的指针
指向代表正在发送的消息的CWPSTRUCT结构变量
返回值:未使用,0
WH _鼠标
nCode为HC_ACTION或HC_NOREMOVE
wParam包含鼠标的事件消息
lParam的指针指向MOUSEHOOKSTRUCT结构变量
返回值:如果不处理返回0,否则将返回非零值所以你必须查阅你的WIN32 API指南来获得不同类型钩子的参数的详细定义和它们返回值的意义。这里还有一个问题需要注意:所有的钩子都串在一个链表上,最近添加的钩子放在链表的头部。当事件发生时,WINDOWS将按照从链表头到链表尾的顺序调用。所以你的钩子函数有责任把消息传递给下一个链中的钩子函数。当然,你不一定要这样做,但你明白这样做的原因。在大多数情况下,消息事件被传递,以便其他钩子有机会处理消息。调用下一个钩子函数来调用CallNextHookEx函数。这个函数的原型如下:
Callnexthookex protohook:dword,ncode: dword,wparam: dword,lparam: dword
hhook是你自己钩子函数的句柄。这个把手可以用来移动钩链。
nCode、wParam和lParam,您可以简单地将传递的参数传递给CallNextHookEx。
请注意:对于远程钩子,钩子函数必须放在DLL中,它们会从DLL映射到其他进程空。当WINDOWS将DLL映射到其他进程空时,它不会同时映射数据段。简而言之,所有进程只共享DLL的代码。至于数据段,每个进程都会有自己的副本。这是一个容易被忽视的问题。您可能想当然地认为,保存在DLL中的值可以在映射DLL的所有进程之间共享。通常,因为每个映射DLL的进程都有自己的数据段,所以您的程序在大多数情况下运行良好。但是钩子函数不是。对于hook函数,DLL的数据段对于所有进程必须是相同的。这样就必须设置数据段是共享的,这可以通过在链路交换机中指定段的属性来实现。您可以在MASM:
/SECTION:,S
初始化的段名是。数据,未初始化的段名是。bss。`加入一个DLL,你想写一个钩子函数,你想让它的未初始化数据段在所有进程间共享。您必须这样做:
link/section:。bss,s/dll/子系统:Windows..........
s表示该段是共享段。例:
有两个模块:一个是GUI部分,一个是安装和卸载钩子的DLL。
;-stdcall
option casemap:none
include \ masm 32 \ include \ windows . Inc
include \ masm 32 \ include \ user 32 . Inc
include \ kernel 32 . Inc
include lib mousehook . Inc
include lib \ masm 32 \ lib \ user 32 . lib
include lib \ masm 32 \ lib \ kernel 32

wsprintfA proto C :DWORD,:DWORD,:VARARG
wsprintf TEXTEQU

。const
IDD _ MAINDLG equ 101
IDC _ class name equ 1000
IDC _ HANDLE equ 1001
IDC _ WNDPROC equ 1002
IDC _ HOOK equ 1004
IDC _ EXIT equ 1005
WM _ mouse HOOK equ WM _ USER+6

DlgFunc PROTO :DWORD,:DWORD,:DWORD,:DWORD

。data
Hook flag DD FALSE
Hook text db " & Hook ",0
UnhookText db "&Unhook ",0
template db "%lx ",0

。数据?
hInstance dd?
hHook dd?
。code
start:
invoke GetModuleHandle,NULL
mov hInstance,eax
invoke DialogBoxParam,hInstance,IDD_MAINDLG,NULL,addr DlgFunc,NULL
invoke ExitProcess,NULL

DlgFunc proc hDlg:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD
LOCAL hLib:DWORD
LOCAL buffer 1[128]:byte
LOCAL buffer 1[128]:byte
LOCAL rect:RECT
。if uMsg==WM_CLOSE
。如果HookFlag==TRUE
调用UninstallHook
。endif
调用EndDialog,hDlg,NULL
。else if uMsg = = WM _ init dialog
invoke GetWindowRect,hDlg,addr rect
invoke SetWindowPos,hDlg,HWND_MOST,rect.left,rect.top,rect.right,rect.bottom,SWP_SHOWWINDOW
。elseif uMsg==WM_MOUSEHOOK
调用GetDlgItemText,hDlg,IDC_HANDLE,addr buffer1,128
调用wsprintf,addr buffer,addr template,wParam
调用lstrcmpi,addr buffer,addr buffer1
。如果eax!=0
调用SetDlgItemText,hDlg,IDC_HANDLE,addr buffer
。endif
调用GetDlgItemText,hDlg,IDC_CLASSNAME,addr buffer1,128
调用GetClassName,wParam,addr buffer,128
调用lstrcmpi,addr buffer,addr buffer1
。如果eax!=0
调用SetDlgItemText,hDlg,IDC_CLASSNAME,addr buffer
。endif
调用GetDlgItemText,hDlg,IDC_WNDPROC,addr buffer1,128
调用GetClassLong,wParam,GCL_WNDPROC
调用wsprintf,addr buffer,addr template,eax
调用lstrcmpi,addr buffer,addr buffer1
。如果eax!=0
调用SetDlgItemText,hDlg,IDC_WNDPROC,addr buffer
。endif
。elseif uMsg==WM_COMMAND
。如果lParam!=0
mov eax,wParam
mov edx,eax
shr edx,16
。如果dx==BN_CLICKED
。if ax==IDC_EXIT
调用SendMessage,hDlg,WM_CLOSE,0,0
。else
。如果HookFlag==FALSE
调用InstallHook,hDlg
。如果eax!=NULL
mov HookFlag,TRUE
invoke SetDlgItemText,hDlg,IDC_HOOK,addr UnhookText
。结束条件

。else
invoke uninstall HOOK
invoke SetDlgItemText,hDlg,IDC_HOOK,addr HookText
mov HookFlag,FALSE
invoke SetDlgItemText,hDlg,IDC_CLASSNAME,NULL
invoke SetDlgItemText,hDlg,IDC_HANDLE,NULL
invoke SetDlgItemText,hDlg,IDC_WNDPROC,NULL
。endif
。endif
。endif
。endif
。else
mov eax,FALSE
ret
。endif
mov eax,TRUE
ret
DlgFunc endp

结束开始

;-stdcall
option casemap:none
include \ masm 32 \ include \ windows . Inc
include \ masm 32 \ include \ kernel 32 . Inc
include lib \ masm 32 \ lib \ kernel 32 . lib
include \ masm 32 \ include \ user 32 . Inc
include lib \ masm 32 \ lib \ user 32 . lib

。const
WM _ MOUSEHOOK equ WM _ USER+6

。数据实例dd 0

。数据?
hHook dd?
hWnd dd?

。code
DllEntry proc hInst:h instance,reason:DWORD,reserved1:DWORD
。if reason = = DLL _ PROCESS _ ATTACH
push hInst
pop hin instance
。endif
mov eax,TRUE
ret
DllEntry Endp

MouseProc proc nCode:DWORD,wParam:DWORD,lParam:DWORD
invoke CallNextHookEx,hHook,nCode,wParam,lParam
mov edx,lParam
assume EDX:PTR MOUSEHOOK struct
invoke window from point,[edx].pt.x,[EDX]. pt . y
invoke PostMessage,hWnd,WM_MOUSEHOOK,eax,0
assume

install hook proc hWnd:DWORD
push hWnd
pop hWnd
invoke SetWindowsHookEx,WH_MOUSE,addr MouseProc,hInstance,NULL
mov hHook,eax
ret
install hook endp

UninstallHook过程
调用UnhookWindowsHookEx,hHook
ret
uninstall hook endp

结束延迟

;-正在生成dll文件-

NAME=mousehook
$(NAME)。dll: $(名称)。obj
链接/节:。bss,S /DLL /DEF:$(名称)。def/SUBSYSTEM:WINDOWS/LIBPATH:c:\ masm \ lib $(NAME)。obj
$(名称)。obj: $(姓名)。asm
ml /c /coff /Cp $(名称)。空对地导弹

解析:
该应用程序的主窗口包括三个编辑控件,分别显示当前鼠标光标所在的窗口类名、窗口句柄和窗口过程地址。还有两个按钮:“挂钩”和“Eixt”。当你按下Hook时,应用程序会挂钩鼠标输入的事件消息,按钮的文本会变成“Unhook”。当鼠标光标在窗口上滑动时,关于该窗口的信息将显示在主窗口中。当你按下“脱钩”,应用程序将卸载挂钩。主窗口使用对话框作为其主窗口。它定义了一个消息WM_MOUSEHOOK,用于在主窗口和DLL之间传递消息。当主窗口收到这条消息时,wParam包含光标所在窗口的句柄。当然,这是我们做的安排。我这样做只是为了方便。您可以使用自己的方法在主应用程序和DLL之间进行通信。
。如果hook flag = = FALSE
调用InstallHook,hDlg
。如果eax!=NULL
mov HookFlag,TRUE
invoke SetDlgItemText,hDlg,IDC_HOOK,addr UnhookText
。结束条件

该应用程序有一个全局变量HookFlag,用于监视钩子的状态。如果安装了钩子,则为真,否则为假。当用户按下钩子按钮时,应用程序检查钩子是否已经安装。如果没有,它将调用从DLL派生的函数InstallHook来安装它。注意,我们将主对话框的句柄传递给了DLL,这样这个钩子DLL就可以将WM_MOUSEHOOK消息传递给正确的窗口。当应用程序被加载时,挂钩DLL也被加载。当主程序被加载到内存中时,DLL将被立即加载。DLL的入口点函数在主程序执行前加载它的第一条语句。所以当主程序被执行时,DLL已经被初始化了。我们将以下代码放在加载点:

。if reason = = DLL _ PROCESS _ ATTACH
push hInst
pop hin instance
。结束条件

这段代码将DLL的实例句柄放入一个全局变量中保存。由于入口点函数是在调用所有函数之前执行的,因此hInstance总是有效的。我们把这个变量放进去。数据,这样每个进程都有自己的这个变量的值。因为当鼠标光标停在一个窗口上时,钩子DLL被映射到进程的地址空。已在添加的dll的默认加载地址加载了其他DLL,挂钩DLL将被映射到其他地址。HInstance将被更新为其他值。当用户按下Unhook然后hook时,将再次调用SetWindowsHookEx。这一次,它将把新地址作为实例句柄。在示例中,这是错误的,加载DLL的地址没有改变。这个钩子会变成局部的,你只能钩住你窗口中发生的鼠标事件,这几乎不能令人满意。

install hook proc hWnd:DWORD
push hWnd
pop hWnd
invoke SetWindowsHookEx,WH_MOUSE,addr MouseProc,hInstance,NULL
mov hHook,eax
ret
install hook endp

InstallHook函数非常简单。它将传递的窗口句柄保存在hWnd中供以后使用。然后调用SetWindowsHookEx函数安装一个鼠标钩子。这个函数的返回值放在全局变量hHook中,以后会在UnhookWindowsHookEx中用到。调用SetWindowsHookEx后,鼠标钩子开始工作。每当鼠标事件发生时,MouseProc函数将被调用:

MouseProc proc nCode:DWORD,wParam:DWORD,lParam:DWORD
invoke CallNextHookEx,hHook,nCode,wParam,lParam
mov edx,lParam
assume EDX:PTR MOUSEHOOK struct
invoke window from point,[edx].pt.x,[EDX]. pt . y
invoke PostMessage,hWnd,WM_MOUSEHOOK,eax,0
assume

钩子首先调用CallNextHookEx函数,让其他钩子处理鼠标事件。然后,调用WindowFromPoint函数在给定的屏幕坐标位置获取窗口句柄。注意:我们使用lParam指向的MOUSEHOOKSTRUCT变量中的POINT成员变量作为当前鼠标位置。我们调用PostMessage函数向主程序发送WM_MOUSEHOOK消息。有一点你一定要记住:不要在hook函数中使用SendMessage函数,会造成死锁。MOUSEHOOKSTRUCT的定义如下:

MOUSEHOOKSTRUCT STRUCT DWORD
pt POINT
hwnd DWORD?
wHitTestCode DWORD?
dwExtraInfo DWORD?
MOUSEHOOKSTRUCT结束


pt是当前鼠标所在的屏幕位置。
hwnd是将接收鼠标消息的窗口的句柄。通常是鼠标所在的窗口,但是如果窗口调用SetCapture,鼠标输入就会到这个窗口。因为不使用这个成员变量,所以用WindowFromPoint函数代替。
wHitTestCode指定点击测试值,该值给出更多的鼠标位置值。它指定鼠标在窗口中的位置。有关值的完整列表,请参考WIN32 API指南中的WM_NCHITTEST消息。
dwExtraInfo该值包含相关信息。一般值由mouse_event函数设置,可以通过调用GetMessageExtraInfo获得。


当主窗口收到WM_MOUSEHOOK消息时,它使用wParam参数中的窗口句柄来查询窗口的消息。

。else if uMsg = = WM _ mouse hook
调用GetDlgItemText,hDlg,IDC_HANDLE,addr buffer1,128
调用wsprintf,addr buffer,addr template,wParam
调用lstrcmpi,addr buffer,addr buffer1
。如果eax!= 0
调用SetDlgItemText,hDlg,IDC_HANDLE,addr buffer
。endif
调用GetDlgItemText,hDlg,IDC_CLASSNAME,addr buffer1,128
调用GetClassName,wParam,addr buffer,128
调用lstrcmpi,addr buffer,addr buffer1
。如果eax!= 0
调用SetDlgItemText,hDlg,IDC_CLASSNAME,addr buffer
。endif
调用GetDlgItemText,hDlg,IDC_WNDPROC,addr buffer1,128
调用GetClassLong,wParam,GCL _ WNDPROC
调用wsprintf,addr buffer,addr template,eax
调用lstrcmpi,addr buffer,addr buffer1
。如果eax!= 0
调用SetDlgItemText,hDlg,IDC_WNDPROC,addr buffer
。结束条件

为了避免重绘文本时的抖动,我们将已经在编辑空过程中的文本与将要显示的文本进行比较。如果相同,可以忽略。获取类名调用GetClassName,获取窗口过程调用GetClassLong并传入GCL_WNDPROC标志,然后将它们格式化为文本字符串并放入相关的编辑室空。

invoke uninstall HOOK
invoke SetDlgItemText,hDlg,IDC_HOOK,addr HookText
mov HookFlag,FALSE
invoke SetDlgItemText,hDlg,IDC_CLASSNAME,NULL
invoke SetDlgItemText,hDlg,IDC_HANDLE,NULL
invoke SetDlgItemText,hDlg,IDC_WNDPROC,NULL

当用户按下Unhook时,主程序调用DLL中的UninstallHook函数。该函数调用UnhookWindowsHookEx函数。然后,它将按钮的文本改回“Hook”,将HookFlag的值设置为FALSE,然后清除编辑控件中的文本。
链接器的开关选项如下:

链接/部分:。bss,S /DLL /DEF:$(名称)。定义/子系统:窗口

它指定了。bss段作为共享段,以便所有映射DLL的进程共享未初始化的数据段。如果不使用这个开关,DLL中的钩子将不能正常工作。

位律师回复
DABAN RP主题是一个优秀的主题,极致后台体验,无插件,集成会员系统
白度搜_经验知识百科全书 » PC辅导:WINDOWS钩子函数

0条评论

发表评论

提供最优质的资源集合

立即查看 了解详情