You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
561 lines
18 KiB
561 lines
18 KiB
/****************************** 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);
|
|
}
|