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