📄 011.txt
字号:
_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.2 Windows钩子(4)
对于每个击键动作,钩子回调函数会在键按下和释放的时候被调用两次,只需根据 lParam的位31中的标志来记录一次,否则得到的是重复信息。
另外,回调函数收到的参数是以按键的扫描码和虚拟码表示的,在送给主窗口前需要将它转换成我们认识的ASCII码,但虚拟码或扫描码和ASCII码之间的对应关系并没有规律,必须进行查表操作才能转换。如果在程序中自己转换的话,需要一个键码对应表和查表程序。
Windows中现成的函数ToAscii可以完成这个功能并自动辨认按键的按下或释放动作。代码如下。
HookProc proc _dwCode,_wParam,_lParam
local @szKeyState[256]:byte
invoke CallNextHookEx,hHook,_dwCode,_wParam,_lParam
invoke GetKeyboardState,addr @szKeyState
invoke GetKeyState,VK_SHIFT
mov @szKeyState + VK_SHIFT,al
mov ecx,_lParam
shr ecx,16
invoke ToAscii,_wParam,ecx,addr @szKeyState,addr szAscii,0
mov byte ptr szAscii [eax],0
invoke SendMessage,hWnd,dwMessage,dword ptr szAscii,NULL
...
ToAscii函数的用法是:
invoke ToAscii,dwVirtKey,uScanCode,lpKeyState,lpBuffer,uFlags
dwVirtKey参数指定按键的虚拟码,在使用时直接用钩子回调函数的wParam参数就可以了,uScanCode指定按键的扫描码,并用位15来表示是按键按下还是按键释放,和回调函数的lParam参数对比可以看出,lParam参数的高16位就是需要的东西,所以程序将lParam右移16位后用做uScanCode参数。
lpKeyState指向一个256字节的缓冲区,其中存放键盘中所有按键的当前状态,一个字节表示一个按键,数值为1表示按下,为0表示释放,数据在缓冲区中的排列位置按照VK_xx虚拟码的顺序排列。这是为了让函数得知键盘上各种控制键的状态(如Shift,Alt和Ctrl等),因为这些键是否按下对转换结果是有影响的,比如同样是按键“1”,如果Shift键不按下,对应的就是“1”,按下的话函数必须返回“!”才是正确的结果。当然不可能自己去填写这个缓存区,使用GetKeyboardState函数就可以让系统根据当前的键盘状态填写这个缓冲区。
lpBuffer指向一个缓存区,用来接收转换后的ASCII码,最后的uFlags参数表示当前是否有一个菜单在激活状态,0表示没有,1表示有菜单正在激活。
函数的返回值表示转换后返回在lpBuffer缓冲区中的字符数量,它可能是0(如按键放开时不产生字符)、1或者是2,下面的语句根据返回字符数将缓冲区中的字符尾部加上一个NULL:
mov byte ptr szAscii [eax],0
对于Shift等控制键来说,GetKeyboardState函数返回的状态是区分左、右键的(分别对应VK_LSHIFT和VK_RSHIFT),而ToAscii函数检测的是VK_SHIFT,不对Shift键进行处理的话,转换结果可能是错误的,所以程序使用GetKeyState函数单独获取VK_SHIFT的状态并手工修改缓冲区中VK_SHIFT位置的状态。
转换完成后,用PostMessage函数将转换后的按键内容传递给主窗口,就大功告成了!不过要注意的还有两点:首先是在这里不要使用SendMessage函数,因为可能造成死循环;其次就是不要向主窗口传递地址,因为钩子DLL被插入到其他进程的地址空间中运行,所以将地址传回去可能是无效的。
不同类型钩子回调函数返回值的定义是不同的。对于键盘钩子,返回0表示允许Windows将消息转发给目标窗口过程,返回非0值表示让Windows将消息丢弃,这样钩子函数可以检测到按键动作,目标程序却无法收到键盘消息,相当于所有的按键都失效了。
3. 钩子链
Windows系统中可以同时存在多个同类型的钩子,多个程序同时安装同一种钩子的时候就会出现这种情况,这些钩子组成一个钩子链,最近加入的钩子放在链表的头部,Windows负责为每种钩子维护一个钩子链。当一个事件发生的时候,Windows调用最后安装的钩子,然后由当前钩子的回调函数发起调用下一个钩子的动作,Windows收到这个动作后,再从链表中取出下一个钩子的地址并将调用传递下去。
在大多数的情况下,一个钩子回调函数最好把消息事件传递下去以便其他的钩子都有获得处理这一消息的机会。调用下一个钩子函数是CallNextHookEx,该函数的用法是:
invoke CallNextHookEx,hHook,dwCode,wParam,lParam
hHook参数是当前钩子的句柄,dwCode,wParam和lParam参数就是当前钩子收到的参数,这个函数让Windows调用钩子链中的下一个钩子。如果调用成功,函数的返回值是下一个钩子回调函数返回的数值。
11.2.3 日志记录钩子
日志记录钩子是一种特殊的钩子,说它特殊是因为它是远程钩子,却不用放在动态链接库中,这就为监视系统范围的消息提供了方便。本节中尝试用日志记录钩子的办法来实现键盘监视的功能,源代码包括在所附光盘的Chapter11\RecHook目录中,包括汇编源文件RecHook.asm和资源脚本文件RecHook.rc,其中RecHook.rc文件的内容和上一个例子的Main.rc文件是一样的。
RecHook.asm文件的内容如下:
.386
.model flat, stdcall
option casemap :none
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Include 文件定义
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
include windows.inc
include user32.inc
includelib user32.lib
include kernel32.inc
includelib kernel32.lib
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Equ 等值定义
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
ICO_MAIN equ 1000
DLG_MAIN equ 1000
IDC_TEXT equ 1001
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 数据段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.data?
hInstance dd ?
hWinMain dd ?
hHook dd ?
szAscii db 32 dup (?)
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 代码段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.code
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 钩子回调函数
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
HookProc proc _dwCode,_wParam,_lParam
local @szKeyState[256]:byte
invoke CallNextHookEx,hHook,_dwCode,_wParam,_lParam
pushad
.if _dwCode == HC_ACTION
mov ebx,_lParam
assume ebx:ptr EVENTMSG
.if [ebx].message == WM_KEYDOWN
invoke GetKeyboardState,addr @szKeyState
invoke GetKeyState,VK_SHIFT
mov @szKeyState + VK_SHIFT,al
mov ecx,[ebx].paramH
shr ecx,16
invoke ToAscii,[ebx].paramL,ecx,\
addr @szKeyState,addr szAscii,0
mov byte ptr szAscii [eax],0
.if szAscii == 0dh
mov word ptr szAscii+1,0ah
.endif
invoke SendDlgItemMessage,hWinMain,IDC_TEXT,\
EM_REPLACESEL,0,addr szAscii
.endif
assume ebx:nothing
.endif
popad
ret
HookProc endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_ProcDlgMain proc uses ebx edi esi hWnd,wMsg,wParam,lParam
mov eax,wMsg
;********************************************************************
.if eax == WM_CLOSE
invoke UnhookWindowsHookEx,hHook
invoke EndDialog,hWnd,NULL
;********************************************************************
.elseif eax == WM_INITDIALOG
push hWnd
pop hWinMain
invoke SetWindowsHookEx,WH_JOURNALRECORD,\
addr HookProc,hInstance,NULL
.if eax
mov hHook,eax
.else
invoke EndDialog,hWnd,NULL
.endif
;********************************************************************
.else
mov eax,FALSE
ret
.endif
mov eax,TRUE
ret
_ProcDlgMain
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -