xref: /reactos/win32ss/user/ntuser/hook.c (revision ccef43f3)
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 appliaction 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++] = pHook->head.h;
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     DECLARE_RETURN(LRESULT);
1380 
1381     TRACE("Enter NtUserCallNextHookEx\n");
1382     UserEnterExclusive();
1383 
1384     pti = GetW32ThreadInfo();
1385 
1386     HookObj = pti->sphkCurrent;
1387 
1388     if (!HookObj) 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     RETURN( lResult);
1411 
1412 CLEANUP:
1413     TRACE("Leave NtUserCallNextHookEx, ret=%i\n",_ret_);
1414     UserLeave();
1415     END_CLEANUP;
1416 }
1417 
1418 HHOOK
1419 APIENTRY
1420 NtUserSetWindowsHookAW( int idHook,
1421                         HOOKPROC lpfn,
1422                         BOOL Ansi)
1423 {
1424     DWORD ThreadId;
1425     UNICODE_STRING USModuleName;
1426 
1427     RtlInitUnicodeString(&USModuleName, NULL);
1428     ThreadId = PtrToUint(NtCurrentTeb()->ClientId.UniqueThread);
1429 
1430     return NtUserSetWindowsHookEx( NULL,
1431                                   &USModuleName,
1432                                    ThreadId,
1433                                    idHook,
1434                                    lpfn,
1435                                    Ansi);
1436 }
1437 
1438 HHOOK
1439 APIENTRY
1440 NtUserSetWindowsHookEx( HINSTANCE Mod,
1441                         PUNICODE_STRING UnsafeModuleName,
1442                         DWORD ThreadId,
1443                         int HookId,
1444                         HOOKPROC HookProc,
1445                         BOOL Ansi)
1446 {
1447     PWINSTATION_OBJECT WinStaObj;
1448     PHOOK Hook = NULL;
1449     UNICODE_STRING ModuleName;
1450     NTSTATUS Status;
1451     HHOOK Handle;
1452     PTHREADINFO pti, ptiHook = NULL;
1453     DECLARE_RETURN(HHOOK);
1454 
1455     TRACE("Enter NtUserSetWindowsHookEx\n");
1456     UserEnterExclusive();
1457 
1458     pti = PsGetCurrentThreadWin32Thread();
1459 
1460     if (HookId < WH_MINHOOK || WH_MAXHOOK < HookId )
1461     {
1462         EngSetLastError(ERROR_INVALID_HOOK_FILTER);
1463         RETURN( NULL);
1464     }
1465 
1466     if (!HookProc)
1467     {
1468         EngSetLastError(ERROR_INVALID_FILTER_PROC);
1469         RETURN( NULL);
1470     }
1471 
1472     if (ThreadId)  /* thread-local hook */
1473     {
1474        if ( HookId == WH_JOURNALRECORD ||
1475             HookId == WH_JOURNALPLAYBACK ||
1476             HookId == WH_KEYBOARD_LL ||
1477             HookId == WH_MOUSE_LL ||
1478             HookId == WH_SYSMSGFILTER)
1479        {
1480            TRACE("Local hook installing Global HookId: %d\n",HookId);
1481            /* these can only be global */
1482            EngSetLastError(ERROR_GLOBAL_ONLY_HOOK);
1483            RETURN( NULL);
1484        }
1485 
1486        if ( !(ptiHook = IntTID2PTI( UlongToHandle(ThreadId) )))
1487        {
1488           ERR("Invalid thread id 0x%x\n", ThreadId);
1489           EngSetLastError(ERROR_INVALID_PARAMETER);
1490           RETURN( NULL);
1491        }
1492 
1493        if ( ptiHook->rpdesk != pti->rpdesk) // gptiCurrent->rpdesk)
1494        {
1495           ERR("Local hook wrong desktop HookId: %d\n",HookId);
1496           EngSetLastError(ERROR_ACCESS_DENIED);
1497           RETURN( NULL);
1498        }
1499 
1500        if (ptiHook->ppi != pti->ppi)
1501        {
1502           if ( !Mod &&
1503               (HookId == WH_GETMESSAGE ||
1504                HookId == WH_CALLWNDPROC ||
1505                HookId == WH_CBT ||
1506                HookId == WH_HARDWARE ||
1507                HookId == WH_DEBUG ||
1508                HookId == WH_SHELL ||
1509                HookId == WH_FOREGROUNDIDLE ||
1510                HookId == WH_CALLWNDPROCRET) )
1511           {
1512              ERR("Local hook needs hMod HookId: %d\n",HookId);
1513              EngSetLastError(ERROR_HOOK_NEEDS_HMOD);
1514              RETURN( NULL);
1515           }
1516 
1517           if ( (ptiHook->TIF_flags & (TIF_CSRSSTHREAD|TIF_SYSTEMTHREAD)) &&
1518                (HookId == WH_GETMESSAGE ||
1519                 HookId == WH_CALLWNDPROC ||
1520                 HookId == WH_CBT ||
1521                 HookId == WH_HARDWARE ||
1522                 HookId == WH_DEBUG ||
1523                 HookId == WH_SHELL ||
1524                 HookId == WH_FOREGROUNDIDLE ||
1525                 HookId == WH_CALLWNDPROCRET) )
1526           {
1527              EngSetLastError(ERROR_HOOK_TYPE_NOT_ALLOWED);
1528              RETURN( NULL);
1529           }
1530        }
1531     }
1532     else  /* System-global hook */
1533     {
1534        ptiHook = pti; // gptiCurrent;
1535        if ( !Mod &&
1536             (HookId == WH_GETMESSAGE ||
1537              HookId == WH_CALLWNDPROC ||
1538              HookId == WH_CBT ||
1539              HookId == WH_SYSMSGFILTER ||
1540              HookId == WH_HARDWARE ||
1541              HookId == WH_DEBUG ||
1542              HookId == WH_SHELL ||
1543              HookId == WH_FOREGROUNDIDLE ||
1544              HookId == WH_CALLWNDPROCRET) )
1545        {
1546           ERR("Global hook needs hMod HookId: %d\n",HookId);
1547           EngSetLastError(ERROR_HOOK_NEEDS_HMOD);
1548           RETURN( NULL);
1549        }
1550     }
1551 
1552     Status = IntValidateWindowStationHandle( PsGetCurrentProcess()->Win32WindowStation,
1553                                              UserMode,
1554                                              0,
1555                                             &WinStaObj,
1556                                              0);
1557 
1558     if (!NT_SUCCESS(Status))
1559     {
1560        SetLastNtError(Status);
1561        RETURN( NULL);
1562     }
1563     ObDereferenceObject(WinStaObj);
1564 
1565     Hook = UserCreateObject(gHandleTable, NULL, ptiHook, (PHANDLE)&Handle, TYPE_HOOK, sizeof(HOOK));
1566 
1567     if (!Hook)
1568     {
1569        RETURN( NULL);
1570     }
1571 
1572     Hook->ihmod   = (INT_PTR)Mod; // Module Index from atom table, Do this for now.
1573     Hook->HookId  = HookId;
1574     Hook->rpdesk  = ptiHook->rpdesk;
1575     Hook->phkNext = NULL; /* Dont use as a chain! Use link lists for chaining. */
1576     Hook->Proc    = HookProc;
1577     Hook->Ansi    = Ansi;
1578 
1579     TRACE("Set Hook Desk %p DeskInfo %p Handle Desk %p\n", pti->rpdesk, pti->pDeskInfo, Hook->head.rpdesk);
1580 
1581     if (ThreadId)  /* Thread-local hook */
1582     {
1583        InsertHeadList(&ptiHook->aphkStart[HOOKID_TO_INDEX(HookId)], &Hook->Chain);
1584        ptiHook->sphkCurrent = NULL;
1585        Hook->ptiHooked = ptiHook;
1586        ptiHook->fsHooks |= HOOKID_TO_FLAG(HookId);
1587 
1588        if (ptiHook->pClientInfo)
1589        {
1590           if ( ptiHook->ppi == pti->ppi) /* gptiCurrent->ppi) */
1591           {
1592              _SEH2_TRY
1593              {
1594                 ptiHook->pClientInfo->fsHooks = ptiHook->fsHooks;
1595                 ptiHook->pClientInfo->phkCurrent = NULL;
1596              }
1597              _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1598              {
1599                 ERR("Problem writing to Local ClientInfo!\n");
1600              }
1601              _SEH2_END;
1602           }
1603           else
1604           {
1605              KAPC_STATE ApcState;
1606 
1607              KeStackAttachProcess(&ptiHook->ppi->peProcess->Pcb, &ApcState);
1608              _SEH2_TRY
1609              {
1610                 ptiHook->pClientInfo->fsHooks = ptiHook->fsHooks;
1611                 ptiHook->pClientInfo->phkCurrent = NULL;
1612              }
1613              _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1614              {
1615                 ERR("Problem writing to Remote ClientInfo!\n");
1616              }
1617              _SEH2_END;
1618              KeUnstackDetachProcess(&ApcState);
1619           }
1620        }
1621     }
1622     else
1623     {
1624        InsertHeadList(&ptiHook->rpdesk->pDeskInfo->aphkStart[HOOKID_TO_INDEX(HookId)], &Hook->Chain);
1625        Hook->ptiHooked = NULL;
1626        //gptiCurrent->pDeskInfo->fsHooks |= HOOKID_TO_FLAG(HookId);
1627        ptiHook->rpdesk->pDeskInfo->fsHooks |= HOOKID_TO_FLAG(HookId);
1628        ptiHook->sphkCurrent = NULL;
1629        ptiHook->pClientInfo->phkCurrent = NULL;
1630     }
1631 
1632     RtlInitUnicodeString(&Hook->ModuleName, NULL);
1633 
1634     if (Mod)
1635     {
1636        Status = MmCopyFromCaller(&ModuleName,
1637                                   UnsafeModuleName,
1638                                   sizeof(UNICODE_STRING));
1639        if (!NT_SUCCESS(Status))
1640        {
1641           IntRemoveHook(Hook);
1642           SetLastNtError(Status);
1643           RETURN( NULL);
1644        }
1645 
1646        Hook->ModuleName.Buffer = ExAllocatePoolWithTag( PagedPool,
1647                                                         ModuleName.MaximumLength,
1648                                                         TAG_HOOK);
1649        if (NULL == Hook->ModuleName.Buffer)
1650        {
1651           IntRemoveHook(Hook);
1652           EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
1653           RETURN( NULL);
1654        }
1655 
1656        Hook->ModuleName.MaximumLength = ModuleName.MaximumLength;
1657        Status = MmCopyFromCaller( Hook->ModuleName.Buffer,
1658                                   ModuleName.Buffer,
1659                                   ModuleName.MaximumLength);
1660        if (!NT_SUCCESS(Status))
1661        {
1662           ExFreePoolWithTag(Hook->ModuleName.Buffer, TAG_HOOK);
1663           Hook->ModuleName.Buffer = NULL;
1664           IntRemoveHook(Hook);
1665           SetLastNtError(Status);
1666           RETURN( NULL);
1667        }
1668 
1669        Hook->ModuleName.Length = ModuleName.Length;
1670        //// FIXME: Need to load from user32 to verify hMod before calling hook with hMod set!!!!
1671        //// Mod + offPfn == new HookProc Justin Case module is from another process.
1672        FIXME("NtUserSetWindowsHookEx Setting process hMod instance addressing.\n");
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     if (Hook)
1684         UserDereferenceObject(Hook);
1685     TRACE("Leave NtUserSetWindowsHookEx, ret=%p\n", _ret_);
1686     UserLeave();
1687     END_CLEANUP;
1688 }
1689 
1690 BOOL
1691 APIENTRY
1692 NtUserUnhookWindowsHookEx(HHOOK Hook)
1693 {
1694     PHOOK HookObj;
1695     DECLARE_RETURN(BOOL);
1696 
1697     TRACE("Enter NtUserUnhookWindowsHookEx\n");
1698     UserEnterExclusive();
1699 
1700     if (!(HookObj = IntGetHookObject(Hook)))
1701     {
1702         ERR("Invalid handle passed to NtUserUnhookWindowsHookEx\n");
1703         /* SetLastNtError(Status); */
1704         RETURN( FALSE);
1705     }
1706 
1707     ASSERT(Hook == UserHMGetHandle(HookObj));
1708 
1709     IntRemoveHook(HookObj);
1710 
1711     UserDereferenceObject(HookObj);
1712 
1713     RETURN( TRUE);
1714 
1715 CLEANUP:
1716     TRACE("Leave NtUserUnhookWindowsHookEx, ret=%i\n",_ret_);
1717     UserLeave();
1718     END_CLEANUP;
1719 }
1720 
1721 BOOL
1722 APIENTRY
1723 NtUserRegisterUserApiHook(
1724     PUNICODE_STRING m_dllname1,
1725     PUNICODE_STRING m_funname1,
1726     DWORD dwUnknown3,
1727     DWORD dwUnknown4)
1728 {
1729     BOOL ret;
1730     UNICODE_STRING strDllNameSafe;
1731     UNICODE_STRING strFuncNameSafe;
1732     NTSTATUS Status;
1733 
1734     /* Probe and capture parameters */
1735     Status = ProbeAndCaptureUnicodeString(&strDllNameSafe, UserMode, m_dllname1);
1736     if(!NT_SUCCESS(Status))
1737     {
1738         EngSetLastError(RtlNtStatusToDosError(Status));
1739         return FALSE;
1740     }
1741 
1742     Status = ProbeAndCaptureUnicodeString(&strFuncNameSafe, UserMode, m_funname1);
1743     if(!NT_SUCCESS(Status))
1744     {
1745         ReleaseCapturedUnicodeString(&strDllNameSafe, UserMode);
1746         EngSetLastError(RtlNtStatusToDosError(Status));
1747         return FALSE;
1748     }
1749 
1750     UserEnterExclusive();
1751 
1752     /* Call internal function */
1753     ret = UserRegisterUserApiHook(&strDllNameSafe, &strFuncNameSafe);
1754 
1755     UserLeave();
1756 
1757     /* Cleanup only in case of failure */
1758     if(ret == FALSE)
1759     {
1760         ReleaseCapturedUnicodeString(&strDllNameSafe, UserMode);
1761         ReleaseCapturedUnicodeString(&strFuncNameSafe, UserMode);
1762     }
1763 
1764     return ret;
1765 }
1766 
1767 BOOL
1768 APIENTRY
1769 NtUserUnregisterUserApiHook(VOID)
1770 {
1771     BOOL ret;
1772 
1773     UserEnterExclusive();
1774     ret = UserUnregisterUserApiHook();
1775     UserLeave();
1776 
1777     return ret;
1778 }
1779 
1780 /* EOF */
1781