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