/****************************** Module Header ******************************\ * Module Name: srvhook.c * * Copyright (c) 1985 - 1999, Microsoft Corporation * * Server side of hook calls and callbacks. * * 05-09-91 ScottLu Created. \***************************************************************************/ #include "precomp.h" #pragma hdrstop LRESULT fnHkINLPCBTCREATESTRUCT(UINT msg, WPARAM wParam, LPCBT_CREATEWND pcbt, PROC xpfnProc, BOOL bAnsi); /***************************************************************************\ * xxxHkCallHook * * This is the server-side stub that calls to the client to call the actual * hook function. * * History: * 05-09-91 ScottLu Rewrote to make all hooks work client/server! * 01-28-91 DavidPe Created. \***************************************************************************/ LRESULT xxxHkCallHook( PHOOK phk, int nCode, WPARAM wParam, LPARAM lParam) { LRESULT nRet; PROC pfnHk, pfnHookProc; PPFNCLIENT ppfnClient; PCWPSTRUCTEX pcwp; PCWPRETSTRUCTEX pcwpret; PCLIENTINFO pci; ULONG_PTR dwHookData; ULONG_PTR dwFlags; struct tagSMS *psms; #ifdef LATER /* * WindowsBug 246329 * The code was supposed to prevent the backdoor * for the surprise foreground change. * However, the implementation below locks the * entire system, preventing the legit, expected * foreground change too. It's obvious on MP systems. * E.g. in the case global hooks such as * GETMESSAGEHOOK are installed, the chances are * pretty high. * For this time being, instead of making the lock * per process or per thread, we decided to simply * disable the foreground lock during the hook * callback. */ TL tlSFWLock; BOOL fLockForeground; #endif DbgValidateHooks(phk, phk->iHook); /* * Only low level hooks are allowed in the RIT context. * Also asssert that the hook is not destroyed */ #ifdef REDIRECTION UserAssert((PtiCurrent() != gptiRit) || (phk->iHook == WH_MOUSE_LL) || (phk->iHook == WH_KEYBOARD_LL) || (phk->iHook == WH_HITTEST)); #else UserAssert((PtiCurrent() != gptiRit) || (phk->iHook == WH_MOUSE_LL) || (phk->iHook == WH_KEYBOARD_LL)); #endif // REDIRECTION /* * While we're still inside the critical section make sure the * hook hasn't been 'freed'. If so just return 0. */ if (phk->offPfn != 0) { pfnHookProc = PFNHOOK(phk); } else { return 0; } ppfnClient = (phk->flags & HF_ANSI) ? &gpsi->apfnClientA : &gpsi->apfnClientW; #ifdef LATER // per 246329 /* * LATER5.0 GerardoB. This might generate some hate reactions but I'm * not sure we want people hooking just to steal the foreground. * Prevent hookprocs from other processes from switching the foreground */ fLockForeground = (GETPTI(phk)->ppi != PpiCurrent()); if (fLockForeground) { ThreadLockSFWLockCount(&tlSFWLock); } #endif switch(phk->iHook) { case WH_CALLWNDPROC: case WH_CALLWNDPROCRET: if (phk->iHook == WH_CALLWNDPROC) { pcwp = (PCWPSTRUCTEX)lParam; psms = pcwp->psmsSender; } else { pcwpret = (PCWPRETSTRUCTEX)lParam; psms = pcwpret->psmsSender; } /* * If the sender has died or timed out, don't call the * hook because any memory the message points to may be invalid. */ if (psms != NULL && (psms->flags & (SMF_SENDERDIED | SMF_REPLY))) { nRet = 0; break; } /* * This is the hardest of the hooks because we need to thunk through * the message hooks in order to deal with synchronously sent messages * that point to structures - to get the structures passed across * alright, etc. * * This will call a special client-side routine that'll rebundle the * arguments and call the hook in the right format. * * Currently, the message thunk callbacks to the client-side don't take * enough parameters to pass wParam (which == fInterThread send msg). * To do this, call one of two functions. */ pci = GetClientInfo(); if (phk->iHook == WH_CALLWNDPROC) { pfnHk = ppfnClient->pfnHkINLPCWPSTRUCT; } else { pfnHk = ppfnClient->pfnHkINLPCWPRETSTRUCT; try { pci->dwHookData = pcwpret->lResult; } except (W32ExceptionHandler(FALSE, RIP_WARNING)) { nRet = 0; break; } } /* * Save current hook state. */ try { dwFlags = pci->CI_flags & CI_INTERTHREAD_HOOK; dwHookData = pci->dwHookData; if (wParam) { pci->CI_flags |= CI_INTERTHREAD_HOOK; } else { pci->CI_flags &= ~CI_INTERTHREAD_HOOK; } } except (W32ExceptionHandler(FALSE, RIP_WARNING)) { nRet = 0; break; } if (phk->iHook == WH_CALLWNDPROC) { nRet = ScSendMessageSMS( PW(pcwp->hwnd), pcwp->message, pcwp->wParam, pcwp->lParam, (ULONG_PTR)pfnHookProc, pfnHk, (phk->flags & HF_ANSI) ? (SCMS_FLAGS_ANSI|SCMS_FLAGS_INONLY) : SCMS_FLAGS_INONLY, psms); } else { nRet = ScSendMessageSMS( PW(pcwpret->hwnd), pcwpret->message, pcwpret->wParam, pcwpret->lParam, (ULONG_PTR)pfnHookProc, pfnHk, (phk->flags & HF_ANSI) ? (SCMS_FLAGS_ANSI|SCMS_FLAGS_INONLY) : SCMS_FLAGS_INONLY, psms); } /* * Restore previous hook state. */ try { pci->CI_flags ^= ((pci->CI_flags ^ dwFlags) & CI_INTERTHREAD_HOOK); pci->dwHookData = dwHookData; } except (W32ExceptionHandler(FALSE, RIP_WARNING)) { nRet = 0; } break; case WH_CBT: /* * There are many different types of CBT hooks! */ switch(nCode) { case HCBT_CLICKSKIPPED: goto MouseHook; break; case HCBT_CREATEWND: /* * This hook type points to a CREATESTRUCT, so we need to * be fancy with it's thunking, because a CREATESTRUCT contains * a pointer to CREATEPARAMS which can be anything... so * funnel this through our message thunks. */ nRet = fnHkINLPCBTCREATESTRUCT( MAKELONG((WORD)nCode, (WORD)phk->iHook), wParam, (LPCBT_CREATEWND)lParam, pfnHookProc, (phk->flags & HF_ANSI) ? TRUE : FALSE); break; #ifdef REDIRECTION case HCBT_GETCURSORPOS: /* * This hook type points to a POINT structure, so it's pretty * simple. */ nRet = fnHkINLPPOINT(MAKELONG((UINT)nCode, (UINT)phk->iHook), wParam, (LPPOINT)lParam, (ULONG_PTR)pfnHookProc, ppfnClient->pfnDispatchHook); break; #endif // REDIRECTION case HCBT_MOVESIZE: /* * This hook type points to a RECT structure, so it's pretty * simple. */ nRet = fnHkINLPRECT(MAKELONG((UINT)nCode, (UINT)phk->iHook), wParam, (LPRECT)lParam, (ULONG_PTR)pfnHookProc, ppfnClient->pfnDispatchHook); break; case HCBT_ACTIVATE: /* * This hook type points to a CBTACTIVATESTRUCT */ nRet = fnHkINLPCBTACTIVATESTRUCT(MAKELONG((UINT)nCode, (UINT)phk->iHook), wParam, (LPCBTACTIVATESTRUCT)lParam, (ULONG_PTR)pfnHookProc, ppfnClient->pfnDispatchHook); break; default: /* * The rest of the cbt hooks are all dword parameters. */ nRet = fnHkINDWORD(MAKELONG((UINT)nCode, (UINT)phk->iHook), wParam, lParam, (ULONG_PTR)pfnHookProc, ppfnClient->pfnDispatchHook, &phk->flags); break; } break; case WH_FOREGROUNDIDLE: /* * These are dword parameters and are therefore real easy. * */ nRet = fnHkINDWORD(MAKELONG((UINT)nCode, (UINT)phk->iHook), wParam, lParam, (ULONG_PTR)pfnHookProc, ppfnClient->pfnDispatchHook, &phk->flags); break; case WH_SHELL: if (nCode == HSHELL_GETMINRECT) { /* * This hook type points to a RECT structure, so it's pretty * simple. */ nRet = fnHkINLPRECT(MAKELONG((UINT)nCode, (UINT)phk->iHook), wParam, (LPRECT)lParam, (ULONG_PTR)pfnHookProc, ppfnClient->pfnDispatchHook); break; } /* * Otherwise fall through to the simple case of DWORD below */ case WH_KEYBOARD: /* * These are dword parameters and are therefore real easy. */ nRet = fnHkINDWORD(MAKELONG((UINT)nCode, (UINT)phk->iHook), wParam, lParam, (ULONG_PTR)pfnHookProc, ppfnClient->pfnDispatchHook, &phk->flags); break; case WH_MSGFILTER: case WH_SYSMSGFILTER: case WH_GETMESSAGE: /* * These take an lpMsg as their last parameter. Since these are * exclusively posted parameters, and since nowhere on the server * do we post a message with a pointer to some other structure in * it, the lpMsg structure contents can all be treated verbatim. */ nRet = fnHkINLPMSG(MAKELONG((UINT)nCode, (UINT)phk->iHook), wParam, (LPMSG)lParam, (ULONG_PTR)pfnHookProc, ppfnClient->pfnDispatchHook, (phk->flags & HF_ANSI) ? TRUE : FALSE, &phk->flags); break; case WH_JOURNALPLAYBACK: #ifdef HOOKBATCH /* * If this hook has cached playback info then we need to grab * the info out of the cache. */ if (phk->cEventMessages) { if (nCode == HC_GETNEXT) { LPEVENTMSG pEventMsg; pEventMsg = (LPEVENTMSG)lParam; if (phk->flags & HF_NEEDHC_SKIP) phk->iCurrentEvent++; if (phk->iCurrentEvent < phk->cEventMessages) { *pEventMsg = phk->aEventCache[phk->iCurrentEvent]; } else { /* * Free the cache set if it is still around */ if (phk->aEventCache) { UserFreePool(phk->aEventCache); phk->aEventCache = NULL; } phk->cEventMessages = 0; phk->iCurrentEvent = 0; goto MakeClientJournalPlaybackCall; } /* * Return the time and zero the batched time so if we sleep * this time we won't sleep again next time */ nRet = pEventMsg->time; if (nRet) phk->aEventCache[phk->iCurrentEvent].time = 0; } else if (nCode == HC_SKIP) { phk->iCurrentEvent++; nRet = 0; } } else { #endif // HOOKBATCH /* * In order to avoid a client/server transition for HC_SKIP we * piggy-back it on top of the next journal playback event and * send it from there. */ // MakeClientJournalPlaybackCall: nRet = fnHkOPTINLPEVENTMSG(MAKELONG((UINT)nCode, (UINT)phk->iHook), (WPARAM)PtoHq(phk), (LPEVENTMSG)lParam, (ULONG_PTR)pfnHookProc, ppfnClient->pfnDispatchHook); #ifdef HOOKBATCH } /* * Determine if we received a cached set of events if so then store * them away off of the hook. * paramL will be the number of events. * paramH will be the array of events. */ if ((nCode == HC_GETNEXT) && (((LPEVENTMSG)lParam)->message == 0x12341234)) { NTSTATUS Status; LPEVENTMSG pEventMsg = (LPEVENTMSG)lParam; /* * We should not be getting another cached set if we aren't * done with the first set */ UserAssert((phk->cEventMessages == 0) || (phk->cEventMessages >= phk->iCurrentEvent)); UserAssert((pEventMsg->paramL < 500) && (pEventMsg->paramL > 1)); /* * Free the last cache set if it is still around */ if (phk->aEventCache) { UserFreePool(phk->aEventCache); phk->aEventCache = NULL; } if (phk->aEventCache = LocalAlloc(LPTR, pEventMsg->paramL*sizeof(EVENTMSG))) { PETHREAD Thread = PsGetCurrentThread(); Status = ZwReadVirtualMemory(Thread->Process->ProcessHandle, (PVOID)pEventMsg->paramH, phk->aEventCache, pEventMsg->paramL*sizeof(EVENTMSG), NULL); if (NT_SUCCESS(Status)) { phk->cEventMessages = pEventMsg->paramL; phk->iCurrentEvent = 0; /* * Fill in the real EventMsg for this message */ *pEventMsg = phk->aEventCache[0]; phk->aEventCache[0].time = 0; } } else { phk->cEventMessages = 0; phk->iCurrentEvent = 0; } } #endif // HOOKBATCH phk->flags &= ~HF_NEEDHC_SKIP; break; case WH_JOURNALRECORD: nRet = fnHkOPTINLPEVENTMSG(MAKELONG((UINT)nCode, (UINT)phk->iHook), wParam, (LPEVENTMSG)lParam, (ULONG_PTR)pfnHookProc, ppfnClient->pfnDispatchHook); break; case WH_DEBUG: /* * This takes an lpDebugHookStruct. */ nRet = fnHkINLPDEBUGHOOKSTRUCT(MAKELONG((UINT)nCode, (UINT)phk->iHook), wParam, (LPDEBUGHOOKINFO)lParam, (ULONG_PTR)pfnHookProc, ppfnClient->pfnDispatchHook); break; case WH_KEYBOARD_LL: /* * This takes an lpKbdHookStruct. */ nRet = fnHkINLPKBDLLHOOKSTRUCT(MAKELONG((UINT)nCode, (UINT)phk->iHook), wParam, (LPKBDLLHOOKSTRUCT)lParam, (ULONG_PTR)pfnHookProc, ppfnClient->pfnDispatchHook); break; case WH_MOUSE_LL: /* * This takes an lpMsllHookStruct. */ nRet = fnHkINLPMSLLHOOKSTRUCT(MAKELONG((UINT)nCode, (UINT)phk->iHook), wParam, (LPMSLLHOOKSTRUCT)lParam, (ULONG_PTR)pfnHookProc, ppfnClient->pfnDispatchHook); break; case WH_MOUSE: /* * This takes an lpMouseHookStructEx. */ MouseHook: nRet = fnHkINLPMOUSEHOOKSTRUCTEX(MAKELONG((UINT)nCode, (UINT)phk->iHook), wParam, (LPMOUSEHOOKSTRUCTEX)lParam, (ULONG_PTR)pfnHookProc, ppfnClient->pfnDispatchHook, &phk->flags); break; #ifdef REDIRECTION case WH_HITTEST: /* * This takes an lpHTHookStruct. */ nRet = fnHkINLPHTHOOKSTRUCT(MAKELONG((UINT)nCode, (UINT)phk->iHook), wParam, (LPHTHOOKSTRUCT)lParam, (ULONG_PTR)pfnHookProc, ppfnClient->pfnDispatchHook); break; #endif // REDIRECTION } #ifdef LATER // per 246329 if (fLockForeground) { ThreadUnlockSFWLockCount(&tlSFWLock); } #endif return nRet; } /***************************************************************************\ * fnHkINLPCWPEXSTRUCT * * This gets thunked through the message thunks, so it has the format * of a c/s message thunk call. * * 05-09-91 ScottLu Created. \***************************************************************************/ LRESULT fnHkINLPCWPEXSTRUCT( PWND pwnd, UINT message, WPARAM wParam, LPARAM lParam, ULONG_PTR xParam) { CWPSTRUCTEX cwp; PCLIENTINFO pci = GetClientInfo(); BOOL bInterThread; UNREFERENCED_PARAMETER(xParam); cwp.hwnd = HW(pwnd); cwp.message = message; cwp.wParam = wParam; cwp.lParam = lParam; cwp.psmsSender = NULL; try { bInterThread = (pci->CI_flags & CI_INTERTHREAD_HOOK) != 0; } except (W32ExceptionHandler(FALSE, RIP_WARNING)) { return 0; } return xxxCallNextHookEx(HC_ACTION, bInterThread, (LPARAM)&cwp); } LRESULT fnHkINLPCWPRETEXSTRUCT( PWND pwnd, UINT message, WPARAM wParam, LPARAM lParam, ULONG_PTR xParam) { CWPRETSTRUCTEX cwp; PCLIENTINFO pci = GetClientInfo(); BOOL bInterThread; UNREFERENCED_PARAMETER(xParam); cwp.hwnd = HW(pwnd); cwp.message = message; cwp.wParam = wParam; cwp.lParam = lParam; cwp.psmsSender = NULL; try { cwp.lResult = pci->dwHookData; bInterThread = (pci->CI_flags & CI_INTERTHREAD_HOOK) != 0; } except (W32ExceptionHandler(FALSE, RIP_WARNING)) { return 0; } return xxxCallNextHookEx(HC_ACTION, bInterThread, (LPARAM)&cwp); }