[Win32k]
[reactos.git] / reactos / win32ss / user / ntuser / hook.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * PURPOSE: Window hooks
5 * FILE: win32ss/user/ntuser/hook.c
6 * PROGRAMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
7 * James Tabor (james.tabor@rectos.org)
8 * Rafal Harabien (rafalh@reactos.org)
9 * NOTE: Most of this code was adapted from Wine,
10 * Copyright (C) 2002 Alexandre Julliard
11 */
12
13 #include <win32k.h>
14 DBG_DEFAULT_CHANNEL(UserHook);
15
16 typedef struct _HOOKPACK
17 {
18 PHOOK pHk;
19 LPARAM lParam;
20 PVOID pHookStructs;
21 } HOOKPACK, *PHOOKPACK;
22
23 UNICODE_STRING strUahModule;
24 UNICODE_STRING strUahInitFunc;
25 PPROCESSINFO ppiUahServer;
26
27 /* PRIVATE FUNCTIONS *********************************************************/
28
29 /* Calls ClientLoadLibrary in user32 in order to load or unload a module */
30 BOOL
31 IntLoadHookModule(int iHookID, HHOOK hHook, BOOL Unload)
32 {
33 PPROCESSINFO ppi;
34 BOOL bResult;
35
36 ppi = PsGetCurrentProcessWin32Process();
37
38 TRACE("IntLoadHookModule. Client PID: %p\n", PsGetProcessId(ppi->peProcess));
39
40 /* Check if this is the api hook */
41 if(iHookID == WH_APIHOOK)
42 {
43 if(!Unload && !(ppi->W32PF_flags & W32PF_APIHOOKLOADED))
44 {
45 /* A callback in user mode can trigger UserLoadApiHook to be called and
46 as a result IntLoadHookModule will be called recursively.
47 To solve this we set the flag that means that the appliaction has
48 loaded the api hook before the callback and in case of error we remove it */
49 ppi->W32PF_flags |= W32PF_APIHOOKLOADED;
50
51 /* Call ClientLoadLibrary in user32 */
52 bResult = co_IntClientLoadLibrary(&strUahModule, &strUahInitFunc, Unload, TRUE);
53 TRACE("co_IntClientLoadLibrary returned %d\n", bResult );
54 if (!bResult)
55 {
56 /* Remove the flag we set before */
57 ppi->W32PF_flags &= ~W32PF_APIHOOKLOADED;
58 }
59 return bResult;
60 }
61 else if(Unload && (ppi->W32PF_flags & W32PF_APIHOOKLOADED))
62 {
63 /* Call ClientLoadLibrary in user32 */
64 bResult = co_IntClientLoadLibrary(NULL, NULL, Unload, TRUE);
65 if (bResult)
66 {
67 ppi->W32PF_flags &= ~W32PF_APIHOOKLOADED;
68 }
69 return bResult;
70 }
71
72 return TRUE;
73 }
74
75 STUB;
76
77 return FALSE;
78 }
79
80 /*
81 IntHookModuleUnloaded:
82 Sends a internal message to all threads of the requested desktop
83 and notifies them that a global hook was destroyed
84 and an injected module must be unloaded.
85 As a result, IntLoadHookModule will be called for all the threads that
86 will receive the special purpose internal message.
87 */
88 BOOL
89 IntHookModuleUnloaded(PDESKTOP pdesk, int iHookID, HHOOK hHook)
90 {
91 PTHREADINFO ptiCurrent;
92 PLIST_ENTRY ListEntry;
93 PPROCESSINFO ppiCsr;
94
95 TRACE("IntHookModuleUnloaded: iHookID=%d\n", iHookID);
96
97 ppiCsr = PsGetProcessWin32Process(gpepCSRSS);
98
99 ListEntry = pdesk->PtiList.Flink;
100 while(ListEntry != &pdesk->PtiList)
101 {
102 ptiCurrent = CONTAINING_RECORD(ListEntry, THREADINFO, PtiLink);
103
104 /* FIXME: Do some more security checks here */
105
106 /* FIXME: The first check is a reactos specific hack for system threads */
107 if(!PsIsSystemProcess(ptiCurrent->ppi->peProcess) &&
108 ptiCurrent->ppi != ppiCsr)
109 {
110 if(ptiCurrent->ppi->W32PF_flags & W32PF_APIHOOKLOADED)
111 {
112 TRACE("IntHookModuleUnloaded: sending message to PID %p, ppi=%p\n", PsGetProcessId(ptiCurrent->ppi->peProcess), ptiCurrent->ppi);
113 co_MsqSendMessageAsync( ptiCurrent,
114 0,
115 iHookID,
116 TRUE,
117 (LPARAM)hHook,
118 NULL,
119 0,
120 FALSE,
121 MSQ_INJECTMODULE);
122 }
123 }
124 ListEntry = ListEntry->Flink;
125 }
126
127 return TRUE;
128 }
129
130 BOOL
131 FASTCALL
132 UserLoadApiHook(VOID)
133 {
134 return IntLoadHookModule(WH_APIHOOK, 0, FALSE);
135 }
136
137 BOOL
138 FASTCALL
139 UserRegisterUserApiHook(
140 PUNICODE_STRING pstrDllName,
141 PUNICODE_STRING pstrFuncName)
142 {
143 PTHREADINFO pti, ptiCurrent;
144 HWND *List;
145 PWND DesktopWindow, pwndCurrent;
146 ULONG i;
147 PPROCESSINFO ppiCsr;
148
149 pti = PsGetCurrentThreadWin32Thread();
150 ppiCsr = PsGetProcessWin32Process(gpepCSRSS);
151
152 /* Fail if the api hook is already registered */
153 if(gpsi->dwSRVIFlags & SRVINFO_APIHOOK)
154 {
155 return FALSE;
156 }
157
158 TRACE("UserRegisterUserApiHook. Server PID: %p\n", PsGetProcessId(pti->ppi->peProcess));
159
160 /* Register the api hook */
161 gpsi->dwSRVIFlags |= SRVINFO_APIHOOK;
162
163 strUahModule = *pstrDllName;
164 strUahInitFunc = *pstrFuncName;
165 ppiUahServer = pti->ppi;
166
167 /* Broadcast an internal message to every top level window */
168 DesktopWindow = UserGetWindowObject(IntGetDesktopWindow());
169 List = IntWinListChildren(DesktopWindow);
170
171 if (List != NULL)
172 {
173 for (i = 0; List[i]; i++)
174 {
175 pwndCurrent = UserGetWindowObject(List[i]);
176 if(pwndCurrent == NULL)
177 {
178 continue;
179 }
180 ptiCurrent = pwndCurrent->head.pti;
181
182 /* FIXME: The first check is a reactos specific hack for system threads */
183 if(PsIsSystemProcess(ptiCurrent->ppi->peProcess) ||
184 ptiCurrent->ppi == ppiCsr)
185 {
186 continue;
187 }
188
189 co_MsqSendMessageAsync( ptiCurrent,
190 0,
191 WH_APIHOOK,
192 FALSE, /* Load the module */
193 0,
194 NULL,
195 0,
196 FALSE,
197 MSQ_INJECTMODULE);
198 }
199 ExFreePoolWithTag(List, USERTAG_WINDOWLIST);
200 }
201
202 return TRUE;
203 }
204
205 BOOL
206 FASTCALL
207 UserUnregisterUserApiHook(VOID)
208 {
209 PTHREADINFO pti;
210
211 pti = PsGetCurrentThreadWin32Thread();
212
213 /* Fail if the api hook is not registered */
214 if(!(gpsi->dwSRVIFlags & SRVINFO_APIHOOK))
215 {
216 return FALSE;
217 }
218
219 /* Only the process that registered the api hook can uregister it */
220 if(ppiUahServer != PsGetCurrentProcessWin32Process())
221 {
222 return FALSE;
223 }
224
225 TRACE("UserUnregisterUserApiHook. Server PID: %p\n", PsGetProcessId(pti->ppi->peProcess));
226
227 /* Unregister the api hook */
228 gpsi->dwSRVIFlags &= ~SRVINFO_APIHOOK;
229 ppiUahServer = NULL;
230 ReleaseCapturedUnicodeString(&strUahModule, UserMode);
231 ReleaseCapturedUnicodeString(&strUahInitFunc, UserMode);
232
233 /* Notify all applications that the api hook module must be unloaded */
234 return IntHookModuleUnloaded(pti->rpdesk, WH_APIHOOK, 0);
235 }
236
237 static
238 LRESULT
239 FASTCALL
240 co_IntCallLowLevelHook(PHOOK Hook,
241 INT Code,
242 WPARAM wParam,
243 LPARAM lParam)
244 {
245 NTSTATUS Status;
246 PTHREADINFO pti;
247 PHOOKPACK pHP;
248 INT Size = 0;
249 UINT uTimeout = 300;
250 BOOL Block = FALSE;
251 ULONG_PTR uResult = 0;
252
253 if (Hook->ptiHooked)
254 pti = Hook->ptiHooked;
255 else
256 pti = Hook->head.pti;
257
258 pHP = ExAllocatePoolWithTag(NonPagedPool, sizeof(HOOKPACK), TAG_HOOK);
259 if (!pHP) return 0;
260
261 pHP->pHk = Hook;
262 pHP->lParam = lParam;
263 pHP->pHookStructs = NULL;
264
265 // This prevents stack corruption from the caller.
266 switch(Hook->HookId)
267 {
268 case WH_JOURNALPLAYBACK:
269 case WH_JOURNALRECORD:
270 uTimeout = 0;
271 Size = sizeof(EVENTMSG);
272 break;
273 case WH_KEYBOARD_LL:
274 Size = sizeof(KBDLLHOOKSTRUCT);
275 break;
276 case WH_MOUSE_LL:
277 Size = sizeof(MSLLHOOKSTRUCT);
278 break;
279 case WH_MOUSE:
280 uTimeout = 200;
281 Block = TRUE;
282 Size = sizeof(MOUSEHOOKSTRUCT);
283 break;
284 case WH_KEYBOARD:
285 uTimeout = 200;
286 Block = TRUE;
287 break;
288 }
289
290 if (Size)
291 {
292 pHP->pHookStructs = ExAllocatePoolWithTag(NonPagedPool, Size, TAG_HOOK);
293 if (pHP->pHookStructs) RtlCopyMemory(pHP->pHookStructs, (PVOID)lParam, Size);
294 }
295
296 /* FIXME: Should get timeout from
297 * HKEY_CURRENT_USER\Control Panel\Desktop\LowLevelHooksTimeout */
298 Status = co_MsqSendMessage( pti,
299 IntToPtr(Code), // hWnd
300 Hook->HookId, // Msg
301 wParam,
302 (LPARAM)pHP,
303 uTimeout,
304 Block,
305 MSQ_ISHOOK,
306 &uResult);
307 if (!NT_SUCCESS(Status))
308 {
309 ERR("Error Hook Call SendMsg. %d Status: 0x%x\n", Hook->HookId, Status);
310 if (pHP->pHookStructs) ExFreePoolWithTag(pHP->pHookStructs, TAG_HOOK);
311 ExFreePoolWithTag(pHP, TAG_HOOK);
312 }
313 return NT_SUCCESS(Status) ? uResult : 0;
314 }
315
316
317 //
318 // Dispatch MsgQueue Hook Call processor!
319 //
320 LRESULT
321 APIENTRY
322 co_CallHook( INT HookId,
323 INT Code,
324 WPARAM wParam,
325 LPARAM lParam)
326 {
327 LRESULT Result;
328 PHOOK phk;
329 PHOOKPACK pHP = (PHOOKPACK)lParam;
330
331 phk = pHP->pHk;
332 lParam = pHP->lParam;
333
334 switch(HookId)
335 {
336 case WH_JOURNALPLAYBACK:
337 case WH_JOURNALRECORD:
338 case WH_KEYBOARD_LL:
339 case WH_MOUSE_LL:
340 case WH_MOUSE:
341 lParam = (LPARAM)pHP->pHookStructs;
342 case WH_KEYBOARD:
343 break;
344 }
345
346 if (!UserObjectInDestroy(UserHMGetHandle(phk))) //// Fix CORE-10549.
347 {
348 /* The odds are high for this to be a Global call. */
349 Result = co_IntCallHookProc( HookId,
350 Code,
351 wParam,
352 lParam,
353 phk->Proc,
354 phk->ihmod,
355 phk->offPfn,
356 phk->Ansi,
357 &phk->ModuleName);
358 }
359 /* The odds so high, no one is waiting for the results. */
360 if (pHP->pHookStructs) ExFreePoolWithTag(pHP->pHookStructs, TAG_HOOK);
361 ExFreePoolWithTag(pHP, TAG_HOOK);
362 return Result;
363 }
364
365 static
366 LRESULT
367 APIENTRY
368 co_HOOK_CallHookNext( PHOOK Hook,
369 INT Code,
370 WPARAM wParam,
371 LPARAM lParam)
372 {
373 TRACE("Calling Next HOOK %d\n", Hook->HookId);
374
375 return co_IntCallHookProc( Hook->HookId,
376 Code,
377 wParam,
378 lParam,
379 Hook->Proc,
380 Hook->ihmod,
381 Hook->offPfn,
382 Hook->Ansi,
383 &Hook->ModuleName);
384 }
385
386 static
387 LRESULT
388 FASTCALL
389 co_IntCallDebugHook(PHOOK Hook,
390 int Code,
391 WPARAM wParam,
392 LPARAM lParam,
393 BOOL Ansi)
394 {
395 LRESULT lResult = 0;
396 ULONG Size;
397 DEBUGHOOKINFO Debug;
398 PVOID HooklParam = NULL;
399 BOOL BadChk = FALSE;
400
401 if (lParam)
402 {
403 _SEH2_TRY
404 {
405 ProbeForRead((PVOID)lParam,
406 sizeof(DEBUGHOOKINFO),
407 1);
408
409 RtlCopyMemory(&Debug,
410 (PVOID)lParam,
411 sizeof(DEBUGHOOKINFO));
412 }
413 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
414 {
415 BadChk = TRUE;
416 }
417 _SEH2_END;
418
419 if (BadChk)
420 {
421 ERR("HOOK WH_DEBUG read from lParam ERROR!\n");
422 return lResult;
423 }
424 }
425 else
426 return lResult; /* Need lParam! */
427
428 switch (wParam)
429 {
430 case WH_CBT:
431 {
432 switch (Debug.code)
433 {
434 case HCBT_CLICKSKIPPED:
435 Size = sizeof(MOUSEHOOKSTRUCTEX);
436 break;
437
438 case HCBT_MOVESIZE:
439 Size = sizeof(RECT);
440 break;
441
442 case HCBT_ACTIVATE:
443 Size = sizeof(CBTACTIVATESTRUCT);
444 break;
445
446 case HCBT_CREATEWND: /* Handle ANSI? */
447 Size = sizeof(CBT_CREATEWND);
448 /* What shall we do? Size += sizeof(HOOKPROC_CBT_CREATEWND_EXTRA_ARGUMENTS); same as CREATESTRUCTEX */
449 break;
450
451 default:
452 Size = sizeof(LPARAM);
453 }
454 }
455 break;
456
457 case WH_MOUSE_LL:
458 Size = sizeof(MSLLHOOKSTRUCT);
459 break;
460
461 case WH_KEYBOARD_LL:
462 Size = sizeof(KBDLLHOOKSTRUCT);
463 break;
464
465 case WH_MSGFILTER:
466 case WH_SYSMSGFILTER:
467 case WH_GETMESSAGE:
468 Size = sizeof(MSG);
469 break;
470
471 case WH_JOURNALPLAYBACK:
472 case WH_JOURNALRECORD:
473 Size = sizeof(EVENTMSG);
474 break;
475
476 case WH_FOREGROUNDIDLE:
477 case WH_KEYBOARD:
478 case WH_SHELL:
479 default:
480 Size = sizeof(LPARAM);
481 }
482
483 if (Size > sizeof(LPARAM))
484 HooklParam = ExAllocatePoolWithTag(NonPagedPool, Size, TAG_HOOK);
485
486 if (HooklParam)
487 {
488 _SEH2_TRY
489 {
490 ProbeForRead((PVOID)Debug.lParam,
491 Size,
492 1);
493
494 RtlCopyMemory(HooklParam,
495 (PVOID)Debug.lParam,
496 Size);
497 }
498 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
499 {
500 BadChk = TRUE;
501 }
502 _SEH2_END;
503
504 if (BadChk)
505 {
506 ERR("HOOK WH_DEBUG read from Debug.lParam ERROR!\n");
507 ExFreePoolWithTag(HooklParam, TAG_HOOK);
508 return lResult;
509 }
510 }
511
512 if (HooklParam) Debug.lParam = (LPARAM)HooklParam;
513 lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&Debug);
514 if (HooklParam) ExFreePoolWithTag(HooklParam, TAG_HOOK);
515
516 return lResult;
517 }
518
519 static
520 LRESULT
521 APIENTRY
522 co_UserCallNextHookEx(PHOOK Hook,
523 int Code,
524 WPARAM wParam,
525 LPARAM lParam,
526 BOOL Ansi)
527 {
528 LRESULT lResult = 0;
529 BOOL BadChk = FALSE;
530
531 /* Handle this one first. */
532 if ((Hook->HookId == WH_MOUSE) ||
533 (Hook->HookId == WH_CBT && Code == HCBT_CLICKSKIPPED))
534 {
535 MOUSEHOOKSTRUCTEX Mouse;
536 if (lParam)
537 {
538 _SEH2_TRY
539 {
540 ProbeForRead((PVOID)lParam,
541 sizeof(MOUSEHOOKSTRUCTEX),
542 1);
543
544 RtlCopyMemory(&Mouse,
545 (PVOID)lParam,
546 sizeof(MOUSEHOOKSTRUCTEX));
547 }
548 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
549 {
550 BadChk = TRUE;
551 }
552 _SEH2_END;
553
554 if (BadChk)
555 {
556 ERR("HOOK WH_MOUSE read from lParam ERROR!\n");
557 }
558 }
559
560 if (!BadChk)
561 {
562 lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&Mouse);
563 }
564
565 return lResult;
566 }
567
568 switch(Hook->HookId)
569 {
570 case WH_MOUSE_LL:
571 {
572 MSLLHOOKSTRUCT Mouse;
573
574 if (lParam)
575 {
576 _SEH2_TRY
577 {
578 ProbeForRead((PVOID)lParam,
579 sizeof(MSLLHOOKSTRUCT),
580 1);
581
582 RtlCopyMemory(&Mouse,
583 (PVOID)lParam,
584 sizeof(MSLLHOOKSTRUCT));
585 }
586 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
587 {
588 BadChk = TRUE;
589 }
590 _SEH2_END;
591
592 if (BadChk)
593 {
594 ERR("HOOK WH_MOUSE_LL read from lParam ERROR!\n");
595 }
596 }
597
598 if (!BadChk)
599 {
600 lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&Mouse);
601 }
602 break;
603 }
604
605 case WH_KEYBOARD_LL:
606 {
607 KBDLLHOOKSTRUCT Keyboard;
608
609 if (lParam)
610 {
611 _SEH2_TRY
612 {
613 ProbeForRead((PVOID)lParam,
614 sizeof(KBDLLHOOKSTRUCT),
615 1);
616
617 RtlCopyMemory(&Keyboard,
618 (PVOID)lParam,
619 sizeof(KBDLLHOOKSTRUCT));
620 }
621 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
622 {
623 BadChk = TRUE;
624 }
625 _SEH2_END;
626
627 if (BadChk)
628 {
629 ERR("HOOK WH_KEYBORD_LL read from lParam ERROR!\n");
630 }
631 }
632
633 if (!BadChk)
634 {
635 lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&Keyboard);
636 }
637 break;
638 }
639
640 case WH_MSGFILTER:
641 case WH_SYSMSGFILTER:
642 case WH_GETMESSAGE:
643 {
644 MSG Msg;
645
646 if (lParam)
647 {
648 _SEH2_TRY
649 {
650 ProbeForRead((PVOID)lParam,
651 sizeof(MSG),
652 1);
653
654 RtlCopyMemory(&Msg,
655 (PVOID)lParam,
656 sizeof(MSG));
657 }
658 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
659 {
660 BadChk = TRUE;
661 }
662 _SEH2_END;
663
664 if (BadChk)
665 {
666 ERR("HOOK WH_XMESSAGEX read from lParam ERROR!\n");
667 }
668 }
669
670 if (!BadChk)
671 {
672 lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&Msg);
673
674 if (lParam && (Hook->HookId == WH_GETMESSAGE))
675 {
676 _SEH2_TRY
677 {
678 ProbeForWrite((PVOID)lParam,
679 sizeof(MSG),
680 1);
681
682 RtlCopyMemory((PVOID)lParam,
683 &Msg,
684 sizeof(MSG));
685 }
686 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
687 {
688 BadChk = TRUE;
689 }
690 _SEH2_END;
691
692 if (BadChk)
693 {
694 ERR("HOOK WH_GETMESSAGE write to lParam ERROR!\n");
695 }
696 }
697 }
698 break;
699 }
700
701 case WH_CBT:
702 TRACE("HOOK WH_CBT!\n");
703 switch (Code)
704 {
705 case HCBT_CREATEWND:
706 {
707 LPCBT_CREATEWNDW pcbtcww = (LPCBT_CREATEWNDW)lParam;
708
709 TRACE("HOOK HCBT_CREATEWND\n");
710 _SEH2_TRY
711 {
712 if (Ansi)
713 {
714 ProbeForRead( pcbtcww,
715 sizeof(CBT_CREATEWNDA),
716 1);
717 ProbeForWrite(pcbtcww->lpcs,
718 sizeof(CREATESTRUCTA),
719 1);
720 ProbeForRead( pcbtcww->lpcs->lpszName,
721 sizeof(CHAR),
722 1);
723
724 if (!IS_ATOM(pcbtcww->lpcs->lpszClass))
725 {
726 _Analysis_assume_(pcbtcww->lpcs->lpszClass != NULL);
727 ProbeForRead(pcbtcww->lpcs->lpszClass,
728 sizeof(CHAR),
729 1);
730 }
731 }
732 else
733 {
734 ProbeForRead( pcbtcww,
735 sizeof(CBT_CREATEWNDW),
736 1);
737 ProbeForWrite(pcbtcww->lpcs,
738 sizeof(CREATESTRUCTW),
739 1);
740 ProbeForRead( pcbtcww->lpcs->lpszName,
741 sizeof(WCHAR),
742 1);
743
744 if (!IS_ATOM(pcbtcww->lpcs->lpszClass))
745 {
746 _Analysis_assume_(pcbtcww->lpcs->lpszClass != NULL);
747 ProbeForRead(pcbtcww->lpcs->lpszClass,
748 sizeof(WCHAR),
749 1);
750 }
751 }
752 }
753 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
754 {
755 BadChk = TRUE;
756 }
757 _SEH2_END;
758
759 if (BadChk)
760 {
761 ERR("HOOK HCBT_CREATEWND write ERROR!\n");
762 }
763 /* The next call handles the structures. */
764 if (!BadChk && Hook->Proc)
765 {
766 lResult = co_HOOK_CallHookNext(Hook, Code, wParam, lParam);
767 }
768 break;
769 }
770
771 case HCBT_MOVESIZE:
772 {
773 RECTL rt;
774
775 TRACE("HOOK HCBT_MOVESIZE\n");
776
777 if (lParam)
778 {
779 _SEH2_TRY
780 {
781 ProbeForRead((PVOID)lParam,
782 sizeof(RECT),
783 1);
784
785 RtlCopyMemory(&rt,
786 (PVOID)lParam,
787 sizeof(RECT));
788 }
789 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
790 {
791 BadChk = TRUE;
792 }
793 _SEH2_END;
794
795 if (BadChk)
796 {
797 ERR("HOOK HCBT_MOVESIZE read from lParam ERROR!\n");
798 }
799 }
800
801 if (!BadChk)
802 {
803 lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&rt);
804 }
805 break;
806 }
807
808 case HCBT_ACTIVATE:
809 {
810 CBTACTIVATESTRUCT CbAs;
811
812 TRACE("HOOK HCBT_ACTIVATE\n");
813 if (lParam)
814 {
815 _SEH2_TRY
816 {
817 ProbeForRead((PVOID)lParam,
818 sizeof(CBTACTIVATESTRUCT),
819 1);
820
821 RtlCopyMemory(&CbAs,
822 (PVOID)lParam,
823 sizeof(CBTACTIVATESTRUCT));
824 }
825 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
826 {
827 BadChk = TRUE;
828 }
829 _SEH2_END;
830
831 if (BadChk)
832 {
833 ERR("HOOK HCBT_ACTIVATE read from lParam ERROR!\n");
834 }
835 }
836
837 if (!BadChk)
838 {
839 lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&CbAs);
840 }
841 break;
842 }
843
844 /* The rest just use default. */
845 default:
846 TRACE("HOOK HCBT_ %d\n",Code);
847 lResult = co_HOOK_CallHookNext(Hook, Code, wParam, lParam);
848 break;
849 }
850 break;
851 /*
852 Note WH_JOURNALPLAYBACK,
853 "To have the system wait before processing the message, the return value
854 must be the amount of time, in clock ticks, that the system should wait."
855 */
856 case WH_JOURNALPLAYBACK:
857 case WH_JOURNALRECORD:
858 {
859 EVENTMSG EventMsg;
860
861 if (lParam)
862 {
863 _SEH2_TRY
864 {
865 ProbeForRead((PVOID)lParam,
866 sizeof(EVENTMSG),
867 1);
868
869 RtlCopyMemory(&EventMsg,
870 (PVOID)lParam,
871 sizeof(EVENTMSG));
872 }
873 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
874 {
875 BadChk = TRUE;
876 }
877 _SEH2_END;
878
879 if (BadChk)
880 {
881 ERR("HOOK WH_JOURNAL read from lParam ERROR!\n");
882 }
883 }
884
885 if (!BadChk)
886 {
887 lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)(lParam ? &EventMsg : NULL));
888
889 if (lParam)
890 {
891 _SEH2_TRY
892 {
893 ProbeForWrite((PVOID)lParam,
894 sizeof(EVENTMSG),
895 1);
896
897 RtlCopyMemory((PVOID)lParam,
898 &EventMsg,
899 sizeof(EVENTMSG));
900 }
901 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
902 {
903 BadChk = TRUE;
904 }
905 _SEH2_END;
906
907 if (BadChk)
908 {
909 ERR("HOOK WH_JOURNAL write to lParam ERROR!\n");
910 }
911 }
912 }
913 break;
914 }
915
916 case WH_DEBUG:
917 lResult = co_IntCallDebugHook(Hook, Code, wParam, lParam, Ansi);
918 break;
919
920 /*
921 * Default the rest like, WH_FOREGROUNDIDLE, WH_KEYBOARD and WH_SHELL.
922 */
923 case WH_FOREGROUNDIDLE:
924 case WH_KEYBOARD:
925 case WH_SHELL:
926 lResult = co_HOOK_CallHookNext(Hook, Code, wParam, lParam);
927 break;
928
929 default:
930 ERR("Unsupported HOOK Id -> %d\n",Hook->HookId);
931 break;
932 }
933 return lResult;
934 }
935
936 PHOOK
937 FASTCALL
938 IntGetHookObject(HHOOK hHook)
939 {
940 PHOOK Hook;
941
942 if (!hHook)
943 {
944 EngSetLastError(ERROR_INVALID_HOOK_HANDLE);
945 return NULL;
946 }
947
948 Hook = (PHOOK)UserGetObject(gHandleTable, hHook, TYPE_HOOK);
949 if (!Hook)
950 {
951 EngSetLastError(ERROR_INVALID_HOOK_HANDLE);
952 return NULL;
953 }
954
955 UserReferenceObject(Hook);
956
957 return Hook;
958 }
959
960 static
961 HHOOK*
962 FASTCALL
963 IntGetGlobalHookHandles(PDESKTOP pdo, int HookId)
964 {
965 PLIST_ENTRY pLastHead, pElem;
966 unsigned i = 0;
967 unsigned cHooks = 0;
968 HHOOK *pList;
969 PHOOK pHook;
970
971 pLastHead = &pdo->pDeskInfo->aphkStart[HOOKID_TO_INDEX(HookId)];
972 for (pElem = pLastHead->Flink; pElem != pLastHead; pElem = pElem->Flink)
973 ++cHooks;
974
975 pList = ExAllocatePoolWithTag(PagedPool, (cHooks + 1) * sizeof(HHOOK), TAG_HOOK);
976 if (!pList)
977 {
978 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
979 return NULL;
980 }
981
982 for (pElem = pLastHead->Flink; pElem != pLastHead; pElem = pElem->Flink)
983 {
984 pHook = CONTAINING_RECORD(pElem, HOOK, Chain);
985 NT_ASSERT(i < cHooks);
986 pList[i++] = pHook->head.h;
987 }
988 pList[i] = NULL;
989
990 return pList;
991 }
992
993 /* Find the next hook in the chain */
994 PHOOK
995 FASTCALL
996 IntGetNextHook(PHOOK Hook)
997 {
998 int HookId = Hook->HookId;
999 PLIST_ENTRY pLastHead, pElem;
1000 PTHREADINFO pti;
1001
1002 if (Hook->ptiHooked)
1003 {
1004 pti = Hook->ptiHooked;
1005 pLastHead = &pti->aphkStart[HOOKID_TO_INDEX(HookId)];
1006 }
1007 else
1008 {
1009 pti = PsGetCurrentThreadWin32Thread();
1010 pLastHead = &pti->rpdesk->pDeskInfo->aphkStart[HOOKID_TO_INDEX(HookId)];
1011 }
1012
1013 pElem = Hook->Chain.Flink;
1014 if (pElem != pLastHead)
1015 return CONTAINING_RECORD(pElem, HOOK, Chain);
1016 return NULL;
1017 }
1018
1019 /* Free a hook, removing it from its chain */
1020 static
1021 VOID
1022 FASTCALL
1023 IntFreeHook(PHOOK Hook)
1024 {
1025 RemoveEntryList(&Hook->Chain);
1026 if (Hook->ModuleName.Buffer)
1027 {
1028 ExFreePoolWithTag(Hook->ModuleName.Buffer, TAG_HOOK);
1029 Hook->ModuleName.Buffer = NULL;
1030 }
1031 /* Close handle */
1032 UserDeleteObject(UserHMGetHandle(Hook), TYPE_HOOK);
1033 }
1034
1035 /* Remove a hook, freeing it from the chain */
1036 BOOLEAN
1037 IntRemoveHook(PVOID Object)
1038 {
1039 INT HookId;
1040 PTHREADINFO pti;
1041 PDESKTOP pdo;
1042 PHOOK Hook = Object;
1043
1044 HookId = Hook->HookId;
1045
1046 if (Hook->ptiHooked) // Local
1047 {
1048 pti = Hook->ptiHooked;
1049
1050 IntFreeHook( Hook);
1051
1052 if ( IsListEmpty(&pti->aphkStart[HOOKID_TO_INDEX(HookId)]) )
1053 {
1054 pti->fsHooks &= ~HOOKID_TO_FLAG(HookId);
1055 _SEH2_TRY
1056 {
1057 pti->pClientInfo->fsHooks = pti->fsHooks;
1058 }
1059 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1060 {
1061 /* Do nothing */
1062 (void)0;
1063 }
1064 _SEH2_END;
1065 }
1066 }
1067 else // Global
1068 {
1069 IntFreeHook( Hook);
1070
1071 pdo = IntGetActiveDesktop();
1072
1073 if ( pdo &&
1074 pdo->pDeskInfo &&
1075 IsListEmpty(&pdo->pDeskInfo->aphkStart[HOOKID_TO_INDEX(HookId)]) )
1076 {
1077 pdo->pDeskInfo->fsHooks &= ~HOOKID_TO_FLAG(HookId);
1078 }
1079 }
1080
1081 return TRUE;
1082 }
1083
1084 /*
1085 Win32k Kernel Space Hook Caller.
1086 */
1087 LRESULT
1088 APIENTRY
1089 co_HOOK_CallHooks( INT HookId,
1090 INT Code,
1091 WPARAM wParam,
1092 LPARAM lParam)
1093 {
1094 PHOOK Hook, SaveHook;
1095 PTHREADINFO pti;
1096 PCLIENTINFO ClientInfo;
1097 PLIST_ENTRY pLastHead;
1098 PDESKTOP pdo;
1099 BOOL Local = FALSE, Global = FALSE;
1100 LRESULT Result = 0;
1101 USER_REFERENCE_ENTRY Ref;
1102
1103 ASSERT(WH_MINHOOK <= HookId && HookId <= WH_MAXHOOK);
1104
1105 pti = PsGetCurrentThreadWin32Thread();
1106 if (!pti || !pti->rpdesk || !pti->rpdesk->pDeskInfo)
1107 {
1108 pdo = IntGetActiveDesktop();
1109 /* If KeyboardThread|MouseThread|(RawInputThread or RIT) aka system threads,
1110 pti->fsHooks most likely, is zero. So process KbT & MsT to "send" the message.
1111 */
1112 if ( !pti || !pdo || (!(HookId == WH_KEYBOARD_LL) && !(HookId == WH_MOUSE_LL)) )
1113 {
1114 TRACE("No PDO %d\n", HookId);
1115 goto Exit;
1116 }
1117 }
1118 else
1119 {
1120 pdo = pti->rpdesk;
1121 }
1122
1123 if ( pti->TIF_flags & (TIF_INCLEANUP|TIF_DISABLEHOOKS))
1124 {
1125 TRACE("Hook Thread dead %d\n", HookId);
1126 goto Exit;
1127 }
1128
1129 if ( ISITHOOKED(HookId) )
1130 {
1131 TRACE("Local Hooker %d\n", HookId);
1132 Local = TRUE;
1133 }
1134
1135 if ( pdo->pDeskInfo->fsHooks & HOOKID_TO_FLAG(HookId) )
1136 {
1137 TRACE("Global Hooker %d\n", HookId);
1138 Global = TRUE;
1139 }
1140
1141 if ( !Local && !Global ) goto Exit; // No work!
1142
1143 Hook = NULL;
1144
1145 /* SetWindowHookEx sorts out the Thread issue by placing the Hook to
1146 the correct Thread if not NULL.
1147 */
1148 if ( Local )
1149 {
1150 pLastHead = &pti->aphkStart[HOOKID_TO_INDEX(HookId)];
1151 if (IsListEmpty(pLastHead))
1152 {
1153 ERR("No Local Hook Found!\n");
1154 goto Exit;
1155 }
1156
1157 Hook = CONTAINING_RECORD(pLastHead->Flink, HOOK, Chain);
1158 ObReferenceObject(pti->pEThread);
1159 IntReferenceThreadInfo(pti);
1160 UserRefObjectCo(Hook, &Ref);
1161
1162 ClientInfo = pti->pClientInfo;
1163 SaveHook = pti->sphkCurrent;
1164 /* Note: Setting pti->sphkCurrent will also lock the next hook to this
1165 * hook ID. So, the CallNextHookEx will only call to that hook ID
1166 * chain anyway. For Thread Hooks....
1167 */
1168
1169 /* Load it for the next call. */
1170 pti->sphkCurrent = Hook;
1171 Hook->phkNext = IntGetNextHook(Hook);
1172 if (ClientInfo)
1173 {
1174 _SEH2_TRY
1175 {
1176 ClientInfo->phkCurrent = Hook;
1177 }
1178 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1179 {
1180 ClientInfo = NULL; // Don't bother next run.
1181 }
1182 _SEH2_END;
1183 }
1184 Result = co_IntCallHookProc( HookId,
1185 Code,
1186 wParam,
1187 lParam,
1188 Hook->Proc,
1189 Hook->ihmod,
1190 Hook->offPfn,
1191 Hook->Ansi,
1192 &Hook->ModuleName);
1193 if (ClientInfo)
1194 {
1195 _SEH2_TRY
1196 {
1197 ClientInfo->phkCurrent = SaveHook;
1198 }
1199 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1200 {
1201 /* Do nothing */
1202 (void)0;
1203 }
1204 _SEH2_END;
1205 }
1206 pti->sphkCurrent = SaveHook;
1207 Hook->phkNext = NULL;
1208 UserDerefObjectCo(Hook);
1209 IntDereferenceThreadInfo(pti);
1210 ObDereferenceObject(pti->pEThread);
1211 }
1212
1213 if ( Global )
1214 {
1215 PTHREADINFO ptiHook;
1216 HHOOK *pHookHandles;
1217 unsigned i;
1218
1219 /* Keep hooks in array because hooks can be destroyed in user world */
1220 pHookHandles = IntGetGlobalHookHandles(pdo, HookId);
1221 if(!pHookHandles)
1222 goto Exit;
1223
1224 /* Performance goes down the drain. If more hooks are associated to this
1225 * hook ID, this will have to post to each of the thread message queues
1226 * or make a direct call.
1227 */
1228 for(i = 0; pHookHandles[i]; ++i)
1229 {
1230 Hook = (PHOOK)UserGetObject(gHandleTable, pHookHandles[i], TYPE_HOOK);
1231 if(!Hook)
1232 {
1233 ERR("Invalid hook!\n");
1234 continue;
1235 }
1236
1237 /* Hook->Thread is null, we hax around this with Hook->head.pti. */
1238 ptiHook = Hook->head.pti;
1239
1240 if ( (pti->TIF_flags & TIF_DISABLEHOOKS) || (ptiHook->TIF_flags & TIF_INCLEANUP))
1241 {
1242 TRACE("Next Hook %p, %p\n", ptiHook->rpdesk, pdo);
1243 continue;
1244 }
1245 UserRefObjectCo(Hook, &Ref);
1246
1247 if (ptiHook != pti )
1248 {
1249 // Block | TimeOut
1250 if ( HookId == WH_JOURNALPLAYBACK || // 1 | 0
1251 HookId == WH_JOURNALRECORD || // 1 | 0
1252 HookId == WH_KEYBOARD || // 1 | 200
1253 HookId == WH_MOUSE || // 1 | 200
1254 HookId == WH_KEYBOARD_LL || // 0 | 300
1255 HookId == WH_MOUSE_LL ) // 0 | 300
1256 {
1257 TRACE("\nGlobal Hook posting to another Thread! %d\n",HookId );
1258 Result = co_IntCallLowLevelHook(Hook, Code, wParam, lParam);
1259 }
1260 else if (ptiHook->ppi == pti->ppi)
1261 {
1262 TRACE("\nGlobal Hook calling to another Thread! %d\n",HookId );
1263 ObReferenceObject(ptiHook->pEThread);
1264 IntReferenceThreadInfo(ptiHook);
1265 Result = co_IntCallHookProc( HookId,
1266 Code,
1267 wParam,
1268 lParam,
1269 Hook->Proc,
1270 Hook->ihmod,
1271 Hook->offPfn,
1272 Hook->Ansi,
1273 &Hook->ModuleName);
1274 IntDereferenceThreadInfo(ptiHook);
1275 ObDereferenceObject(ptiHook->pEThread);
1276 }
1277 }
1278 else
1279 { /* Make the direct call. */
1280 TRACE("Global going Local Hook calling to Thread! %d\n",HookId );
1281 ObReferenceObject(pti->pEThread);
1282 IntReferenceThreadInfo(pti);
1283 Result = co_IntCallHookProc( HookId,
1284 Code,
1285 wParam,
1286 lParam,
1287 Hook->Proc,
1288 Hook->ihmod,
1289 Hook->offPfn,
1290 Hook->Ansi,
1291 &Hook->ModuleName);
1292 IntDereferenceThreadInfo(pti);
1293 ObDereferenceObject(pti->pEThread);
1294 }
1295 UserDerefObjectCo(Hook);
1296 }
1297 ExFreePoolWithTag(pHookHandles, TAG_HOOK);
1298 TRACE("Ret: Global HookId %d Result 0x%x\n", HookId,Result);
1299 }
1300 Exit:
1301 return Result;
1302 }
1303
1304 BOOL
1305 FASTCALL
1306 IntUnhookWindowsHook(int HookId, HOOKPROC pfnFilterProc)
1307 {
1308 PHOOK Hook;
1309 PLIST_ENTRY pLastHead, pElement;
1310 PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
1311
1312 if (HookId < WH_MINHOOK || WH_MAXHOOK < HookId )
1313 {
1314 EngSetLastError(ERROR_INVALID_HOOK_FILTER);
1315 return FALSE;
1316 }
1317
1318 if (pti->fsHooks)
1319 {
1320 pLastHead = &pti->aphkStart[HOOKID_TO_INDEX(HookId)];
1321
1322 pElement = pLastHead->Flink;
1323 while (pElement != pLastHead)
1324 {
1325 Hook = CONTAINING_RECORD(pElement, HOOK, Chain);
1326
1327 /* Get the next element now, we might free the hook in what follows */
1328 pElement = Hook->Chain.Flink;
1329
1330 if (Hook->Proc == pfnFilterProc)
1331 {
1332 if (Hook->head.pti == pti)
1333 {
1334 IntRemoveHook(Hook);
1335 return TRUE;
1336 }
1337 else
1338 {
1339 EngSetLastError(ERROR_ACCESS_DENIED);
1340 return FALSE;
1341 }
1342 }
1343 }
1344 }
1345 return FALSE;
1346 }
1347
1348 /*
1349 * Support for compatibility only? Global hooks are processed in kernel space.
1350 * This is very thread specific! Never seeing applications with more than one
1351 * hook per thread installed. Most of the applications are Global hookers and
1352 * associated with just one hook Id. Maybe it's for diagnostic testing or a
1353 * throw back to 3.11?
1354 */
1355 LRESULT
1356 APIENTRY
1357 NtUserCallNextHookEx( int Code,
1358 WPARAM wParam,
1359 LPARAM lParam,
1360 BOOL Ansi)
1361 {
1362 PTHREADINFO pti;
1363 PHOOK HookObj, NextObj;
1364 PCLIENTINFO ClientInfo;
1365 LRESULT lResult = 0;
1366 DECLARE_RETURN(LRESULT);
1367
1368 TRACE("Enter NtUserCallNextHookEx\n");
1369 UserEnterExclusive();
1370
1371 pti = GetW32ThreadInfo();
1372
1373 HookObj = pti->sphkCurrent;
1374
1375 if (!HookObj) RETURN( 0);
1376
1377 NextObj = HookObj->phkNext;
1378
1379 pti->sphkCurrent = NextObj;
1380 ClientInfo = pti->pClientInfo;
1381 _SEH2_TRY
1382 {
1383 ClientInfo->phkCurrent = NextObj;
1384 }
1385 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1386 {
1387 ClientInfo = NULL;
1388 }
1389 _SEH2_END;
1390
1391 /* Now in List run down. */
1392 if (ClientInfo && NextObj)
1393 {
1394 NextObj->phkNext = IntGetNextHook(NextObj);
1395 lResult = co_UserCallNextHookEx( NextObj, Code, wParam, lParam, NextObj->Ansi);
1396 }
1397 RETURN( lResult);
1398
1399 CLEANUP:
1400 TRACE("Leave NtUserCallNextHookEx, ret=%i\n",_ret_);
1401 UserLeave();
1402 END_CLEANUP;
1403 }
1404
1405 HHOOK
1406 APIENTRY
1407 NtUserSetWindowsHookAW( int idHook,
1408 HOOKPROC lpfn,
1409 BOOL Ansi)
1410 {
1411 DWORD ThreadId;
1412 UNICODE_STRING USModuleName;
1413
1414 RtlInitUnicodeString(&USModuleName, NULL);
1415 ThreadId = PtrToUint(NtCurrentTeb()->ClientId.UniqueThread);
1416
1417 return NtUserSetWindowsHookEx( NULL,
1418 &USModuleName,
1419 ThreadId,
1420 idHook,
1421 lpfn,
1422 Ansi);
1423 }
1424
1425 HHOOK
1426 APIENTRY
1427 NtUserSetWindowsHookEx( HINSTANCE Mod,
1428 PUNICODE_STRING UnsafeModuleName,
1429 DWORD ThreadId,
1430 int HookId,
1431 HOOKPROC HookProc,
1432 BOOL Ansi)
1433 {
1434 PWINSTATION_OBJECT WinStaObj;
1435 PHOOK Hook = NULL;
1436 UNICODE_STRING ModuleName;
1437 NTSTATUS Status;
1438 HHOOK Handle;
1439 PTHREADINFO pti, ptiHook = NULL;
1440 DECLARE_RETURN(HHOOK);
1441
1442 TRACE("Enter NtUserSetWindowsHookEx\n");
1443 UserEnterExclusive();
1444
1445 pti = PsGetCurrentThreadWin32Thread();
1446
1447 if (HookId < WH_MINHOOK || WH_MAXHOOK < HookId )
1448 {
1449 EngSetLastError(ERROR_INVALID_HOOK_FILTER);
1450 RETURN( NULL);
1451 }
1452
1453 if (!HookProc)
1454 {
1455 EngSetLastError(ERROR_INVALID_FILTER_PROC);
1456 RETURN( NULL);
1457 }
1458
1459 if (ThreadId) /* thread-local hook */
1460 {
1461 if ( HookId == WH_JOURNALRECORD ||
1462 HookId == WH_JOURNALPLAYBACK ||
1463 HookId == WH_KEYBOARD_LL ||
1464 HookId == WH_MOUSE_LL ||
1465 HookId == WH_SYSMSGFILTER)
1466 {
1467 TRACE("Local hook installing Global HookId: %d\n",HookId);
1468 /* these can only be global */
1469 EngSetLastError(ERROR_GLOBAL_ONLY_HOOK);
1470 RETURN( NULL);
1471 }
1472
1473 if ( !(ptiHook = IntTID2PTI( (HANDLE)ThreadId )))
1474 {
1475 ERR("Invalid thread id 0x%x\n", ThreadId);
1476 EngSetLastError(ERROR_INVALID_PARAMETER);
1477 RETURN( NULL);
1478 }
1479
1480 if ( ptiHook->rpdesk != pti->rpdesk) // gptiCurrent->rpdesk)
1481 {
1482 ERR("Local hook wrong desktop HookId: %d\n",HookId);
1483 EngSetLastError(ERROR_ACCESS_DENIED);
1484 RETURN( NULL);
1485 }
1486
1487 if (ptiHook->ppi != pti->ppi)
1488 {
1489 if ( !Mod &&
1490 (HookId == WH_GETMESSAGE ||
1491 HookId == WH_CALLWNDPROC ||
1492 HookId == WH_CBT ||
1493 HookId == WH_HARDWARE ||
1494 HookId == WH_DEBUG ||
1495 HookId == WH_SHELL ||
1496 HookId == WH_FOREGROUNDIDLE ||
1497 HookId == WH_CALLWNDPROCRET) )
1498 {
1499 ERR("Local hook needs hMod HookId: %d\n",HookId);
1500 EngSetLastError(ERROR_HOOK_NEEDS_HMOD);
1501 RETURN( NULL);
1502 }
1503
1504 if ( (ptiHook->TIF_flags & (TIF_CSRSSTHREAD|TIF_SYSTEMTHREAD)) &&
1505 (HookId == WH_GETMESSAGE ||
1506 HookId == WH_CALLWNDPROC ||
1507 HookId == WH_CBT ||
1508 HookId == WH_HARDWARE ||
1509 HookId == WH_DEBUG ||
1510 HookId == WH_SHELL ||
1511 HookId == WH_FOREGROUNDIDLE ||
1512 HookId == WH_CALLWNDPROCRET) )
1513 {
1514 EngSetLastError(ERROR_HOOK_TYPE_NOT_ALLOWED);
1515 RETURN( NULL);
1516 }
1517 }
1518 }
1519 else /* System-global hook */
1520 {
1521 ptiHook = pti; // gptiCurrent;
1522 if ( !Mod &&
1523 (HookId == WH_GETMESSAGE ||
1524 HookId == WH_CALLWNDPROC ||
1525 HookId == WH_CBT ||
1526 HookId == WH_SYSMSGFILTER ||
1527 HookId == WH_HARDWARE ||
1528 HookId == WH_DEBUG ||
1529 HookId == WH_SHELL ||
1530 HookId == WH_FOREGROUNDIDLE ||
1531 HookId == WH_CALLWNDPROCRET) )
1532 {
1533 ERR("Global hook needs hMod HookId: %d\n",HookId);
1534 EngSetLastError(ERROR_HOOK_NEEDS_HMOD);
1535 RETURN( NULL);
1536 }
1537 }
1538
1539 Status = IntValidateWindowStationHandle( PsGetCurrentProcess()->Win32WindowStation,
1540 UserMode,
1541 0,
1542 &WinStaObj,
1543 0);
1544
1545 if (!NT_SUCCESS(Status))
1546 {
1547 SetLastNtError(Status);
1548 RETURN( NULL);
1549 }
1550 ObDereferenceObject(WinStaObj);
1551
1552 Hook = UserCreateObject(gHandleTable, NULL, ptiHook, (PHANDLE)&Handle, TYPE_HOOK, sizeof(HOOK));
1553
1554 if (!Hook)
1555 {
1556 RETURN( NULL);
1557 }
1558
1559 Hook->ihmod = (INT)Mod; // Module Index from atom table, Do this for now.
1560 Hook->HookId = HookId;
1561 Hook->rpdesk = ptiHook->rpdesk;
1562 Hook->phkNext = NULL; /* Dont use as a chain! Use link lists for chaining. */
1563 Hook->Proc = HookProc;
1564 Hook->Ansi = Ansi;
1565
1566 TRACE("Set Hook Desk %p DeskInfo %p Handle Desk %p\n", pti->rpdesk, pti->pDeskInfo, Hook->head.rpdesk);
1567
1568 if (ThreadId) /* Thread-local hook */
1569 {
1570 InsertHeadList(&ptiHook->aphkStart[HOOKID_TO_INDEX(HookId)], &Hook->Chain);
1571 ptiHook->sphkCurrent = NULL;
1572 Hook->ptiHooked = ptiHook;
1573 ptiHook->fsHooks |= HOOKID_TO_FLAG(HookId);
1574
1575 if (ptiHook->pClientInfo)
1576 {
1577 if ( ptiHook->ppi == pti->ppi) /* gptiCurrent->ppi) */
1578 {
1579 _SEH2_TRY
1580 {
1581 ptiHook->pClientInfo->fsHooks = ptiHook->fsHooks;
1582 ptiHook->pClientInfo->phkCurrent = NULL;
1583 }
1584 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1585 {
1586 ERR("Problem writing to Local ClientInfo!\n");
1587 }
1588 _SEH2_END;
1589 }
1590 else
1591 {
1592 KeAttachProcess(&ptiHook->ppi->peProcess->Pcb);
1593 _SEH2_TRY
1594 {
1595 ptiHook->pClientInfo->fsHooks = ptiHook->fsHooks;
1596 ptiHook->pClientInfo->phkCurrent = NULL;
1597 }
1598 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1599 {
1600 ERR("Problem writing to Remote ClientInfo!\n");
1601 }
1602 _SEH2_END;
1603 KeDetachProcess();
1604 }
1605 }
1606 }
1607 else
1608 {
1609 InsertHeadList(&ptiHook->rpdesk->pDeskInfo->aphkStart[HOOKID_TO_INDEX(HookId)], &Hook->Chain);
1610 Hook->ptiHooked = NULL;
1611 //gptiCurrent->pDeskInfo->fsHooks |= HOOKID_TO_FLAG(HookId);
1612 ptiHook->rpdesk->pDeskInfo->fsHooks |= HOOKID_TO_FLAG(HookId);
1613 ptiHook->sphkCurrent = NULL;
1614 ptiHook->pClientInfo->phkCurrent = NULL;
1615 }
1616
1617 RtlInitUnicodeString(&Hook->ModuleName, NULL);
1618
1619 if (Mod)
1620 {
1621 Status = MmCopyFromCaller(&ModuleName,
1622 UnsafeModuleName,
1623 sizeof(UNICODE_STRING));
1624 if (!NT_SUCCESS(Status))
1625 {
1626 IntRemoveHook(Hook);
1627 SetLastNtError(Status);
1628 RETURN( NULL);
1629 }
1630
1631 Hook->ModuleName.Buffer = ExAllocatePoolWithTag( PagedPool,
1632 ModuleName.MaximumLength,
1633 TAG_HOOK);
1634 if (NULL == Hook->ModuleName.Buffer)
1635 {
1636 IntRemoveHook(Hook);
1637 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
1638 RETURN( NULL);
1639 }
1640
1641 Hook->ModuleName.MaximumLength = ModuleName.MaximumLength;
1642 Status = MmCopyFromCaller( Hook->ModuleName.Buffer,
1643 ModuleName.Buffer,
1644 ModuleName.MaximumLength);
1645 if (!NT_SUCCESS(Status))
1646 {
1647 ExFreePoolWithTag(Hook->ModuleName.Buffer, TAG_HOOK);
1648 Hook->ModuleName.Buffer = NULL;
1649 IntRemoveHook(Hook);
1650 SetLastNtError(Status);
1651 RETURN( NULL);
1652 }
1653
1654 Hook->ModuleName.Length = ModuleName.Length;
1655 //// FIXME: Need to load from user32 to verify hMod before calling hook with hMod set!!!!
1656 //// Mod + offPfn == new HookProc Justin Case module is from another process.
1657 FIXME("NtUserSetWindowsHookEx Setting process hMod instance addressing.\n");
1658 /* Make proc relative to the module base */
1659 Hook->offPfn = (ULONG_PTR)((char *)HookProc - (char *)Mod);
1660 }
1661 else
1662 Hook->offPfn = 0;
1663
1664 TRACE("Installing: HookId %d Global %s\n", HookId, !ThreadId ? "TRUE" : "FALSE");
1665 RETURN( Handle);
1666
1667 CLEANUP:
1668 if (Hook)
1669 UserDereferenceObject(Hook);
1670 TRACE("Leave NtUserSetWindowsHookEx, ret=%p\n", _ret_);
1671 UserLeave();
1672 END_CLEANUP;
1673 }
1674
1675 BOOL
1676 APIENTRY
1677 NtUserUnhookWindowsHookEx(HHOOK Hook)
1678 {
1679 PHOOK HookObj;
1680 DECLARE_RETURN(BOOL);
1681
1682 TRACE("Enter NtUserUnhookWindowsHookEx\n");
1683 UserEnterExclusive();
1684
1685 if (!(HookObj = IntGetHookObject(Hook)))
1686 {
1687 ERR("Invalid handle passed to NtUserUnhookWindowsHookEx\n");
1688 /* SetLastNtError(Status); */
1689 RETURN( FALSE);
1690 }
1691
1692 ASSERT(Hook == UserHMGetHandle(HookObj));
1693
1694 IntRemoveHook(HookObj);
1695
1696 UserDereferenceObject(HookObj);
1697
1698 RETURN( TRUE);
1699
1700 CLEANUP:
1701 TRACE("Leave NtUserUnhookWindowsHookEx, ret=%i\n",_ret_);
1702 UserLeave();
1703 END_CLEANUP;
1704 }
1705
1706 BOOL
1707 APIENTRY
1708 NtUserRegisterUserApiHook(
1709 PUNICODE_STRING m_dllname1,
1710 PUNICODE_STRING m_funname1,
1711 DWORD dwUnknown3,
1712 DWORD dwUnknown4)
1713 {
1714 BOOL ret;
1715 UNICODE_STRING strDllNameSafe;
1716 UNICODE_STRING strFuncNameSafe;
1717 NTSTATUS Status;
1718
1719 /* Probe and capture parameters */
1720 Status = ProbeAndCaptureUnicodeString(&strDllNameSafe, UserMode, m_dllname1);
1721 if(!NT_SUCCESS(Status))
1722 {
1723 EngSetLastError(RtlNtStatusToDosError(Status));
1724 return FALSE;
1725 }
1726
1727 Status = ProbeAndCaptureUnicodeString(&strFuncNameSafe, UserMode, m_funname1);
1728 if(!NT_SUCCESS(Status))
1729 {
1730 ReleaseCapturedUnicodeString(&strDllNameSafe, UserMode);
1731 EngSetLastError(RtlNtStatusToDosError(Status));
1732 return FALSE;
1733 }
1734
1735 UserEnterExclusive();
1736
1737 /* Call internal function */
1738 ret = UserRegisterUserApiHook(&strDllNameSafe, &strFuncNameSafe);
1739
1740 UserLeave();
1741
1742 /* Cleanup only in case of failure */
1743 if(ret == FALSE)
1744 {
1745 ReleaseCapturedUnicodeString(&strDllNameSafe, UserMode);
1746 ReleaseCapturedUnicodeString(&strFuncNameSafe, UserMode);
1747 }
1748
1749 return ret;
1750 }
1751
1752 BOOL
1753 APIENTRY
1754 NtUserUnregisterUserApiHook(VOID)
1755 {
1756 BOOL ret;
1757
1758 UserEnterExclusive();
1759 ret = UserUnregisterUserApiHook();
1760 UserLeave();
1761
1762 return ret;
1763 }
1764
1765 /* EOF */