2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * PURPOSE: Window hooks
5 * PROGRAMER: Casper S. Hornstrup <chorns@users.sourceforge.net>
6 * James Tabor <james.tabor@reactos.org>
7 * Rafal Harabien <rafalh@reactos.org>
8 * NOTE: Most of this code was adapted from Wine,
9 * Copyright (C) 2002 Alexandre Julliard
13 DBG_DEFAULT_CHANNEL(UserHook
);
15 typedef struct _HOOKPACK
20 } HOOKPACK
, *PHOOKPACK
;
22 UNICODE_STRING strUahModule
;
23 UNICODE_STRING strUahInitFunc
;
24 PPROCESSINFO ppiUahServer
;
26 /* PRIVATE FUNCTIONS *********************************************************/
28 /* Calls ClientLoadLibrary in user32 in order to load or unload a module */
30 IntLoadHookModule(int iHookID
, HHOOK hHook
, BOOL Unload
)
35 ppi
= PsGetCurrentProcessWin32Process();
37 TRACE("IntLoadHookModule. Client PID: %p\n", PsGetProcessId(ppi
->peProcess
));
39 /* Check if this is the api hook */
40 if(iHookID
== WH_APIHOOK
)
42 if(!Unload
&& !(ppi
->W32PF_flags
& W32PF_APIHOOKLOADED
))
44 /* A callback in user mode can trigger UserLoadApiHook to be called and
45 as a result IntLoadHookModule will be called recursively.
46 To solve this we set the flag that means that the application has
47 loaded the api hook before the callback and in case of error we remove it */
48 ppi
->W32PF_flags
|= W32PF_APIHOOKLOADED
;
50 /* Call ClientLoadLibrary in user32 */
51 bResult
= co_IntClientLoadLibrary(&strUahModule
, &strUahInitFunc
, Unload
, TRUE
);
52 TRACE("co_IntClientLoadLibrary returned %d\n", bResult
);
55 /* Remove the flag we set before */
56 ppi
->W32PF_flags
&= ~W32PF_APIHOOKLOADED
;
60 else if(Unload
&& (ppi
->W32PF_flags
& W32PF_APIHOOKLOADED
))
62 /* Call ClientLoadLibrary in user32 */
63 bResult
= co_IntClientLoadLibrary(NULL
, NULL
, Unload
, TRUE
);
66 ppi
->W32PF_flags
&= ~W32PF_APIHOOKLOADED
;
80 IntHookModuleUnloaded:
81 Sends a internal message to all threads of the requested desktop
82 and notifies them that a global hook was destroyed
83 and an injected module must be unloaded.
84 As a result, IntLoadHookModule will be called for all the threads that
85 will receive the special purpose internal message.
88 IntHookModuleUnloaded(PDESKTOP pdesk
, int iHookID
, HHOOK hHook
)
90 PTHREADINFO ptiCurrent
;
91 PLIST_ENTRY ListEntry
;
94 TRACE("IntHookModuleUnloaded: iHookID=%d\n", iHookID
);
96 ppiCsr
= PsGetProcessWin32Process(gpepCSRSS
);
98 ListEntry
= pdesk
->PtiList
.Flink
;
99 while(ListEntry
!= &pdesk
->PtiList
)
101 ptiCurrent
= CONTAINING_RECORD(ListEntry
, THREADINFO
, PtiLink
);
103 /* FIXME: Do some more security checks here */
105 /* FIXME: HACK: The first check is a reactos specific hack for system threads */
106 if(!PsIsSystemProcess(ptiCurrent
->ppi
->peProcess
) &&
107 ptiCurrent
->ppi
!= ppiCsr
)
109 if(ptiCurrent
->ppi
->W32PF_flags
& W32PF_APIHOOKLOADED
)
111 TRACE("IntHookModuleUnloaded: sending message to PID %p, ppi=%p\n", PsGetProcessId(ptiCurrent
->ppi
->peProcess
), ptiCurrent
->ppi
);
112 co_MsqSendMessageAsync( ptiCurrent
,
123 ListEntry
= ListEntry
->Flink
;
131 UserLoadApiHook(VOID
)
133 return IntLoadHookModule(WH_APIHOOK
, 0, FALSE
);
138 UserRegisterUserApiHook(
139 PUNICODE_STRING pstrDllName
,
140 PUNICODE_STRING pstrFuncName
)
142 PTHREADINFO pti
, ptiCurrent
;
144 PWND DesktopWindow
, pwndCurrent
;
148 pti
= PsGetCurrentThreadWin32Thread();
149 ppiCsr
= PsGetProcessWin32Process(gpepCSRSS
);
151 /* Fail if the api hook is already registered */
152 if(gpsi
->dwSRVIFlags
& SRVINFO_APIHOOK
)
157 TRACE("UserRegisterUserApiHook. Server PID: %p\n", PsGetProcessId(pti
->ppi
->peProcess
));
159 /* Register the api hook */
160 gpsi
->dwSRVIFlags
|= SRVINFO_APIHOOK
;
162 strUahModule
= *pstrDllName
;
163 strUahInitFunc
= *pstrFuncName
;
164 ppiUahServer
= pti
->ppi
;
166 /* Broadcast an internal message to every top level window */
167 DesktopWindow
= UserGetWindowObject(IntGetDesktopWindow());
168 List
= IntWinListChildren(DesktopWindow
);
172 for (i
= 0; List
[i
]; i
++)
174 pwndCurrent
= UserGetWindowObject(List
[i
]);
175 if(pwndCurrent
== NULL
)
179 ptiCurrent
= pwndCurrent
->head
.pti
;
181 /* FIXME: The first check is a reactos specific hack for system threads */
182 if(PsIsSystemProcess(ptiCurrent
->ppi
->peProcess
) ||
183 ptiCurrent
->ppi
== ppiCsr
)
188 co_MsqSendMessageAsync( ptiCurrent
,
191 FALSE
, /* Load the module */
198 ExFreePoolWithTag(List
, USERTAG_WINDOWLIST
);
206 UserUnregisterUserApiHook(VOID
)
210 pti
= PsGetCurrentThreadWin32Thread();
212 /* Fail if the api hook is not registered */
213 if(!(gpsi
->dwSRVIFlags
& SRVINFO_APIHOOK
))
218 /* Only the process that registered the api hook can uregister it */
219 if(ppiUahServer
!= PsGetCurrentProcessWin32Process())
224 TRACE("UserUnregisterUserApiHook. Server PID: %p\n", PsGetProcessId(pti
->ppi
->peProcess
));
226 /* Unregister the api hook */
227 gpsi
->dwSRVIFlags
&= ~SRVINFO_APIHOOK
;
229 ReleaseCapturedUnicodeString(&strUahModule
, UserMode
);
230 ReleaseCapturedUnicodeString(&strUahInitFunc
, UserMode
);
232 /* Notify all applications that the api hook module must be unloaded */
233 return IntHookModuleUnloaded(pti
->rpdesk
, WH_APIHOOK
, 0);
239 co_IntCallLowLevelHook(PHOOK Hook
,
250 ULONG_PTR uResult
= 0;
253 pti
= Hook
->ptiHooked
;
255 pti
= Hook
->head
.pti
;
257 pHP
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(HOOKPACK
), TAG_HOOK
);
261 pHP
->lParam
= lParam
;
262 pHP
->pHookStructs
= NULL
;
264 // This prevents stack corruption from the caller.
267 case WH_JOURNALPLAYBACK
:
268 case WH_JOURNALRECORD
:
270 Size
= sizeof(EVENTMSG
);
273 Size
= sizeof(KBDLLHOOKSTRUCT
);
276 Size
= sizeof(MSLLHOOKSTRUCT
);
281 Size
= sizeof(MOUSEHOOKSTRUCT
);
291 pHP
->pHookStructs
= ExAllocatePoolWithTag(NonPagedPool
, Size
, TAG_HOOK
);
292 if (pHP
->pHookStructs
) RtlCopyMemory(pHP
->pHookStructs
, (PVOID
)lParam
, Size
);
295 /* FIXME: Should get timeout from
296 * HKEY_CURRENT_USER\Control Panel\Desktop\LowLevelHooksTimeout */
297 Status
= co_MsqSendMessage( pti
,
298 IntToPtr(Code
), // hWnd
306 if (!NT_SUCCESS(Status
))
308 ERR("Error Hook Call SendMsg. %d Status: 0x%x\n", Hook
->HookId
, Status
);
309 if (pHP
->pHookStructs
) ExFreePoolWithTag(pHP
->pHookStructs
, TAG_HOOK
);
310 ExFreePoolWithTag(pHP
, TAG_HOOK
);
312 return NT_SUCCESS(Status
) ? uResult
: 0;
317 // Dispatch MsgQueue Hook Call processor!
321 co_CallHook( INT HookId
,
328 PHOOKPACK pHP
= (PHOOKPACK
)lParam
;
331 lParam
= pHP
->lParam
;
335 case WH_JOURNALPLAYBACK
:
336 case WH_JOURNALRECORD
:
340 lParam
= (LPARAM
)pHP
->pHookStructs
;
345 if (!UserObjectInDestroy(UserHMGetHandle(phk
))) //// Fix CORE-10549.
347 /* The odds are high for this to be a Global call. */
348 Result
= co_IntCallHookProc( HookId
,
358 /* The odds so high, no one is waiting for the results. */
359 if (pHP
->pHookStructs
) ExFreePoolWithTag(pHP
->pHookStructs
, TAG_HOOK
);
360 ExFreePoolWithTag(pHP
, TAG_HOOK
);
367 co_HOOK_CallHookNext( PHOOK Hook
,
372 TRACE("Calling Next HOOK %d\n", Hook
->HookId
);
374 return co_IntCallHookProc( Hook
->HookId
,
388 co_IntCallDebugHook(PHOOK Hook
,
397 PVOID HooklParam
= NULL
;
404 ProbeForRead((PVOID
)lParam
,
405 sizeof(DEBUGHOOKINFO
),
408 RtlCopyMemory(&Debug
,
410 sizeof(DEBUGHOOKINFO
));
412 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
420 ERR("HOOK WH_DEBUG read from lParam ERROR!\n");
425 return lResult
; /* Need lParam! */
433 case HCBT_CLICKSKIPPED
:
434 Size
= sizeof(MOUSEHOOKSTRUCTEX
);
442 Size
= sizeof(CBTACTIVATESTRUCT
);
445 case HCBT_CREATEWND
: /* Handle ANSI? */
446 Size
= sizeof(CBT_CREATEWND
);
447 /* What shall we do? Size += sizeof(HOOKPROC_CBT_CREATEWND_EXTRA_ARGUMENTS); same as CREATESTRUCTEX */
451 Size
= sizeof(LPARAM
);
457 Size
= sizeof(MSLLHOOKSTRUCT
);
461 Size
= sizeof(KBDLLHOOKSTRUCT
);
465 case WH_SYSMSGFILTER
:
470 case WH_JOURNALPLAYBACK
:
471 case WH_JOURNALRECORD
:
472 Size
= sizeof(EVENTMSG
);
475 case WH_FOREGROUNDIDLE
:
479 Size
= sizeof(LPARAM
);
482 if (Size
> sizeof(LPARAM
))
483 HooklParam
= ExAllocatePoolWithTag(NonPagedPool
, Size
, TAG_HOOK
);
489 ProbeForRead((PVOID
)Debug
.lParam
,
493 RtlCopyMemory(HooklParam
,
497 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
505 ERR("HOOK WH_DEBUG read from Debug.lParam ERROR!\n");
506 ExFreePoolWithTag(HooklParam
, TAG_HOOK
);
511 if (HooklParam
) Debug
.lParam
= (LPARAM
)HooklParam
;
512 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, (LPARAM
)&Debug
);
513 if (HooklParam
) ExFreePoolWithTag(HooklParam
, TAG_HOOK
);
521 co_UserCallNextHookEx(PHOOK Hook
,
530 /* Handle this one first. */
531 if ((Hook
->HookId
== WH_MOUSE
) ||
532 (Hook
->HookId
== WH_CBT
&& Code
== HCBT_CLICKSKIPPED
))
534 MOUSEHOOKSTRUCTEX Mouse
;
539 ProbeForRead((PVOID
)lParam
,
540 sizeof(MOUSEHOOKSTRUCTEX
),
543 RtlCopyMemory(&Mouse
,
545 sizeof(MOUSEHOOKSTRUCTEX
));
547 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
555 ERR("HOOK WH_MOUSE read from lParam ERROR!\n");
561 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, (LPARAM
)&Mouse
);
571 MSLLHOOKSTRUCT Mouse
;
577 ProbeForRead((PVOID
)lParam
,
578 sizeof(MSLLHOOKSTRUCT
),
581 RtlCopyMemory(&Mouse
,
583 sizeof(MSLLHOOKSTRUCT
));
585 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
593 ERR("HOOK WH_MOUSE_LL read from lParam ERROR!\n");
599 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, (LPARAM
)&Mouse
);
606 KBDLLHOOKSTRUCT Keyboard
;
612 ProbeForRead((PVOID
)lParam
,
613 sizeof(KBDLLHOOKSTRUCT
),
616 RtlCopyMemory(&Keyboard
,
618 sizeof(KBDLLHOOKSTRUCT
));
620 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
628 ERR("HOOK WH_KEYBORD_LL read from lParam ERROR!\n");
634 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, (LPARAM
)&Keyboard
);
640 case WH_SYSMSGFILTER
:
649 ProbeForRead((PVOID
)lParam
,
657 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
665 ERR("HOOK WH_XMESSAGEX read from lParam ERROR!\n");
671 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, (LPARAM
)&Msg
);
673 if (lParam
&& (Hook
->HookId
== WH_GETMESSAGE
))
677 ProbeForWrite((PVOID
)lParam
,
681 RtlCopyMemory((PVOID
)lParam
,
685 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
693 ERR("HOOK WH_GETMESSAGE write to lParam ERROR!\n");
701 TRACE("HOOK WH_CBT!\n");
706 LPCBT_CREATEWNDW pcbtcww
= (LPCBT_CREATEWNDW
)lParam
;
708 TRACE("HOOK HCBT_CREATEWND\n");
713 ProbeForRead( pcbtcww
,
714 sizeof(CBT_CREATEWNDA
),
716 ProbeForWrite(pcbtcww
->lpcs
,
717 sizeof(CREATESTRUCTA
),
719 ProbeForRead( pcbtcww
->lpcs
->lpszName
,
723 if (!IS_ATOM(pcbtcww
->lpcs
->lpszClass
))
725 _Analysis_assume_(pcbtcww
->lpcs
->lpszClass
!= NULL
);
726 ProbeForRead(pcbtcww
->lpcs
->lpszClass
,
733 ProbeForRead( pcbtcww
,
734 sizeof(CBT_CREATEWNDW
),
736 ProbeForWrite(pcbtcww
->lpcs
,
737 sizeof(CREATESTRUCTW
),
739 ProbeForRead( pcbtcww
->lpcs
->lpszName
,
743 if (!IS_ATOM(pcbtcww
->lpcs
->lpszClass
))
745 _Analysis_assume_(pcbtcww
->lpcs
->lpszClass
!= NULL
);
746 ProbeForRead(pcbtcww
->lpcs
->lpszClass
,
752 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
760 ERR("HOOK HCBT_CREATEWND write ERROR!\n");
762 /* The next call handles the structures. */
763 if (!BadChk
&& Hook
->Proc
)
765 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, lParam
);
774 TRACE("HOOK HCBT_MOVESIZE\n");
780 ProbeForRead((PVOID
)lParam
,
788 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
796 ERR("HOOK HCBT_MOVESIZE read from lParam ERROR!\n");
802 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, (LPARAM
)&rt
);
809 CBTACTIVATESTRUCT CbAs
;
811 TRACE("HOOK HCBT_ACTIVATE\n");
816 ProbeForRead((PVOID
)lParam
,
817 sizeof(CBTACTIVATESTRUCT
),
822 sizeof(CBTACTIVATESTRUCT
));
824 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
832 ERR("HOOK HCBT_ACTIVATE read from lParam ERROR!\n");
838 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, (LPARAM
)&CbAs
);
843 /* The rest just use default. */
845 TRACE("HOOK HCBT_ %d\n",Code
);
846 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, lParam
);
851 Note WH_JOURNALPLAYBACK,
852 "To have the system wait before processing the message, the return value
853 must be the amount of time, in clock ticks, that the system should wait."
855 case WH_JOURNALPLAYBACK
:
856 case WH_JOURNALRECORD
:
864 ProbeForRead((PVOID
)lParam
,
868 RtlCopyMemory(&EventMsg
,
872 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
880 ERR("HOOK WH_JOURNAL read from lParam ERROR!\n");
886 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, (LPARAM
)(lParam
? &EventMsg
: NULL
));
892 ProbeForWrite((PVOID
)lParam
,
896 RtlCopyMemory((PVOID
)lParam
,
900 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
908 ERR("HOOK WH_JOURNAL write to lParam ERROR!\n");
916 lResult
= co_IntCallDebugHook(Hook
, Code
, wParam
, lParam
, Ansi
);
920 * Default the rest like, WH_FOREGROUNDIDLE, WH_KEYBOARD and WH_SHELL.
922 case WH_FOREGROUNDIDLE
:
925 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, lParam
);
929 ERR("Unsupported HOOK Id -> %d\n",Hook
->HookId
);
937 IntGetHookObject(HHOOK hHook
)
943 EngSetLastError(ERROR_INVALID_HOOK_HANDLE
);
947 Hook
= (PHOOK
)UserGetObject(gHandleTable
, hHook
, TYPE_HOOK
);
950 EngSetLastError(ERROR_INVALID_HOOK_HANDLE
);
954 UserReferenceObject(Hook
);
962 IntGetGlobalHookHandles(PDESKTOP pdo
, int HookId
)
964 PLIST_ENTRY pLastHead
, pElem
;
970 pLastHead
= &pdo
->pDeskInfo
->aphkStart
[HOOKID_TO_INDEX(HookId
)];
971 for (pElem
= pLastHead
->Flink
; pElem
!= pLastHead
; pElem
= pElem
->Flink
)
974 pList
= ExAllocatePoolWithTag(PagedPool
, (cHooks
+ 1) * sizeof(HHOOK
), TAG_HOOK
);
977 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
981 for (pElem
= pLastHead
->Flink
; pElem
!= pLastHead
; pElem
= pElem
->Flink
)
983 pHook
= CONTAINING_RECORD(pElem
, HOOK
, Chain
);
984 NT_ASSERT(i
< cHooks
);
985 pList
[i
++] = UserHMGetHandle(pHook
);
992 /* Find the next hook in the chain */
995 IntGetNextHook(PHOOK Hook
)
997 int HookId
= Hook
->HookId
;
998 PLIST_ENTRY pLastHead
, pElem
;
1001 if (Hook
->ptiHooked
)
1003 pti
= Hook
->ptiHooked
;
1004 pLastHead
= &pti
->aphkStart
[HOOKID_TO_INDEX(HookId
)];
1008 pti
= PsGetCurrentThreadWin32Thread();
1009 pLastHead
= &pti
->rpdesk
->pDeskInfo
->aphkStart
[HOOKID_TO_INDEX(HookId
)];
1012 pElem
= Hook
->Chain
.Flink
;
1013 if (pElem
!= pLastHead
)
1014 return CONTAINING_RECORD(pElem
, HOOK
, Chain
);
1018 /* Free a hook, removing it from its chain */
1022 IntFreeHook(PHOOK Hook
)
1024 RemoveEntryList(&Hook
->Chain
);
1025 if (Hook
->ModuleName
.Buffer
)
1027 ExFreePoolWithTag(Hook
->ModuleName
.Buffer
, TAG_HOOK
);
1028 Hook
->ModuleName
.Buffer
= NULL
;
1031 UserDeleteObject(UserHMGetHandle(Hook
), TYPE_HOOK
);
1034 /* Remove a hook, freeing it from the chain */
1036 IntRemoveHook(PVOID Object
)
1039 PTHREADINFO ptiHook
, pti
;
1041 PHOOK Hook
= Object
;
1043 NT_ASSERT(UserIsEnteredExclusive());
1045 HookId
= Hook
->HookId
;
1046 pti
= PsGetCurrentThreadWin32Thread();
1048 if (Hook
->ptiHooked
) // Local
1050 ptiHook
= Hook
->ptiHooked
;
1054 if (IsListEmpty(&ptiHook
->aphkStart
[HOOKID_TO_INDEX(HookId
)]))
1057 KAPC_STATE ApcState
;
1059 ptiHook
->fsHooks
&= ~HOOKID_TO_FLAG(HookId
);
1060 bOtherProcess
= (ptiHook
->ppi
!= pti
->ppi
);
1063 KeStackAttachProcess(&ptiHook
->ppi
->peProcess
->Pcb
, &ApcState
);
1067 ptiHook
->pClientInfo
->fsHooks
= ptiHook
->fsHooks
;
1069 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1077 KeUnstackDetachProcess(&ApcState
);
1084 pdo
= IntGetActiveDesktop();
1088 IsListEmpty(&pdo
->pDeskInfo
->aphkStart
[HOOKID_TO_INDEX(HookId
)]))
1090 pdo
->pDeskInfo
->fsHooks
&= ~HOOKID_TO_FLAG(HookId
);
1098 Win32k Kernel Space Hook Caller.
1102 co_HOOK_CallHooks( INT HookId
,
1107 PHOOK Hook
, SaveHook
;
1109 PCLIENTINFO ClientInfo
;
1110 PLIST_ENTRY pLastHead
;
1112 BOOL Local
= FALSE
, Global
= FALSE
;
1114 USER_REFERENCE_ENTRY Ref
;
1116 ASSERT(WH_MINHOOK
<= HookId
&& HookId
<= WH_MAXHOOK
);
1118 pti
= PsGetCurrentThreadWin32Thread();
1119 if (!pti
|| !pti
->rpdesk
|| !pti
->rpdesk
->pDeskInfo
)
1121 pdo
= IntGetActiveDesktop();
1122 /* If KeyboardThread|MouseThread|(RawInputThread or RIT) aka system threads,
1123 pti->fsHooks most likely, is zero. So process KbT & MsT to "send" the message.
1125 if ( !pti
|| !pdo
|| (!(HookId
== WH_KEYBOARD_LL
) && !(HookId
== WH_MOUSE_LL
)) )
1127 TRACE("No PDO %d\n", HookId
);
1136 if ( pti
->TIF_flags
& (TIF_INCLEANUP
|TIF_DISABLEHOOKS
))
1138 TRACE("Hook Thread dead %d\n", HookId
);
1142 if ( ISITHOOKED(HookId
) )
1144 TRACE("Local Hooker %d\n", HookId
);
1148 if ( pdo
->pDeskInfo
->fsHooks
& HOOKID_TO_FLAG(HookId
) )
1150 TRACE("Global Hooker %d\n", HookId
);
1154 if ( !Local
&& !Global
) goto Exit
; // No work!
1158 /* SetWindowHookEx sorts out the Thread issue by placing the Hook to
1159 the correct Thread if not NULL.
1163 pLastHead
= &pti
->aphkStart
[HOOKID_TO_INDEX(HookId
)];
1164 if (IsListEmpty(pLastHead
))
1166 ERR("No Local Hook Found!\n");
1170 Hook
= CONTAINING_RECORD(pLastHead
->Flink
, HOOK
, Chain
);
1171 ObReferenceObject(pti
->pEThread
);
1172 IntReferenceThreadInfo(pti
);
1173 UserRefObjectCo(Hook
, &Ref
);
1175 ClientInfo
= pti
->pClientInfo
;
1176 SaveHook
= pti
->sphkCurrent
;
1177 /* Note: Setting pti->sphkCurrent will also lock the next hook to this
1178 * hook ID. So, the CallNextHookEx will only call to that hook ID
1179 * chain anyway. For Thread Hooks....
1182 /* Load it for the next call. */
1183 pti
->sphkCurrent
= Hook
;
1184 Hook
->phkNext
= IntGetNextHook(Hook
);
1189 ClientInfo
->phkCurrent
= Hook
;
1191 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1193 ClientInfo
= NULL
; // Don't bother next run.
1197 Result
= co_IntCallHookProc( HookId
,
1210 ClientInfo
->phkCurrent
= SaveHook
;
1212 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1219 pti
->sphkCurrent
= SaveHook
;
1220 Hook
->phkNext
= NULL
;
1221 UserDerefObjectCo(Hook
);
1222 IntDereferenceThreadInfo(pti
);
1223 ObDereferenceObject(pti
->pEThread
);
1228 PTHREADINFO ptiHook
;
1229 HHOOK
*pHookHandles
;
1232 /* Keep hooks in array because hooks can be destroyed in user world */
1233 pHookHandles
= IntGetGlobalHookHandles(pdo
, HookId
);
1237 /* Performance goes down the drain. If more hooks are associated to this
1238 * hook ID, this will have to post to each of the thread message queues
1239 * or make a direct call.
1241 for(i
= 0; pHookHandles
[i
]; ++i
)
1243 Hook
= (PHOOK
)UserGetObject(gHandleTable
, pHookHandles
[i
], TYPE_HOOK
);
1246 ERR("Invalid hook!\n");
1250 /* Hook->Thread is null, we hax around this with Hook->head.pti. */
1251 ptiHook
= Hook
->head
.pti
;
1253 if ( (pti
->TIF_flags
& TIF_DISABLEHOOKS
) || (ptiHook
->TIF_flags
& TIF_INCLEANUP
))
1255 TRACE("Next Hook %p, %p\n", ptiHook
->rpdesk
, pdo
);
1258 UserRefObjectCo(Hook
, &Ref
);
1260 if (ptiHook
!= pti
)
1263 if ( HookId
== WH_JOURNALPLAYBACK
|| // 1 | 0
1264 HookId
== WH_JOURNALRECORD
|| // 1 | 0
1265 HookId
== WH_KEYBOARD
|| // 1 | 200
1266 HookId
== WH_MOUSE
|| // 1 | 200
1267 HookId
== WH_KEYBOARD_LL
|| // 0 | 300
1268 HookId
== WH_MOUSE_LL
) // 0 | 300
1270 TRACE("\nGlobal Hook posting to another Thread! %d\n",HookId
);
1271 Result
= co_IntCallLowLevelHook(Hook
, Code
, wParam
, lParam
);
1273 else if (ptiHook
->ppi
== pti
->ppi
)
1275 TRACE("\nGlobal Hook calling to another Thread! %d\n",HookId
);
1276 ObReferenceObject(ptiHook
->pEThread
);
1277 IntReferenceThreadInfo(ptiHook
);
1278 Result
= co_IntCallHookProc( HookId
,
1287 IntDereferenceThreadInfo(ptiHook
);
1288 ObDereferenceObject(ptiHook
->pEThread
);
1292 { /* Make the direct call. */
1293 TRACE("Global going Local Hook calling to Thread! %d\n",HookId
);
1294 ObReferenceObject(pti
->pEThread
);
1295 IntReferenceThreadInfo(pti
);
1296 Result
= co_IntCallHookProc( HookId
,
1305 IntDereferenceThreadInfo(pti
);
1306 ObDereferenceObject(pti
->pEThread
);
1308 UserDerefObjectCo(Hook
);
1310 ExFreePoolWithTag(pHookHandles
, TAG_HOOK
);
1311 TRACE("Ret: Global HookId %d Result 0x%x\n", HookId
,Result
);
1319 IntUnhookWindowsHook(int HookId
, HOOKPROC pfnFilterProc
)
1322 PLIST_ENTRY pLastHead
, pElement
;
1323 PTHREADINFO pti
= PsGetCurrentThreadWin32Thread();
1325 if (HookId
< WH_MINHOOK
|| WH_MAXHOOK
< HookId
)
1327 EngSetLastError(ERROR_INVALID_HOOK_FILTER
);
1333 pLastHead
= &pti
->aphkStart
[HOOKID_TO_INDEX(HookId
)];
1335 pElement
= pLastHead
->Flink
;
1336 while (pElement
!= pLastHead
)
1338 Hook
= CONTAINING_RECORD(pElement
, HOOK
, Chain
);
1340 /* Get the next element now, we might free the hook in what follows */
1341 pElement
= Hook
->Chain
.Flink
;
1343 if (Hook
->Proc
== pfnFilterProc
)
1345 if (Hook
->head
.pti
== pti
)
1347 IntRemoveHook(Hook
);
1352 EngSetLastError(ERROR_ACCESS_DENIED
);
1362 * Support for compatibility only? Global hooks are processed in kernel space.
1363 * This is very thread specific! Never seeing applications with more than one
1364 * hook per thread installed. Most of the applications are Global hookers and
1365 * associated with just one hook Id. Maybe it's for diagnostic testing or a
1366 * throw back to 3.11?
1370 NtUserCallNextHookEx( int Code
,
1376 PHOOK HookObj
, NextObj
;
1377 PCLIENTINFO ClientInfo
;
1378 LRESULT lResult
= 0;
1380 TRACE("Enter NtUserCallNextHookEx\n");
1381 UserEnterExclusive();
1383 pti
= GetW32ThreadInfo();
1385 HookObj
= pti
->sphkCurrent
;
1388 goto Exit
; // Return 0
1390 NextObj
= HookObj
->phkNext
;
1392 pti
->sphkCurrent
= NextObj
;
1393 ClientInfo
= pti
->pClientInfo
;
1396 ClientInfo
->phkCurrent
= NextObj
;
1398 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1404 /* Now in List run down. */
1405 if (ClientInfo
&& NextObj
)
1407 NextObj
->phkNext
= IntGetNextHook(NextObj
);
1408 lResult
= co_UserCallNextHookEx( NextObj
, Code
, wParam
, lParam
, NextObj
->Ansi
);
1412 TRACE("Leave NtUserCallNextHookEx, ret=%i\n", lResult
);
1419 NtUserSetWindowsHookAW( int idHook
,
1424 UNICODE_STRING USModuleName
;
1426 RtlInitUnicodeString(&USModuleName
, NULL
);
1427 ThreadId
= PtrToUint(NtCurrentTeb()->ClientId
.UniqueThread
);
1429 return NtUserSetWindowsHookEx( NULL
,
1439 NtUserSetWindowsHookEx( HINSTANCE Mod
,
1440 PUNICODE_STRING UnsafeModuleName
,
1446 PWINSTATION_OBJECT WinStaObj
;
1448 UNICODE_STRING ModuleName
;
1451 PTHREADINFO pti
, ptiHook
= NULL
;
1454 TRACE("Enter NtUserSetWindowsHookEx\n");
1455 UserEnterExclusive();
1457 pti
= PsGetCurrentThreadWin32Thread();
1459 if (HookId
< WH_MINHOOK
|| WH_MAXHOOK
< HookId
)
1461 EngSetLastError(ERROR_INVALID_HOOK_FILTER
);
1462 goto Cleanup
; // Return NULL
1467 EngSetLastError(ERROR_INVALID_FILTER_PROC
);
1468 goto Cleanup
; // Return NULL
1471 if (ThreadId
) /* thread-local hook */
1473 if ( HookId
== WH_JOURNALRECORD
||
1474 HookId
== WH_JOURNALPLAYBACK
||
1475 HookId
== WH_KEYBOARD_LL
||
1476 HookId
== WH_MOUSE_LL
||
1477 HookId
== WH_SYSMSGFILTER
)
1479 TRACE("Local hook installing Global HookId: %d\n",HookId
);
1480 /* these can only be global */
1481 EngSetLastError(ERROR_GLOBAL_ONLY_HOOK
);
1482 goto Cleanup
; // Return NULL
1485 if ( !(ptiHook
= IntTID2PTI( UlongToHandle(ThreadId
) )))
1487 ERR("Invalid thread id 0x%x\n", ThreadId
);
1488 EngSetLastError(ERROR_INVALID_PARAMETER
);
1489 goto Cleanup
; // Return NULL
1492 if ( ptiHook
->rpdesk
!= pti
->rpdesk
) // gptiCurrent->rpdesk)
1494 ERR("Local hook wrong desktop HookId: %d\n",HookId
);
1495 EngSetLastError(ERROR_ACCESS_DENIED
);
1496 goto Cleanup
; // Return NULL
1499 if (ptiHook
->ppi
!= pti
->ppi
)
1502 (HookId
== WH_GETMESSAGE
||
1503 HookId
== WH_CALLWNDPROC
||
1505 HookId
== WH_HARDWARE
||
1506 HookId
== WH_DEBUG
||
1507 HookId
== WH_SHELL
||
1508 HookId
== WH_FOREGROUNDIDLE
||
1509 HookId
== WH_CALLWNDPROCRET
) )
1511 ERR("Local hook needs hMod HookId: %d\n",HookId
);
1512 EngSetLastError(ERROR_HOOK_NEEDS_HMOD
);
1513 goto Cleanup
; // Return NULL
1516 if ( (ptiHook
->TIF_flags
& (TIF_CSRSSTHREAD
|TIF_SYSTEMTHREAD
)) &&
1517 (HookId
== WH_GETMESSAGE
||
1518 HookId
== WH_CALLWNDPROC
||
1520 HookId
== WH_HARDWARE
||
1521 HookId
== WH_DEBUG
||
1522 HookId
== WH_SHELL
||
1523 HookId
== WH_FOREGROUNDIDLE
||
1524 HookId
== WH_CALLWNDPROCRET
) )
1526 EngSetLastError(ERROR_HOOK_TYPE_NOT_ALLOWED
);
1527 goto Cleanup
; // Return NULL
1531 else /* System-global hook */
1533 ptiHook
= pti
; // gptiCurrent;
1535 (HookId
== WH_GETMESSAGE
||
1536 HookId
== WH_CALLWNDPROC
||
1538 HookId
== WH_SYSMSGFILTER
||
1539 HookId
== WH_HARDWARE
||
1540 HookId
== WH_DEBUG
||
1541 HookId
== WH_SHELL
||
1542 HookId
== WH_FOREGROUNDIDLE
||
1543 HookId
== WH_CALLWNDPROCRET
) )
1545 ERR("Global hook needs hMod HookId: %d\n",HookId
);
1546 EngSetLastError(ERROR_HOOK_NEEDS_HMOD
);
1547 goto Cleanup
; // Return NULL
1551 Status
= IntValidateWindowStationHandle( PsGetCurrentProcess()->Win32WindowStation
,
1557 if (!NT_SUCCESS(Status
))
1559 SetLastNtError(Status
);
1560 goto Cleanup
; // Return NULL
1562 ObDereferenceObject(WinStaObj
);
1564 Hook
= UserCreateObject(gHandleTable
, NULL
, ptiHook
, (PHANDLE
)&Handle
, TYPE_HOOK
, sizeof(HOOK
));
1568 goto Cleanup
; // Return NULL
1571 Hook
->ihmod
= (INT_PTR
)Mod
; // Module Index from atom table, Do this for now.
1572 Hook
->HookId
= HookId
;
1573 Hook
->rpdesk
= ptiHook
->rpdesk
;
1574 Hook
->phkNext
= NULL
; /* Dont use as a chain! Use link lists for chaining. */
1575 Hook
->Proc
= HookProc
;
1578 TRACE("Set Hook Desk %p DeskInfo %p Handle Desk %p\n", pti
->rpdesk
, pti
->pDeskInfo
, Hook
->head
.rpdesk
);
1580 if (ThreadId
) /* Thread-local hook */
1582 InsertHeadList(&ptiHook
->aphkStart
[HOOKID_TO_INDEX(HookId
)], &Hook
->Chain
);
1583 ptiHook
->sphkCurrent
= NULL
;
1584 Hook
->ptiHooked
= ptiHook
;
1585 ptiHook
->fsHooks
|= HOOKID_TO_FLAG(HookId
);
1587 if (ptiHook
->pClientInfo
)
1589 if ( ptiHook
->ppi
== pti
->ppi
) /* gptiCurrent->ppi) */
1593 ptiHook
->pClientInfo
->fsHooks
= ptiHook
->fsHooks
;
1594 ptiHook
->pClientInfo
->phkCurrent
= NULL
;
1596 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1598 ERR("Problem writing to Local ClientInfo!\n");
1604 KAPC_STATE ApcState
;
1606 KeStackAttachProcess(&ptiHook
->ppi
->peProcess
->Pcb
, &ApcState
);
1609 ptiHook
->pClientInfo
->fsHooks
= ptiHook
->fsHooks
;
1610 ptiHook
->pClientInfo
->phkCurrent
= NULL
;
1612 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1614 ERR("Problem writing to Remote ClientInfo!\n");
1617 KeUnstackDetachProcess(&ApcState
);
1623 InsertHeadList(&ptiHook
->rpdesk
->pDeskInfo
->aphkStart
[HOOKID_TO_INDEX(HookId
)], &Hook
->Chain
);
1624 Hook
->ptiHooked
= NULL
;
1625 //gptiCurrent->pDeskInfo->fsHooks |= HOOKID_TO_FLAG(HookId);
1626 ptiHook
->rpdesk
->pDeskInfo
->fsHooks
|= HOOKID_TO_FLAG(HookId
);
1627 ptiHook
->sphkCurrent
= NULL
;
1628 ptiHook
->pClientInfo
->phkCurrent
= NULL
;
1631 RtlInitUnicodeString(&Hook
->ModuleName
, NULL
);
1635 Status
= MmCopyFromCaller(&ModuleName
,
1637 sizeof(UNICODE_STRING
));
1638 if (!NT_SUCCESS(Status
))
1640 IntRemoveHook(Hook
);
1641 SetLastNtError(Status
);
1642 goto Cleanup
; // Return NULL
1645 Hook
->ModuleName
.Buffer
= ExAllocatePoolWithTag( PagedPool
,
1646 ModuleName
.MaximumLength
,
1648 if (NULL
== Hook
->ModuleName
.Buffer
)
1650 IntRemoveHook(Hook
);
1651 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1652 goto Cleanup
; // Return NULL
1655 Hook
->ModuleName
.MaximumLength
= ModuleName
.MaximumLength
;
1656 Status
= MmCopyFromCaller( Hook
->ModuleName
.Buffer
,
1658 ModuleName
.MaximumLength
);
1659 if (!NT_SUCCESS(Status
))
1661 ExFreePoolWithTag(Hook
->ModuleName
.Buffer
, TAG_HOOK
);
1662 Hook
->ModuleName
.Buffer
= NULL
;
1663 IntRemoveHook(Hook
);
1664 SetLastNtError(Status
);
1665 goto Cleanup
; // Return NULL
1668 Hook
->ModuleName
.Length
= ModuleName
.Length
;
1669 //// FIXME: Need to load from user32 to verify hMod before calling hook with hMod set!!!!
1670 //// Mod + offPfn == new HookProc Justin Case module is from another process.
1671 FIXME("NtUserSetWindowsHookEx Setting process hMod instance addressing.\n");
1672 /* Make proc relative to the module base */
1673 Hook
->offPfn
= (ULONG_PTR
)((char *)HookProc
- (char *)Mod
);
1678 TRACE("Installing: HookId %d Global %s\n", HookId
, !ThreadId
? "TRUE" : "FALSE");
1683 UserDereferenceObject(Hook
);
1684 TRACE("Leave NtUserSetWindowsHookEx, ret=%p\n", Ret
);
1691 NtUserUnhookWindowsHookEx(HHOOK Hook
)
1696 TRACE("Enter NtUserUnhookWindowsHookEx\n");
1697 UserEnterExclusive();
1699 if (!(HookObj
= IntGetHookObject(Hook
)))
1701 ERR("Invalid handle passed to NtUserUnhookWindowsHookEx\n");
1702 /* SetLastNtError(Status); */
1703 goto Exit
; // Return FALSE
1706 ASSERT(Hook
== UserHMGetHandle(HookObj
));
1708 IntRemoveHook(HookObj
);
1710 UserDereferenceObject(HookObj
);
1715 TRACE("Leave NtUserUnhookWindowsHookEx, ret=%i\n", Ret
);
1722 NtUserRegisterUserApiHook(
1723 PUNICODE_STRING m_dllname1
,
1724 PUNICODE_STRING m_funname1
,
1729 UNICODE_STRING strDllNameSafe
;
1730 UNICODE_STRING strFuncNameSafe
;
1733 /* Probe and capture parameters */
1734 Status
= ProbeAndCaptureUnicodeString(&strDllNameSafe
, UserMode
, m_dllname1
);
1735 if(!NT_SUCCESS(Status
))
1737 EngSetLastError(RtlNtStatusToDosError(Status
));
1741 Status
= ProbeAndCaptureUnicodeString(&strFuncNameSafe
, UserMode
, m_funname1
);
1742 if(!NT_SUCCESS(Status
))
1744 ReleaseCapturedUnicodeString(&strDllNameSafe
, UserMode
);
1745 EngSetLastError(RtlNtStatusToDosError(Status
));
1749 UserEnterExclusive();
1751 /* Call internal function */
1752 ret
= UserRegisterUserApiHook(&strDllNameSafe
, &strFuncNameSafe
);
1756 /* Cleanup only in case of failure */
1759 ReleaseCapturedUnicodeString(&strDllNameSafe
, UserMode
);
1760 ReleaseCapturedUnicodeString(&strFuncNameSafe
, UserMode
);
1768 NtUserUnregisterUserApiHook(VOID
)
1772 UserEnterExclusive();
1773 ret
= UserUnregisterUserApiHook();