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