| ||
Now, it is time to consider a powerful tool most frequently used for debugging programs. This is the hooks (sometimes called traps) mechanism. The idea of this mechanism is that the programmer can trace the messages both within a single application and within the framework of the entire system by introducing filters. Filters are classified as global (in force within the entire system) or local (in force only within the framework of a single process). Working with filters, it is necessary to bear in mind that they can considerably slow down the operation of the entire system. This is especially true for global filters. From the programming point of view, this mechanism defines a function called by the system if a specific event occurs. It is also possible to describe messages that arrive to the filter function.
Consider some tools for working with filters. The main types of filter messages are as follows :
WH_CALLWNDPROC The filter is triggered when the SendMessage function is called.
WH_CALLWNDPROCRET The filter is triggered when the SendMessage function returns control.
WH_CBT The message arrives when something happens to the window.
WH_DEBUG This message is sent before a message is sent to some other filter.
WH_GETMESAGE This filter is triggered when the GetMessage function receives some message from the queue.
WH_JOURNALRECORD This message arrives to the filter procedure when the system deletes a message from the queue.
WH_JOURNALPLAYBACK This message is sent after the arrival of the WH_JOURNALRECORD message.
WH_KEYBOARD This message arrives when keyboard events occur.
WH_MOUSE This is similar to the previous message but relates to mouse events.
WH_MSGFILTER This message arrives if user input events have occurred to a dialog box, menu, or scroll bar before these events were handled within this process.
WH_SHELL This filter is triggered when something happens to the Windows shell.
WH_SYSMSGFILTER This is similar to the WH_MSGFILTER message but relates to the entire system.
The filter is set using the SetWindowsHookEx function. Consider the parameters accepted by this function:
First parameterThis is the filter type. It can take one of the previously listed values.
Second parameterThis is the address of the filter procedure. If you specify a global filter, this procedure must be located in a DLL. The only exceptions are two types of filters: WH_JOURNALRECORD and WH_JOURNALPLAYBACK .
Third parameterThis is the DLL handle, provided that the filter is intended for the entire system. The only exceptions are the two previously mentioned filter types.
Fourth parameterThis is the thread identifier if you need to trace one of the threads. If the value of this parameter is zero, then a global filter intended for the entire system will be created. In general, the thread can relate to your process or to any other process running in the system.
The SetWindowsHookEx function returns the filter descriptor.
The filter function receives three parameters. The first parameter determines the type of event depending on the filter type. The next two parameters are the pointer to the hook procedure and the handle to the DLL containing that procedure. Because there are several types of events for every hook type, I won't list them here. They can be found in Microsoft's documentation.
After completing the operation, the hook must be closed using the UnhookWindcwsHookEx function, whose only parameter is the handle to the hook that needs to be removed.
In general, a hook is only part of the chain of system calls. Therefore, the programmer must call the callNextHookEx function from the hook procedure. That function will pass the required information along the chain. The first parameter of this function is the handle to the hook. The second, third, and fourth parameters correspond exactly to the three parameters passed to the hook procedure.
Listing 19.2 demonstrates an example of a simple hook that traps all events related to pressing the space bar within the entire system. Note that if the hook is global, the hook procedure must be placed into a DLL.
// The DIAL.RC file for the DLLE.ASM program // Definitions of constants #define WS_SYSMENU 0x00080000L #define WS_MINIMIZEBOX 0x00020000L #define WS_MAXIMIZEBOX 0x000l0000L // Dialog box definition DIAL1 DIALOG 0, 0, 240, 120 STYLE WS_SYSMENU WS_MINIMIZEBOX WS_MAXIMIZEBOX CAPTION "An example of program with a hook" FONT 8, "Arial" { } ; The main module - DLLE.ASM, ; which sets a hook in a DLL .586P ; Flat memory model .MODEL FLAT, stdcall ; Constants ; This message arrives when the window is closed WM_CLOSE equ 10h WM_INITDIALOG equ 110h WH_KEYBOARD equ 2 ; Message structure MSGSTRUCT STRUC MSHWND DD ? MSMESSAGE DD ? MSWPARAM DD ? MSLPARAM DD ? MSTIME DD ? MSPT DD ? MSGSTRUCT ENDS ; Prototypes of external procedures IFDEF MASM ; MASM EXTERN UnhookWindowsHookEx@4:NEAR EXTERN SetWindowsHookExA@16:NEAR EXTERN EndDialog@8:NEAR EXTERN DialogBoxParamA@20:NEAR EXTERN GetProcAddress@8:NEAR EXTERN LoadLibraryA@ 4:NEAR EXTERN FreeLibrary@4:NEAR EXTERN ExitProcess@4:NEAR EXTERN MessageBoxA@16:NEAR ; INCLUDELIB directives for the linker includelib c:\masm32\lib\user32.lib includelib c:\masm32\lib\kernel32.lib ELSE EXTERN UnhookWindowsHookEx:NEAR EXTERN SetWindowsHookExA:NEAER EXTERN EndDialog:NEAR EXTERN DialogBoxParamA:NEAR EXTERN GetProcAddress:NEAR EXTERN LoadLibraryA:NEAR EXTERN FreeLibrary:NEAR EXTERN ExitProcess:NEAR EXTERN MessageBoxA:NEAR UnhookWindowsHookEx@4 = UnhookWindowsHookEx SetWindowsHookExA@16 = SetWindowsHookExA EndDialog@8 = EndDialog DialogBoxParamA@20 = DialogBoxParamA GetProcAddress@8 = GetProcAddress LoadLibraryA@4 = LoadLibraryA FreeLibrary@4 = FreeLibrary ExitProcess@4 = ExitProcess MessageBoxA@16 = MessageBoxA ; INCLUDELIB directives for the linker includelib c:\tasm32\lib\import32.lib ENDIF ;------------------------------------------------- ; Data segment _DATA SEGMENT MSG MSGSTRUCT <?> HINST DD 0 ; Application descriptor PA DB "DIAL1", 0 LIBR DB 'DLL2.DLL', 0 HLIB DD ? APROC DD ? HH DD ? ATOH DD ? IFDEF MASM NAMEPROC DB' _HOOK@0', 0 NAMEPROC1 DB' _TOH@0', 0 ELSE NAMEPROC1 DB '_TOH', 0 NAMEPROC DB 'HOOK', 0 ENDIF _DATA ENDS ; Code segment _TEXT SEGMENT START: ; Load the DLL PUSH OFFSET LIBR CALL LoadLibraryA@4 CMP EAX, 0 JE _EXIT MOV HLIB, EAX ; Get the address of the hook procedure PUSH OFFSET NAMEPROC PUSH HLIB CALL GetProcAddress@8 CMP EAX, 0 JE _EXIT MOV APROC, EAX ; Get the address of the auxiliary procedure PUSH OFFSET NAMEPROC1 PUSH HLIB CALL GetProcAddress@8 CMP EAX, 0 JE _EXIT MOV ATOH, EAX ; Install the hook PUSH 0 PUSH HLIB PUSH APROC PUSH WH_KEYBOARD CALL SetWindowsHookExA@16 MOV HH, EAX ; Store the hook and pass it to the library MOV EAX, ATOH PUSH HH CALL ATOH ; Open the dialog PUSH 0 PUSH OFFSET WNDPROC PUSH 0 PUSH OFFSET PA PUSH [HINST] CALL DialogBoxParamA@20 ; Remove the hook PUSH HH CALL UnhookWindowsHookEx@4 ; Close the library ; The library will close automatically ; when exiting the program PUSH OFFSET NAMEPROC PUSH HLIB CALL FreeLibrary@4 ; Exit _EXIT: PUSH 0 CALL ExitProcess@4 ; Window procedure ; Position of parameters in the stack ; [BP+014H] ; LPARAM ; [BP+10H] ; WAPARAM ; [BP+0CH] ; MES ; [BP+8] ; HWND WNDPROC PROC PUSH EBP MOV EBP, ESP PUSH EBX PUSH ESI PUSH EDI ;----------------- CMP DWORD PTR [EBP+0CH], WM_CLOSE JNE L1 PUSH 0 PUSH DWORD PTR [EBP+08H] CALL EndDialog@8 JMP FINISH L1: CMP DWORD PTR [EBP+0CH], WM_INITDIALOG JNE FINISH FINISH: POP EDI POP ESI POP EBX POP EBP MOV EAX, 0 RET 16 WNDPROC ENDP _TEXT ENDS END START ; The DLL2.ASM DLL ; containing the hook procedure .586P ; Flat memory model IFDEF MASM .MODEL FLAT stdcall ELSE .MODEL FLAT ENDIF PUBLIC HOOK, TOH ; Constants ; These messages arrive when ; the DLL is opened DLL_PROCESS_DETACH equ 0 DLL_PROCESS_ATTACH equ 1 DLL_THREAD_ATTACH equ 2 DLL_THREAD_DETACH equ 3 IFDEF MASM ; MASM ; Prototypes of external procedures EXTERN CallNextHookEx@16:NEAR EXTERN MessageBoxA@16:NEAR ; INCLUDELIB directives for the linker includelib c:\masm32\lib\user32.lib includelib c: \masm32\lib\kernel32.lib ELSE ; TASM EXTERN CallNextHookEx:NEAR EXTERN MessageBoxA:NEAR CallNextHookEx@16 = CallNextHookEx MessageBoxA@16 = MessageBoxA includelib c:\tasm32\lib\import32.lib ENDIF ;----------------------------------------------- ; Data segment _DATA SEGMENT HDL DD ? HHOOK DD ? CAP DB "Hook message", 0 MES DB "Blank key is pressed", 0 _DATA ENDS ; Code segment _TEXT SEGMENT ; [EBP+10H], ; Reserved parameter ; [EBP+0CH] ; Cause of call ; [EBP+8] ; DLL identifier DLLENTRY: MOV EAX, DWORD PTR [EBP+0CH] CMP EAX, 0 JNE D1 ; Close the library JMP _EXIT D1: CMP EAX, 1 JNE _EXIT ; Open the library ; Store the DLL identifier MOV EDX, DWORD PTR [EBP+08H] MOV HDL, EDX _EXIT: MOV EAX, 1 RET 12 ;------------------- TOH PROC EXPORT PUSH EBP MOV EBP, ESP MOV EAX, DWORD PTR [EBP+08H] MOV HHOOK, EAX POP EBP RET TOH ENDP ; Hook procedure HOOK PROC EXPORT PUSH EBP MOV EBP, ESP ; Send the message along the chain PUSH DWORD PTR [EBP+010H] PUSH DWORD PTR [EBP+0CH] PUSH DWORD PTR [EBP+08H] PUSH HHOOK CALL CallNextHookEx@16 ; Check whether the space bar has been pressed CMP DWORD PTR [EBP+0CH], 32 JNE _EX ; If yes, then display the message PUSH 0 ; MB_OK PUSH OFFSET CAP PUSH OFFSET MES PUSH 0 ; In the window CALL MessageBoxA@16 _EX: POP EBP RET HOOK ENDP _TEXT ENDS END DLLENTRY
To translate the program in Listing 19.2, issue the following commands for MASM32:
DLL
ml /c /coff /DMASM dll2.asm link /subsystem:windows /DLL dll2.obj
Main program
ml /c /coff /DMMSM dllex.asm rc dial.rc link /subsystem:windows dllex.obj dial.res
Issue the following commands for TASM32:
DLL
TASM32 /ml dll2.asm Tlink32 /subsystem:windows -aa -Tpd dll2.obj
Main program
TASM32 /ml dllex.asm Brcc32 dial.re Tlink32 -aa dllex.obj,,,,,dial.res
When considering the program presented in Listing 19.2, note the role of the TOH procedure. Also note that the second and the third parameters of the hook procedure exactly correspond the values of similar parameters of the WM_KEYDOWN message. By the way, I hope that you understand why two messages are sent when the space bar is pressedone for pressing the button and one for releasing it.
| ||