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