735578d0eafc00a92c9b381cc551f0dc60debff7
[reactos.git] / 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: HACK: 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 = 0;
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 ptiHook, pti;
1041 PDESKTOP pdo;
1042 PHOOK Hook = Object;
1043 BOOL bOtherProcess;
1044
1045 NT_ASSERT(UserIsEnteredExclusive());
1046
1047 HookId = Hook->HookId;
1048 pti = PsGetCurrentThreadWin32Thread();
1049
1050 if (Hook->ptiHooked) // Local
1051 {
1052 ptiHook = Hook->ptiHooked;
1053
1054 IntFreeHook(Hook);
1055
1056 if (IsListEmpty(&ptiHook->aphkStart[HOOKID_TO_INDEX(HookId)]))
1057 {
1058 ptiHook->fsHooks &= ~HOOKID_TO_FLAG(HookId);
1059 bOtherProcess = (ptiHook->ppi != pti->ppi);
1060
1061 if (bOtherProcess)
1062 KeAttachProcess(&ptiHook->ppi->peProcess->Pcb);
1063
1064 _SEH2_TRY
1065 {
1066 ptiHook->pClientInfo->fsHooks = ptiHook->fsHooks;
1067 }
1068 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1069 {
1070 /* Do nothing */
1071 (void)0;
1072 }
1073 _SEH2_END;
1074
1075 if (bOtherProcess)
1076 KeDetachProcess();
1077 }
1078 }
1079 else // Global
1080 {
1081 IntFreeHook(Hook);
1082
1083 pdo = IntGetActiveDesktop();
1084
1085 if (pdo &&
1086 pdo->pDeskInfo &&
1087 IsListEmpty(&pdo->pDeskInfo->aphkStart[HOOKID_TO_INDEX(HookId)]))
1088 {
1089 pdo->pDeskInfo->fsHooks &= ~HOOKID_TO_FLAG(HookId);
1090 }
1091 }
1092
1093 return TRUE;
1094 }
1095
1096 /*
1097 Win32k Kernel Space Hook Caller.
1098 */
1099 LRESULT
1100 APIENTRY
1101 co_HOOK_CallHooks( INT HookId,
1102 INT Code,
1103 WPARAM wParam,
1104 LPARAM lParam)
1105 {
1106 PHOOK Hook, SaveHook;
1107 PTHREADINFO pti;
1108 PCLIENTINFO ClientInfo;
1109 PLIST_ENTRY pLastHead;
1110 PDESKTOP pdo;
1111 BOOL Local = FALSE, Global = FALSE;
1112 LRESULT Result = 0;
1113 USER_REFERENCE_ENTRY Ref;
1114
1115 ASSERT(WH_MINHOOK <= HookId && HookId <= WH_MAXHOOK);
1116
1117 pti = PsGetCurrentThreadWin32Thread();
1118 if (!pti || !pti->rpdesk || !pti->rpdesk->pDeskInfo)
1119 {
1120 pdo = IntGetActiveDesktop();
1121 /* If KeyboardThread|MouseThread|(RawInputThread or RIT) aka system threads,
1122 pti->fsHooks most likely, is zero. So process KbT & MsT to "send" the message.
1123 */
1124 if ( !pti || !pdo || (!(HookId == WH_KEYBOARD_LL) && !(HookId == WH_MOUSE_LL)) )
1125 {
1126 TRACE("No PDO %d\n", HookId);
1127 goto Exit;
1128 }
1129 }
1130 else
1131 {
1132 pdo = pti->rpdesk;
1133 }
1134
1135 if ( pti->TIF_flags & (TIF_INCLEANUP|TIF_DISABLEHOOKS))
1136 {
1137 TRACE("Hook Thread dead %d\n", HookId);
1138 goto Exit;
1139 }
1140
1141 if ( ISITHOOKED(HookId) )
1142 {
1143 TRACE("Local Hooker %d\n", HookId);
1144 Local = TRUE;
1145 }
1146
1147 if ( pdo->pDeskInfo->fsHooks & HOOKID_TO_FLAG(HookId) )
1148 {
1149 TRACE("Global Hooker %d\n", HookId);
1150 Global = TRUE;
1151 }
1152
1153 if ( !Local && !Global ) goto Exit; // No work!
1154
1155 Hook = NULL;
1156
1157 /* SetWindowHookEx sorts out the Thread issue by placing the Hook to
1158 the correct Thread if not NULL.
1159 */
1160 if ( Local )
1161 {
1162 pLastHead = &pti->aphkStart[HOOKID_TO_INDEX(HookId)];
1163 if (IsListEmpty(pLastHead))
1164 {
1165 ERR("No Local Hook Found!\n");
1166 goto Exit;
1167 }
1168
1169 Hook = CONTAINING_RECORD(pLastHead->Flink, HOOK, Chain);
1170 ObReferenceObject(pti->pEThread);
1171 IntReferenceThreadInfo(pti);
1172 UserRefObjectCo(Hook, &Ref);
1173
1174 ClientInfo = pti->pClientInfo;
1175 SaveHook = pti->sphkCurrent;
1176 /* Note: Setting pti->sphkCurrent will also lock the next hook to this
1177 * hook ID. So, the CallNextHookEx will only call to that hook ID
1178 * chain anyway. For Thread Hooks....
1179 */
1180
1181 /* Load it for the next call. */
1182 pti->sphkCurrent = Hook;
1183 Hook->phkNext = IntGetNextHook(Hook);
1184 if (ClientInfo)
1185 {
1186 _SEH2_TRY
1187 {
1188 ClientInfo->phkCurrent = Hook;
1189 }
1190 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1191 {
1192 ClientInfo = NULL; // Don't bother next run.
1193 }
1194 _SEH2_END;
1195 }
1196 Result = co_IntCallHookProc( HookId,
1197 Code,
1198 wParam,
1199 lParam,
1200 Hook->Proc,
1201 Hook->ihmod,
1202 Hook->offPfn,
1203 Hook->Ansi,
1204 &Hook->ModuleName);
1205 if (ClientInfo)
1206 {
1207 _SEH2_TRY
1208 {
1209 ClientInfo->phkCurrent = SaveHook;
1210 }
1211 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1212 {
1213 /* Do nothing */
1214 (void)0;
1215 }
1216 _SEH2_END;
1217 }
1218 pti->sphkCurrent = SaveHook;
1219 Hook->phkNext = NULL;
1220 UserDerefObjectCo(Hook);
1221 IntDereferenceThreadInfo(pti);
1222 ObDereferenceObject(pti->pEThread);
1223 }
1224
1225 if ( Global )
1226 {
1227 PTHREADINFO ptiHook;
1228 HHOOK *pHookHandles;
1229 unsigned i;
1230
1231 /* Keep hooks in array because hooks can be destroyed in user world */
1232 pHookHandles = IntGetGlobalHookHandles(pdo, HookId);
1233 if(!pHookHandles)
1234 goto Exit;
1235
1236 /* Performance goes down the drain. If more hooks are associated to this
1237 * hook ID, this will have to post to each of the thread message queues
1238 * or make a direct call.
1239 */
1240 for(i = 0; pHookHandles[i]; ++i)
1241 {
1242 Hook = (PHOOK)UserGetObject(gHandleTable, pHookHandles[i], TYPE_HOOK);
1243 if(!Hook)
1244 {
1245 ERR("Invalid hook!\n");
1246 continue;
1247 }
1248
1249 /* Hook->Thread is null, we hax around this with Hook->head.pti. */
1250 ptiHook = Hook->head.pti;
1251
1252 if ( (pti->TIF_flags & TIF_DISABLEHOOKS) || (ptiHook->TIF_flags & TIF_INCLEANUP))
1253 {
1254 TRACE("Next Hook %p, %p\n", ptiHook->rpdesk, pdo);
1255 continue;
1256 }
1257 UserRefObjectCo(Hook, &Ref);
1258
1259 if (ptiHook != pti )
1260 {
1261 // Block | TimeOut
1262 if ( HookId == WH_JOURNALPLAYBACK || // 1 | 0
1263 HookId == WH_JOURNALRECORD || // 1 | 0
1264 HookId == WH_KEYBOARD || // 1 | 200
1265 HookId == WH_MOUSE || // 1 | 200
1266 HookId == WH_KEYBOARD_LL || // 0 | 300
1267 HookId == WH_MOUSE_LL ) // 0 | 300
1268 {
1269 TRACE("\nGlobal Hook posting to another Thread! %d\n",HookId );
1270 Result = co_IntCallLowLevelHook(Hook, Code, wParam, lParam);
1271 }
1272 else if (ptiHook->ppi == pti->ppi)
1273 {
1274 TRACE("\nGlobal Hook calling to another Thread! %d\n",HookId );
1275 ObReferenceObject(ptiHook->pEThread);
1276 IntReferenceThreadInfo(ptiHook);
1277 Result = co_IntCallHookProc( HookId,
1278 Code,
1279 wParam,
1280 lParam,
1281 Hook->Proc,
1282 Hook->ihmod,
1283 Hook->offPfn,
1284 Hook->Ansi,
1285 &Hook->ModuleName);
1286 IntDereferenceThreadInfo(ptiHook);
1287 ObDereferenceObject(ptiHook->pEThread);
1288 }
1289 }
1290 else
1291 { /* Make the direct call. */
1292 TRACE("Global going Local Hook calling to Thread! %d\n",HookId );
1293 ObReferenceObject(pti->pEThread);
1294 IntReferenceThreadInfo(pti);
1295 Result = co_IntCallHookProc( HookId,
1296 Code,
1297 wParam,
1298 lParam,
1299 Hook->Proc,
1300 Hook->ihmod,
1301 Hook->offPfn,
1302 Hook->Ansi,
1303 &Hook->ModuleName);
1304 IntDereferenceThreadInfo(pti);
1305 ObDereferenceObject(pti->pEThread);
1306 }
1307 UserDerefObjectCo(Hook);
1308 }
1309 ExFreePoolWithTag(pHookHandles, TAG_HOOK);
1310 TRACE("Ret: Global HookId %d Result 0x%x\n", HookId,Result);
1311 }
1312 Exit:
1313 return Result;
1314 }
1315
1316 BOOL
1317 FASTCALL
1318 IntUnhookWindowsHook(int HookId, HOOKPROC pfnFilterProc)
1319 {
1320 PHOOK Hook;
1321 PLIST_ENTRY pLastHead, pElement;
1322 PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
1323
1324 if (HookId < WH_MINHOOK || WH_MAXHOOK < HookId )
1325 {
1326 EngSetLastError(ERROR_INVALID_HOOK_FILTER);
1327 return FALSE;
1328 }
1329
1330 if (pti->fsHooks)
1331 {
1332 pLastHead = &pti->aphkStart[HOOKID_TO_INDEX(HookId)];
1333
1334 pElement = pLastHead->Flink;
1335 while (pElement != pLastHead)
1336 {
1337 Hook = CONTAINING_RECORD(pElement, HOOK, Chain);
1338
1339 /* Get the next element now, we might free the hook in what follows */
1340 pElement = Hook->Chain.Flink;
1341
1342 if (Hook->Proc == pfnFilterProc)
1343 {
1344 if (Hook->head.pti == pti)
1345 {
1346 IntRemoveHook(Hook);
1347 return TRUE;
1348 }
1349 else
1350 {
1351 EngSetLastError(ERROR_ACCESS_DENIED);
1352 return FALSE;
1353 }
1354 }
1355 }
1356 }
1357 return FALSE;
1358 }
1359
1360 /*
1361 * Support for compatibility only? Global hooks are processed in kernel space.
1362 * This is very thread specific! Never seeing applications with more than one
1363 * hook per thread installed. Most of the applications are Global hookers and
1364 * associated with just one hook Id. Maybe it's for diagnostic testing or a
1365 * throw back to 3.11?
1366 */
1367 LRESULT
1368 APIENTRY
1369 NtUserCallNextHookEx( int Code,
1370 WPARAM wParam,
1371 LPARAM lParam,
1372 BOOL Ansi)
1373 {
1374 PTHREADINFO pti;
1375 PHOOK HookObj, NextObj;
1376 PCLIENTINFO ClientInfo;
1377 LRESULT lResult = 0;
1378 DECLARE_RETURN(LRESULT);
1379
1380 TRACE("Enter NtUserCallNextHookEx\n");
1381 UserEnterExclusive();
1382
1383 pti = GetW32ThreadInfo();
1384
1385 HookObj = pti->sphkCurrent;
1386
1387 if (!HookObj) RETURN( 0);
1388
1389 NextObj = HookObj->phkNext;
1390
1391 pti->sphkCurrent = NextObj;
1392 ClientInfo = pti->pClientInfo;
1393 _SEH2_TRY
1394 {
1395 ClientInfo->phkCurrent = NextObj;
1396 }
1397 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1398 {
1399 ClientInfo = NULL;
1400 }
1401 _SEH2_END;
1402
1403 /* Now in List run down. */
1404 if (ClientInfo && NextObj)
1405 {
1406 NextObj->phkNext = IntGetNextHook(NextObj);
1407 lResult = co_UserCallNextHookEx( NextObj, Code, wParam, lParam, NextObj->Ansi);
1408 }
1409 RETURN( lResult);
1410
1411 CLEANUP:
1412 TRACE("Leave NtUserCallNextHookEx, ret=%i\n",_ret_);
1413 UserLeave();
1414 END_CLEANUP;
1415 }
1416
1417 HHOOK
1418 APIENTRY
1419 NtUserSetWindowsHookAW( int idHook,
1420 HOOKPROC lpfn,
1421 BOOL Ansi)
1422 {
1423 DWORD ThreadId;
1424 UNICODE_STRING USModuleName;
1425
1426 RtlInitUnicodeString(&USModuleName, NULL);
1427 ThreadId = PtrToUint(NtCurrentTeb()->ClientId.UniqueThread);
1428
1429 return NtUserSetWindowsHookEx( NULL,
1430 &USModuleName,
1431 ThreadId,
1432 idHook,
1433 lpfn,
1434 Ansi);
1435 }
1436
1437 HHOOK
1438 APIENTRY
1439 NtUserSetWindowsHookEx( HINSTANCE Mod,
1440 PUNICODE_STRING UnsafeModuleName,
1441 DWORD ThreadId,
1442 int HookId,
1443 HOOKPROC HookProc,
1444 BOOL Ansi)
1445 {
1446 PWINSTATION_OBJECT WinStaObj;
1447 PHOOK Hook = NULL;
1448 UNICODE_STRING ModuleName;
1449 NTSTATUS Status;
1450 HHOOK Handle;
1451 PTHREADINFO pti, ptiHook = NULL;
1452 DECLARE_RETURN(HHOOK);
1453
1454 TRACE("Enter NtUserSetWindowsHookEx\n");
1455 UserEnterExclusive();
1456
1457 pti = PsGetCurrentThreadWin32Thread();
1458
1459 if (HookId < WH_MINHOOK || WH_MAXHOOK < HookId )
1460 {
1461 EngSetLastError(ERROR_INVALID_HOOK_FILTER);
1462 RETURN( NULL);
1463 }
1464
1465 if (!HookProc)
1466 {
1467 EngSetLastError(ERROR_INVALID_FILTER_PROC);
1468 RETURN( NULL);
1469 }
1470
1471 if (ThreadId) /* thread-local hook */
1472 {
1473 if ( HookId == WH_JOURNALRECORD ||
1474 HookId == WH_JOURNALPLAYBACK ||
1475 HookId == WH_KEYBOARD_LL ||
1476 HookId == WH_MOUSE_LL ||
1477 HookId == WH_SYSMSGFILTER)
1478 {
1479 TRACE("Local hook installing Global HookId: %d\n",HookId);
1480 /* these can only be global */
1481 EngSetLastError(ERROR_GLOBAL_ONLY_HOOK);
1482 RETURN( NULL);
1483 }
1484
1485 if ( !(ptiHook = IntTID2PTI( UlongToHandle(ThreadId) )))
1486 {
1487 ERR("Invalid thread id 0x%x\n", ThreadId);
1488 EngSetLastError(ERROR_INVALID_PARAMETER);
1489 RETURN( NULL);
1490 }
1491
1492 if ( ptiHook->rpdesk != pti->rpdesk) // gptiCurrent->rpdesk)
1493 {
1494 ERR("Local hook wrong desktop HookId: %d\n",HookId);
1495 EngSetLastError(ERROR_ACCESS_DENIED);
1496 RETURN( NULL);
1497 }
1498
1499 if (ptiHook->ppi != pti->ppi)
1500 {
1501 if ( !Mod &&
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 ERR("Local hook needs hMod HookId: %d\n",HookId);
1512 EngSetLastError(ERROR_HOOK_NEEDS_HMOD);
1513 RETURN( NULL);
1514 }
1515
1516 if ( (ptiHook->TIF_flags & (TIF_CSRSSTHREAD|TIF_SYSTEMTHREAD)) &&
1517 (HookId == WH_GETMESSAGE ||
1518 HookId == WH_CALLWNDPROC ||
1519 HookId == WH_CBT ||
1520 HookId == WH_HARDWARE ||
1521 HookId == WH_DEBUG ||
1522 HookId == WH_SHELL ||
1523 HookId == WH_FOREGROUNDIDLE ||
1524 HookId == WH_CALLWNDPROCRET) )
1525 {
1526 EngSetLastError(ERROR_HOOK_TYPE_NOT_ALLOWED);
1527 RETURN( NULL);
1528 }
1529 }
1530 }
1531 else /* System-global hook */
1532 {
1533 ptiHook = pti; // gptiCurrent;
1534 if ( !Mod &&
1535 (HookId == WH_GETMESSAGE ||
1536 HookId == WH_CALLWNDPROC ||
1537 HookId == WH_CBT ||
1538 HookId == WH_SYSMSGFILTER ||
1539 HookId == WH_HARDWARE ||
1540 HookId == WH_DEBUG ||
1541 HookId == WH_SHELL ||
1542 HookId == WH_FOREGROUNDIDLE ||
1543 HookId == WH_CALLWNDPROCRET) )
1544 {
1545 ERR("Global hook needs hMod HookId: %d\n",HookId);
1546 EngSetLastError(ERROR_HOOK_NEEDS_HMOD);
1547 RETURN( NULL);
1548 }
1549 }
1550
1551 Status = IntValidateWindowStationHandle( PsGetCurrentProcess()->Win32WindowStation,
1552 UserMode,
1553 0,
1554 &WinStaObj,
1555 0);
1556
1557 if (!NT_SUCCESS(Status))
1558 {
1559 SetLastNtError(Status);
1560 RETURN( NULL);
1561 }
1562 ObDereferenceObject(WinStaObj);
1563
1564 Hook = UserCreateObject(gHandleTable, NULL, ptiHook, (PHANDLE)&Handle, TYPE_HOOK, sizeof(HOOK));
1565
1566 if (!Hook)
1567 {
1568 RETURN( NULL);
1569 }
1570
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;
1576 Hook->Ansi = Ansi;
1577
1578 TRACE("Set Hook Desk %p DeskInfo %p Handle Desk %p\n", pti->rpdesk, pti->pDeskInfo, Hook->head.rpdesk);
1579
1580 if (ThreadId) /* Thread-local hook */
1581 {
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);
1586
1587 if (ptiHook->pClientInfo)
1588 {
1589 if ( ptiHook->ppi == pti->ppi) /* gptiCurrent->ppi) */
1590 {
1591 _SEH2_TRY
1592 {
1593 ptiHook->pClientInfo->fsHooks = ptiHook->fsHooks;
1594 ptiHook->pClientInfo->phkCurrent = NULL;
1595 }
1596 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1597 {
1598 ERR("Problem writing to Local ClientInfo!\n");
1599 }
1600 _SEH2_END;
1601 }
1602 else
1603 {
1604 KeAttachProcess(&ptiHook->ppi->peProcess->Pcb);
1605 _SEH2_TRY
1606 {
1607 ptiHook->pClientInfo->fsHooks = ptiHook->fsHooks;
1608 ptiHook->pClientInfo->phkCurrent = NULL;
1609 }
1610 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1611 {
1612 ERR("Problem writing to Remote ClientInfo!\n");
1613 }
1614 _SEH2_END;
1615 KeDetachProcess();
1616 }
1617 }
1618 }
1619 else
1620 {
1621 InsertHeadList(&ptiHook->rpdesk->pDeskInfo->aphkStart[HOOKID_TO_INDEX(HookId)], &Hook->Chain);
1622 Hook->ptiHooked = NULL;
1623 //gptiCurrent->pDeskInfo->fsHooks |= HOOKID_TO_FLAG(HookId);
1624 ptiHook->rpdesk->pDeskInfo->fsHooks |= HOOKID_TO_FLAG(HookId);
1625 ptiHook->sphkCurrent = NULL;
1626 ptiHook->pClientInfo->phkCurrent = NULL;
1627 }
1628
1629 RtlInitUnicodeString(&Hook->ModuleName, NULL);
1630
1631 if (Mod)
1632 {
1633 Status = MmCopyFromCaller(&ModuleName,
1634 UnsafeModuleName,
1635 sizeof(UNICODE_STRING));
1636 if (!NT_SUCCESS(Status))
1637 {
1638 IntRemoveHook(Hook);
1639 SetLastNtError(Status);
1640 RETURN( NULL);
1641 }
1642
1643 Hook->ModuleName.Buffer = ExAllocatePoolWithTag( PagedPool,
1644 ModuleName.MaximumLength,
1645 TAG_HOOK);
1646 if (NULL == Hook->ModuleName.Buffer)
1647 {
1648 IntRemoveHook(Hook);
1649 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
1650 RETURN( NULL);
1651 }
1652
1653 Hook->ModuleName.MaximumLength = ModuleName.MaximumLength;
1654 Status = MmCopyFromCaller( Hook->ModuleName.Buffer,
1655 ModuleName.Buffer,
1656 ModuleName.MaximumLength);
1657 if (!NT_SUCCESS(Status))
1658 {
1659 ExFreePoolWithTag(Hook->ModuleName.Buffer, TAG_HOOK);
1660 Hook->ModuleName.Buffer = NULL;
1661 IntRemoveHook(Hook);
1662 SetLastNtError(Status);
1663 RETURN( NULL);
1664 }
1665
1666 Hook->ModuleName.Length = ModuleName.Length;
1667 //// FIXME: Need to load from user32 to verify hMod before calling hook with hMod set!!!!
1668 //// Mod + offPfn == new HookProc Justin Case module is from another process.
1669 FIXME("NtUserSetWindowsHookEx Setting process hMod instance addressing.\n");
1670 /* Make proc relative to the module base */
1671 Hook->offPfn = (ULONG_PTR)((char *)HookProc - (char *)Mod);
1672 }
1673 else
1674 Hook->offPfn = 0;
1675
1676 TRACE("Installing: HookId %d Global %s\n", HookId, !ThreadId ? "TRUE" : "FALSE");
1677 RETURN( Handle);
1678
1679 CLEANUP:
1680 if (Hook)
1681 UserDereferenceObject(Hook);
1682 TRACE("Leave NtUserSetWindowsHookEx, ret=%p\n", _ret_);
1683 UserLeave();
1684 END_CLEANUP;
1685 }
1686
1687 BOOL
1688 APIENTRY
1689 NtUserUnhookWindowsHookEx(HHOOK Hook)
1690 {
1691 PHOOK HookObj;
1692 DECLARE_RETURN(BOOL);
1693
1694 TRACE("Enter NtUserUnhookWindowsHookEx\n");
1695 UserEnterExclusive();
1696
1697 if (!(HookObj = IntGetHookObject(Hook)))
1698 {
1699 ERR("Invalid handle passed to NtUserUnhookWindowsHookEx\n");
1700 /* SetLastNtError(Status); */
1701 RETURN( FALSE);
1702 }
1703
1704 ASSERT(Hook == UserHMGetHandle(HookObj));
1705
1706 IntRemoveHook(HookObj);
1707
1708 UserDereferenceObject(HookObj);
1709
1710 RETURN( TRUE);
1711
1712 CLEANUP:
1713 TRACE("Leave NtUserUnhookWindowsHookEx, ret=%i\n",_ret_);
1714 UserLeave();
1715 END_CLEANUP;
1716 }
1717
1718 BOOL
1719 APIENTRY
1720 NtUserRegisterUserApiHook(
1721 PUNICODE_STRING m_dllname1,
1722 PUNICODE_STRING m_funname1,
1723 DWORD dwUnknown3,
1724 DWORD dwUnknown4)
1725 {
1726 BOOL ret;
1727 UNICODE_STRING strDllNameSafe;
1728 UNICODE_STRING strFuncNameSafe;
1729 NTSTATUS Status;
1730
1731 /* Probe and capture parameters */
1732 Status = ProbeAndCaptureUnicodeString(&strDllNameSafe, UserMode, m_dllname1);
1733 if(!NT_SUCCESS(Status))
1734 {
1735 EngSetLastError(RtlNtStatusToDosError(Status));
1736 return FALSE;
1737 }
1738
1739 Status = ProbeAndCaptureUnicodeString(&strFuncNameSafe, UserMode, m_funname1);
1740 if(!NT_SUCCESS(Status))
1741 {
1742 ReleaseCapturedUnicodeString(&strDllNameSafe, UserMode);
1743 EngSetLastError(RtlNtStatusToDosError(Status));
1744 return FALSE;
1745 }
1746
1747 UserEnterExclusive();
1748
1749 /* Call internal function */
1750 ret = UserRegisterUserApiHook(&strDllNameSafe, &strFuncNameSafe);
1751
1752 UserLeave();
1753
1754 /* Cleanup only in case of failure */
1755 if(ret == FALSE)
1756 {
1757 ReleaseCapturedUnicodeString(&strDllNameSafe, UserMode);
1758 ReleaseCapturedUnicodeString(&strFuncNameSafe, UserMode);
1759 }
1760
1761 return ret;
1762 }
1763
1764 BOOL
1765 APIENTRY
1766 NtUserUnregisterUserApiHook(VOID)
1767 {
1768 BOOL ret;
1769
1770 UserEnterExclusive();
1771 ret = UserUnregisterUserApiHook();
1772 UserLeave();
1773
1774 return ret;
1775 }
1776
1777 /* EOF */