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