[WIN32K:NTUSER]
[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 /* The odds are high for this to be a Global call. */
346 Result = co_IntCallHookProc( HookId,
347 Code,
348 wParam,
349 lParam,
350 phk->Proc,
351 phk->ihmod,
352 phk->offPfn,
353 phk->Ansi,
354 &phk->ModuleName);
355
356 /* The odds so high, no one is waiting for the results. */
357 if (pHP->pHookStructs) ExFreePoolWithTag(pHP->pHookStructs, TAG_HOOK);
358 ExFreePoolWithTag(pHP, TAG_HOOK);
359 return Result;
360 }
361
362 static
363 LRESULT
364 APIENTRY
365 co_HOOK_CallHookNext( PHOOK Hook,
366 INT Code,
367 WPARAM wParam,
368 LPARAM lParam)
369 {
370 TRACE("Calling Next HOOK %d\n", Hook->HookId);
371
372 return co_IntCallHookProc( Hook->HookId,
373 Code,
374 wParam,
375 lParam,
376 Hook->Proc,
377 Hook->ihmod,
378 Hook->offPfn,
379 Hook->Ansi,
380 &Hook->ModuleName);
381 }
382
383 static
384 LRESULT
385 FASTCALL
386 co_IntCallDebugHook(PHOOK Hook,
387 int Code,
388 WPARAM wParam,
389 LPARAM lParam,
390 BOOL Ansi)
391 {
392 LRESULT lResult = 0;
393 ULONG Size;
394 DEBUGHOOKINFO Debug;
395 PVOID HooklParam = NULL;
396 BOOL BadChk = FALSE;
397
398 if (lParam)
399 {
400 _SEH2_TRY
401 {
402 ProbeForRead((PVOID)lParam,
403 sizeof(DEBUGHOOKINFO),
404 1);
405
406 RtlCopyMemory(&Debug,
407 (PVOID)lParam,
408 sizeof(DEBUGHOOKINFO));
409 }
410 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
411 {
412 BadChk = TRUE;
413 }
414 _SEH2_END;
415
416 if (BadChk)
417 {
418 ERR("HOOK WH_DEBUG read from lParam ERROR!\n");
419 return lResult;
420 }
421 }
422 else
423 return lResult; /* Need lParam! */
424
425 switch (wParam)
426 {
427 case WH_CBT:
428 {
429 switch (Debug.code)
430 {
431 case HCBT_CLICKSKIPPED:
432 Size = sizeof(MOUSEHOOKSTRUCTEX);
433 break;
434
435 case HCBT_MOVESIZE:
436 Size = sizeof(RECT);
437 break;
438
439 case HCBT_ACTIVATE:
440 Size = sizeof(CBTACTIVATESTRUCT);
441 break;
442
443 case HCBT_CREATEWND: /* Handle ANSI? */
444 Size = sizeof(CBT_CREATEWND);
445 /* What shall we do? Size += sizeof(HOOKPROC_CBT_CREATEWND_EXTRA_ARGUMENTS); same as CREATESTRUCTEX */
446 break;
447
448 default:
449 Size = sizeof(LPARAM);
450 }
451 }
452 break;
453
454 case WH_MOUSE_LL:
455 Size = sizeof(MSLLHOOKSTRUCT);
456 break;
457
458 case WH_KEYBOARD_LL:
459 Size = sizeof(KBDLLHOOKSTRUCT);
460 break;
461
462 case WH_MSGFILTER:
463 case WH_SYSMSGFILTER:
464 case WH_GETMESSAGE:
465 Size = sizeof(MSG);
466 break;
467
468 case WH_JOURNALPLAYBACK:
469 case WH_JOURNALRECORD:
470 Size = sizeof(EVENTMSG);
471 break;
472
473 case WH_FOREGROUNDIDLE:
474 case WH_KEYBOARD:
475 case WH_SHELL:
476 default:
477 Size = sizeof(LPARAM);
478 }
479
480 if (Size > sizeof(LPARAM))
481 HooklParam = ExAllocatePoolWithTag(NonPagedPool, Size, TAG_HOOK);
482
483 if (HooklParam)
484 {
485 _SEH2_TRY
486 {
487 ProbeForRead((PVOID)Debug.lParam,
488 Size,
489 1);
490
491 RtlCopyMemory(HooklParam,
492 (PVOID)Debug.lParam,
493 Size);
494 }
495 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
496 {
497 BadChk = TRUE;
498 }
499 _SEH2_END;
500
501 if (BadChk)
502 {
503 ERR("HOOK WH_DEBUG read from Debug.lParam ERROR!\n");
504 ExFreePoolWithTag(HooklParam, TAG_HOOK);
505 return lResult;
506 }
507 }
508
509 if (HooklParam) Debug.lParam = (LPARAM)HooklParam;
510 lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&Debug);
511 if (HooklParam) ExFreePoolWithTag(HooklParam, TAG_HOOK);
512
513 return lResult;
514 }
515
516 static
517 LRESULT
518 APIENTRY
519 co_UserCallNextHookEx(PHOOK Hook,
520 int Code,
521 WPARAM wParam,
522 LPARAM lParam,
523 BOOL Ansi)
524 {
525 LRESULT lResult = 0;
526 BOOL BadChk = FALSE;
527
528 /* Handle this one first. */
529 if ((Hook->HookId == WH_MOUSE) ||
530 (Hook->HookId == WH_CBT && Code == HCBT_CLICKSKIPPED))
531 {
532 MOUSEHOOKSTRUCTEX Mouse;
533 if (lParam)
534 {
535 _SEH2_TRY
536 {
537 ProbeForRead((PVOID)lParam,
538 sizeof(MOUSEHOOKSTRUCTEX),
539 1);
540
541 RtlCopyMemory(&Mouse,
542 (PVOID)lParam,
543 sizeof(MOUSEHOOKSTRUCTEX));
544 }
545 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
546 {
547 BadChk = TRUE;
548 }
549 _SEH2_END;
550
551 if (BadChk)
552 {
553 ERR("HOOK WH_MOUSE read from lParam ERROR!\n");
554 }
555 }
556
557 if (!BadChk)
558 {
559 lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&Mouse);
560 }
561
562 return lResult;
563 }
564
565 switch(Hook->HookId)
566 {
567 case WH_MOUSE_LL:
568 {
569 MSLLHOOKSTRUCT Mouse;
570
571 if (lParam)
572 {
573 _SEH2_TRY
574 {
575 ProbeForRead((PVOID)lParam,
576 sizeof(MSLLHOOKSTRUCT),
577 1);
578
579 RtlCopyMemory(&Mouse,
580 (PVOID)lParam,
581 sizeof(MSLLHOOKSTRUCT));
582 }
583 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
584 {
585 BadChk = TRUE;
586 }
587 _SEH2_END;
588
589 if (BadChk)
590 {
591 ERR("HOOK WH_MOUSE_LL read from lParam ERROR!\n");
592 }
593 }
594
595 if (!BadChk)
596 {
597 lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&Mouse);
598 }
599 break;
600 }
601
602 case WH_KEYBOARD_LL:
603 {
604 KBDLLHOOKSTRUCT Keyboard;
605
606 if (lParam)
607 {
608 _SEH2_TRY
609 {
610 ProbeForRead((PVOID)lParam,
611 sizeof(KBDLLHOOKSTRUCT),
612 1);
613
614 RtlCopyMemory(&Keyboard,
615 (PVOID)lParam,
616 sizeof(KBDLLHOOKSTRUCT));
617 }
618 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
619 {
620 BadChk = TRUE;
621 }
622 _SEH2_END;
623
624 if (BadChk)
625 {
626 ERR("HOOK WH_KEYBORD_LL read from lParam ERROR!\n");
627 }
628 }
629
630 if (!BadChk)
631 {
632 lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&Keyboard);
633 }
634 break;
635 }
636
637 case WH_MSGFILTER:
638 case WH_SYSMSGFILTER:
639 case WH_GETMESSAGE:
640 {
641 MSG Msg;
642
643 if (lParam)
644 {
645 _SEH2_TRY
646 {
647 ProbeForRead((PVOID)lParam,
648 sizeof(MSG),
649 1);
650
651 RtlCopyMemory(&Msg,
652 (PVOID)lParam,
653 sizeof(MSG));
654 }
655 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
656 {
657 BadChk = TRUE;
658 }
659 _SEH2_END;
660
661 if (BadChk)
662 {
663 ERR("HOOK WH_XMESSAGEX read from lParam ERROR!\n");
664 }
665 }
666
667 if (!BadChk)
668 {
669 lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&Msg);
670
671 if (lParam && (Hook->HookId == WH_GETMESSAGE))
672 {
673 _SEH2_TRY
674 {
675 ProbeForWrite((PVOID)lParam,
676 sizeof(MSG),
677 1);
678
679 RtlCopyMemory((PVOID)lParam,
680 &Msg,
681 sizeof(MSG));
682 }
683 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
684 {
685 BadChk = TRUE;
686 }
687 _SEH2_END;
688
689 if (BadChk)
690 {
691 ERR("HOOK WH_GETMESSAGE write to lParam ERROR!\n");
692 }
693 }
694 }
695 break;
696 }
697
698 case WH_CBT:
699 TRACE("HOOK WH_CBT!\n");
700 switch (Code)
701 {
702 case HCBT_CREATEWND:
703 {
704 LPCBT_CREATEWNDW pcbtcww = (LPCBT_CREATEWNDW)lParam;
705
706 TRACE("HOOK HCBT_CREATEWND\n");
707 _SEH2_TRY
708 {
709 if (Ansi)
710 {
711 ProbeForRead( pcbtcww,
712 sizeof(CBT_CREATEWNDA),
713 1);
714 ProbeForWrite(pcbtcww->lpcs,
715 sizeof(CREATESTRUCTA),
716 1);
717 ProbeForRead( pcbtcww->lpcs->lpszName,
718 sizeof(CHAR),
719 1);
720
721 if (!IS_ATOM(pcbtcww->lpcs->lpszClass))
722 {
723 _Analysis_assume_(pcbtcww->lpcs->lpszClass != NULL);
724 ProbeForRead(pcbtcww->lpcs->lpszClass,
725 sizeof(CHAR),
726 1);
727 }
728 }
729 else
730 {
731 ProbeForRead( pcbtcww,
732 sizeof(CBT_CREATEWNDW),
733 1);
734 ProbeForWrite(pcbtcww->lpcs,
735 sizeof(CREATESTRUCTW),
736 1);
737 ProbeForRead( pcbtcww->lpcs->lpszName,
738 sizeof(WCHAR),
739 1);
740
741 if (!IS_ATOM(pcbtcww->lpcs->lpszClass))
742 {
743 _Analysis_assume_(pcbtcww->lpcs->lpszClass != NULL);
744 ProbeForRead(pcbtcww->lpcs->lpszClass,
745 sizeof(WCHAR),
746 1);
747 }
748 }
749 }
750 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
751 {
752 BadChk = TRUE;
753 }
754 _SEH2_END;
755
756 if (BadChk)
757 {
758 ERR("HOOK HCBT_CREATEWND write ERROR!\n");
759 }
760 /* The next call handles the structures. */
761 if (!BadChk && Hook->Proc)
762 {
763 lResult = co_HOOK_CallHookNext(Hook, Code, wParam, lParam);
764 }
765 break;
766 }
767
768 case HCBT_MOVESIZE:
769 {
770 RECTL rt;
771
772 TRACE("HOOK HCBT_MOVESIZE\n");
773
774 if (lParam)
775 {
776 _SEH2_TRY
777 {
778 ProbeForRead((PVOID)lParam,
779 sizeof(RECT),
780 1);
781
782 RtlCopyMemory(&rt,
783 (PVOID)lParam,
784 sizeof(RECT));
785 }
786 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
787 {
788 BadChk = TRUE;
789 }
790 _SEH2_END;
791
792 if (BadChk)
793 {
794 ERR("HOOK HCBT_MOVESIZE read from lParam ERROR!\n");
795 }
796 }
797
798 if (!BadChk)
799 {
800 lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&rt);
801 }
802 break;
803 }
804
805 case HCBT_ACTIVATE:
806 {
807 CBTACTIVATESTRUCT CbAs;
808
809 TRACE("HOOK HCBT_ACTIVATE\n");
810 if (lParam)
811 {
812 _SEH2_TRY
813 {
814 ProbeForRead((PVOID)lParam,
815 sizeof(CBTACTIVATESTRUCT),
816 1);
817
818 RtlCopyMemory(&CbAs,
819 (PVOID)lParam,
820 sizeof(CBTACTIVATESTRUCT));
821 }
822 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
823 {
824 BadChk = TRUE;
825 }
826 _SEH2_END;
827
828 if (BadChk)
829 {
830 ERR("HOOK HCBT_ACTIVATE read from lParam ERROR!\n");
831 }
832 }
833
834 if (!BadChk)
835 {
836 lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&CbAs);
837 }
838 break;
839 }
840
841 /* The rest just use default. */
842 default:
843 TRACE("HOOK HCBT_ %d\n",Code);
844 lResult = co_HOOK_CallHookNext(Hook, Code, wParam, lParam);
845 break;
846 }
847 break;
848 /*
849 Note WH_JOURNALPLAYBACK,
850 "To have the system wait before processing the message, the return value
851 must be the amount of time, in clock ticks, that the system should wait."
852 */
853 case WH_JOURNALPLAYBACK:
854 case WH_JOURNALRECORD:
855 {
856 EVENTMSG EventMsg;
857
858 if (lParam)
859 {
860 _SEH2_TRY
861 {
862 ProbeForRead((PVOID)lParam,
863 sizeof(EVENTMSG),
864 1);
865
866 RtlCopyMemory(&EventMsg,
867 (PVOID)lParam,
868 sizeof(EVENTMSG));
869 }
870 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
871 {
872 BadChk = TRUE;
873 }
874 _SEH2_END;
875
876 if (BadChk)
877 {
878 ERR("HOOK WH_JOURNAL read from lParam ERROR!\n");
879 }
880 }
881
882 if (!BadChk)
883 {
884 lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)(lParam ? &EventMsg : NULL));
885
886 if (lParam)
887 {
888 _SEH2_TRY
889 {
890 ProbeForWrite((PVOID)lParam,
891 sizeof(EVENTMSG),
892 1);
893
894 RtlCopyMemory((PVOID)lParam,
895 &EventMsg,
896 sizeof(EVENTMSG));
897 }
898 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
899 {
900 BadChk = TRUE;
901 }
902 _SEH2_END;
903
904 if (BadChk)
905 {
906 ERR("HOOK WH_JOURNAL write to lParam ERROR!\n");
907 }
908 }
909 }
910 break;
911 }
912
913 case WH_DEBUG:
914 lResult = co_IntCallDebugHook(Hook, Code, wParam, lParam, Ansi);
915 break;
916
917 /*
918 * Default the rest like, WH_FOREGROUNDIDLE, WH_KEYBOARD and WH_SHELL.
919 */
920 case WH_FOREGROUNDIDLE:
921 case WH_KEYBOARD:
922 case WH_SHELL:
923 lResult = co_HOOK_CallHookNext(Hook, Code, wParam, lParam);
924 break;
925
926 default:
927 ERR("Unsupported HOOK Id -> %d\n",Hook->HookId);
928 break;
929 }
930 return lResult;
931 }
932
933 PHOOK
934 FASTCALL
935 IntGetHookObject(HHOOK hHook)
936 {
937 PHOOK Hook;
938
939 if (!hHook)
940 {
941 EngSetLastError(ERROR_INVALID_HOOK_HANDLE);
942 return NULL;
943 }
944
945 Hook = (PHOOK)UserGetObject(gHandleTable, hHook, TYPE_HOOK);
946 if (!Hook)
947 {
948 EngSetLastError(ERROR_INVALID_HOOK_HANDLE);
949 return NULL;
950 }
951
952 UserReferenceObject(Hook);
953
954 return Hook;
955 }
956
957 static
958 HHOOK*
959 FASTCALL
960 IntGetGlobalHookHandles(PDESKTOP pdo, int HookId)
961 {
962 PLIST_ENTRY pLastHead, pElem;
963 unsigned i = 0;
964 unsigned cHooks = 0;
965 HHOOK *pList;
966 PHOOK pHook;
967
968 pLastHead = &pdo->pDeskInfo->aphkStart[HOOKID_TO_INDEX(HookId)];
969 for (pElem = pLastHead->Flink; pElem != pLastHead; pElem = pElem->Flink)
970 ++cHooks;
971
972 pList = ExAllocatePoolWithTag(PagedPool, (cHooks + 1) * sizeof(HHOOK), TAG_HOOK);
973 if (!pList)
974 {
975 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
976 return NULL;
977 }
978
979 for (pElem = pLastHead->Flink; pElem != pLastHead; pElem = pElem->Flink)
980 {
981 pHook = CONTAINING_RECORD(pElem, HOOK, Chain);
982 NT_ASSERT(i < cHooks);
983 pList[i++] = pHook->head.h;
984 }
985 pList[i] = NULL;
986
987 return pList;
988 }
989
990 /* Find the next hook in the chain */
991 PHOOK
992 FASTCALL
993 IntGetNextHook(PHOOK Hook)
994 {
995 int HookId = Hook->HookId;
996 PLIST_ENTRY pLastHead, pElem;
997 PTHREADINFO pti;
998
999 if (Hook->ptiHooked)
1000 {
1001 pti = Hook->ptiHooked;
1002 pLastHead = &pti->aphkStart[HOOKID_TO_INDEX(HookId)];
1003 }
1004 else
1005 {
1006 pti = PsGetCurrentThreadWin32Thread();
1007 pLastHead = &pti->rpdesk->pDeskInfo->aphkStart[HOOKID_TO_INDEX(HookId)];
1008 }
1009
1010 pElem = Hook->Chain.Flink;
1011 if (pElem != pLastHead)
1012 return CONTAINING_RECORD(pElem, HOOK, Chain);
1013 return NULL;
1014 }
1015
1016 /* Free a hook, removing it from its chain */
1017 static
1018 VOID
1019 FASTCALL
1020 IntFreeHook(PHOOK Hook)
1021 {
1022 RemoveEntryList(&Hook->Chain);
1023 if (Hook->ModuleName.Buffer)
1024 {
1025 ExFreePoolWithTag(Hook->ModuleName.Buffer, TAG_HOOK);
1026 Hook->ModuleName.Buffer = NULL;
1027 }
1028 /* Close handle */
1029 UserDeleteObject(UserHMGetHandle(Hook), TYPE_HOOK);
1030 }
1031
1032 /* Remove a hook, freeing it from the chain */
1033 BOOLEAN
1034 IntRemoveHook(PVOID Object)
1035 {
1036 INT HookId;
1037 PTHREADINFO pti;
1038 PDESKTOP pdo;
1039 PHOOK Hook = Object;
1040
1041 HookId = Hook->HookId;
1042
1043 if (Hook->ptiHooked) // Local
1044 {
1045 pti = Hook->ptiHooked;
1046
1047 IntFreeHook( Hook);
1048
1049 if ( IsListEmpty(&pti->aphkStart[HOOKID_TO_INDEX(HookId)]) )
1050 {
1051 pti->fsHooks &= ~HOOKID_TO_FLAG(HookId);
1052 _SEH2_TRY
1053 {
1054 pti->pClientInfo->fsHooks = pti->fsHooks;
1055 }
1056 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1057 {
1058 /* Do nothing */
1059 (void)0;
1060 }
1061 _SEH2_END;
1062 }
1063 }
1064 else // Global
1065 {
1066 IntFreeHook( Hook);
1067
1068 pdo = IntGetActiveDesktop();
1069
1070 if ( pdo &&
1071 pdo->pDeskInfo &&
1072 IsListEmpty(&pdo->pDeskInfo->aphkStart[HOOKID_TO_INDEX(HookId)]) )
1073 {
1074 pdo->pDeskInfo->fsHooks &= ~HOOKID_TO_FLAG(HookId);
1075 }
1076 }
1077
1078 return TRUE;
1079 }
1080
1081 /*
1082 Win32k Kernel Space Hook Caller.
1083 */
1084 LRESULT
1085 APIENTRY
1086 co_HOOK_CallHooks( INT HookId,
1087 INT Code,
1088 WPARAM wParam,
1089 LPARAM lParam)
1090 {
1091 PHOOK Hook, SaveHook;
1092 PTHREADINFO pti;
1093 PCLIENTINFO ClientInfo;
1094 PLIST_ENTRY pLastHead;
1095 PDESKTOP pdo;
1096 BOOL Local = FALSE, Global = FALSE;
1097 LRESULT Result = 0;
1098 USER_REFERENCE_ENTRY Ref;
1099
1100 ASSERT(WH_MINHOOK <= HookId && HookId <= WH_MAXHOOK);
1101
1102 pti = PsGetCurrentThreadWin32Thread();
1103 if (!pti || !pti->rpdesk || !pti->rpdesk->pDeskInfo)
1104 {
1105 pdo = IntGetActiveDesktop();
1106 /* If KeyboardThread|MouseThread|(RawInputThread or RIT) aka system threads,
1107 pti->fsHooks most likely, is zero. So process KbT & MsT to "send" the message.
1108 */
1109 if ( !pti || !pdo || (!(HookId == WH_KEYBOARD_LL) && !(HookId == WH_MOUSE_LL)) )
1110 {
1111 TRACE("No PDO %d\n", HookId);
1112 goto Exit;
1113 }
1114 }
1115 else
1116 {
1117 pdo = pti->rpdesk;
1118 }
1119
1120 if ( pti->TIF_flags & (TIF_INCLEANUP|TIF_DISABLEHOOKS))
1121 {
1122 TRACE("Hook Thread dead %d\n", HookId);
1123 goto Exit;
1124 }
1125
1126 if ( ISITHOOKED(HookId) )
1127 {
1128 TRACE("Local Hooker %d\n", HookId);
1129 Local = TRUE;
1130 }
1131
1132 if ( pdo->pDeskInfo->fsHooks & HOOKID_TO_FLAG(HookId) )
1133 {
1134 TRACE("Global Hooker %d\n", HookId);
1135 Global = TRUE;
1136 }
1137
1138 if ( !Local && !Global ) goto Exit; // No work!
1139
1140 Hook = NULL;
1141
1142 /* SetWindowHookEx sorts out the Thread issue by placing the Hook to
1143 the correct Thread if not NULL.
1144 */
1145 if ( Local )
1146 {
1147 pLastHead = &pti->aphkStart[HOOKID_TO_INDEX(HookId)];
1148 if (IsListEmpty(pLastHead))
1149 {
1150 ERR("No Local Hook Found!\n");
1151 goto Exit;
1152 }
1153
1154 Hook = CONTAINING_RECORD(pLastHead->Flink, HOOK, Chain);
1155 ObReferenceObject(pti->pEThread);
1156 IntReferenceThreadInfo(pti);
1157 UserRefObjectCo(Hook, &Ref);
1158
1159 ClientInfo = pti->pClientInfo;
1160 SaveHook = pti->sphkCurrent;
1161 /* Note: Setting pti->sphkCurrent will also lock the next hook to this
1162 * hook ID. So, the CallNextHookEx will only call to that hook ID
1163 * chain anyway. For Thread Hooks....
1164 */
1165
1166 /* Load it for the next call. */
1167 pti->sphkCurrent = Hook;
1168 Hook->phkNext = IntGetNextHook(Hook);
1169 if (ClientInfo)
1170 {
1171 _SEH2_TRY
1172 {
1173 ClientInfo->phkCurrent = Hook;
1174 }
1175 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1176 {
1177 ClientInfo = NULL; // Don't bother next run.
1178 }
1179 _SEH2_END;
1180 }
1181 Result = co_IntCallHookProc( HookId,
1182 Code,
1183 wParam,
1184 lParam,
1185 Hook->Proc,
1186 Hook->ihmod,
1187 Hook->offPfn,
1188 Hook->Ansi,
1189 &Hook->ModuleName);
1190 if (ClientInfo)
1191 {
1192 _SEH2_TRY
1193 {
1194 ClientInfo->phkCurrent = SaveHook;
1195 }
1196 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1197 {
1198 /* Do nothing */
1199 (void)0;
1200 }
1201 _SEH2_END;
1202 }
1203 pti->sphkCurrent = SaveHook;
1204 Hook->phkNext = NULL;
1205 UserDerefObjectCo(Hook);
1206 IntDereferenceThreadInfo(pti);
1207 ObDereferenceObject(pti->pEThread);
1208 }
1209
1210 if ( Global )
1211 {
1212 PTHREADINFO ptiHook;
1213 HHOOK *pHookHandles;
1214 unsigned i;
1215
1216 /* Keep hooks in array because hooks can be destroyed in user world */
1217 pHookHandles = IntGetGlobalHookHandles(pdo, HookId);
1218 if(!pHookHandles)
1219 goto Exit;
1220
1221 /* Performance goes down the drain. If more hooks are associated to this
1222 * hook ID, this will have to post to each of the thread message queues
1223 * or make a direct call.
1224 */
1225 for(i = 0; pHookHandles[i]; ++i)
1226 {
1227 Hook = (PHOOK)UserGetObject(gHandleTable, pHookHandles[i], TYPE_HOOK);
1228 if(!Hook)
1229 {
1230 ERR("Invalid hook!\n");
1231 continue;
1232 }
1233
1234 /* Hook->Thread is null, we hax around this with Hook->head.pti. */
1235 ptiHook = Hook->head.pti;
1236
1237 if ( (pti->TIF_flags & TIF_DISABLEHOOKS) || (ptiHook->TIF_flags & TIF_INCLEANUP))
1238 {
1239 TRACE("Next Hook %p, %p\n", ptiHook->rpdesk, pdo);
1240 continue;
1241 }
1242 UserRefObjectCo(Hook, &Ref);
1243
1244 if (ptiHook != pti )
1245 {
1246 // Block | TimeOut
1247 if ( HookId == WH_JOURNALPLAYBACK || // 1 | 0
1248 HookId == WH_JOURNALRECORD || // 1 | 0
1249 HookId == WH_KEYBOARD || // 1 | 200
1250 HookId == WH_MOUSE || // 1 | 200
1251 HookId == WH_KEYBOARD_LL || // 0 | 300
1252 HookId == WH_MOUSE_LL ) // 0 | 300
1253 {
1254 TRACE("\nGlobal Hook posting to another Thread! %d\n",HookId );
1255 Result = co_IntCallLowLevelHook(Hook, Code, wParam, lParam);
1256 }
1257 else if (ptiHook->ppi == pti->ppi)
1258 {
1259 TRACE("\nGlobal Hook calling to another Thread! %d\n",HookId );
1260 ObReferenceObject(ptiHook->pEThread);
1261 IntReferenceThreadInfo(ptiHook);
1262 Result = co_IntCallHookProc( HookId,
1263 Code,
1264 wParam,
1265 lParam,
1266 Hook->Proc,
1267 Hook->ihmod,
1268 Hook->offPfn,
1269 Hook->Ansi,
1270 &Hook->ModuleName);
1271 IntDereferenceThreadInfo(ptiHook);
1272 ObDereferenceObject(ptiHook->pEThread);
1273 }
1274 }
1275 else
1276 { /* Make the direct call. */
1277 TRACE("Global going Local Hook calling to Thread! %d\n",HookId );
1278 ObReferenceObject(pti->pEThread);
1279 IntReferenceThreadInfo(pti);
1280 Result = co_IntCallHookProc( HookId,
1281 Code,
1282 wParam,
1283 lParam,
1284 Hook->Proc,
1285 Hook->ihmod,
1286 Hook->offPfn,
1287 Hook->Ansi,
1288 &Hook->ModuleName);
1289 IntDereferenceThreadInfo(pti);
1290 ObDereferenceObject(pti->pEThread);
1291 }
1292 UserDerefObjectCo(Hook);
1293 }
1294 ExFreePoolWithTag(pHookHandles, TAG_HOOK);
1295 TRACE("Ret: Global HookId %d Result 0x%x\n", HookId,Result);
1296 }
1297 Exit:
1298 return Result;
1299 }
1300
1301 BOOL
1302 FASTCALL
1303 IntUnhookWindowsHook(int HookId, HOOKPROC pfnFilterProc)
1304 {
1305 PHOOK Hook;
1306 PLIST_ENTRY pLastHead, pElement;
1307 PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
1308
1309 if (HookId < WH_MINHOOK || WH_MAXHOOK < HookId )
1310 {
1311 EngSetLastError(ERROR_INVALID_HOOK_FILTER);
1312 return FALSE;
1313 }
1314
1315 if (pti->fsHooks)
1316 {
1317 pLastHead = &pti->aphkStart[HOOKID_TO_INDEX(HookId)];
1318
1319 pElement = pLastHead->Flink;
1320 while (pElement != pLastHead)
1321 {
1322 Hook = CONTAINING_RECORD(pElement, HOOK, Chain);
1323
1324 /* Get the next element now, we might free the hook in what follows */
1325 pElement = Hook->Chain.Flink;
1326
1327 if (Hook->Proc == pfnFilterProc)
1328 {
1329 if (Hook->head.pti == pti)
1330 {
1331 IntRemoveHook(Hook);
1332 return TRUE;
1333 }
1334 else
1335 {
1336 EngSetLastError(ERROR_ACCESS_DENIED);
1337 return FALSE;
1338 }
1339 }
1340 }
1341 }
1342 return FALSE;
1343 }
1344
1345 /*
1346 * Support for compatibility only? Global hooks are processed in kernel space.
1347 * This is very thread specific! Never seeing applications with more than one
1348 * hook per thread installed. Most of the applications are Global hookers and
1349 * associated with just one hook Id. Maybe it's for diagnostic testing or a
1350 * throw back to 3.11?
1351 */
1352 LRESULT
1353 APIENTRY
1354 NtUserCallNextHookEx( int Code,
1355 WPARAM wParam,
1356 LPARAM lParam,
1357 BOOL Ansi)
1358 {
1359 PTHREADINFO pti;
1360 PHOOK HookObj, NextObj;
1361 PCLIENTINFO ClientInfo;
1362 LRESULT lResult = 0;
1363 DECLARE_RETURN(LRESULT);
1364
1365 TRACE("Enter NtUserCallNextHookEx\n");
1366 UserEnterExclusive();
1367
1368 pti = GetW32ThreadInfo();
1369
1370 HookObj = pti->sphkCurrent;
1371
1372 if (!HookObj) RETURN( 0);
1373
1374 NextObj = HookObj->phkNext;
1375
1376 pti->sphkCurrent = NextObj;
1377 ClientInfo = pti->pClientInfo;
1378 _SEH2_TRY
1379 {
1380 ClientInfo->phkCurrent = NextObj;
1381 }
1382 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1383 {
1384 ClientInfo = NULL;
1385 }
1386 _SEH2_END;
1387
1388 /* Now in List run down. */
1389 if (ClientInfo && NextObj)
1390 {
1391 NextObj->phkNext = IntGetNextHook(NextObj);
1392 lResult = co_UserCallNextHookEx( NextObj, Code, wParam, lParam, NextObj->Ansi);
1393 }
1394 RETURN( lResult);
1395
1396 CLEANUP:
1397 TRACE("Leave NtUserCallNextHookEx, ret=%i\n",_ret_);
1398 UserLeave();
1399 END_CLEANUP;
1400 }
1401
1402 HHOOK
1403 APIENTRY
1404 NtUserSetWindowsHookAW( int idHook,
1405 HOOKPROC lpfn,
1406 BOOL Ansi)
1407 {
1408 DWORD ThreadId;
1409 UNICODE_STRING USModuleName;
1410
1411 RtlInitUnicodeString(&USModuleName, NULL);
1412 ThreadId = PtrToUint(NtCurrentTeb()->ClientId.UniqueThread);
1413
1414 return NtUserSetWindowsHookEx( NULL,
1415 &USModuleName,
1416 ThreadId,
1417 idHook,
1418 lpfn,
1419 Ansi);
1420 }
1421
1422 HHOOK
1423 APIENTRY
1424 NtUserSetWindowsHookEx( HINSTANCE Mod,
1425 PUNICODE_STRING UnsafeModuleName,
1426 DWORD ThreadId,
1427 int HookId,
1428 HOOKPROC HookProc,
1429 BOOL Ansi)
1430 {
1431 PWINSTATION_OBJECT WinStaObj;
1432 PHOOK Hook = NULL;
1433 UNICODE_STRING ModuleName;
1434 NTSTATUS Status;
1435 HHOOK Handle;
1436 PTHREADINFO pti, ptiHook = NULL;
1437 DECLARE_RETURN(HHOOK);
1438
1439 TRACE("Enter NtUserSetWindowsHookEx\n");
1440 UserEnterExclusive();
1441
1442 pti = PsGetCurrentThreadWin32Thread();
1443
1444 if (HookId < WH_MINHOOK || WH_MAXHOOK < HookId )
1445 {
1446 EngSetLastError(ERROR_INVALID_HOOK_FILTER);
1447 RETURN( NULL);
1448 }
1449
1450 if (!HookProc)
1451 {
1452 EngSetLastError(ERROR_INVALID_FILTER_PROC);
1453 RETURN( NULL);
1454 }
1455
1456 if (ThreadId) /* thread-local hook */
1457 {
1458 if ( HookId == WH_JOURNALRECORD ||
1459 HookId == WH_JOURNALPLAYBACK ||
1460 HookId == WH_KEYBOARD_LL ||
1461 HookId == WH_MOUSE_LL ||
1462 HookId == WH_SYSMSGFILTER)
1463 {
1464 TRACE("Local hook installing Global HookId: %d\n",HookId);
1465 /* these can only be global */
1466 EngSetLastError(ERROR_GLOBAL_ONLY_HOOK);
1467 RETURN( NULL);
1468 }
1469
1470 if ( !(ptiHook = IntTID2PTI( (HANDLE)ThreadId )))
1471 {
1472 ERR("Invalid thread id 0x%x\n", ThreadId);
1473 EngSetLastError(ERROR_INVALID_PARAMETER);
1474 RETURN( NULL);
1475 }
1476
1477 if ( ptiHook->rpdesk != pti->rpdesk) // gptiCurrent->rpdesk)
1478 {
1479 ERR("Local hook wrong desktop HookId: %d\n",HookId);
1480 EngSetLastError(ERROR_ACCESS_DENIED);
1481 RETURN( NULL);
1482 }
1483
1484 if (ptiHook->ppi != pti->ppi)
1485 {
1486 if ( !Mod &&
1487 (HookId == WH_GETMESSAGE ||
1488 HookId == WH_CALLWNDPROC ||
1489 HookId == WH_CBT ||
1490 HookId == WH_HARDWARE ||
1491 HookId == WH_DEBUG ||
1492 HookId == WH_SHELL ||
1493 HookId == WH_FOREGROUNDIDLE ||
1494 HookId == WH_CALLWNDPROCRET) )
1495 {
1496 ERR("Local hook needs hMod HookId: %d\n",HookId);
1497 EngSetLastError(ERROR_HOOK_NEEDS_HMOD);
1498 RETURN( NULL);
1499 }
1500
1501 if ( (ptiHook->TIF_flags & (TIF_CSRSSTHREAD|TIF_SYSTEMTHREAD)) &&
1502 (HookId == WH_GETMESSAGE ||
1503 HookId == WH_CALLWNDPROC ||
1504 HookId == WH_CBT ||
1505 HookId == WH_HARDWARE ||
1506 HookId == WH_DEBUG ||
1507 HookId == WH_SHELL ||
1508 HookId == WH_FOREGROUNDIDLE ||
1509 HookId == WH_CALLWNDPROCRET) )
1510 {
1511 EngSetLastError(ERROR_HOOK_TYPE_NOT_ALLOWED);
1512 RETURN( NULL);
1513 }
1514 }
1515 }
1516 else /* System-global hook */
1517 {
1518 ptiHook = pti; // gptiCurrent;
1519 if ( !Mod &&
1520 (HookId == WH_GETMESSAGE ||
1521 HookId == WH_CALLWNDPROC ||
1522 HookId == WH_CBT ||
1523 HookId == WH_SYSMSGFILTER ||
1524 HookId == WH_HARDWARE ||
1525 HookId == WH_DEBUG ||
1526 HookId == WH_SHELL ||
1527 HookId == WH_FOREGROUNDIDLE ||
1528 HookId == WH_CALLWNDPROCRET) )
1529 {
1530 ERR("Global hook needs hMod HookId: %d\n",HookId);
1531 EngSetLastError(ERROR_HOOK_NEEDS_HMOD);
1532 RETURN( NULL);
1533 }
1534 }
1535
1536 Status = IntValidateWindowStationHandle( PsGetCurrentProcess()->Win32WindowStation,
1537 KernelMode,
1538 0,
1539 &WinStaObj,
1540 0);
1541
1542 if (!NT_SUCCESS(Status))
1543 {
1544 SetLastNtError(Status);
1545 RETURN( NULL);
1546 }
1547 ObDereferenceObject(WinStaObj);
1548
1549 Hook = UserCreateObject(gHandleTable, NULL, ptiHook, (PHANDLE)&Handle, TYPE_HOOK, sizeof(HOOK));
1550
1551 if (!Hook)
1552 {
1553 RETURN( NULL);
1554 }
1555
1556 Hook->ihmod = (INT)Mod; // Module Index from atom table, Do this for now.
1557 Hook->HookId = HookId;
1558 Hook->rpdesk = ptiHook->rpdesk;
1559 Hook->phkNext = NULL; /* Dont use as a chain! Use link lists for chaining. */
1560 Hook->Proc = HookProc;
1561 Hook->Ansi = Ansi;
1562
1563 TRACE("Set Hook Desk %p DeskInfo %p Handle Desk %p\n", pti->rpdesk, pti->pDeskInfo, Hook->head.rpdesk);
1564
1565 if (ThreadId) /* Thread-local hook */
1566 {
1567 InsertHeadList(&ptiHook->aphkStart[HOOKID_TO_INDEX(HookId)], &Hook->Chain);
1568 ptiHook->sphkCurrent = NULL;
1569 Hook->ptiHooked = ptiHook;
1570 ptiHook->fsHooks |= HOOKID_TO_FLAG(HookId);
1571
1572 if (ptiHook->pClientInfo)
1573 {
1574 if ( ptiHook->ppi == pti->ppi) /* gptiCurrent->ppi) */
1575 {
1576 _SEH2_TRY
1577 {
1578 ptiHook->pClientInfo->fsHooks = ptiHook->fsHooks;
1579 ptiHook->pClientInfo->phkCurrent = NULL;
1580 }
1581 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1582 {
1583 ERR("Problem writing to Local ClientInfo!\n");
1584 }
1585 _SEH2_END;
1586 }
1587 else
1588 {
1589 KeAttachProcess(&ptiHook->ppi->peProcess->Pcb);
1590 _SEH2_TRY
1591 {
1592 ptiHook->pClientInfo->fsHooks = ptiHook->fsHooks;
1593 ptiHook->pClientInfo->phkCurrent = NULL;
1594 }
1595 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1596 {
1597 ERR("Problem writing to Remote ClientInfo!\n");
1598 }
1599 _SEH2_END;
1600 KeDetachProcess();
1601 }
1602 }
1603 }
1604 else
1605 {
1606 InsertHeadList(&ptiHook->rpdesk->pDeskInfo->aphkStart[HOOKID_TO_INDEX(HookId)], &Hook->Chain);
1607 Hook->ptiHooked = NULL;
1608 //gptiCurrent->pDeskInfo->fsHooks |= HOOKID_TO_FLAG(HookId);
1609 ptiHook->rpdesk->pDeskInfo->fsHooks |= HOOKID_TO_FLAG(HookId);
1610 ptiHook->sphkCurrent = NULL;
1611 ptiHook->pClientInfo->phkCurrent = NULL;
1612 }
1613
1614 RtlInitUnicodeString(&Hook->ModuleName, NULL);
1615
1616 if (Mod)
1617 {
1618 Status = MmCopyFromCaller(&ModuleName,
1619 UnsafeModuleName,
1620 sizeof(UNICODE_STRING));
1621 if (!NT_SUCCESS(Status))
1622 {
1623 IntRemoveHook(Hook);
1624 SetLastNtError(Status);
1625 RETURN( NULL);
1626 }
1627
1628 Hook->ModuleName.Buffer = ExAllocatePoolWithTag( PagedPool,
1629 ModuleName.MaximumLength,
1630 TAG_HOOK);
1631 if (NULL == Hook->ModuleName.Buffer)
1632 {
1633 IntRemoveHook(Hook);
1634 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
1635 RETURN( NULL);
1636 }
1637
1638 Hook->ModuleName.MaximumLength = ModuleName.MaximumLength;
1639 Status = MmCopyFromCaller( Hook->ModuleName.Buffer,
1640 ModuleName.Buffer,
1641 ModuleName.MaximumLength);
1642 if (!NT_SUCCESS(Status))
1643 {
1644 ExFreePoolWithTag(Hook->ModuleName.Buffer, TAG_HOOK);
1645 Hook->ModuleName.Buffer = NULL;
1646 IntRemoveHook(Hook);
1647 SetLastNtError(Status);
1648 RETURN( NULL);
1649 }
1650
1651 Hook->ModuleName.Length = ModuleName.Length;
1652 //// FIXME: Need to load from user32 to verify hMod before calling hook with hMod set!!!!
1653 //// Mod + offPfn == new HookProc Justin Case module is from another process.
1654 FIXME("NtUserSetWindowsHookEx Setting process hMod instance addressing.\n");
1655 /* Make proc relative to the module base */
1656 Hook->offPfn = (ULONG_PTR)((char *)HookProc - (char *)Mod);
1657 }
1658 else
1659 Hook->offPfn = 0;
1660
1661 TRACE("Installing: HookId %d Global %s\n", HookId, !ThreadId ? "TRUE" : "FALSE");
1662 RETURN( Handle);
1663
1664 CLEANUP:
1665 if (Hook)
1666 UserDereferenceObject(Hook);
1667 TRACE("Leave NtUserSetWindowsHookEx, ret=%p\n", _ret_);
1668 UserLeave();
1669 END_CLEANUP;
1670 }
1671
1672 BOOL
1673 APIENTRY
1674 NtUserUnhookWindowsHookEx(HHOOK Hook)
1675 {
1676 PHOOK HookObj;
1677 DECLARE_RETURN(BOOL);
1678
1679 TRACE("Enter NtUserUnhookWindowsHookEx\n");
1680 UserEnterExclusive();
1681
1682 if (!(HookObj = IntGetHookObject(Hook)))
1683 {
1684 ERR("Invalid handle passed to NtUserUnhookWindowsHookEx\n");
1685 /* SetLastNtError(Status); */
1686 RETURN( FALSE);
1687 }
1688
1689 ASSERT(Hook == UserHMGetHandle(HookObj));
1690
1691 IntRemoveHook(HookObj);
1692
1693 UserDereferenceObject(HookObj);
1694
1695 RETURN( TRUE);
1696
1697 CLEANUP:
1698 TRACE("Leave NtUserUnhookWindowsHookEx, ret=%i\n",_ret_);
1699 UserLeave();
1700 END_CLEANUP;
1701 }
1702
1703 BOOL
1704 APIENTRY
1705 NtUserRegisterUserApiHook(
1706 PUNICODE_STRING m_dllname1,
1707 PUNICODE_STRING m_funname1,
1708 DWORD dwUnknown3,
1709 DWORD dwUnknown4)
1710 {
1711 BOOL ret;
1712 UNICODE_STRING strDllNameSafe;
1713 UNICODE_STRING strFuncNameSafe;
1714 NTSTATUS Status;
1715
1716 /* Probe and capture parameters */
1717 Status = ProbeAndCaptureUnicodeString(&strDllNameSafe, UserMode, m_dllname1);
1718 if(!NT_SUCCESS(Status))
1719 {
1720 EngSetLastError(RtlNtStatusToDosError(Status));
1721 return FALSE;
1722 }
1723
1724 Status = ProbeAndCaptureUnicodeString(&strFuncNameSafe, UserMode, m_funname1);
1725 if(!NT_SUCCESS(Status))
1726 {
1727 ReleaseCapturedUnicodeString(&strDllNameSafe, UserMode);
1728 EngSetLastError(RtlNtStatusToDosError(Status));
1729 return FALSE;
1730 }
1731
1732 UserEnterExclusive();
1733
1734 /* Call internal function */
1735 ret = UserRegisterUserApiHook(&strDllNameSafe, &strFuncNameSafe);
1736
1737 UserLeave();
1738
1739 /* Cleanup only in case of failure */
1740 if(ret == FALSE)
1741 {
1742 ReleaseCapturedUnicodeString(&strDllNameSafe, UserMode);
1743 ReleaseCapturedUnicodeString(&strFuncNameSafe, UserMode);
1744 }
1745
1746 return ret;
1747 }
1748
1749 BOOL
1750 APIENTRY
1751 NtUserUnregisterUserApiHook(VOID)
1752 {
1753 BOOL ret;
1754
1755 UserEnterExclusive();
1756 ret = UserUnregisterUserApiHook();
1757 UserLeave();
1758
1759 return ret;
1760 }
1761
1762 /* EOF */