WIN32汇编语言教程:第11章 动态链接库和钩子 · 11.2 Windows钩子(3)
如果钩子安装成功,函数返回钩子句柄,否则返回NULL。钩子句柄必须被保存下来,因为在回调函数和卸载钩子的时候还要用到这个句柄。
动态链接库导出的另一个函数是UninstallHook,用来供主程序卸载钩子。程序在这里使用UnhookWidowHookEx函数卸载钩子,这个函数的输入参数只有一个,就是安装钩子时候得到的钩子句柄。
现在来看主程序。Main.rc文件的内容如下:
//################################################################## #include <resource.h> //################################################################## #define ICO_MAIN 1000 #define DLG_MAIN 1000 #define IDC_TEXT 1001 //################################################################## ICO_MAIN ICON "Main.ico" //################################################################## DLG_MAIN DIALOG 208, 130, 234, 167 STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "键盘钩子" FONT 9, "宋体" { EDITTEXT IDC_TEXT, 5, 5, 224, 158, ES_MULTILINE | ES_AUTOVSCROLL | WS_BORDER | WS_VSCROLL | WS_TABSTOP | ES_READONLY } //##################################################################
资源脚本文件中的定义很简单,仅定义了一个对话框,对话框中有个多行的编辑控件,用来显示“钩住”的按键。Main.asm的内容如下:
.386 .model flat, stdcall option casemap :none ;#################################################################### ; Include 文件定义 ;#################################################################### include windows.inc include user32.inc includelib user32.lib include kernel32.inc includelib kernel32.lib include Hookdll.inc includelib Hookdll.lib ;#################################################################### ; Equ 等值定义 ;#################################################################### ICO_MAIN equ 1000 DLG_MAIN equ 1000 IDC_TEXT equ 1001 WM_HOOK equ WM_USER + 100h ;#################################################################### ; 代码段 ;#################################################################### .code ;#################################################################### _ProcDlgMain proc uses ebx edi esi hWnd,wMsg,wParam,lParam local @dwTemp mov eax,wMsg ;******************************************************************** .if eax == WM_CLOSE invoke UninstallHook invoke EndDialog,hWnd,NULL ;******************************************************************** .elseif eax == WM_INITDIALOG invoke InstallHook,hWnd,WM_HOOK .if ! eax invoke EndDialog,hWnd,NULL .endif ;******************************************************************** .elseif eax == WM_HOOK mov eax,wParam .if al == 0dh mov eax,0a0dh .endif mov @dwTemp,eax invoke SendDlgItemMessage,hWnd,IDC_TEXT,\ EM_REPLACESEL,0,addr @dwTemp .else mov eax,FALSE ret .endif mov eax,TRUE ret _ProcDlgMain endp ;#################################################################### start: invoke GetModuleHandle,NULL invoke DialogBoxParam,eax,DLG_MAIN,NULL,\ offset _ProcDlgMain,NULL invoke ExitProcess,NULL ;#################################################################### end start
为了使用动态链接库中的导出函数InstallHook和UninstallHook,在程序的开头需要用include语句和includelib语句将动态链接库的函数声明和导入库包含进来。
在对话框初始化的消息WM_INITDIALOG 中,程序调用InstallHook函数安装钩子,输入的参数是主窗口句柄和自定义的消息ID:WM_HOOK(Windows系统中ID值在WM_USER以后的值都可以由用户使用,在这里将WM_HOOK定义为WM_USER+100h),这样每当钩子回调函数得到按键消息的时候,就可以通过这个消息ID通知主窗口。接下来程序对返回值进行检查,如果返回值表示失败则直接退出程序。在关闭对话框的WM_CLOSE消息中,程序调用UninstallHook函数卸载钩子。
在平时,主程序等待自定义消息WM_HOOK,并将传递过来的按键字符串通过发送EM_REPLACESEL消息添加到编辑框中,在添加之前先检测按键是否为回车键,如果是,再人为插入一个换行符(0ah),以便将编辑框中的内容换行显示。
2. 钩子回调函数
现在回过头来看HookDll.asm程序中的钩子回调函数,回调函数的写法一般如下:
HookProc proc dwCode,wParam,lParam invoke CallNextHookEx,hHook,_dwCode,_wParam,_lParam ;处理消息的代码 mov eax,返回值 ret HookProc endp
各种类型钩子的回调函数的参数都是这样3个,但是它们的定义各不相同,就像窗口过程在收到各种不同消息的时候,wParam和lParam的定义也各不相同。不同类型的钩子回调函数的返回值定义也是各不相同的。
对于键盘钩子来说,参数的定义如下所示。
● dwCode——键盘消息的处理方式。如果是HC_ACTION,表示收到一个正常的击键消息;如果是HC_NOREMOVE,表示对应消息并没有从消息队列中移去(当某个进程用指定PM_NOREMOVE 标志的PeekMessage函数获取消息时就是如此)。
● wParam——按键的虚拟码(即Windows.inc中定义的VK_xxx值)。
● lParam——按键的重复次数、扫描码和标志等数据,不同数据位的定义如下:
■ 位0~15:按键的重复次数。
■ 位16~23:按键的扫描码。
■ 位24:按键是否是扩展键(F1与F2等Fx键,小键盘数字键等),如果此位是1表示按键是扩展键。
■ 位25~28:未定义。
■ 位29:如果Alt键在按下状态,此位置1,否则置0。
■ 位30:按键的原先状态,消息发送前按键原来是按下的,此位被设置为1,否则置0。
■ 位31:按键的当前动作,如果是按键按下,那么此位被设置为0;按键释放的话被设置为1。
上页:第11章 动态链接库和钩子 · 11.2 Windows钩子(2) 下页:第11章 动态链接库和钩子 · 11.2 Windows钩子(4)