SetWindowsHookEx proto HookType:DWORD, pHookProc:DWORD, hInstance:DWORD, ThreadID:DWORDYou can uninstall a hook by calling UnhookWindowsHookEx which accepts only one parameter, the handle of the hook you want to uninstall. If the call succeeds, it returns a non-zero value in eax. Otherwise, it returns NULL.If the call is successful, it returns the hook handle in eax. If not, NULL is returned. You must save the hook handle for unhooking later.
- HookType is one of the values listed above, e.g., WH_MOUSE, WH_KEYBOARD
- pHookProc is the address of the hook procedure that will be called to process the messages for the specified hook. If the hook is a remote one, it must reside in a DLL. If not, it must be in your process.
- hInstance is the instance handle of the DLL in which the hook procedure resides. If the hook is a local one, this value must be NULL
- ThreadID is the ID of the thread you want to install the hook to spy on. This parameter is the one that determines whether a hook is local or remote. If this parameter is NULL, Windows will interpret the hook as a system-wide remote hook that affects all threads in the system. If you specify the thread ID of a thread in your own process, this hook is a local one. If you specify the thread ID from other process, the hook is a thread-specific remote one. There are two exceptions to this rule: WH_JOURNALRECORD and WH_JOURNALPLAYBACK are always local system-wide hooks that are not required to be in a DLL. And WH_SYSMSGFILTER is always a system-wide remote hook. Actually it is identical to WH_MSGFILTER hook with ThreadID==0.
WH_CALLWNDPROCThe bottom line is: you must consult your win32 api reference for details about the meanings of the parameters and return value of the hook you want to install.WH_MOUSE
- nCode can be only HC_ACTION which means there is a message sent to a window
- wParam contains the message being sent, if it's not zero
- lParam points to a CWPSTRUCT structure
- return value: not used, return zero
- nCode can be HC_ACTION or HC_NOREMOVE
- wParam contains the mouse message
- lParam points to a MOUSEHOOKSTRUCT structure
- return value: zero if the message should be processed. 1 if the message should be discarded.
CallNextHookEx proto hHook:DWORD, nCode:DWORD, wParam:DWORD, lParam:DWORDAn important note about remote hooks: the hook procedure must reside in a DLL which will be mapped into other processes. When Windows maps the DLL into other processes, it will not map the data section(s) into the other processes. In short, all processes share a single copy of code but they will have their own private copy of the DLL's data section! This can be a big surprise to the unwary. You may think that when you store a value into a variable in the data section of a DLL, that value will be shared among all processes that load the DLL into their process address space. It's simply not true. In normal situation, this behavior is desirable since it provides the illusion that each process has its own copy of the DLL. But not when Windows hook is concerned. We want the DLL to be identical in all processes, including the data. The solution: you must mark the data section as shared. You can do this by specifying the section(s) attribute in the linker switch. For MASM, you need to use this switch:
- hHook is your own hook handle. The function uses this handle to traverse the linked list and search for the hook procedure it should call next.
- nCode, wParam and lParam you can just pass those three values you receive from Windows to CallNextHookEx.
/SECTION:<section name>, SThe name of the initialized data section is .data and the uninitialized data is .bss. For example if you want to assemble a DLL which contains a hook procedure and you want the uninitialized data section to be shared amoung processes, you must use the following line:
link /section:.bss,S /DLL /SUBSYSTEM:WINDOWS ..........S attribute marks the section as shared.
;--------------------------------------------- This is the source code of the main program --------------------------------------
Main:
call
'Kernel32.GetModuleHandleA' 0 | mov D$hInstance eax
call
'USER32.DialogBoxParamA' 0 IDD_MAINDLG 0 DialogFunc 0
call
'Kernel32.ExitProcess' 0
____________________________________________________________________________________________
____________________________________________________________________________________________
Proc DialogFunc:
Arguments
@Adressee, @Message, @wParam, @lParam
pushad
...If
D@message = &WM_CLOSE
On D$HookFlag = &TRUE, call 'mousehook.UninstallHook'
call 'user32.EndDialog' D@Adressee 0
...Else_If
D@message = &WM_INITDIALOG
; Nop.
...Else_If
D@Message = WM_MOUSEHOOK
call 'user32.GetDlgItemTextA' D@Adressee IDC_HANDLE buffer1 128
call 'user32.wsprintfA' buffer template D@wParam | add esp 12
call 'kernel32.lstrcmpi' buffer buffer1
On eax <> 0, call 'user32.SetDlgItemTextA' D@Adressee IDC_HANDLE buffer
call 'user32.GetDlgItemTextA' D@Adressee IDC_CLASSNAME buffer1 128
call 'user32.GetClassNameA' D@wParam buffer 128
call 'kernel32.lstrcmpi' buffer buffer1
On eax <> 0, call 'user32.SetDlgItemTextA' D@Adressee IDC_CLASSNAME
buffer
call 'user32.GetDlgItemTextA' D@Adressee IDC_WNDPROC buffer1 128
call 'user32.GetClassLongA' D@wParam &GCL_WNDPROC
call 'user32.wsprintfA' buffer template eax | add esp 12
call 'kernel32.lstrcmpi' buffer buffer1
On eax <> 0, call 'user32.SetDlgItemTextA' D@Adressee IDC_WNDPROC buffer
...Else_If
D@message = &WM_COMMAND
..If D@lParam = 0
; No Handle >>> nop.
..Else_If W@wParam = IDC_EXIT
call 'user32.SendMessageA' D@Adressee &WM_CLOSE 0 0 ;| jmp L2>>
..Else_If W@wParam+2 = &BN_CLICKED
.If D$HookFlag = 0
call 'mousehook.InstallHook' D@Adressee
If eax <> 0
mov D$HookFlag &TRUE
call 'user32.SetDlgItemTextA' D@Adressee IDC_HOOK UnhookText
End_If
.Else
call 'mousehook.UninstallHook'
call 'user32.SetDlgItemTextA' D@Adressee IDC_HOOK HookText
mov D$HookFlag &FALSE
call 'user32.SetDlgItemTextA' D@Adressee IDC_CLASSNAME 0
call 'user32.SetDlgItemTextA' D@Adressee IDC_HANDLE 0
call 'user32.SetDlgItemTextA' D@Adressee IDC_WNDPROC 0
.End_If
..End_If
...Else
L8:
popad | mov eax &FALSE | Exit
...End_If
L9: popad | mov eax &TRUE
EndP
;-----------------------------------------------------
This is the source code of the DLL --------------------------------------
Proc Main:
Arguments
@Instance, @Reason, @Reserved
If D@Reason = &DLL_PROCESS_ATTACH
move D$hInstance D@Instance
Else_If D@Reason = &DLL_PROCESS_DETACH
; ...
Else_If D@Reason = &DLL_THREAD_ATTACH
; ...
Else_If D@Reason = &DLL_THREAD_DETACH
; ...
End_If
mov eax &TRUE
Endp
____________________________________________________________________________________________
proc MouseProc::
Arguments
@nCode, @wParam, @lParam
call
'user32.CallNextHookEx' D$hHook D@nCode D@wParam D@lParam
mov
edx D@lParam
;
edx Points to a Point structure
;
first dword = x second = y
call
'user32.WindowFromPoint' D$edx D$edx+4
call
'user32.PostMessageA' D$hWnd WM_MOUSEHOOK eax 0
xor
eax eax
EndP
Proc InstallHook::
Arguments
@hwnd2
push D@hwnd2 | pop D$hWnd
call 'user32.SetWindowsHookExA' &WH_MOUSE MouseProc D$hInstance &NULL
mov D$hHook eax
EndP
Proc UninstallHook::
call 'user32.UnhookWindowsHookEx' D$hHook
EndP
.If D$HookFlag = 0
call 'mousehook.InstallHook' D@Adressee
If eax <> 0
mov D$HookFlag &TRUE
call 'user32.SetDlgItemTextA' D@Adressee IDC_HOOK UnhookText
End_If
The program maintains a flag,
HookFlag, to monitor the state of the hook. It's FALSE if the hook is not
installed and TRUE if the hook is installed.
When the user presses Hook
button, the program checks if the hook is already installed. If it is not,
it call InstallHook function in the hook DLL to install it. Note that we
pass the handle of the main dialog as the parameter of the function so
the hook DLL can send the WM_MOUSEHOOK messages to the right window i.e.
our own.
When the program is loaded,
the hook DLL is loaded too. Actually, DLLs are loaded immediately after
the program is in memory. The DLL entrypoint function is called before
the first instruction in the main program is execute even. So when the
main program executes the DLL(s) is/are initialized. We put the following
code in the DLL entrypoint function of the hook DLL:
If
D@Reason = &DLL_PROCESS_ATTACH
move D$hInstance D@Instance
The code just saves the instance
handle of the hook DLL itself to a global variable named hInstance for
use within the InstallHook function. Since the DLL entrypoint function
is called before other functions in the DLL are called , hInstance is always
valid. We put hInstance in .data section so that this value is kept on
per-process basis. Since when the mouse cursor hovers over a window, the
hook DLL is mapped into the process. Imagine that there is already a DLL
that occupies the intended load address of the hook DLL, the hook DLL would
be remapped to another address. The value of hInstance will be updated
to those of the new load address. When the user presses Unhook button and
then Hook button, SetWindowsHookEx will be called again. However, this
time, it will use the new load address as the instance handle which will
be wrong because in the example process, the hook DLL's load address hasn't
been changed. The hook will be a local one where you can hook only the
mouse events that occur in your own window. Hardly desirable.
Proc InstallHook::
Arguments
@hwnd2
push D@hwnd2 | pop D$hWnd
call 'user32.SetWindowsHookExA' &WH_MOUSE MouseProc D$hInstance &NULL
mov D$hHook eax
EndP
The InstallHook function
itself is very simple. It saves the window handle passed as its parameter
to a global variable named hWnd for future use. It then calls SetWindowsHookEx
to install a mouse hook. The return value of SetWindowsHookEx is stored
in a global variable named hHook for use with UnhookWindowsHookEx.
After SetWindowsHookEx is
called, the mouse hook is functional. Whenever a mouse event occurs in
the system, MouseProc ( your hook procedure) is called.
proc MouseProc::
Arguments
@nCode, @wParam, @lParam
call
'user32.CallNextHookEx' D$hHook D@nCode D@wParam D@lParam
mov
edx D@lParam
;
edx Points to a Point structure
;
first dword = x second = y
call
'user32.WindowFromPoint' D$edx D$edx+4
call
'user32.PostMessageA' D$hWnd WM_MOUSEHOOK eax 0
xor
eax eax
EndP
The first thing it does is to call CallNextHookEx to give the other hooks the chance to process the mouse event. After that, it calls WindowFromPoint function to retrieve the handle of the window at the specified screen coordinate. Note that we use the POINT structure in the MOUSEHOOKSTRUCT structure pointed to by lParam as the current mouse coordinate. After that we send the window handle to the main program via PostMessage with WM_MOUSEHOOK message. One thing you should remember is that: you should not use SendMessage inside the hook procedure, it can cause message deadlock. PostMessage is recommended. The MOUSEHOOKSTRUCT structure is defined below:
[MOUSEHOOKSTRUCT: D§
ptX: ?
ptY: ?
hwnd: ?
wHitTestCode:
?
dwExtraInfo:
?]
...Else_If D@Message = WM_MOUSEHOOK
call 'user32.GetDlgItemTextA' D@Adressee IDC_HANDLE buffer1 128
call 'user32.wsprintfA' buffer template D@wParam | add esp 12
call 'kernel32.lstrcmpi' buffer buffer1
On eax <> 0, call 'user32.SetDlgItemTextA' D@Adressee IDC_HANDLE buffer
call 'user32.GetDlgItemTextA' D@Adressee IDC_CLASSNAME buffer1 128
call 'user32.GetClassNameA' D@wParam buffer 128
call 'kernel32.lstrcmpi' buffer buffer1
On eax <> 0, call 'user32.SetDlgItemTextA' D@Adressee IDC_CLASSNAME
buffer
call 'user32.GetDlgItemTextA' D@Adressee IDC_WNDPROC buffer1 128
call 'user32.GetClassLongA' D@wParam &GCL_WNDPROC
call 'user32.wsprintfA' buffer template eax | add esp 12
call 'kernel32.lstrcmpi' buffer buffer1
On eax <> 0, call 'user32.SetDlgItemTextA' D@Adressee IDC_WNDPROC buffer
To avoid flickers, we check
the text already in the edit controls and the text we will put into them
if they are identical. If they are, we skip them.
We retrieve the class name
by calling GetClassName, the address of the window procedure by calling
GetClassLong with GCL_WNDPROC and then format them into strings and put
them into the appropriate edit controls.
call 'mousehook.UninstallHook'
call 'user32.SetDlgItemTextA' D@Adressee IDC_HOOK HookText
mov D$HookFlag &FALSE
call 'user32.SetDlgItemTextA' D@Adressee IDC_CLASSNAME 0
call 'user32.SetDlgItemTextA' D@Adressee IDC_HANDLE 0
call 'user32.SetDlgItemTextA' D@Adressee IDC_WNDPROC 0
When the user presses Unhook
button, the program calls UninstallHook function in the hook DLL. UninstallHook
just calls UnhookWindowsHookEx. After that, it changes the text of the
button back to "Hook", HookFlag to FALSE and clears the content of the
edit controls.
Note the linker switch in
the makefile.
The DLL .Data must be specified
as a shared section to make all processes share the same section of the
hook DLL. Without this switch, your hook DLL will not function correctly.