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