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