Sie können einen Hook deinstallieren, indem Sie UnhookWindowsHookEx aufrufen, die nur einen Parameter akzeptiert, das Handle des Hooks den Sie deinstallieren wollen. Wenn der Aufruf erfolgreich war, wir ein Wert ungleich 0 in EAX zurückgegeben, ansonsten NULL.
Nun, da Sie wissen, wie man Hooks installiert/deinstalliert, schauen wir uns die Hook-Prozedur an.
Die Hook-Prozedur wir immer aufgerufen, wenn ein Ereigniss auftritt, das mit der Art des Hooks, den Sie installiert haben, verbunden ist. Wenn Sie zum Beispiel einenWH_MOUSE Hook installieren, wird Ihre Hook Prozedur aufgerufen, wenn ein Maus-Ereigniss auftritt. Egal was für Art von Hook Sie installieren, hat die Hook-Prozedur immer folgenden Prototyp:
HookProc
proto nCode:DWORD, wParam:DWORD, lParam:DWORD
-
nCode spezifiziert den Hook Code.
-
wParam
und lParam enthalten zusätzlichen Informationen über das Ereigniss
HookProc ist eigentlich ein Platzhalter für den Funktions-Namen. Sie können sie benennen, wie Sie wollen, solange es den obigen Prototypen hat. Die Interpretation von nCode, wParam und lParam ist abhängig von der Art des Hooks den Sie installieren. Genauso wie der Rückgabewert der Hook-Prozedur. Zum Beispiel:
WH_CALLWNDPROC
-
nCode kann lediglich HC_ACTION sein, was bedeutet, dass eine Nachricht an ein Fenster gesendet wurde
-
wParam enthält die Nachricht, die gesendet wurde, wenn nicht gleich 0
-
lParam zeigt auf eine CWPSTRUCT Struktur
-
return
value: wird nicht benutzt, 0 wird zurückgegeben
WH_MOUSE
-
nCode kann HC_ACTION oder HC_NOREMOVE sein
-
wParam enthält die Maus-Nachricht
-
lParam
zeigt auf eine MOUSEHOOKSTRUCT Struktur
-
return
value: 0 wenn die Nachricht bearbeitet werden soll. 1 wenn die Nachricht verworfen werden soll.
Die Schlussfolgerung ist: Sie müssen ihre Win32 API Referenz für Details über die Bedeutung der Parameter und Rückgabewerte des Hooks, den Sie installieren, konsultieren.
Es gibt noch einen kleinen Haken bei der Hook-Prozedur. Wie Sie sich erinnern, werden die Hooks in eine verkettete Liste geschrieben, mit dem zuletzt installierten Hook am Anfang. Wenn ein Ereigniss auftritt, ruft Windows nur den ersten Hook in der Kette auf. Ihre Hook-Prozedur ist dafür verantwortlich den nächsten Hook aus der Kette aufzurufen. Sie brauchen den Hook nicht aufrufen, aber Sie sollten dann wissen, was Sie tun. Meistens ist es besser, die nächste Prozedur aufzurufen, damit andere Hooks das Ereigniss bearbeiten können. Sie können den nächsten Hook aufrufen, indem Sie CallNextHookEx aufrufen, was folgenden Prototypen hat:
CallNextHookEx
proto hHook:DWORD, nCode:DWORD, wParam:DWORD, lParam:DWORD
-
hHook
ist Ihr eigenes Hook Handle. Die Funktion benutzt dieses Handle, um die verketteten Listen zu durchqueren und sucht die Hook-Prozedur die als nächstes aufgerufen werden soll.
-
nCode,
wParam
und lParam Sie können einfach diese drei Werte übergeben, die Sie von Windows erhalten für CallNextHookEx.
Eine wichtige Anmwekung über Remote Hooks: die Hook-Prozedur muss sich in einer DLL befinden, welche in andere Prozesse gemapped wird. Wenn Windows eine DLL in andere Prozesse mapped, wird es nicht die Daten-Sektion(en) in die Prozesse mappen. Kurz gesagt, alle Prozesse teilen eine einzige Kopie des Codes, aber sie haben ihre eigene Kopie der DLL Daten-Sektion. Das kann eine große Überraschung für den Unachtsamen sein. Sie denken vielleicht, dass wenn Sie einen Wert in einer Variablen in der Daten-Sektion der DLL speichern, dass dieser Wert von den anderen Prozessen geteilt wird, die die DLL in ihren Address-Raum laden. Das stimmt einfach nicht. In normalen Situationen ist dieses Verhalten wünschenswert, da es die Illusion vorgibt, dass jeder Prozess seine eigene Kopie der DLL hat. Aber nicht wenn es um Windows-Hooks geht. Wir wollen die DLL in jedem Prozess identisch haben, inklusive der Daten. Die Lösung: Sie müssen eine Daten-Sektion als shared markieren. Sie können das machen, indem Sie die Sektions Attribute im Linker-Parameter angeben. Für MASM müssen Sie diesen Parameter benutzen:
/SECTION:<section
name>, S
Der Name der initialisierten Daten-Sektion ist .data und der uninitialisierten Daten ist .bss. Wenn Sie zum Beispiel eine DLL assemblieren wollen, die eine Hook-Prozedur enthält und Sie wollen die uninitalisierte Daten-Sektion unter mehreren Prozessen teilen, müssen Sie folgende Zeile benutzen:
link
/section:.bss,S /DLL /SUBSYSTEM:WINDOWS ..........
Das S Attribut markiert die Sektion als shared.
Beispiel:
Es gibt zwei Module: eins ist das Hauptprogramm, das den GUI Teil mach und das andere ist die DLL die den Hook de/installiert.
;--------------------------------------------- Das ist der Source Code des Haupt-Programms --------------------------------------
.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include mousehook.inc
includelib mousehook.lib
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
wsprintfA proto C :DWORD,:DWORD,:VARARG
wsprintf TEXTEQU
.const
IDD_MAINDLG equ 101
IDC_CLASSNAME equ 1000
IDC_HANDLE equ 1001
IDC_WNDPROC equ 1002
IDC_HOOK equ 1004
IDC_EXIT equ 1005
WM_MOUSEHOOK equ WM_USER+6
DlgFunc PROTO :DWORD,:DWORD,:DWORD,:DWORD
.data
HookFlag dd FALSE
HookText db "&Hook",0
UnhookText db "&Unhook",0
template db "%lx",0
.data?
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[128]:byte
LOCAL buffer1[128]:byte
LOCAL rect:RECT
.if uMsg==WM_CLOSE
.if HookFlag==TRUE
invoke UninstallHook
.endif
invoke EndDialog,hDlg,NULL
.elseif uMsg==WM_INITDIALOG
invoke GetWindowRect,hDlg,addr rect
invoke SetWindowPos, hDlg, HWND_TOPMOST, rect.left, rect.top, rect.right, rect.bottom, SWP_SHOWWINDOW
.elseif uMsg==WM_MOUSEHOOK
invoke GetDlgItemText,hDlg,IDC_HANDLE,addr buffer1,128
invoke wsprintf,addr buffer,addr template,wParam
invoke lstrcmpi,addr buffer,addr buffer1
.if eax!=0
invoke SetDlgItemText,hDlg,IDC_HANDLE,addr buffer
.endif
invoke GetDlgItemText,hDlg,IDC_CLASSNAME,addr buffer1,128
invoke GetClassName,wParam,addr buffer,128
invoke lstrcmpi,addr buffer,addr buffer1
.if eax!=0
invoke SetDlgItemText,hDlg,IDC_CLASSNAME,addr buffer
.endif
invoke GetDlgItemText,hDlg,IDC_WNDPROC,addr buffer1,128
invoke GetClassLong,wParam,GCL_WNDPROC
invoke wsprintf,addr buffer,addr template,eax
invoke lstrcmpi,addr buffer,addr buffer1
.if eax!=0
invoke SetDlgItemText,hDlg,IDC_WNDPROC,addr buffer
.endif
.elseif uMsg==WM_COMMAND
.if lParam!=0
mov eax,wParam
mov edx,eax
shr edx,16
.if dx==BN_CLICKED
.if ax==IDC_EXIT
invoke SendMessage,hDlg,WM_CLOSE,0,0
.else
.if HookFlag==FALSE
invoke InstallHook,hDlg
.if eax!=NULL
mov HookFlag,TRUE
invoke SetDlgItemText,hDlg,IDC_HOOK,addr UnhookText
.endif
.else
invoke UninstallHook
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
end start
;----------------------------------------------------- Das ist der Source Code der DLL --------------------------------------
.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
include \masm32\include\user32.inc
includelib \masm32\lib\user32.lib
.const
WM_MOUSEHOOK equ WM_USER+6
.data
hInstance dd 0
.data?
hHook dd ?
hWnd dd ?
.code
DllEntry proc hInst:HINSTANCE, reason:DWORD, reserved1:DWORD
.if reason==DLL_PROCESS_ATTACH
push hInst
pop hInstance
.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 MOUSEHOOKSTRUCT
invoke WindowFromPoint,[edx].pt.x,[edx].pt.y
invoke PostMessage,hWnd,WM_MOUSEHOOK,eax,0
assume edx:nothing
xor eax,eax
ret
MouseProc endp
InstallHook proc hwnd:DWORD
push hwnd
pop hWnd
invoke SetWindowsHookEx,WH_MOUSE,addr MouseProc,hInstance,NULL
mov hHook,eax
ret
InstallHook endp
UninstallHook proc
invoke UnhookWindowsHookEx,hHook
ret
UninstallHook endp
End DllEntry
;---------------------------------------------- Das ist das Makefile der DLL ----------------------------------------------
NAME=mousehook
$(NAME).dll: $(NAME).obj
Link /SECTION:.bss,S /DLL /DEF:$(NAME).def /SUBSYSTEM:WINDOWS /LIBPATH:c:\masm\lib $(NAME).obj
$(NAME).obj: $(NAME).asm
ml /c /coff /Cp $(NAME).asm
Analyse:
Das Beispiel wird eine Dialog Box mit drei Edit Steuerelementen anzeigen, die mit dem Klassen-Namen, Fenster-Handle und der Adresses der Fenster-Prozedur, die zu dem Fenster gehört, über dem der Mauszeiger ist, anzeigt. Es gibt zwei Buttons, Hook und Exit, wenn Sie den Hook Button klicken, hooked das Programm die Maus-Eingabe und der Button Text ändert sich zu Unhook. Wenn Sie den Maus-Zeiger über ein Fenster bewegen, werden die Informationen über das Fenster, in dem Hauptfenster des Beispiels angezeigt. Wenn Sie den Unhook Button klicken, entfernt das Programm den Maus-Hook wieder.
Das Hauptprogramm benutzt eine Dialog Box als Haupt-Fenster. Es definiert eine eigene Nachricht, WM_MOUSEHOOK, welche zwischen dem Haupt-Programm und der Hook-DLL gesendet wird. Wenn das Programm diese Nachricht erhält, enthält wParam das Handle des Fensters, über das sich der Mauszeiger befindet. Natürlich ist das eine willkürliche Lösung. Ich habe mich entschlossen, das Handle in wParam zu übergeben, nur der Einfachheit halber. Sie können Ihre eigene Methode der Kommunikation zwischen Haupt-Programm und Hook-DLL benutzen.
.if HookFlag==FALSE
invoke InstallHook,hDlg
.if eax!=NULL
mov HookFlag,TRUE
invoke SetDlgItemText,hDlg,IDC_HOOK,addr UnhookText
.endif
Das Programm enthält ein Flag, HookFlag, um den Status des Hooks zu überwachen. Wenn es FALSE ist, ist der Hook nicht installiert und TRUE wenn der Hook installiert ist.
Wenn der Benutzer den Hook Button drückt, überprüft das Programm, ob der Hook schon installiert ist. Wenn nicht, wird die InstallHook Funktion in der Hook DLL aufgerufen, um ihn zu installieren. Beachten Sie, dass wir das Handle des Haupt-Dialogs als Parameter der Funktion übergeben, so dass die Hook DLL die WM_MOUSEHOOK Nachrichten an das richtige Fenster, dh. an unser eigenes sendet.
Wenn das Programm geladen ist, wird auch die Hook DLL geladen. Eigentlich werden DLLs immer unverzüglich geladen, nachdem das Programm im Speicher ist. Die DLL Einstiegspunkt-Funktion wird ausgeführt, bevor der erste Befehl des Programms ausgeführt wird. So, dass wenn das Programm ausgeführt wird, die DLL(s) schon initialisiert ist/sind. Wir haben folgenden Code in der Einstiegs-Funktion der Hook DLL:
.if reason==DLL_PROCESS_ATTACH
push hInst
pop hInstance
.endif
Der Code speichert nur das Instanz-Handle der Hook-DLL in einer globalen Variablen names hInstance um sie innerhalb der InstallHook-Funktion zu benutzen. Da die DLL Einstiegs-Funktion aufgerufen wird, bevor andere Funktionen der DLL aufgerufen werden, ist hInstance immer gültig. Wir speichern hInstance in der .data Sektion, so dass dieser Wert für jeden Prozess gespeichert wird. Da, wenn der Maus-Cursor über ein anderes Fenster schwebt, die Hook-DLL in den Prozess gemapped wird. Stellen Sie sich vor, dass da schon eine DLL ist, die die zu ladende Hook DLL Adresse beschäftigt, würde die Hook DLL in eine andere Adresse gemapped werden. Der Wert von hInstance würde beim laden der neuen Adresse erneuert werden. Wenn der Benutzer den Unhook Button klickt und dann den Hook Button, wird SetWindowsHookEx erneut aufgerufen. Diesmal würde es die neue Lade Adresse als das Instanz-Handle benutzen, welches falsch ist, da im Beispiel-Prozess die Hook DLL Lade Adresse sich nicht geändert hat. Der Hook ist ein lokaler, wenn Sie nur die Maus-Ereignisse, die in ihrem Fenster passieren hooken können. Kaum wünschenswert.
InstallHook proc hwnd:DWORD
push hwnd
pop hWnd
invoke SetWindowsHookEx,WH_MOUSE,addr MouseProc,hInstance,NULL
mov hHook,eax
ret
InstallHook endp
Die InstallHook Funktion selbst hingegen ist sehr einfach. Sie sichert das Fenster-Handle, das als ihr Parameter übergeben wurde, in einer globalen Variable namens hWnd für späteren Gebrauch. Dann ruft sie SetWindowsHookEx auf, um den Maus-Hook zu installieren. Der Rückgabewert von SetWindowsHookEx wird in einer globalen Variable namens hHook gespeichert, um sie mit UnhookWindowsHookEx zu benutzen.
Nachdem SetWindowsHookEx aufgerufen wurde, ist der Maus-Hook funktionsfähig. Wann immer auch ein Maus-Ereigniss im System auftritt, wird MouseProc (Ihre Hook Prozedur) aufgerufen.
MouseProc proc nCode:DWORD,wParam:DWORD,lParam:DWORD
invoke CallNextHookEx,hHook,nCode,wParam,lParam
mov edx,lParam
assume edx:PTR MOUSEHOOKSTRUCT
invoke WindowFromPoint,[edx].pt.x,[edx].pt.y
invoke PostMessage,hWnd,WM_MOUSEHOOK,eax,0
assume edx:nothing
xor eax,eax
ret
MouseProc endp
Als erstes wird CallNextHookEx aufgerufen, um anderen Hooks die Chance zu geben, Maus-Ereignisse zu bearbeiten. Danach wird die WindowFromPoint Funktion aufgerufen um das Handle des Fensters an den angegebenen Bildschirm-Koordinaten zu ermitteln. Beachten Sie, dass wir die POINT Struktur in der MOUSEHOOKSTRUCT Struktur benutzen, auf die lParam, als aktuelle Muas-Koordinaten, zeigt. Danch senden wir das Fenster-Handle zum Hauptprogramm via PostMessage mit der WM_MOUSEHOOK Nachricht. An was Sie denken sollten: Sie sollten nicht SendeMessage innerhalb einer Hook-Prozedur benutzen, da es einen Nachricht Deadlock erzeugen kann. PostMessage wird dafür benötigt.
Die MOUSEHOOKSTRUCT Struktur ist wie folgt definiert:
MOUSEHOOKSTRUCT STRUCT DWORD
pt POINT
hwnd DWORD ?
wHitTestCode DWORD ?
dwExtraInfo DWORD ?
MOUSEHOOKSTRUCT ENDS
-
pt sind die aktuellen Bildschirm Koordinaten des Maus-Cursor
-
hwnd ist das Handle des Fensters, dass die Maus-Nachrichten empfängt. Es ist normalerweise das Fenster unter dem Maus-Cursor, aber nicht immer. Wenn ein Fenster SetCapture aufruft, wird die Maus-Eingabe zu diesem Fenster statt dessen umgeleitet. Aus diesem Grund benutze ich das hwnd Elemente dieser Struktur nicht, sondern rufe statt dessen WindowFromPoint auf.
-
wHitTestCode spezifiziert den Hit-Test Wert. Der Hit-Test Wert gibt mehr Informationen über die aktuelle Maus-Cursor-Position. Es spezifiziert, auf welchem Teil des Fensters der Maus-Cursor ist. Für eine komplette Liste, schauen Sie in Ihrer Win32 API unter WM_NCHITTEST Nachricht nach.
-
dwExtraInfo enthält zusätliche Informationen die mit der Nachricht verbunden sind. Normalerweise wird dieser Wert gesetzt indem mouse_event aufgerufen wird und ermittelt durch einen GetMessageExtraInfo Aufruf.
Wenn das Haupt-Fenster ein WM_MOUSEHOOK Nachricht erhält, benutzt es das Fenster Handle in wParam um Information über das Fenster zu erhalten.
.elseif uMsg==WM_MOUSEHOOK
invoke GetDlgItemText,hDlg,IDC_HANDLE,addr buffer1,128
invoke wsprintf,addr buffer,addr template,wParam
invoke lstrcmpi,addr buffer,addr buffer1
.if eax!=0
invoke SetDlgItemText,hDlg,IDC_HANDLE,addr buffer
.endif
invoke GetDlgItemText,hDlg,IDC_CLASSNAME,addr buffer1,128
invoke GetClassName,wParam,addr buffer,128
invoke lstrcmpi,addr buffer,addr buffer1
.if eax!=0
invoke SetDlgItemText,hDlg,IDC_CLASSNAME,addr buffer
.endif
invoke GetDlgItemText,hDlg,IDC_WNDPROC,addr buffer1,128
invoke GetClassLong,wParam,GCL_WNDPROC
invoke wsprintf,addr buffer,addr template,eax
invoke lstrcmpi,addr buffer,addr buffer1
.if eax!=0
invoke SetDlgItemText,hDlg,IDC_WNDPROC,addr buffer
.endif
Um Flackern zu vermeiden, überprüfen wir, ob der Text im Edit Steuerelement mit dem identisch ist, den wir schreiben wollen. Falls ja, überspringen wir den Text.
Wir ermitteln den Klassen-Namen, indem wir GetClassName aufrufen, die Adresse der Fenster-Prozedur indem wir GetClassLong mit GCL_WNDPROC aufrufen und formatieren sie dann in Strings und zeigen sie im vorgesehenen Edit Steuerelement an.
invoke UninstallHook
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
Wenn der Benutzer den Unhook Button klickt, ruft das Programm die UninstallHook Funktion in der Hook DLL auf. UninstallHook ruft nur UnhookWindowsHookEx auf. Danach ändern wir den Text des Buttons zurück auf "Hook", HookFlag auf FALSE und löschen den Inhalt der Edit Steurelemente.
Beachten Sie den Linker Parameter im Makefile.
Link /SECTION:.bss,S /DLL /DEF:$(NAME).def /SUBSYSTEM:WINDOWS
Die .bss Sektion wird als Shared-Sektion (geteilte Sektion) angegeben, damit alle Prozesse die selbe uninitialisierte Daten-Sektion der Hook-DLL benutzen. Ohne diesen Parameter wird Ihre Hook-DLL nicht korrekt arbeiten.
Deutsche Übersetzung: Joachim Rohde
Die original Win32Asm-Tutorials stammen von Iczelion's Win32 Assembly HomePage