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