xref: /reactos/win32ss/user/ntuser/hook.c (revision 9cfd8dd9)
1 /*
2  * COPYRIGHT:        See COPYING in the top level directory
3  * PROJECT:          ReactOS kernel
4  * PURPOSE:          Window hooks
5  * PROGRAMER:        Casper S. Hornstrup <chorns@users.sourceforge.net>
6  *                   James Tabor <james.tabor@reactos.org>
7  *                   Rafal Harabien <rafalh@reactos.org>
8   * NOTE:            Most of this code was adapted from Wine,
9  *                   Copyright (C) 2002 Alexandre Julliard
10  */
11 
12 #include <win32k.h>
13 DBG_DEFAULT_CHANNEL(UserHook);
14 
15 typedef struct _HOOKPACK
16 {
17   PHOOK pHk;
18   LPARAM lParam;
19   PVOID pHookStructs;
20 } HOOKPACK, *PHOOKPACK;
21 
22 UNICODE_STRING strUahModule;
23 UNICODE_STRING strUahInitFunc;
24 PPROCESSINFO ppiUahServer;
25 
26 /* PRIVATE FUNCTIONS *********************************************************/
27 
28 /* Calls ClientLoadLibrary in user32 in order to load or unload a module */
29 BOOL
30 IntLoadHookModule(int iHookID, HHOOK hHook, BOOL Unload)
31 {
32    PPROCESSINFO ppi;
33    BOOL bResult;
34 
35    ppi = PsGetCurrentProcessWin32Process();
36 
37    TRACE("IntLoadHookModule. Client PID: %p\n", PsGetProcessId(ppi->peProcess));
38 
39     /* Check if this is the api hook */
40     if(iHookID == WH_APIHOOK)
41     {
42         if(!Unload && !(ppi->W32PF_flags & W32PF_APIHOOKLOADED))
43         {
44             /* A callback in user mode can trigger UserLoadApiHook to be called and
45                as a result IntLoadHookModule will be called recursively.
46                To solve this we set the flag that means that the application has
47                loaded the api hook before the callback and in case of error we remove it */
48             ppi->W32PF_flags |= W32PF_APIHOOKLOADED;
49 
50             /* Call ClientLoadLibrary in user32 */
51             bResult = co_IntClientLoadLibrary(&strUahModule, &strUahInitFunc, Unload, TRUE);
52             TRACE("co_IntClientLoadLibrary returned %d\n", bResult );
53             if (!bResult)
54             {
55                 /* Remove the flag we set before */
56                 ppi->W32PF_flags &= ~W32PF_APIHOOKLOADED;
57             }
58             return bResult;
59         }
60         else if(Unload && (ppi->W32PF_flags & W32PF_APIHOOKLOADED))
61         {
62             /* Call ClientLoadLibrary in user32 */
63             bResult = co_IntClientLoadLibrary(NULL, NULL, Unload, TRUE);
64             if (bResult)
65             {
66                 ppi->W32PF_flags &= ~W32PF_APIHOOKLOADED;
67             }
68             return bResult;
69         }
70 
71         return TRUE;
72     }
73 
74     STUB;
75 
76     return FALSE;
77 }
78 
79 /*
80 IntHookModuleUnloaded:
81 Sends a internal message to all threads of the requested desktop
82 and notifies them that a global hook was destroyed
83 and an injected module must be unloaded.
84 As a result, IntLoadHookModule will be called for all the threads that
85 will receive the special purpose internal message.
86 */
87 BOOL
88 IntHookModuleUnloaded(PDESKTOP pdesk, int iHookID, HHOOK hHook)
89 {
90     PTHREADINFO ptiCurrent;
91     PLIST_ENTRY ListEntry;
92     PPROCESSINFO ppiCsr;
93 
94     TRACE("IntHookModuleUnloaded: iHookID=%d\n", iHookID);
95 
96     ppiCsr = PsGetProcessWin32Process(gpepCSRSS);
97 
98     ListEntry = pdesk->PtiList.Flink;
99     while(ListEntry != &pdesk->PtiList)
100     {
101         ptiCurrent = CONTAINING_RECORD(ListEntry, THREADINFO, PtiLink);
102 
103         /* FIXME: Do some more security checks here */
104 
105         /* FIXME: HACK: The first check is a reactos specific hack for system threads */
106         if(!PsIsSystemProcess(ptiCurrent->ppi->peProcess) &&
107            ptiCurrent->ppi != ppiCsr)
108         {
109             if(ptiCurrent->ppi->W32PF_flags & W32PF_APIHOOKLOADED)
110             {
111                 TRACE("IntHookModuleUnloaded: sending message to PID %p, ppi=%p\n", PsGetProcessId(ptiCurrent->ppi->peProcess), ptiCurrent->ppi);
112                 co_MsqSendMessageAsync( ptiCurrent,
113                                         0,
114                                         iHookID,
115                                         TRUE,
116                                         (LPARAM)hHook,
117                                         NULL,
118                                         0,
119                                         FALSE,
120                                         MSQ_INJECTMODULE);
121             }
122         }
123         ListEntry = ListEntry->Flink;
124     }
125 
126     return TRUE;
127 }
128 
129 BOOL
130 FASTCALL
131 UserLoadApiHook(VOID)
132 {
133     return IntLoadHookModule(WH_APIHOOK, 0, FALSE);
134 }
135 
136 BOOL
137 FASTCALL
138 UserRegisterUserApiHook(
139     PUNICODE_STRING pstrDllName,
140     PUNICODE_STRING pstrFuncName)
141 {
142     PTHREADINFO pti, ptiCurrent;
143     HWND *List;
144     PWND DesktopWindow, pwndCurrent;
145     ULONG i;
146     PPROCESSINFO ppiCsr;
147 
148     pti = PsGetCurrentThreadWin32Thread();
149     ppiCsr = PsGetProcessWin32Process(gpepCSRSS);
150 
151     /* Fail if the api hook is already registered */
152     if(gpsi->dwSRVIFlags & SRVINFO_APIHOOK)
153     {
154         return FALSE;
155     }
156 
157     TRACE("UserRegisterUserApiHook. Server PID: %p\n", PsGetProcessId(pti->ppi->peProcess));
158 
159     /* Register the api hook */
160     gpsi->dwSRVIFlags |= SRVINFO_APIHOOK;
161 
162     strUahModule = *pstrDllName;
163     strUahInitFunc = *pstrFuncName;
164     ppiUahServer = pti->ppi;
165 
166     /* Broadcast an internal message to every top level window */
167     DesktopWindow = UserGetWindowObject(IntGetDesktopWindow());
168     List = IntWinListChildren(DesktopWindow);
169 
170     if (List != NULL)
171     {
172         for (i = 0; List[i]; i++)
173         {
174             pwndCurrent = UserGetWindowObject(List[i]);
175             if(pwndCurrent == NULL)
176             {
177                 continue;
178             }
179             ptiCurrent = pwndCurrent->head.pti;
180 
181            /* FIXME: The first check is a reactos specific hack for system threads */
182             if(PsIsSystemProcess(ptiCurrent->ppi->peProcess) ||
183                 ptiCurrent->ppi == ppiCsr)
184             {
185                 continue;
186             }
187 
188             co_MsqSendMessageAsync( ptiCurrent,
189                                     0,
190                                     WH_APIHOOK,
191                                     FALSE,   /* Load the module */
192                                     0,
193                                     NULL,
194                                     0,
195                                     FALSE,
196                                     MSQ_INJECTMODULE);
197         }
198         ExFreePoolWithTag(List, USERTAG_WINDOWLIST);
199     }
200 
201     return TRUE;
202 }
203 
204 BOOL
205 FASTCALL
206 UserUnregisterUserApiHook(VOID)
207 {
208     PTHREADINFO pti;
209 
210     pti = PsGetCurrentThreadWin32Thread();
211 
212     /* Fail if the api hook is not registered */
213     if(!(gpsi->dwSRVIFlags & SRVINFO_APIHOOK))
214     {
215         return FALSE;
216     }
217 
218     /* Only the process that registered the api hook can uregister it */
219     if(ppiUahServer != PsGetCurrentProcessWin32Process())
220     {
221         return FALSE;
222     }
223 
224     TRACE("UserUnregisterUserApiHook. Server PID: %p\n", PsGetProcessId(pti->ppi->peProcess));
225 
226     /* Unregister the api hook */
227     gpsi->dwSRVIFlags &= ~SRVINFO_APIHOOK;
228     ppiUahServer = NULL;
229     ReleaseCapturedUnicodeString(&strUahModule, UserMode);
230     ReleaseCapturedUnicodeString(&strUahInitFunc, UserMode);
231 
232     /* Notify all applications that the api hook module must be unloaded */
233     return IntHookModuleUnloaded(pti->rpdesk, WH_APIHOOK, 0);
234 }
235 
236 static
237 LRESULT
238 FASTCALL
239 co_IntCallLowLevelHook(PHOOK Hook,
240                      INT Code,
241                      WPARAM wParam,
242                      LPARAM lParam)
243 {
244     NTSTATUS Status;
245     PTHREADINFO pti;
246     PHOOKPACK pHP;
247     INT Size = 0;
248     UINT uTimeout = 300;
249     BOOL Block = FALSE;
250     ULONG_PTR uResult = 0;
251 
252     if (Hook->ptiHooked)
253        pti = Hook->ptiHooked;
254     else
255        pti = Hook->head.pti;
256 
257     pHP = ExAllocatePoolWithTag(NonPagedPool, sizeof(HOOKPACK), TAG_HOOK);
258     if (!pHP) return 0;
259 
260     pHP->pHk = Hook;
261     pHP->lParam = lParam;
262     pHP->pHookStructs = NULL;
263 
264 // This prevents stack corruption from the caller.
265     switch(Hook->HookId)
266     {
267        case WH_JOURNALPLAYBACK:
268        case WH_JOURNALRECORD:
269           uTimeout = 0;
270           Size = sizeof(EVENTMSG);
271           break;
272        case WH_KEYBOARD_LL:
273           Size = sizeof(KBDLLHOOKSTRUCT);
274           break;
275        case WH_MOUSE_LL:
276           Size = sizeof(MSLLHOOKSTRUCT);
277           break;
278        case WH_MOUSE:
279           uTimeout = 200;
280           Block = TRUE;
281           Size = sizeof(MOUSEHOOKSTRUCT);
282           break;
283        case WH_KEYBOARD:
284           uTimeout = 200;
285           Block = TRUE;
286           break;
287     }
288 
289     if (Size)
290     {
291        pHP->pHookStructs = ExAllocatePoolWithTag(NonPagedPool, Size, TAG_HOOK);
292        if (pHP->pHookStructs) RtlCopyMemory(pHP->pHookStructs, (PVOID)lParam, Size);
293     }
294 
295     /* FIXME: Should get timeout from
296      * HKEY_CURRENT_USER\Control Panel\Desktop\LowLevelHooksTimeout */
297     Status = co_MsqSendMessage( pti,
298                                 IntToPtr(Code), // hWnd
299                                 Hook->HookId,   // Msg
300                                 wParam,
301                                (LPARAM)pHP,
302                                 uTimeout,
303                                 Block,
304                                 MSQ_ISHOOK,
305                                &uResult);
306     if (!NT_SUCCESS(Status))
307     {
308        ERR("Error Hook Call SendMsg. %d Status: 0x%x\n", Hook->HookId, Status);
309        if (pHP->pHookStructs) ExFreePoolWithTag(pHP->pHookStructs, TAG_HOOK);
310        ExFreePoolWithTag(pHP, TAG_HOOK);
311     }
312     return NT_SUCCESS(Status) ? uResult : 0;
313 }
314 
315 
316 //
317 // Dispatch MsgQueue Hook Call processor!
318 //
319 LRESULT
320 APIENTRY
321 co_CallHook( INT HookId,
322              INT Code,
323              WPARAM wParam,
324              LPARAM lParam)
325 {
326     LRESULT Result = 0;
327     PHOOK phk;
328     PHOOKPACK pHP = (PHOOKPACK)lParam;
329 
330     phk = pHP->pHk;
331     lParam = pHP->lParam;
332 
333     switch(HookId)
334     {
335        case WH_JOURNALPLAYBACK:
336        case WH_JOURNALRECORD:
337        case WH_KEYBOARD_LL:
338        case WH_MOUSE_LL:
339        case WH_MOUSE:
340           lParam = (LPARAM)pHP->pHookStructs;
341        case WH_KEYBOARD:
342           break;
343     }
344 
345     if (!UserObjectInDestroy(UserHMGetHandle(phk))) //// Fix CORE-10549.
346     {
347     /* The odds are high for this to be a Global call. */
348     Result = co_IntCallHookProc( HookId,
349                                  Code,
350                                  wParam,
351                                  lParam,
352                                  phk->Proc,
353                                  phk->ihmod,
354                                  phk->offPfn,
355                                  phk->Ansi,
356                                 &phk->ModuleName);
357     }
358     /* The odds so high, no one is waiting for the results. */
359     if (pHP->pHookStructs) ExFreePoolWithTag(pHP->pHookStructs, TAG_HOOK);
360     ExFreePoolWithTag(pHP, TAG_HOOK);
361     return Result;
362 }
363 
364 static
365 LRESULT
366 APIENTRY
367 co_HOOK_CallHookNext( PHOOK Hook,
368                       INT Code,
369                       WPARAM wParam,
370                       LPARAM lParam)
371 {
372     TRACE("Calling Next HOOK %d\n", Hook->HookId);
373 
374     return co_IntCallHookProc( Hook->HookId,
375                                Code,
376                                wParam,
377                                lParam,
378                                Hook->Proc,
379                                Hook->ihmod,
380                                Hook->offPfn,
381                                Hook->Ansi,
382                               &Hook->ModuleName);
383 }
384 
385 static
386 LRESULT
387 FASTCALL
388 co_IntCallDebugHook(PHOOK Hook,
389                   int Code,
390                   WPARAM wParam,
391                   LPARAM lParam,
392                   BOOL Ansi)
393 {
394     LRESULT lResult = 0;
395     ULONG Size;
396     DEBUGHOOKINFO Debug;
397     PVOID HooklParam = NULL;
398     BOOL BadChk = FALSE;
399 
400     if (lParam)
401     {
402         _SEH2_TRY
403         {
404             ProbeForRead((PVOID)lParam,
405                          sizeof(DEBUGHOOKINFO),
406                          1);
407 
408             RtlCopyMemory(&Debug,
409                           (PVOID)lParam,
410                           sizeof(DEBUGHOOKINFO));
411         }
412         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
413         {
414             BadChk = TRUE;
415         }
416         _SEH2_END;
417 
418         if (BadChk)
419         {
420             ERR("HOOK WH_DEBUG read from lParam ERROR!\n");
421             return lResult;
422         }
423     }
424     else
425         return lResult; /* Need lParam! */
426 
427     switch (wParam)
428     {
429         case WH_CBT:
430         {
431             switch (Debug.code)
432             {
433                 case HCBT_CLICKSKIPPED:
434                     Size = sizeof(MOUSEHOOKSTRUCTEX);
435                     break;
436 
437                 case HCBT_MOVESIZE:
438                     Size = sizeof(RECT);
439                     break;
440 
441                 case HCBT_ACTIVATE:
442                     Size = sizeof(CBTACTIVATESTRUCT);
443                     break;
444 
445                 case HCBT_CREATEWND: /* Handle ANSI? */
446                     Size = sizeof(CBT_CREATEWND);
447                     /* What shall we do? Size += sizeof(HOOKPROC_CBT_CREATEWND_EXTRA_ARGUMENTS); same as CREATESTRUCTEX */
448                     break;
449 
450                 default:
451                     Size = sizeof(LPARAM);
452             }
453         }
454         break;
455 
456         case WH_MOUSE_LL:
457             Size = sizeof(MSLLHOOKSTRUCT);
458             break;
459 
460         case WH_KEYBOARD_LL:
461             Size = sizeof(KBDLLHOOKSTRUCT);
462             break;
463 
464         case WH_MSGFILTER:
465         case WH_SYSMSGFILTER:
466         case WH_GETMESSAGE:
467             Size = sizeof(MSG);
468             break;
469 
470         case WH_JOURNALPLAYBACK:
471         case WH_JOURNALRECORD:
472             Size = sizeof(EVENTMSG);
473             break;
474 
475         case WH_FOREGROUNDIDLE:
476         case WH_KEYBOARD:
477         case WH_SHELL:
478         default:
479             Size = sizeof(LPARAM);
480     }
481 
482     if (Size > sizeof(LPARAM))
483         HooklParam = ExAllocatePoolWithTag(NonPagedPool, Size, TAG_HOOK);
484 
485     if (HooklParam)
486     {
487         _SEH2_TRY
488         {
489             ProbeForRead((PVOID)Debug.lParam,
490                          Size,
491                          1);
492 
493             RtlCopyMemory(HooklParam,
494                           (PVOID)Debug.lParam,
495                           Size);
496         }
497         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
498         {
499             BadChk = TRUE;
500         }
501         _SEH2_END;
502 
503         if (BadChk)
504         {
505             ERR("HOOK WH_DEBUG read from Debug.lParam ERROR!\n");
506             ExFreePoolWithTag(HooklParam, TAG_HOOK);
507             return lResult;
508         }
509     }
510 
511     if (HooklParam) Debug.lParam = (LPARAM)HooklParam;
512     lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&Debug);
513     if (HooklParam) ExFreePoolWithTag(HooklParam, TAG_HOOK);
514 
515     return lResult;
516 }
517 
518 static
519 LRESULT
520 APIENTRY
521 co_UserCallNextHookEx(PHOOK Hook,
522                     int Code,
523                     WPARAM wParam,
524                     LPARAM lParam,
525                     BOOL Ansi)
526 {
527     LRESULT lResult = 0;
528     BOOL BadChk = FALSE;
529 
530     /* Handle this one first. */
531     if ((Hook->HookId == WH_MOUSE) ||
532         (Hook->HookId == WH_CBT && Code == HCBT_CLICKSKIPPED))
533     {
534         MOUSEHOOKSTRUCTEX Mouse;
535         if (lParam)
536         {
537             _SEH2_TRY
538             {
539                 ProbeForRead((PVOID)lParam,
540                              sizeof(MOUSEHOOKSTRUCTEX),
541                              1);
542 
543                 RtlCopyMemory(&Mouse,
544                               (PVOID)lParam,
545                               sizeof(MOUSEHOOKSTRUCTEX));
546             }
547             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
548             {
549                 BadChk = TRUE;
550             }
551             _SEH2_END;
552 
553             if (BadChk)
554             {
555                 ERR("HOOK WH_MOUSE read from lParam ERROR!\n");
556             }
557         }
558 
559         if (!BadChk)
560         {
561             lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&Mouse);
562         }
563 
564         return lResult;
565     }
566 
567     switch(Hook->HookId)
568     {
569         case WH_MOUSE_LL:
570         {
571             MSLLHOOKSTRUCT Mouse;
572 
573             if (lParam)
574             {
575                 _SEH2_TRY
576                 {
577                     ProbeForRead((PVOID)lParam,
578                                  sizeof(MSLLHOOKSTRUCT),
579                                  1);
580 
581                     RtlCopyMemory(&Mouse,
582                                   (PVOID)lParam,
583                                   sizeof(MSLLHOOKSTRUCT));
584                 }
585                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
586                 {
587                     BadChk = TRUE;
588                 }
589                 _SEH2_END;
590 
591                 if (BadChk)
592                 {
593                     ERR("HOOK WH_MOUSE_LL read from lParam ERROR!\n");
594                 }
595             }
596 
597             if (!BadChk)
598             {
599                 lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&Mouse);
600             }
601             break;
602         }
603 
604         case WH_KEYBOARD_LL:
605         {
606             KBDLLHOOKSTRUCT Keyboard;
607 
608             if (lParam)
609             {
610                 _SEH2_TRY
611                 {
612                     ProbeForRead((PVOID)lParam,
613                                  sizeof(KBDLLHOOKSTRUCT),
614                                  1);
615 
616                     RtlCopyMemory(&Keyboard,
617                                   (PVOID)lParam,
618                                   sizeof(KBDLLHOOKSTRUCT));
619                 }
620                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
621                 {
622                     BadChk = TRUE;
623                 }
624                 _SEH2_END;
625 
626                 if (BadChk)
627                 {
628                     ERR("HOOK WH_KEYBORD_LL read from lParam ERROR!\n");
629                 }
630             }
631 
632             if (!BadChk)
633             {
634                 lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&Keyboard);
635             }
636             break;
637         }
638 
639         case WH_MSGFILTER:
640         case WH_SYSMSGFILTER:
641         case WH_GETMESSAGE:
642         {
643             MSG Msg;
644 
645             if (lParam)
646             {
647                 _SEH2_TRY
648                 {
649                     ProbeForRead((PVOID)lParam,
650                                  sizeof(MSG),
651                                  1);
652 
653                     RtlCopyMemory(&Msg,
654                                   (PVOID)lParam,
655                                   sizeof(MSG));
656                 }
657                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
658                 {
659                     BadChk = TRUE;
660                 }
661                 _SEH2_END;
662 
663                 if (BadChk)
664                 {
665                     ERR("HOOK WH_XMESSAGEX read from lParam ERROR!\n");
666                 }
667             }
668 
669             if (!BadChk)
670             {
671                 lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&Msg);
672 
673                 if (lParam && (Hook->HookId == WH_GETMESSAGE))
674                 {
675                     _SEH2_TRY
676                     {
677                         ProbeForWrite((PVOID)lParam,
678                                       sizeof(MSG),
679                                       1);
680 
681                         RtlCopyMemory((PVOID)lParam,
682                                       &Msg,
683                                       sizeof(MSG));
684                     }
685                     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
686                     {
687                         BadChk = TRUE;
688                     }
689                     _SEH2_END;
690 
691                     if (BadChk)
692                     {
693                         ERR("HOOK WH_GETMESSAGE write to lParam ERROR!\n");
694                     }
695                 }
696             }
697             break;
698         }
699 
700         case WH_CBT:
701             TRACE("HOOK WH_CBT!\n");
702             switch (Code)
703             {
704                 case HCBT_CREATEWND:
705                 {
706                     LPCBT_CREATEWNDW pcbtcww = (LPCBT_CREATEWNDW)lParam;
707 
708                     TRACE("HOOK HCBT_CREATEWND\n");
709                     _SEH2_TRY
710                     {
711                         if (Ansi)
712                         {
713                             ProbeForRead( pcbtcww,
714                                           sizeof(CBT_CREATEWNDA),
715                                           1);
716                             ProbeForWrite(pcbtcww->lpcs,
717                                           sizeof(CREATESTRUCTA),
718                                           1);
719                             ProbeForRead( pcbtcww->lpcs->lpszName,
720                                           sizeof(CHAR),
721                                           1);
722 
723                             if (!IS_ATOM(pcbtcww->lpcs->lpszClass))
724                             {
725                                 _Analysis_assume_(pcbtcww->lpcs->lpszClass != NULL);
726                                 ProbeForRead(pcbtcww->lpcs->lpszClass,
727                                              sizeof(CHAR),
728                                              1);
729                             }
730                         }
731                         else
732                         {
733                             ProbeForRead( pcbtcww,
734                                           sizeof(CBT_CREATEWNDW),
735                                           1);
736                             ProbeForWrite(pcbtcww->lpcs,
737                                           sizeof(CREATESTRUCTW),
738                                           1);
739                             ProbeForRead( pcbtcww->lpcs->lpszName,
740                                           sizeof(WCHAR),
741                                           1);
742 
743                             if (!IS_ATOM(pcbtcww->lpcs->lpszClass))
744                             {
745                                 _Analysis_assume_(pcbtcww->lpcs->lpszClass != NULL);
746                                 ProbeForRead(pcbtcww->lpcs->lpszClass,
747                                              sizeof(WCHAR),
748                                              1);
749                             }
750                         }
751                     }
752                     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
753                     {
754                         BadChk = TRUE;
755                     }
756                     _SEH2_END;
757 
758                     if (BadChk)
759                     {
760                         ERR("HOOK HCBT_CREATEWND write ERROR!\n");
761                     }
762                     /* The next call handles the structures. */
763                     if (!BadChk && Hook->Proc)
764                     {
765                        lResult = co_HOOK_CallHookNext(Hook, Code, wParam, lParam);
766                     }
767                     break;
768                 }
769 
770                 case HCBT_MOVESIZE:
771                 {
772                     RECTL rt;
773 
774                     TRACE("HOOK HCBT_MOVESIZE\n");
775 
776                     if (lParam)
777                     {
778                         _SEH2_TRY
779                         {
780                             ProbeForRead((PVOID)lParam,
781                                          sizeof(RECT),
782                                          1);
783 
784                             RtlCopyMemory(&rt,
785                                           (PVOID)lParam,
786                                           sizeof(RECT));
787                         }
788                         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
789                         {
790                             BadChk = TRUE;
791                         }
792                         _SEH2_END;
793 
794                         if (BadChk)
795                         {
796                             ERR("HOOK HCBT_MOVESIZE read from lParam ERROR!\n");
797                         }
798                     }
799 
800                     if (!BadChk)
801                     {
802                         lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&rt);
803                     }
804                     break;
805                 }
806 
807                 case HCBT_ACTIVATE:
808                 {
809                     CBTACTIVATESTRUCT CbAs;
810 
811                     TRACE("HOOK HCBT_ACTIVATE\n");
812                     if (lParam)
813                     {
814                         _SEH2_TRY
815                         {
816                             ProbeForRead((PVOID)lParam,
817                                          sizeof(CBTACTIVATESTRUCT),
818                                          1);
819 
820                             RtlCopyMemory(&CbAs,
821                                           (PVOID)lParam,
822                                           sizeof(CBTACTIVATESTRUCT));
823                         }
824                         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
825                         {
826                             BadChk = TRUE;
827                         }
828                         _SEH2_END;
829 
830                         if (BadChk)
831                         {
832                             ERR("HOOK HCBT_ACTIVATE read from lParam ERROR!\n");
833                         }
834                     }
835 
836                     if (!BadChk)
837                     {
838                         lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&CbAs);
839                     }
840                     break;
841                 }
842 
843                 /* The rest just use default. */
844                 default:
845                     TRACE("HOOK HCBT_ %d\n",Code);
846                     lResult = co_HOOK_CallHookNext(Hook, Code, wParam, lParam);
847                     break;
848             }
849             break;
850 /*
851  Note WH_JOURNALPLAYBACK,
852     "To have the system wait before processing the message, the return value
853      must be the amount of time, in clock ticks, that the system should wait."
854  */
855         case WH_JOURNALPLAYBACK:
856         case WH_JOURNALRECORD:
857         {
858             EVENTMSG EventMsg;
859 
860             if (lParam)
861             {
862                 _SEH2_TRY
863                 {
864                     ProbeForRead((PVOID)lParam,
865                                  sizeof(EVENTMSG),
866                                  1);
867 
868                     RtlCopyMemory(&EventMsg,
869                                   (PVOID)lParam,
870                                   sizeof(EVENTMSG));
871                 }
872                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
873                 {
874                     BadChk = TRUE;
875                 }
876                 _SEH2_END;
877 
878                 if (BadChk)
879                 {
880                     ERR("HOOK WH_JOURNAL read from lParam ERROR!\n");
881                 }
882             }
883 
884             if (!BadChk)
885             {
886                 lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)(lParam ? &EventMsg : NULL));
887 
888                 if (lParam)
889                 {
890                     _SEH2_TRY
891                     {
892                         ProbeForWrite((PVOID)lParam,
893                                       sizeof(EVENTMSG),
894                                       1);
895 
896                         RtlCopyMemory((PVOID)lParam,
897                                       &EventMsg,
898                                       sizeof(EVENTMSG));
899                     }
900                     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
901                     {
902                         BadChk = TRUE;
903                     }
904                     _SEH2_END;
905 
906                     if (BadChk)
907                     {
908                         ERR("HOOK WH_JOURNAL write to lParam ERROR!\n");
909                     }
910                 }
911             }
912             break;
913         }
914 
915         case WH_DEBUG:
916             lResult = co_IntCallDebugHook(Hook, Code, wParam, lParam, Ansi);
917             break;
918 
919         /*
920          * Default the rest like, WH_FOREGROUNDIDLE, WH_KEYBOARD and WH_SHELL.
921          */
922         case WH_FOREGROUNDIDLE:
923         case WH_KEYBOARD:
924         case WH_SHELL:
925             lResult = co_HOOK_CallHookNext(Hook, Code, wParam, lParam);
926             break;
927 
928         default:
929             ERR("Unsupported HOOK Id -> %d\n",Hook->HookId);
930             break;
931     }
932     return lResult;
933 }
934 
935 PHOOK
936 FASTCALL
937 IntGetHookObject(HHOOK hHook)
938 {
939     PHOOK Hook;
940 
941     if (!hHook)
942     {
943        EngSetLastError(ERROR_INVALID_HOOK_HANDLE);
944        return NULL;
945     }
946 
947     Hook = (PHOOK)UserGetObject(gHandleTable, hHook, TYPE_HOOK);
948     if (!Hook)
949     {
950        EngSetLastError(ERROR_INVALID_HOOK_HANDLE);
951        return NULL;
952     }
953 
954     UserReferenceObject(Hook);
955 
956     return Hook;
957 }
958 
959 static
960 HHOOK*
961 FASTCALL
962 IntGetGlobalHookHandles(PDESKTOP pdo, int HookId)
963 {
964     PLIST_ENTRY pLastHead, pElem;
965     unsigned i = 0;
966     unsigned cHooks = 0;
967     HHOOK *pList;
968     PHOOK pHook;
969 
970     pLastHead = &pdo->pDeskInfo->aphkStart[HOOKID_TO_INDEX(HookId)];
971     for (pElem = pLastHead->Flink; pElem != pLastHead; pElem = pElem->Flink)
972       ++cHooks;
973 
974     pList = ExAllocatePoolWithTag(PagedPool, (cHooks + 1) * sizeof(HHOOK), TAG_HOOK);
975     if (!pList)
976     {
977         EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
978         return NULL;
979     }
980 
981     for (pElem = pLastHead->Flink; pElem != pLastHead; pElem = pElem->Flink)
982     {
983         pHook = CONTAINING_RECORD(pElem, HOOK, Chain);
984         NT_ASSERT(i < cHooks);
985         pList[i++] = 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 
1380     TRACE("Enter NtUserCallNextHookEx\n");
1381     UserEnterExclusive();
1382 
1383     pti = GetW32ThreadInfo();
1384 
1385     HookObj = pti->sphkCurrent;
1386 
1387     if (!HookObj)
1388        goto Exit; // Return 0
1389 
1390     NextObj = HookObj->phkNext;
1391 
1392     pti->sphkCurrent = NextObj;
1393     ClientInfo = pti->pClientInfo;
1394     _SEH2_TRY
1395     {
1396        ClientInfo->phkCurrent = NextObj;
1397     }
1398     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1399     {
1400        ClientInfo = NULL;
1401     }
1402     _SEH2_END;
1403 
1404     /* Now in List run down. */
1405     if (ClientInfo && NextObj)
1406     {
1407        NextObj->phkNext = IntGetNextHook(NextObj);
1408        lResult = co_UserCallNextHookEx( NextObj, Code, wParam, lParam, NextObj->Ansi);
1409     }
1410 
1411 Exit:
1412     TRACE("Leave NtUserCallNextHookEx, ret=%i\n", lResult);
1413     UserLeave();
1414     return lResult;
1415 }
1416 
1417 HHOOK
1418 APIENTRY
1419 NtUserSetWindowsHookAW( int idHook,
1420                         HOOKPROC lpfn,
1421                         BOOL Ansi)
1422 {
1423     DWORD ThreadId;
1424     UNICODE_STRING USModuleName;
1425 
1426     RtlInitUnicodeString(&USModuleName, NULL);
1427     ThreadId = PtrToUint(NtCurrentTeb()->ClientId.UniqueThread);
1428 
1429     return NtUserSetWindowsHookEx( NULL,
1430                                   &USModuleName,
1431                                    ThreadId,
1432                                    idHook,
1433                                    lpfn,
1434                                    Ansi);
1435 }
1436 
1437 HHOOK
1438 APIENTRY
1439 NtUserSetWindowsHookEx( HINSTANCE Mod,
1440                         PUNICODE_STRING UnsafeModuleName,
1441                         DWORD ThreadId,
1442                         int HookId,
1443                         HOOKPROC HookProc,
1444                         BOOL Ansi)
1445 {
1446     PWINSTATION_OBJECT WinStaObj;
1447     PHOOK Hook = NULL;
1448     UNICODE_STRING ModuleName;
1449     NTSTATUS Status;
1450     HHOOK Handle;
1451     PTHREADINFO pti, ptiHook = NULL;
1452     HHOOK Ret = NULL;
1453 
1454     TRACE("Enter NtUserSetWindowsHookEx\n");
1455     UserEnterExclusive();
1456 
1457     pti = PsGetCurrentThreadWin32Thread();
1458 
1459     if (HookId < WH_MINHOOK || WH_MAXHOOK < HookId )
1460     {
1461         EngSetLastError(ERROR_INVALID_HOOK_FILTER);
1462         goto Cleanup; // Return NULL
1463     }
1464 
1465     if (!HookProc)
1466     {
1467         EngSetLastError(ERROR_INVALID_FILTER_PROC);
1468         goto Cleanup; // Return NULL
1469     }
1470 
1471     if (ThreadId)  /* thread-local hook */
1472     {
1473        if ( HookId == WH_JOURNALRECORD ||
1474             HookId == WH_JOURNALPLAYBACK ||
1475             HookId == WH_KEYBOARD_LL ||
1476             HookId == WH_MOUSE_LL ||
1477             HookId == WH_SYSMSGFILTER)
1478        {
1479            TRACE("Local hook installing Global HookId: %d\n",HookId);
1480            /* these can only be global */
1481            EngSetLastError(ERROR_GLOBAL_ONLY_HOOK);
1482            goto Cleanup; // Return NULL
1483        }
1484 
1485        if ( !(ptiHook = IntTID2PTI( UlongToHandle(ThreadId) )))
1486        {
1487           ERR("Invalid thread id 0x%x\n", ThreadId);
1488           EngSetLastError(ERROR_INVALID_PARAMETER);
1489           goto Cleanup; // Return NULL
1490        }
1491 
1492        if ( ptiHook->rpdesk != pti->rpdesk) // gptiCurrent->rpdesk)
1493        {
1494           ERR("Local hook wrong desktop HookId: %d\n",HookId);
1495           EngSetLastError(ERROR_ACCESS_DENIED);
1496           goto Cleanup; // Return NULL
1497        }
1498 
1499        if (ptiHook->ppi != pti->ppi)
1500        {
1501           if ( !Mod &&
1502               (HookId == WH_GETMESSAGE ||
1503                HookId == WH_CALLWNDPROC ||
1504                HookId == WH_CBT ||
1505                HookId == WH_HARDWARE ||
1506                HookId == WH_DEBUG ||
1507                HookId == WH_SHELL ||
1508                HookId == WH_FOREGROUNDIDLE ||
1509                HookId == WH_CALLWNDPROCRET) )
1510           {
1511              ERR("Local hook needs hMod HookId: %d\n",HookId);
1512              EngSetLastError(ERROR_HOOK_NEEDS_HMOD);
1513              goto Cleanup; // Return NULL
1514           }
1515 
1516           if ( (ptiHook->TIF_flags & (TIF_CSRSSTHREAD|TIF_SYSTEMTHREAD)) &&
1517                (HookId == WH_GETMESSAGE ||
1518                 HookId == WH_CALLWNDPROC ||
1519                 HookId == WH_CBT ||
1520                 HookId == WH_HARDWARE ||
1521                 HookId == WH_DEBUG ||
1522                 HookId == WH_SHELL ||
1523                 HookId == WH_FOREGROUNDIDLE ||
1524                 HookId == WH_CALLWNDPROCRET) )
1525           {
1526              EngSetLastError(ERROR_HOOK_TYPE_NOT_ALLOWED);
1527              goto Cleanup; // Return NULL
1528           }
1529        }
1530     }
1531     else  /* System-global hook */
1532     {
1533        ptiHook = pti; // gptiCurrent;
1534        if ( !Mod &&
1535             (HookId == WH_GETMESSAGE ||
1536              HookId == WH_CALLWNDPROC ||
1537              HookId == WH_CBT ||
1538              HookId == WH_SYSMSGFILTER ||
1539              HookId == WH_HARDWARE ||
1540              HookId == WH_DEBUG ||
1541              HookId == WH_SHELL ||
1542              HookId == WH_FOREGROUNDIDLE ||
1543              HookId == WH_CALLWNDPROCRET) )
1544        {
1545           ERR("Global hook needs hMod HookId: %d\n",HookId);
1546           EngSetLastError(ERROR_HOOK_NEEDS_HMOD);
1547           goto Cleanup; // Return NULL
1548        }
1549     }
1550 
1551     Status = IntValidateWindowStationHandle( PsGetCurrentProcess()->Win32WindowStation,
1552                                              UserMode,
1553                                              0,
1554                                             &WinStaObj,
1555                                              0);
1556 
1557     if (!NT_SUCCESS(Status))
1558     {
1559        SetLastNtError(Status);
1560        goto Cleanup; // Return NULL
1561     }
1562     ObDereferenceObject(WinStaObj);
1563 
1564     Hook = UserCreateObject(gHandleTable, NULL, ptiHook, (PHANDLE)&Handle, TYPE_HOOK, sizeof(HOOK));
1565 
1566     if (!Hook)
1567     {
1568        goto Cleanup; // Return NULL
1569     }
1570 
1571     Hook->ihmod   = (INT_PTR)Mod; // Module Index from atom table, Do this for now.
1572     Hook->HookId  = HookId;
1573     Hook->rpdesk  = ptiHook->rpdesk;
1574     Hook->phkNext = NULL; /* Dont use as a chain! Use link lists for chaining. */
1575     Hook->Proc    = HookProc;
1576     Hook->Ansi    = Ansi;
1577 
1578     TRACE("Set Hook Desk %p DeskInfo %p Handle Desk %p\n", pti->rpdesk, pti->pDeskInfo, Hook->head.rpdesk);
1579 
1580     if (ThreadId)  /* Thread-local hook */
1581     {
1582        InsertHeadList(&ptiHook->aphkStart[HOOKID_TO_INDEX(HookId)], &Hook->Chain);
1583        ptiHook->sphkCurrent = NULL;
1584        Hook->ptiHooked = ptiHook;
1585        ptiHook->fsHooks |= HOOKID_TO_FLAG(HookId);
1586 
1587        if (ptiHook->pClientInfo)
1588        {
1589           if ( ptiHook->ppi == pti->ppi) /* gptiCurrent->ppi) */
1590           {
1591              _SEH2_TRY
1592              {
1593                 ptiHook->pClientInfo->fsHooks = ptiHook->fsHooks;
1594                 ptiHook->pClientInfo->phkCurrent = NULL;
1595              }
1596              _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1597              {
1598                 ERR("Problem writing to Local ClientInfo!\n");
1599              }
1600              _SEH2_END;
1601           }
1602           else
1603           {
1604              KAPC_STATE ApcState;
1605 
1606              KeStackAttachProcess(&ptiHook->ppi->peProcess->Pcb, &ApcState);
1607              _SEH2_TRY
1608              {
1609                 ptiHook->pClientInfo->fsHooks = ptiHook->fsHooks;
1610                 ptiHook->pClientInfo->phkCurrent = NULL;
1611              }
1612              _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1613              {
1614                 ERR("Problem writing to Remote ClientInfo!\n");
1615              }
1616              _SEH2_END;
1617              KeUnstackDetachProcess(&ApcState);
1618           }
1619        }
1620     }
1621     else
1622     {
1623        InsertHeadList(&ptiHook->rpdesk->pDeskInfo->aphkStart[HOOKID_TO_INDEX(HookId)], &Hook->Chain);
1624        Hook->ptiHooked = NULL;
1625        //gptiCurrent->pDeskInfo->fsHooks |= HOOKID_TO_FLAG(HookId);
1626        ptiHook->rpdesk->pDeskInfo->fsHooks |= HOOKID_TO_FLAG(HookId);
1627        ptiHook->sphkCurrent = NULL;
1628        ptiHook->pClientInfo->phkCurrent = NULL;
1629     }
1630 
1631     RtlInitUnicodeString(&Hook->ModuleName, NULL);
1632 
1633     if (Mod)
1634     {
1635        Status = MmCopyFromCaller(&ModuleName,
1636                                   UnsafeModuleName,
1637                                   sizeof(UNICODE_STRING));
1638        if (!NT_SUCCESS(Status))
1639        {
1640           IntRemoveHook(Hook);
1641           SetLastNtError(Status);
1642           goto Cleanup; // Return NULL
1643        }
1644 
1645        Hook->ModuleName.Buffer = ExAllocatePoolWithTag( PagedPool,
1646                                                         ModuleName.MaximumLength,
1647                                                         TAG_HOOK);
1648        if (NULL == Hook->ModuleName.Buffer)
1649        {
1650           IntRemoveHook(Hook);
1651           EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
1652           goto Cleanup; // Return NULL
1653        }
1654 
1655        Hook->ModuleName.MaximumLength = ModuleName.MaximumLength;
1656        Status = MmCopyFromCaller( Hook->ModuleName.Buffer,
1657                                   ModuleName.Buffer,
1658                                   ModuleName.MaximumLength);
1659        if (!NT_SUCCESS(Status))
1660        {
1661           ExFreePoolWithTag(Hook->ModuleName.Buffer, TAG_HOOK);
1662           Hook->ModuleName.Buffer = NULL;
1663           IntRemoveHook(Hook);
1664           SetLastNtError(Status);
1665           goto Cleanup; // Return NULL
1666        }
1667 
1668        Hook->ModuleName.Length = ModuleName.Length;
1669        //// FIXME: Need to load from user32 to verify hMod before calling hook with hMod set!!!!
1670        //// Mod + offPfn == new HookProc Justin Case module is from another process.
1671        FIXME("NtUserSetWindowsHookEx Setting process hMod instance addressing.\n");
1672        /* Make proc relative to the module base */
1673        Hook->offPfn = (ULONG_PTR)((char *)HookProc - (char *)Mod);
1674     }
1675     else
1676        Hook->offPfn = 0;
1677 
1678     TRACE("Installing: HookId %d Global %s\n", HookId, !ThreadId ? "TRUE" : "FALSE");
1679     Ret = Handle;
1680 
1681 Cleanup:
1682     if (Hook)
1683         UserDereferenceObject(Hook);
1684     TRACE("Leave NtUserSetWindowsHookEx, ret=%p\n", Ret);
1685     UserLeave();
1686     return Ret;
1687 }
1688 
1689 BOOL
1690 APIENTRY
1691 NtUserUnhookWindowsHookEx(HHOOK Hook)
1692 {
1693     PHOOK HookObj;
1694     BOOL Ret = FALSE;
1695 
1696     TRACE("Enter NtUserUnhookWindowsHookEx\n");
1697     UserEnterExclusive();
1698 
1699     if (!(HookObj = IntGetHookObject(Hook)))
1700     {
1701         ERR("Invalid handle passed to NtUserUnhookWindowsHookEx\n");
1702         /* SetLastNtError(Status); */
1703         goto Exit; // Return FALSE
1704     }
1705 
1706     ASSERT(Hook == UserHMGetHandle(HookObj));
1707 
1708     IntRemoveHook(HookObj);
1709 
1710     UserDereferenceObject(HookObj);
1711 
1712     Ret = TRUE;
1713 
1714 Exit:
1715     TRACE("Leave NtUserUnhookWindowsHookEx, ret=%i\n", Ret);
1716     UserLeave();
1717     return Ret;
1718 }
1719 
1720 BOOL
1721 APIENTRY
1722 NtUserRegisterUserApiHook(
1723     PUNICODE_STRING m_dllname1,
1724     PUNICODE_STRING m_funname1,
1725     DWORD dwUnknown3,
1726     DWORD dwUnknown4)
1727 {
1728     BOOL ret;
1729     UNICODE_STRING strDllNameSafe;
1730     UNICODE_STRING strFuncNameSafe;
1731     NTSTATUS Status;
1732 
1733     /* Probe and capture parameters */
1734     Status = ProbeAndCaptureUnicodeString(&strDllNameSafe, UserMode, m_dllname1);
1735     if(!NT_SUCCESS(Status))
1736     {
1737         EngSetLastError(RtlNtStatusToDosError(Status));
1738         return FALSE;
1739     }
1740 
1741     Status = ProbeAndCaptureUnicodeString(&strFuncNameSafe, UserMode, m_funname1);
1742     if(!NT_SUCCESS(Status))
1743     {
1744         ReleaseCapturedUnicodeString(&strDllNameSafe, UserMode);
1745         EngSetLastError(RtlNtStatusToDosError(Status));
1746         return FALSE;
1747     }
1748 
1749     UserEnterExclusive();
1750 
1751     /* Call internal function */
1752     ret = UserRegisterUserApiHook(&strDllNameSafe, &strFuncNameSafe);
1753 
1754     UserLeave();
1755 
1756     /* Cleanup only in case of failure */
1757     if(ret == FALSE)
1758     {
1759         ReleaseCapturedUnicodeString(&strDllNameSafe, UserMode);
1760         ReleaseCapturedUnicodeString(&strFuncNameSafe, UserMode);
1761     }
1762 
1763     return ret;
1764 }
1765 
1766 BOOL
1767 APIENTRY
1768 NtUserUnregisterUserApiHook(VOID)
1769 {
1770     BOOL ret;
1771 
1772     UserEnterExclusive();
1773     ret = UserUnregisterUserApiHook();
1774     UserLeave();
1775 
1776     return ret;
1777 }
1778 
1779 /* EOF */
1780