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