mirror of
https://github.com/reactos/reactos.git
synced 2024-08-24 20:28:57 +00:00
1782 lines
50 KiB
C
1782 lines
50 KiB
C
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS kernel
|
|
* PURPOSE: Window hooks
|
|
* FILE: win32ss/user/ntuser/hook.c
|
|
* PROGRAMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
|
|
* James Tabor (james.tabor@rectos.org)
|
|
* Rafal Harabien (rafalh@reactos.org)
|
|
* NOTE: Most of this code was adapted from Wine,
|
|
* Copyright (C) 2002 Alexandre Julliard
|
|
*/
|
|
|
|
#include <win32k.h>
|
|
DBG_DEFAULT_CHANNEL(UserHook);
|
|
|
|
typedef struct _HOOKPACK
|
|
{
|
|
PHOOK pHk;
|
|
LPARAM lParam;
|
|
PVOID pHookStructs;
|
|
} HOOKPACK, *PHOOKPACK;
|
|
|
|
UNICODE_STRING strUahModule;
|
|
UNICODE_STRING strUahInitFunc;
|
|
PPROCESSINFO ppiUahServer;
|
|
|
|
/* PRIVATE FUNCTIONS *********************************************************/
|
|
|
|
/* Calls ClientLoadLibrary in user32 in order to load or unload a module */
|
|
BOOL
|
|
IntLoadHookModule(int iHookID, HHOOK hHook, BOOL Unload)
|
|
{
|
|
PPROCESSINFO ppi;
|
|
BOOL bResult;
|
|
|
|
ppi = PsGetCurrentProcessWin32Process();
|
|
|
|
TRACE("IntLoadHookModule. Client PID: %p\n", PsGetProcessId(ppi->peProcess));
|
|
|
|
/* Check if this is the api hook */
|
|
if(iHookID == WH_APIHOOK)
|
|
{
|
|
if(!Unload && !(ppi->W32PF_flags & W32PF_APIHOOKLOADED))
|
|
{
|
|
/* A callback in user mode can trigger UserLoadApiHook to be called and
|
|
as a result IntLoadHookModule will be called recursively.
|
|
To solve this we set the flag that means that the appliaction has
|
|
loaded the api hook before the callback and in case of error we remove it */
|
|
ppi->W32PF_flags |= W32PF_APIHOOKLOADED;
|
|
|
|
/* Call ClientLoadLibrary in user32 */
|
|
bResult = co_IntClientLoadLibrary(&strUahModule, &strUahInitFunc, Unload, TRUE);
|
|
TRACE("co_IntClientLoadLibrary returned %d\n", bResult );
|
|
if (!bResult)
|
|
{
|
|
/* Remove the flag we set before */
|
|
ppi->W32PF_flags &= ~W32PF_APIHOOKLOADED;
|
|
}
|
|
return bResult;
|
|
}
|
|
else if(Unload && (ppi->W32PF_flags & W32PF_APIHOOKLOADED))
|
|
{
|
|
/* Call ClientLoadLibrary in user32 */
|
|
bResult = co_IntClientLoadLibrary(NULL, NULL, Unload, TRUE);
|
|
if (bResult)
|
|
{
|
|
ppi->W32PF_flags &= ~W32PF_APIHOOKLOADED;
|
|
}
|
|
return bResult;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
STUB;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
IntHookModuleUnloaded:
|
|
Sends a internal message to all threads of the requested desktop
|
|
and notifies them that a global hook was destroyed
|
|
and an injected module must be unloaded.
|
|
As a result, IntLoadHookModule will be called for all the threads that
|
|
will receive the special purpose internal message.
|
|
*/
|
|
BOOL
|
|
IntHookModuleUnloaded(PDESKTOP pdesk, int iHookID, HHOOK hHook)
|
|
{
|
|
PTHREADINFO ptiCurrent;
|
|
PLIST_ENTRY ListEntry;
|
|
PPROCESSINFO ppiCsr;
|
|
|
|
TRACE("IntHookModuleUnloaded: iHookID=%d\n", iHookID);
|
|
|
|
ppiCsr = PsGetProcessWin32Process(gpepCSRSS);
|
|
|
|
ListEntry = pdesk->PtiList.Flink;
|
|
while(ListEntry != &pdesk->PtiList)
|
|
{
|
|
ptiCurrent = CONTAINING_RECORD(ListEntry, THREADINFO, PtiLink);
|
|
|
|
/* FIXME: Do some more security checks here */
|
|
|
|
/* FIXME: HACK: The first check is a reactos specific hack for system threads */
|
|
if(!PsIsSystemProcess(ptiCurrent->ppi->peProcess) &&
|
|
ptiCurrent->ppi != ppiCsr)
|
|
{
|
|
if(ptiCurrent->ppi->W32PF_flags & W32PF_APIHOOKLOADED)
|
|
{
|
|
TRACE("IntHookModuleUnloaded: sending message to PID %p, ppi=%p\n", PsGetProcessId(ptiCurrent->ppi->peProcess), ptiCurrent->ppi);
|
|
co_MsqSendMessageAsync( ptiCurrent,
|
|
0,
|
|
iHookID,
|
|
TRUE,
|
|
(LPARAM)hHook,
|
|
NULL,
|
|
0,
|
|
FALSE,
|
|
MSQ_INJECTMODULE);
|
|
}
|
|
}
|
|
ListEntry = ListEntry->Flink;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
FASTCALL
|
|
UserLoadApiHook(VOID)
|
|
{
|
|
return IntLoadHookModule(WH_APIHOOK, 0, FALSE);
|
|
}
|
|
|
|
BOOL
|
|
FASTCALL
|
|
UserRegisterUserApiHook(
|
|
PUNICODE_STRING pstrDllName,
|
|
PUNICODE_STRING pstrFuncName)
|
|
{
|
|
PTHREADINFO pti, ptiCurrent;
|
|
HWND *List;
|
|
PWND DesktopWindow, pwndCurrent;
|
|
ULONG i;
|
|
PPROCESSINFO ppiCsr;
|
|
|
|
pti = PsGetCurrentThreadWin32Thread();
|
|
ppiCsr = PsGetProcessWin32Process(gpepCSRSS);
|
|
|
|
/* Fail if the api hook is already registered */
|
|
if(gpsi->dwSRVIFlags & SRVINFO_APIHOOK)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
TRACE("UserRegisterUserApiHook. Server PID: %p\n", PsGetProcessId(pti->ppi->peProcess));
|
|
|
|
/* Register the api hook */
|
|
gpsi->dwSRVIFlags |= SRVINFO_APIHOOK;
|
|
|
|
strUahModule = *pstrDllName;
|
|
strUahInitFunc = *pstrFuncName;
|
|
ppiUahServer = pti->ppi;
|
|
|
|
/* Broadcast an internal message to every top level window */
|
|
DesktopWindow = UserGetWindowObject(IntGetDesktopWindow());
|
|
List = IntWinListChildren(DesktopWindow);
|
|
|
|
if (List != NULL)
|
|
{
|
|
for (i = 0; List[i]; i++)
|
|
{
|
|
pwndCurrent = UserGetWindowObject(List[i]);
|
|
if(pwndCurrent == NULL)
|
|
{
|
|
continue;
|
|
}
|
|
ptiCurrent = pwndCurrent->head.pti;
|
|
|
|
/* FIXME: The first check is a reactos specific hack for system threads */
|
|
if(PsIsSystemProcess(ptiCurrent->ppi->peProcess) ||
|
|
ptiCurrent->ppi == ppiCsr)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
co_MsqSendMessageAsync( ptiCurrent,
|
|
0,
|
|
WH_APIHOOK,
|
|
FALSE, /* Load the module */
|
|
0,
|
|
NULL,
|
|
0,
|
|
FALSE,
|
|
MSQ_INJECTMODULE);
|
|
}
|
|
ExFreePoolWithTag(List, USERTAG_WINDOWLIST);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
FASTCALL
|
|
UserUnregisterUserApiHook(VOID)
|
|
{
|
|
PTHREADINFO pti;
|
|
|
|
pti = PsGetCurrentThreadWin32Thread();
|
|
|
|
/* Fail if the api hook is not registered */
|
|
if(!(gpsi->dwSRVIFlags & SRVINFO_APIHOOK))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
/* Only the process that registered the api hook can uregister it */
|
|
if(ppiUahServer != PsGetCurrentProcessWin32Process())
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
TRACE("UserUnregisterUserApiHook. Server PID: %p\n", PsGetProcessId(pti->ppi->peProcess));
|
|
|
|
/* Unregister the api hook */
|
|
gpsi->dwSRVIFlags &= ~SRVINFO_APIHOOK;
|
|
ppiUahServer = NULL;
|
|
ReleaseCapturedUnicodeString(&strUahModule, UserMode);
|
|
ReleaseCapturedUnicodeString(&strUahInitFunc, UserMode);
|
|
|
|
/* Notify all applications that the api hook module must be unloaded */
|
|
return IntHookModuleUnloaded(pti->rpdesk, WH_APIHOOK, 0);
|
|
}
|
|
|
|
static
|
|
LRESULT
|
|
FASTCALL
|
|
co_IntCallLowLevelHook(PHOOK Hook,
|
|
INT Code,
|
|
WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
NTSTATUS Status;
|
|
PTHREADINFO pti;
|
|
PHOOKPACK pHP;
|
|
INT Size = 0;
|
|
UINT uTimeout = 300;
|
|
BOOL Block = FALSE;
|
|
ULONG_PTR uResult = 0;
|
|
|
|
if (Hook->ptiHooked)
|
|
pti = Hook->ptiHooked;
|
|
else
|
|
pti = Hook->head.pti;
|
|
|
|
pHP = ExAllocatePoolWithTag(NonPagedPool, sizeof(HOOKPACK), TAG_HOOK);
|
|
if (!pHP) return 0;
|
|
|
|
pHP->pHk = Hook;
|
|
pHP->lParam = lParam;
|
|
pHP->pHookStructs = NULL;
|
|
|
|
// This prevents stack corruption from the caller.
|
|
switch(Hook->HookId)
|
|
{
|
|
case WH_JOURNALPLAYBACK:
|
|
case WH_JOURNALRECORD:
|
|
uTimeout = 0;
|
|
Size = sizeof(EVENTMSG);
|
|
break;
|
|
case WH_KEYBOARD_LL:
|
|
Size = sizeof(KBDLLHOOKSTRUCT);
|
|
break;
|
|
case WH_MOUSE_LL:
|
|
Size = sizeof(MSLLHOOKSTRUCT);
|
|
break;
|
|
case WH_MOUSE:
|
|
uTimeout = 200;
|
|
Block = TRUE;
|
|
Size = sizeof(MOUSEHOOKSTRUCT);
|
|
break;
|
|
case WH_KEYBOARD:
|
|
uTimeout = 200;
|
|
Block = TRUE;
|
|
break;
|
|
}
|
|
|
|
if (Size)
|
|
{
|
|
pHP->pHookStructs = ExAllocatePoolWithTag(NonPagedPool, Size, TAG_HOOK);
|
|
if (pHP->pHookStructs) RtlCopyMemory(pHP->pHookStructs, (PVOID)lParam, Size);
|
|
}
|
|
|
|
/* FIXME: Should get timeout from
|
|
* HKEY_CURRENT_USER\Control Panel\Desktop\LowLevelHooksTimeout */
|
|
Status = co_MsqSendMessage( pti,
|
|
IntToPtr(Code), // hWnd
|
|
Hook->HookId, // Msg
|
|
wParam,
|
|
(LPARAM)pHP,
|
|
uTimeout,
|
|
Block,
|
|
MSQ_ISHOOK,
|
|
&uResult);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ERR("Error Hook Call SendMsg. %d Status: 0x%x\n", Hook->HookId, Status);
|
|
if (pHP->pHookStructs) ExFreePoolWithTag(pHP->pHookStructs, TAG_HOOK);
|
|
ExFreePoolWithTag(pHP, TAG_HOOK);
|
|
}
|
|
return NT_SUCCESS(Status) ? uResult : 0;
|
|
}
|
|
|
|
|
|
//
|
|
// Dispatch MsgQueue Hook Call processor!
|
|
//
|
|
LRESULT
|
|
APIENTRY
|
|
co_CallHook( INT HookId,
|
|
INT Code,
|
|
WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
LRESULT Result = 0;
|
|
PHOOK phk;
|
|
PHOOKPACK pHP = (PHOOKPACK)lParam;
|
|
|
|
phk = pHP->pHk;
|
|
lParam = pHP->lParam;
|
|
|
|
switch(HookId)
|
|
{
|
|
case WH_JOURNALPLAYBACK:
|
|
case WH_JOURNALRECORD:
|
|
case WH_KEYBOARD_LL:
|
|
case WH_MOUSE_LL:
|
|
case WH_MOUSE:
|
|
lParam = (LPARAM)pHP->pHookStructs;
|
|
case WH_KEYBOARD:
|
|
break;
|
|
}
|
|
|
|
if (!UserObjectInDestroy(UserHMGetHandle(phk))) //// Fix CORE-10549.
|
|
{
|
|
/* The odds are high for this to be a Global call. */
|
|
Result = co_IntCallHookProc( HookId,
|
|
Code,
|
|
wParam,
|
|
lParam,
|
|
phk->Proc,
|
|
phk->ihmod,
|
|
phk->offPfn,
|
|
phk->Ansi,
|
|
&phk->ModuleName);
|
|
}
|
|
/* The odds so high, no one is waiting for the results. */
|
|
if (pHP->pHookStructs) ExFreePoolWithTag(pHP->pHookStructs, TAG_HOOK);
|
|
ExFreePoolWithTag(pHP, TAG_HOOK);
|
|
return Result;
|
|
}
|
|
|
|
static
|
|
LRESULT
|
|
APIENTRY
|
|
co_HOOK_CallHookNext( PHOOK Hook,
|
|
INT Code,
|
|
WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
TRACE("Calling Next HOOK %d\n", Hook->HookId);
|
|
|
|
return co_IntCallHookProc( Hook->HookId,
|
|
Code,
|
|
wParam,
|
|
lParam,
|
|
Hook->Proc,
|
|
Hook->ihmod,
|
|
Hook->offPfn,
|
|
Hook->Ansi,
|
|
&Hook->ModuleName);
|
|
}
|
|
|
|
static
|
|
LRESULT
|
|
FASTCALL
|
|
co_IntCallDebugHook(PHOOK Hook,
|
|
int Code,
|
|
WPARAM wParam,
|
|
LPARAM lParam,
|
|
BOOL Ansi)
|
|
{
|
|
LRESULT lResult = 0;
|
|
ULONG Size;
|
|
DEBUGHOOKINFO Debug;
|
|
PVOID HooklParam = NULL;
|
|
BOOL BadChk = FALSE;
|
|
|
|
if (lParam)
|
|
{
|
|
_SEH2_TRY
|
|
{
|
|
ProbeForRead((PVOID)lParam,
|
|
sizeof(DEBUGHOOKINFO),
|
|
1);
|
|
|
|
RtlCopyMemory(&Debug,
|
|
(PVOID)lParam,
|
|
sizeof(DEBUGHOOKINFO));
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
BadChk = TRUE;
|
|
}
|
|
_SEH2_END;
|
|
|
|
if (BadChk)
|
|
{
|
|
ERR("HOOK WH_DEBUG read from lParam ERROR!\n");
|
|
return lResult;
|
|
}
|
|
}
|
|
else
|
|
return lResult; /* Need lParam! */
|
|
|
|
switch (wParam)
|
|
{
|
|
case WH_CBT:
|
|
{
|
|
switch (Debug.code)
|
|
{
|
|
case HCBT_CLICKSKIPPED:
|
|
Size = sizeof(MOUSEHOOKSTRUCTEX);
|
|
break;
|
|
|
|
case HCBT_MOVESIZE:
|
|
Size = sizeof(RECT);
|
|
break;
|
|
|
|
case HCBT_ACTIVATE:
|
|
Size = sizeof(CBTACTIVATESTRUCT);
|
|
break;
|
|
|
|
case HCBT_CREATEWND: /* Handle ANSI? */
|
|
Size = sizeof(CBT_CREATEWND);
|
|
/* What shall we do? Size += sizeof(HOOKPROC_CBT_CREATEWND_EXTRA_ARGUMENTS); same as CREATESTRUCTEX */
|
|
break;
|
|
|
|
default:
|
|
Size = sizeof(LPARAM);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WH_MOUSE_LL:
|
|
Size = sizeof(MSLLHOOKSTRUCT);
|
|
break;
|
|
|
|
case WH_KEYBOARD_LL:
|
|
Size = sizeof(KBDLLHOOKSTRUCT);
|
|
break;
|
|
|
|
case WH_MSGFILTER:
|
|
case WH_SYSMSGFILTER:
|
|
case WH_GETMESSAGE:
|
|
Size = sizeof(MSG);
|
|
break;
|
|
|
|
case WH_JOURNALPLAYBACK:
|
|
case WH_JOURNALRECORD:
|
|
Size = sizeof(EVENTMSG);
|
|
break;
|
|
|
|
case WH_FOREGROUNDIDLE:
|
|
case WH_KEYBOARD:
|
|
case WH_SHELL:
|
|
default:
|
|
Size = sizeof(LPARAM);
|
|
}
|
|
|
|
if (Size > sizeof(LPARAM))
|
|
HooklParam = ExAllocatePoolWithTag(NonPagedPool, Size, TAG_HOOK);
|
|
|
|
if (HooklParam)
|
|
{
|
|
_SEH2_TRY
|
|
{
|
|
ProbeForRead((PVOID)Debug.lParam,
|
|
Size,
|
|
1);
|
|
|
|
RtlCopyMemory(HooklParam,
|
|
(PVOID)Debug.lParam,
|
|
Size);
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
BadChk = TRUE;
|
|
}
|
|
_SEH2_END;
|
|
|
|
if (BadChk)
|
|
{
|
|
ERR("HOOK WH_DEBUG read from Debug.lParam ERROR!\n");
|
|
ExFreePoolWithTag(HooklParam, TAG_HOOK);
|
|
return lResult;
|
|
}
|
|
}
|
|
|
|
if (HooklParam) Debug.lParam = (LPARAM)HooklParam;
|
|
lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&Debug);
|
|
if (HooklParam) ExFreePoolWithTag(HooklParam, TAG_HOOK);
|
|
|
|
return lResult;
|
|
}
|
|
|
|
static
|
|
LRESULT
|
|
APIENTRY
|
|
co_UserCallNextHookEx(PHOOK Hook,
|
|
int Code,
|
|
WPARAM wParam,
|
|
LPARAM lParam,
|
|
BOOL Ansi)
|
|
{
|
|
LRESULT lResult = 0;
|
|
BOOL BadChk = FALSE;
|
|
|
|
/* Handle this one first. */
|
|
if ((Hook->HookId == WH_MOUSE) ||
|
|
(Hook->HookId == WH_CBT && Code == HCBT_CLICKSKIPPED))
|
|
{
|
|
MOUSEHOOKSTRUCTEX Mouse;
|
|
if (lParam)
|
|
{
|
|
_SEH2_TRY
|
|
{
|
|
ProbeForRead((PVOID)lParam,
|
|
sizeof(MOUSEHOOKSTRUCTEX),
|
|
1);
|
|
|
|
RtlCopyMemory(&Mouse,
|
|
(PVOID)lParam,
|
|
sizeof(MOUSEHOOKSTRUCTEX));
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
BadChk = TRUE;
|
|
}
|
|
_SEH2_END;
|
|
|
|
if (BadChk)
|
|
{
|
|
ERR("HOOK WH_MOUSE read from lParam ERROR!\n");
|
|
}
|
|
}
|
|
|
|
if (!BadChk)
|
|
{
|
|
lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&Mouse);
|
|
}
|
|
|
|
return lResult;
|
|
}
|
|
|
|
switch(Hook->HookId)
|
|
{
|
|
case WH_MOUSE_LL:
|
|
{
|
|
MSLLHOOKSTRUCT Mouse;
|
|
|
|
if (lParam)
|
|
{
|
|
_SEH2_TRY
|
|
{
|
|
ProbeForRead((PVOID)lParam,
|
|
sizeof(MSLLHOOKSTRUCT),
|
|
1);
|
|
|
|
RtlCopyMemory(&Mouse,
|
|
(PVOID)lParam,
|
|
sizeof(MSLLHOOKSTRUCT));
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
BadChk = TRUE;
|
|
}
|
|
_SEH2_END;
|
|
|
|
if (BadChk)
|
|
{
|
|
ERR("HOOK WH_MOUSE_LL read from lParam ERROR!\n");
|
|
}
|
|
}
|
|
|
|
if (!BadChk)
|
|
{
|
|
lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&Mouse);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case WH_KEYBOARD_LL:
|
|
{
|
|
KBDLLHOOKSTRUCT Keyboard;
|
|
|
|
if (lParam)
|
|
{
|
|
_SEH2_TRY
|
|
{
|
|
ProbeForRead((PVOID)lParam,
|
|
sizeof(KBDLLHOOKSTRUCT),
|
|
1);
|
|
|
|
RtlCopyMemory(&Keyboard,
|
|
(PVOID)lParam,
|
|
sizeof(KBDLLHOOKSTRUCT));
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
BadChk = TRUE;
|
|
}
|
|
_SEH2_END;
|
|
|
|
if (BadChk)
|
|
{
|
|
ERR("HOOK WH_KEYBORD_LL read from lParam ERROR!\n");
|
|
}
|
|
}
|
|
|
|
if (!BadChk)
|
|
{
|
|
lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&Keyboard);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case WH_MSGFILTER:
|
|
case WH_SYSMSGFILTER:
|
|
case WH_GETMESSAGE:
|
|
{
|
|
MSG Msg;
|
|
|
|
if (lParam)
|
|
{
|
|
_SEH2_TRY
|
|
{
|
|
ProbeForRead((PVOID)lParam,
|
|
sizeof(MSG),
|
|
1);
|
|
|
|
RtlCopyMemory(&Msg,
|
|
(PVOID)lParam,
|
|
sizeof(MSG));
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
BadChk = TRUE;
|
|
}
|
|
_SEH2_END;
|
|
|
|
if (BadChk)
|
|
{
|
|
ERR("HOOK WH_XMESSAGEX read from lParam ERROR!\n");
|
|
}
|
|
}
|
|
|
|
if (!BadChk)
|
|
{
|
|
lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&Msg);
|
|
|
|
if (lParam && (Hook->HookId == WH_GETMESSAGE))
|
|
{
|
|
_SEH2_TRY
|
|
{
|
|
ProbeForWrite((PVOID)lParam,
|
|
sizeof(MSG),
|
|
1);
|
|
|
|
RtlCopyMemory((PVOID)lParam,
|
|
&Msg,
|
|
sizeof(MSG));
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
BadChk = TRUE;
|
|
}
|
|
_SEH2_END;
|
|
|
|
if (BadChk)
|
|
{
|
|
ERR("HOOK WH_GETMESSAGE write to lParam ERROR!\n");
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case WH_CBT:
|
|
TRACE("HOOK WH_CBT!\n");
|
|
switch (Code)
|
|
{
|
|
case HCBT_CREATEWND:
|
|
{
|
|
LPCBT_CREATEWNDW pcbtcww = (LPCBT_CREATEWNDW)lParam;
|
|
|
|
TRACE("HOOK HCBT_CREATEWND\n");
|
|
_SEH2_TRY
|
|
{
|
|
if (Ansi)
|
|
{
|
|
ProbeForRead( pcbtcww,
|
|
sizeof(CBT_CREATEWNDA),
|
|
1);
|
|
ProbeForWrite(pcbtcww->lpcs,
|
|
sizeof(CREATESTRUCTA),
|
|
1);
|
|
ProbeForRead( pcbtcww->lpcs->lpszName,
|
|
sizeof(CHAR),
|
|
1);
|
|
|
|
if (!IS_ATOM(pcbtcww->lpcs->lpszClass))
|
|
{
|
|
_Analysis_assume_(pcbtcww->lpcs->lpszClass != NULL);
|
|
ProbeForRead(pcbtcww->lpcs->lpszClass,
|
|
sizeof(CHAR),
|
|
1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ProbeForRead( pcbtcww,
|
|
sizeof(CBT_CREATEWNDW),
|
|
1);
|
|
ProbeForWrite(pcbtcww->lpcs,
|
|
sizeof(CREATESTRUCTW),
|
|
1);
|
|
ProbeForRead( pcbtcww->lpcs->lpszName,
|
|
sizeof(WCHAR),
|
|
1);
|
|
|
|
if (!IS_ATOM(pcbtcww->lpcs->lpszClass))
|
|
{
|
|
_Analysis_assume_(pcbtcww->lpcs->lpszClass != NULL);
|
|
ProbeForRead(pcbtcww->lpcs->lpszClass,
|
|
sizeof(WCHAR),
|
|
1);
|
|
}
|
|
}
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
BadChk = TRUE;
|
|
}
|
|
_SEH2_END;
|
|
|
|
if (BadChk)
|
|
{
|
|
ERR("HOOK HCBT_CREATEWND write ERROR!\n");
|
|
}
|
|
/* The next call handles the structures. */
|
|
if (!BadChk && Hook->Proc)
|
|
{
|
|
lResult = co_HOOK_CallHookNext(Hook, Code, wParam, lParam);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case HCBT_MOVESIZE:
|
|
{
|
|
RECTL rt;
|
|
|
|
TRACE("HOOK HCBT_MOVESIZE\n");
|
|
|
|
if (lParam)
|
|
{
|
|
_SEH2_TRY
|
|
{
|
|
ProbeForRead((PVOID)lParam,
|
|
sizeof(RECT),
|
|
1);
|
|
|
|
RtlCopyMemory(&rt,
|
|
(PVOID)lParam,
|
|
sizeof(RECT));
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
BadChk = TRUE;
|
|
}
|
|
_SEH2_END;
|
|
|
|
if (BadChk)
|
|
{
|
|
ERR("HOOK HCBT_MOVESIZE read from lParam ERROR!\n");
|
|
}
|
|
}
|
|
|
|
if (!BadChk)
|
|
{
|
|
lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&rt);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case HCBT_ACTIVATE:
|
|
{
|
|
CBTACTIVATESTRUCT CbAs;
|
|
|
|
TRACE("HOOK HCBT_ACTIVATE\n");
|
|
if (lParam)
|
|
{
|
|
_SEH2_TRY
|
|
{
|
|
ProbeForRead((PVOID)lParam,
|
|
sizeof(CBTACTIVATESTRUCT),
|
|
1);
|
|
|
|
RtlCopyMemory(&CbAs,
|
|
(PVOID)lParam,
|
|
sizeof(CBTACTIVATESTRUCT));
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
BadChk = TRUE;
|
|
}
|
|
_SEH2_END;
|
|
|
|
if (BadChk)
|
|
{
|
|
ERR("HOOK HCBT_ACTIVATE read from lParam ERROR!\n");
|
|
}
|
|
}
|
|
|
|
if (!BadChk)
|
|
{
|
|
lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&CbAs);
|
|
}
|
|
break;
|
|
}
|
|
|
|
/* The rest just use default. */
|
|
default:
|
|
TRACE("HOOK HCBT_ %d\n",Code);
|
|
lResult = co_HOOK_CallHookNext(Hook, Code, wParam, lParam);
|
|
break;
|
|
}
|
|
break;
|
|
/*
|
|
Note WH_JOURNALPLAYBACK,
|
|
"To have the system wait before processing the message, the return value
|
|
must be the amount of time, in clock ticks, that the system should wait."
|
|
*/
|
|
case WH_JOURNALPLAYBACK:
|
|
case WH_JOURNALRECORD:
|
|
{
|
|
EVENTMSG EventMsg;
|
|
|
|
if (lParam)
|
|
{
|
|
_SEH2_TRY
|
|
{
|
|
ProbeForRead((PVOID)lParam,
|
|
sizeof(EVENTMSG),
|
|
1);
|
|
|
|
RtlCopyMemory(&EventMsg,
|
|
(PVOID)lParam,
|
|
sizeof(EVENTMSG));
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
BadChk = TRUE;
|
|
}
|
|
_SEH2_END;
|
|
|
|
if (BadChk)
|
|
{
|
|
ERR("HOOK WH_JOURNAL read from lParam ERROR!\n");
|
|
}
|
|
}
|
|
|
|
if (!BadChk)
|
|
{
|
|
lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)(lParam ? &EventMsg : NULL));
|
|
|
|
if (lParam)
|
|
{
|
|
_SEH2_TRY
|
|
{
|
|
ProbeForWrite((PVOID)lParam,
|
|
sizeof(EVENTMSG),
|
|
1);
|
|
|
|
RtlCopyMemory((PVOID)lParam,
|
|
&EventMsg,
|
|
sizeof(EVENTMSG));
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
BadChk = TRUE;
|
|
}
|
|
_SEH2_END;
|
|
|
|
if (BadChk)
|
|
{
|
|
ERR("HOOK WH_JOURNAL write to lParam ERROR!\n");
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case WH_DEBUG:
|
|
lResult = co_IntCallDebugHook(Hook, Code, wParam, lParam, Ansi);
|
|
break;
|
|
|
|
/*
|
|
* Default the rest like, WH_FOREGROUNDIDLE, WH_KEYBOARD and WH_SHELL.
|
|
*/
|
|
case WH_FOREGROUNDIDLE:
|
|
case WH_KEYBOARD:
|
|
case WH_SHELL:
|
|
lResult = co_HOOK_CallHookNext(Hook, Code, wParam, lParam);
|
|
break;
|
|
|
|
default:
|
|
ERR("Unsupported HOOK Id -> %d\n",Hook->HookId);
|
|
break;
|
|
}
|
|
return lResult;
|
|
}
|
|
|
|
PHOOK
|
|
FASTCALL
|
|
IntGetHookObject(HHOOK hHook)
|
|
{
|
|
PHOOK Hook;
|
|
|
|
if (!hHook)
|
|
{
|
|
EngSetLastError(ERROR_INVALID_HOOK_HANDLE);
|
|
return NULL;
|
|
}
|
|
|
|
Hook = (PHOOK)UserGetObject(gHandleTable, hHook, TYPE_HOOK);
|
|
if (!Hook)
|
|
{
|
|
EngSetLastError(ERROR_INVALID_HOOK_HANDLE);
|
|
return NULL;
|
|
}
|
|
|
|
UserReferenceObject(Hook);
|
|
|
|
return Hook;
|
|
}
|
|
|
|
static
|
|
HHOOK*
|
|
FASTCALL
|
|
IntGetGlobalHookHandles(PDESKTOP pdo, int HookId)
|
|
{
|
|
PLIST_ENTRY pLastHead, pElem;
|
|
unsigned i = 0;
|
|
unsigned cHooks = 0;
|
|
HHOOK *pList;
|
|
PHOOK pHook;
|
|
|
|
pLastHead = &pdo->pDeskInfo->aphkStart[HOOKID_TO_INDEX(HookId)];
|
|
for (pElem = pLastHead->Flink; pElem != pLastHead; pElem = pElem->Flink)
|
|
++cHooks;
|
|
|
|
pList = ExAllocatePoolWithTag(PagedPool, (cHooks + 1) * sizeof(HHOOK), TAG_HOOK);
|
|
if (!pList)
|
|
{
|
|
EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return NULL;
|
|
}
|
|
|
|
for (pElem = pLastHead->Flink; pElem != pLastHead; pElem = pElem->Flink)
|
|
{
|
|
pHook = CONTAINING_RECORD(pElem, HOOK, Chain);
|
|
NT_ASSERT(i < cHooks);
|
|
pList[i++] = pHook->head.h;
|
|
}
|
|
pList[i] = NULL;
|
|
|
|
return pList;
|
|
}
|
|
|
|
/* Find the next hook in the chain */
|
|
PHOOK
|
|
FASTCALL
|
|
IntGetNextHook(PHOOK Hook)
|
|
{
|
|
int HookId = Hook->HookId;
|
|
PLIST_ENTRY pLastHead, pElem;
|
|
PTHREADINFO pti;
|
|
|
|
if (Hook->ptiHooked)
|
|
{
|
|
pti = Hook->ptiHooked;
|
|
pLastHead = &pti->aphkStart[HOOKID_TO_INDEX(HookId)];
|
|
}
|
|
else
|
|
{
|
|
pti = PsGetCurrentThreadWin32Thread();
|
|
pLastHead = &pti->rpdesk->pDeskInfo->aphkStart[HOOKID_TO_INDEX(HookId)];
|
|
}
|
|
|
|
pElem = Hook->Chain.Flink;
|
|
if (pElem != pLastHead)
|
|
return CONTAINING_RECORD(pElem, HOOK, Chain);
|
|
return NULL;
|
|
}
|
|
|
|
/* Free a hook, removing it from its chain */
|
|
static
|
|
VOID
|
|
FASTCALL
|
|
IntFreeHook(PHOOK Hook)
|
|
{
|
|
RemoveEntryList(&Hook->Chain);
|
|
if (Hook->ModuleName.Buffer)
|
|
{
|
|
ExFreePoolWithTag(Hook->ModuleName.Buffer, TAG_HOOK);
|
|
Hook->ModuleName.Buffer = NULL;
|
|
}
|
|
/* Close handle */
|
|
UserDeleteObject(UserHMGetHandle(Hook), TYPE_HOOK);
|
|
}
|
|
|
|
/* Remove a hook, freeing it from the chain */
|
|
BOOLEAN
|
|
IntRemoveHook(PVOID Object)
|
|
{
|
|
INT HookId;
|
|
PTHREADINFO ptiHook, pti;
|
|
PDESKTOP pdo;
|
|
PHOOK Hook = Object;
|
|
|
|
NT_ASSERT(UserIsEnteredExclusive());
|
|
|
|
HookId = Hook->HookId;
|
|
pti = PsGetCurrentThreadWin32Thread();
|
|
|
|
if (Hook->ptiHooked) // Local
|
|
{
|
|
ptiHook = Hook->ptiHooked;
|
|
|
|
IntFreeHook(Hook);
|
|
|
|
if (IsListEmpty(&ptiHook->aphkStart[HOOKID_TO_INDEX(HookId)]))
|
|
{
|
|
BOOL bOtherProcess;
|
|
KAPC_STATE ApcState;
|
|
|
|
ptiHook->fsHooks &= ~HOOKID_TO_FLAG(HookId);
|
|
bOtherProcess = (ptiHook->ppi != pti->ppi);
|
|
|
|
if (bOtherProcess)
|
|
KeStackAttachProcess(&ptiHook->ppi->peProcess->Pcb, &ApcState);
|
|
|
|
_SEH2_TRY
|
|
{
|
|
ptiHook->pClientInfo->fsHooks = ptiHook->fsHooks;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Do nothing */
|
|
(void)0;
|
|
}
|
|
_SEH2_END;
|
|
|
|
if (bOtherProcess)
|
|
KeUnstackDetachProcess(&ApcState);
|
|
}
|
|
}
|
|
else // Global
|
|
{
|
|
IntFreeHook(Hook);
|
|
|
|
pdo = IntGetActiveDesktop();
|
|
|
|
if (pdo &&
|
|
pdo->pDeskInfo &&
|
|
IsListEmpty(&pdo->pDeskInfo->aphkStart[HOOKID_TO_INDEX(HookId)]))
|
|
{
|
|
pdo->pDeskInfo->fsHooks &= ~HOOKID_TO_FLAG(HookId);
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
Win32k Kernel Space Hook Caller.
|
|
*/
|
|
LRESULT
|
|
APIENTRY
|
|
co_HOOK_CallHooks( INT HookId,
|
|
INT Code,
|
|
WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
PHOOK Hook, SaveHook;
|
|
PTHREADINFO pti;
|
|
PCLIENTINFO ClientInfo;
|
|
PLIST_ENTRY pLastHead;
|
|
PDESKTOP pdo;
|
|
BOOL Local = FALSE, Global = FALSE;
|
|
LRESULT Result = 0;
|
|
USER_REFERENCE_ENTRY Ref;
|
|
|
|
ASSERT(WH_MINHOOK <= HookId && HookId <= WH_MAXHOOK);
|
|
|
|
pti = PsGetCurrentThreadWin32Thread();
|
|
if (!pti || !pti->rpdesk || !pti->rpdesk->pDeskInfo)
|
|
{
|
|
pdo = IntGetActiveDesktop();
|
|
/* If KeyboardThread|MouseThread|(RawInputThread or RIT) aka system threads,
|
|
pti->fsHooks most likely, is zero. So process KbT & MsT to "send" the message.
|
|
*/
|
|
if ( !pti || !pdo || (!(HookId == WH_KEYBOARD_LL) && !(HookId == WH_MOUSE_LL)) )
|
|
{
|
|
TRACE("No PDO %d\n", HookId);
|
|
goto Exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pdo = pti->rpdesk;
|
|
}
|
|
|
|
if ( pti->TIF_flags & (TIF_INCLEANUP|TIF_DISABLEHOOKS))
|
|
{
|
|
TRACE("Hook Thread dead %d\n", HookId);
|
|
goto Exit;
|
|
}
|
|
|
|
if ( ISITHOOKED(HookId) )
|
|
{
|
|
TRACE("Local Hooker %d\n", HookId);
|
|
Local = TRUE;
|
|
}
|
|
|
|
if ( pdo->pDeskInfo->fsHooks & HOOKID_TO_FLAG(HookId) )
|
|
{
|
|
TRACE("Global Hooker %d\n", HookId);
|
|
Global = TRUE;
|
|
}
|
|
|
|
if ( !Local && !Global ) goto Exit; // No work!
|
|
|
|
Hook = NULL;
|
|
|
|
/* SetWindowHookEx sorts out the Thread issue by placing the Hook to
|
|
the correct Thread if not NULL.
|
|
*/
|
|
if ( Local )
|
|
{
|
|
pLastHead = &pti->aphkStart[HOOKID_TO_INDEX(HookId)];
|
|
if (IsListEmpty(pLastHead))
|
|
{
|
|
ERR("No Local Hook Found!\n");
|
|
goto Exit;
|
|
}
|
|
|
|
Hook = CONTAINING_RECORD(pLastHead->Flink, HOOK, Chain);
|
|
ObReferenceObject(pti->pEThread);
|
|
IntReferenceThreadInfo(pti);
|
|
UserRefObjectCo(Hook, &Ref);
|
|
|
|
ClientInfo = pti->pClientInfo;
|
|
SaveHook = pti->sphkCurrent;
|
|
/* Note: Setting pti->sphkCurrent will also lock the next hook to this
|
|
* hook ID. So, the CallNextHookEx will only call to that hook ID
|
|
* chain anyway. For Thread Hooks....
|
|
*/
|
|
|
|
/* Load it for the next call. */
|
|
pti->sphkCurrent = Hook;
|
|
Hook->phkNext = IntGetNextHook(Hook);
|
|
if (ClientInfo)
|
|
{
|
|
_SEH2_TRY
|
|
{
|
|
ClientInfo->phkCurrent = Hook;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
ClientInfo = NULL; // Don't bother next run.
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
Result = co_IntCallHookProc( HookId,
|
|
Code,
|
|
wParam,
|
|
lParam,
|
|
Hook->Proc,
|
|
Hook->ihmod,
|
|
Hook->offPfn,
|
|
Hook->Ansi,
|
|
&Hook->ModuleName);
|
|
if (ClientInfo)
|
|
{
|
|
_SEH2_TRY
|
|
{
|
|
ClientInfo->phkCurrent = SaveHook;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Do nothing */
|
|
(void)0;
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
pti->sphkCurrent = SaveHook;
|
|
Hook->phkNext = NULL;
|
|
UserDerefObjectCo(Hook);
|
|
IntDereferenceThreadInfo(pti);
|
|
ObDereferenceObject(pti->pEThread);
|
|
}
|
|
|
|
if ( Global )
|
|
{
|
|
PTHREADINFO ptiHook;
|
|
HHOOK *pHookHandles;
|
|
unsigned i;
|
|
|
|
/* Keep hooks in array because hooks can be destroyed in user world */
|
|
pHookHandles = IntGetGlobalHookHandles(pdo, HookId);
|
|
if(!pHookHandles)
|
|
goto Exit;
|
|
|
|
/* Performance goes down the drain. If more hooks are associated to this
|
|
* hook ID, this will have to post to each of the thread message queues
|
|
* or make a direct call.
|
|
*/
|
|
for(i = 0; pHookHandles[i]; ++i)
|
|
{
|
|
Hook = (PHOOK)UserGetObject(gHandleTable, pHookHandles[i], TYPE_HOOK);
|
|
if(!Hook)
|
|
{
|
|
ERR("Invalid hook!\n");
|
|
continue;
|
|
}
|
|
|
|
/* Hook->Thread is null, we hax around this with Hook->head.pti. */
|
|
ptiHook = Hook->head.pti;
|
|
|
|
if ( (pti->TIF_flags & TIF_DISABLEHOOKS) || (ptiHook->TIF_flags & TIF_INCLEANUP))
|
|
{
|
|
TRACE("Next Hook %p, %p\n", ptiHook->rpdesk, pdo);
|
|
continue;
|
|
}
|
|
UserRefObjectCo(Hook, &Ref);
|
|
|
|
if (ptiHook != pti )
|
|
{
|
|
// Block | TimeOut
|
|
if ( HookId == WH_JOURNALPLAYBACK || // 1 | 0
|
|
HookId == WH_JOURNALRECORD || // 1 | 0
|
|
HookId == WH_KEYBOARD || // 1 | 200
|
|
HookId == WH_MOUSE || // 1 | 200
|
|
HookId == WH_KEYBOARD_LL || // 0 | 300
|
|
HookId == WH_MOUSE_LL ) // 0 | 300
|
|
{
|
|
TRACE("\nGlobal Hook posting to another Thread! %d\n",HookId );
|
|
Result = co_IntCallLowLevelHook(Hook, Code, wParam, lParam);
|
|
}
|
|
else if (ptiHook->ppi == pti->ppi)
|
|
{
|
|
TRACE("\nGlobal Hook calling to another Thread! %d\n",HookId );
|
|
ObReferenceObject(ptiHook->pEThread);
|
|
IntReferenceThreadInfo(ptiHook);
|
|
Result = co_IntCallHookProc( HookId,
|
|
Code,
|
|
wParam,
|
|
lParam,
|
|
Hook->Proc,
|
|
Hook->ihmod,
|
|
Hook->offPfn,
|
|
Hook->Ansi,
|
|
&Hook->ModuleName);
|
|
IntDereferenceThreadInfo(ptiHook);
|
|
ObDereferenceObject(ptiHook->pEThread);
|
|
}
|
|
}
|
|
else
|
|
{ /* Make the direct call. */
|
|
TRACE("Global going Local Hook calling to Thread! %d\n",HookId );
|
|
ObReferenceObject(pti->pEThread);
|
|
IntReferenceThreadInfo(pti);
|
|
Result = co_IntCallHookProc( HookId,
|
|
Code,
|
|
wParam,
|
|
lParam,
|
|
Hook->Proc,
|
|
Hook->ihmod,
|
|
Hook->offPfn,
|
|
Hook->Ansi,
|
|
&Hook->ModuleName);
|
|
IntDereferenceThreadInfo(pti);
|
|
ObDereferenceObject(pti->pEThread);
|
|
}
|
|
UserDerefObjectCo(Hook);
|
|
}
|
|
ExFreePoolWithTag(pHookHandles, TAG_HOOK);
|
|
TRACE("Ret: Global HookId %d Result 0x%x\n", HookId,Result);
|
|
}
|
|
Exit:
|
|
return Result;
|
|
}
|
|
|
|
BOOL
|
|
FASTCALL
|
|
IntUnhookWindowsHook(int HookId, HOOKPROC pfnFilterProc)
|
|
{
|
|
PHOOK Hook;
|
|
PLIST_ENTRY pLastHead, pElement;
|
|
PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
|
|
|
|
if (HookId < WH_MINHOOK || WH_MAXHOOK < HookId )
|
|
{
|
|
EngSetLastError(ERROR_INVALID_HOOK_FILTER);
|
|
return FALSE;
|
|
}
|
|
|
|
if (pti->fsHooks)
|
|
{
|
|
pLastHead = &pti->aphkStart[HOOKID_TO_INDEX(HookId)];
|
|
|
|
pElement = pLastHead->Flink;
|
|
while (pElement != pLastHead)
|
|
{
|
|
Hook = CONTAINING_RECORD(pElement, HOOK, Chain);
|
|
|
|
/* Get the next element now, we might free the hook in what follows */
|
|
pElement = Hook->Chain.Flink;
|
|
|
|
if (Hook->Proc == pfnFilterProc)
|
|
{
|
|
if (Hook->head.pti == pti)
|
|
{
|
|
IntRemoveHook(Hook);
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
EngSetLastError(ERROR_ACCESS_DENIED);
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Support for compatibility only? Global hooks are processed in kernel space.
|
|
* This is very thread specific! Never seeing applications with more than one
|
|
* hook per thread installed. Most of the applications are Global hookers and
|
|
* associated with just one hook Id. Maybe it's for diagnostic testing or a
|
|
* throw back to 3.11?
|
|
*/
|
|
LRESULT
|
|
APIENTRY
|
|
NtUserCallNextHookEx( int Code,
|
|
WPARAM wParam,
|
|
LPARAM lParam,
|
|
BOOL Ansi)
|
|
{
|
|
PTHREADINFO pti;
|
|
PHOOK HookObj, NextObj;
|
|
PCLIENTINFO ClientInfo;
|
|
LRESULT lResult = 0;
|
|
DECLARE_RETURN(LRESULT);
|
|
|
|
TRACE("Enter NtUserCallNextHookEx\n");
|
|
UserEnterExclusive();
|
|
|
|
pti = GetW32ThreadInfo();
|
|
|
|
HookObj = pti->sphkCurrent;
|
|
|
|
if (!HookObj) RETURN( 0);
|
|
|
|
NextObj = HookObj->phkNext;
|
|
|
|
pti->sphkCurrent = NextObj;
|
|
ClientInfo = pti->pClientInfo;
|
|
_SEH2_TRY
|
|
{
|
|
ClientInfo->phkCurrent = NextObj;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
ClientInfo = NULL;
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Now in List run down. */
|
|
if (ClientInfo && NextObj)
|
|
{
|
|
NextObj->phkNext = IntGetNextHook(NextObj);
|
|
lResult = co_UserCallNextHookEx( NextObj, Code, wParam, lParam, NextObj->Ansi);
|
|
}
|
|
RETURN( lResult);
|
|
|
|
CLEANUP:
|
|
TRACE("Leave NtUserCallNextHookEx, ret=%i\n",_ret_);
|
|
UserLeave();
|
|
END_CLEANUP;
|
|
}
|
|
|
|
HHOOK
|
|
APIENTRY
|
|
NtUserSetWindowsHookAW( int idHook,
|
|
HOOKPROC lpfn,
|
|
BOOL Ansi)
|
|
{
|
|
DWORD ThreadId;
|
|
UNICODE_STRING USModuleName;
|
|
|
|
RtlInitUnicodeString(&USModuleName, NULL);
|
|
ThreadId = PtrToUint(NtCurrentTeb()->ClientId.UniqueThread);
|
|
|
|
return NtUserSetWindowsHookEx( NULL,
|
|
&USModuleName,
|
|
ThreadId,
|
|
idHook,
|
|
lpfn,
|
|
Ansi);
|
|
}
|
|
|
|
HHOOK
|
|
APIENTRY
|
|
NtUserSetWindowsHookEx( HINSTANCE Mod,
|
|
PUNICODE_STRING UnsafeModuleName,
|
|
DWORD ThreadId,
|
|
int HookId,
|
|
HOOKPROC HookProc,
|
|
BOOL Ansi)
|
|
{
|
|
PWINSTATION_OBJECT WinStaObj;
|
|
PHOOK Hook = NULL;
|
|
UNICODE_STRING ModuleName;
|
|
NTSTATUS Status;
|
|
HHOOK Handle;
|
|
PTHREADINFO pti, ptiHook = NULL;
|
|
DECLARE_RETURN(HHOOK);
|
|
|
|
TRACE("Enter NtUserSetWindowsHookEx\n");
|
|
UserEnterExclusive();
|
|
|
|
pti = PsGetCurrentThreadWin32Thread();
|
|
|
|
if (HookId < WH_MINHOOK || WH_MAXHOOK < HookId )
|
|
{
|
|
EngSetLastError(ERROR_INVALID_HOOK_FILTER);
|
|
RETURN( NULL);
|
|
}
|
|
|
|
if (!HookProc)
|
|
{
|
|
EngSetLastError(ERROR_INVALID_FILTER_PROC);
|
|
RETURN( NULL);
|
|
}
|
|
|
|
if (ThreadId) /* thread-local hook */
|
|
{
|
|
if ( HookId == WH_JOURNALRECORD ||
|
|
HookId == WH_JOURNALPLAYBACK ||
|
|
HookId == WH_KEYBOARD_LL ||
|
|
HookId == WH_MOUSE_LL ||
|
|
HookId == WH_SYSMSGFILTER)
|
|
{
|
|
TRACE("Local hook installing Global HookId: %d\n",HookId);
|
|
/* these can only be global */
|
|
EngSetLastError(ERROR_GLOBAL_ONLY_HOOK);
|
|
RETURN( NULL);
|
|
}
|
|
|
|
if ( !(ptiHook = IntTID2PTI( UlongToHandle(ThreadId) )))
|
|
{
|
|
ERR("Invalid thread id 0x%x\n", ThreadId);
|
|
EngSetLastError(ERROR_INVALID_PARAMETER);
|
|
RETURN( NULL);
|
|
}
|
|
|
|
if ( ptiHook->rpdesk != pti->rpdesk) // gptiCurrent->rpdesk)
|
|
{
|
|
ERR("Local hook wrong desktop HookId: %d\n",HookId);
|
|
EngSetLastError(ERROR_ACCESS_DENIED);
|
|
RETURN( NULL);
|
|
}
|
|
|
|
if (ptiHook->ppi != pti->ppi)
|
|
{
|
|
if ( !Mod &&
|
|
(HookId == WH_GETMESSAGE ||
|
|
HookId == WH_CALLWNDPROC ||
|
|
HookId == WH_CBT ||
|
|
HookId == WH_HARDWARE ||
|
|
HookId == WH_DEBUG ||
|
|
HookId == WH_SHELL ||
|
|
HookId == WH_FOREGROUNDIDLE ||
|
|
HookId == WH_CALLWNDPROCRET) )
|
|
{
|
|
ERR("Local hook needs hMod HookId: %d\n",HookId);
|
|
EngSetLastError(ERROR_HOOK_NEEDS_HMOD);
|
|
RETURN( NULL);
|
|
}
|
|
|
|
if ( (ptiHook->TIF_flags & (TIF_CSRSSTHREAD|TIF_SYSTEMTHREAD)) &&
|
|
(HookId == WH_GETMESSAGE ||
|
|
HookId == WH_CALLWNDPROC ||
|
|
HookId == WH_CBT ||
|
|
HookId == WH_HARDWARE ||
|
|
HookId == WH_DEBUG ||
|
|
HookId == WH_SHELL ||
|
|
HookId == WH_FOREGROUNDIDLE ||
|
|
HookId == WH_CALLWNDPROCRET) )
|
|
{
|
|
EngSetLastError(ERROR_HOOK_TYPE_NOT_ALLOWED);
|
|
RETURN( NULL);
|
|
}
|
|
}
|
|
}
|
|
else /* System-global hook */
|
|
{
|
|
ptiHook = pti; // gptiCurrent;
|
|
if ( !Mod &&
|
|
(HookId == WH_GETMESSAGE ||
|
|
HookId == WH_CALLWNDPROC ||
|
|
HookId == WH_CBT ||
|
|
HookId == WH_SYSMSGFILTER ||
|
|
HookId == WH_HARDWARE ||
|
|
HookId == WH_DEBUG ||
|
|
HookId == WH_SHELL ||
|
|
HookId == WH_FOREGROUNDIDLE ||
|
|
HookId == WH_CALLWNDPROCRET) )
|
|
{
|
|
ERR("Global hook needs hMod HookId: %d\n",HookId);
|
|
EngSetLastError(ERROR_HOOK_NEEDS_HMOD);
|
|
RETURN( NULL);
|
|
}
|
|
}
|
|
|
|
Status = IntValidateWindowStationHandle( PsGetCurrentProcess()->Win32WindowStation,
|
|
UserMode,
|
|
0,
|
|
&WinStaObj,
|
|
0);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
SetLastNtError(Status);
|
|
RETURN( NULL);
|
|
}
|
|
ObDereferenceObject(WinStaObj);
|
|
|
|
Hook = UserCreateObject(gHandleTable, NULL, ptiHook, (PHANDLE)&Handle, TYPE_HOOK, sizeof(HOOK));
|
|
|
|
if (!Hook)
|
|
{
|
|
RETURN( NULL);
|
|
}
|
|
|
|
Hook->ihmod = (INT_PTR)Mod; // Module Index from atom table, Do this for now.
|
|
Hook->HookId = HookId;
|
|
Hook->rpdesk = ptiHook->rpdesk;
|
|
Hook->phkNext = NULL; /* Dont use as a chain! Use link lists for chaining. */
|
|
Hook->Proc = HookProc;
|
|
Hook->Ansi = Ansi;
|
|
|
|
TRACE("Set Hook Desk %p DeskInfo %p Handle Desk %p\n", pti->rpdesk, pti->pDeskInfo, Hook->head.rpdesk);
|
|
|
|
if (ThreadId) /* Thread-local hook */
|
|
{
|
|
InsertHeadList(&ptiHook->aphkStart[HOOKID_TO_INDEX(HookId)], &Hook->Chain);
|
|
ptiHook->sphkCurrent = NULL;
|
|
Hook->ptiHooked = ptiHook;
|
|
ptiHook->fsHooks |= HOOKID_TO_FLAG(HookId);
|
|
|
|
if (ptiHook->pClientInfo)
|
|
{
|
|
if ( ptiHook->ppi == pti->ppi) /* gptiCurrent->ppi) */
|
|
{
|
|
_SEH2_TRY
|
|
{
|
|
ptiHook->pClientInfo->fsHooks = ptiHook->fsHooks;
|
|
ptiHook->pClientInfo->phkCurrent = NULL;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
ERR("Problem writing to Local ClientInfo!\n");
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
else
|
|
{
|
|
KAPC_STATE ApcState;
|
|
|
|
KeStackAttachProcess(&ptiHook->ppi->peProcess->Pcb, &ApcState);
|
|
_SEH2_TRY
|
|
{
|
|
ptiHook->pClientInfo->fsHooks = ptiHook->fsHooks;
|
|
ptiHook->pClientInfo->phkCurrent = NULL;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
ERR("Problem writing to Remote ClientInfo!\n");
|
|
}
|
|
_SEH2_END;
|
|
KeUnstackDetachProcess(&ApcState);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
InsertHeadList(&ptiHook->rpdesk->pDeskInfo->aphkStart[HOOKID_TO_INDEX(HookId)], &Hook->Chain);
|
|
Hook->ptiHooked = NULL;
|
|
//gptiCurrent->pDeskInfo->fsHooks |= HOOKID_TO_FLAG(HookId);
|
|
ptiHook->rpdesk->pDeskInfo->fsHooks |= HOOKID_TO_FLAG(HookId);
|
|
ptiHook->sphkCurrent = NULL;
|
|
ptiHook->pClientInfo->phkCurrent = NULL;
|
|
}
|
|
|
|
RtlInitUnicodeString(&Hook->ModuleName, NULL);
|
|
|
|
if (Mod)
|
|
{
|
|
Status = MmCopyFromCaller(&ModuleName,
|
|
UnsafeModuleName,
|
|
sizeof(UNICODE_STRING));
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
IntRemoveHook(Hook);
|
|
SetLastNtError(Status);
|
|
RETURN( NULL);
|
|
}
|
|
|
|
Hook->ModuleName.Buffer = ExAllocatePoolWithTag( PagedPool,
|
|
ModuleName.MaximumLength,
|
|
TAG_HOOK);
|
|
if (NULL == Hook->ModuleName.Buffer)
|
|
{
|
|
IntRemoveHook(Hook);
|
|
EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
RETURN( NULL);
|
|
}
|
|
|
|
Hook->ModuleName.MaximumLength = ModuleName.MaximumLength;
|
|
Status = MmCopyFromCaller( Hook->ModuleName.Buffer,
|
|
ModuleName.Buffer,
|
|
ModuleName.MaximumLength);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ExFreePoolWithTag(Hook->ModuleName.Buffer, TAG_HOOK);
|
|
Hook->ModuleName.Buffer = NULL;
|
|
IntRemoveHook(Hook);
|
|
SetLastNtError(Status);
|
|
RETURN( NULL);
|
|
}
|
|
|
|
Hook->ModuleName.Length = ModuleName.Length;
|
|
//// FIXME: Need to load from user32 to verify hMod before calling hook with hMod set!!!!
|
|
//// Mod + offPfn == new HookProc Justin Case module is from another process.
|
|
FIXME("NtUserSetWindowsHookEx Setting process hMod instance addressing.\n");
|
|
/* Make proc relative to the module base */
|
|
Hook->offPfn = (ULONG_PTR)((char *)HookProc - (char *)Mod);
|
|
}
|
|
else
|
|
Hook->offPfn = 0;
|
|
|
|
TRACE("Installing: HookId %d Global %s\n", HookId, !ThreadId ? "TRUE" : "FALSE");
|
|
RETURN( Handle);
|
|
|
|
CLEANUP:
|
|
if (Hook)
|
|
UserDereferenceObject(Hook);
|
|
TRACE("Leave NtUserSetWindowsHookEx, ret=%p\n", _ret_);
|
|
UserLeave();
|
|
END_CLEANUP;
|
|
}
|
|
|
|
BOOL
|
|
APIENTRY
|
|
NtUserUnhookWindowsHookEx(HHOOK Hook)
|
|
{
|
|
PHOOK HookObj;
|
|
DECLARE_RETURN(BOOL);
|
|
|
|
TRACE("Enter NtUserUnhookWindowsHookEx\n");
|
|
UserEnterExclusive();
|
|
|
|
if (!(HookObj = IntGetHookObject(Hook)))
|
|
{
|
|
ERR("Invalid handle passed to NtUserUnhookWindowsHookEx\n");
|
|
/* SetLastNtError(Status); */
|
|
RETURN( FALSE);
|
|
}
|
|
|
|
ASSERT(Hook == UserHMGetHandle(HookObj));
|
|
|
|
IntRemoveHook(HookObj);
|
|
|
|
UserDereferenceObject(HookObj);
|
|
|
|
RETURN( TRUE);
|
|
|
|
CLEANUP:
|
|
TRACE("Leave NtUserUnhookWindowsHookEx, ret=%i\n",_ret_);
|
|
UserLeave();
|
|
END_CLEANUP;
|
|
}
|
|
|
|
BOOL
|
|
APIENTRY
|
|
NtUserRegisterUserApiHook(
|
|
PUNICODE_STRING m_dllname1,
|
|
PUNICODE_STRING m_funname1,
|
|
DWORD dwUnknown3,
|
|
DWORD dwUnknown4)
|
|
{
|
|
BOOL ret;
|
|
UNICODE_STRING strDllNameSafe;
|
|
UNICODE_STRING strFuncNameSafe;
|
|
NTSTATUS Status;
|
|
|
|
/* Probe and capture parameters */
|
|
Status = ProbeAndCaptureUnicodeString(&strDllNameSafe, UserMode, m_dllname1);
|
|
if(!NT_SUCCESS(Status))
|
|
{
|
|
EngSetLastError(RtlNtStatusToDosError(Status));
|
|
return FALSE;
|
|
}
|
|
|
|
Status = ProbeAndCaptureUnicodeString(&strFuncNameSafe, UserMode, m_funname1);
|
|
if(!NT_SUCCESS(Status))
|
|
{
|
|
ReleaseCapturedUnicodeString(&strDllNameSafe, UserMode);
|
|
EngSetLastError(RtlNtStatusToDosError(Status));
|
|
return FALSE;
|
|
}
|
|
|
|
UserEnterExclusive();
|
|
|
|
/* Call internal function */
|
|
ret = UserRegisterUserApiHook(&strDllNameSafe, &strFuncNameSafe);
|
|
|
|
UserLeave();
|
|
|
|
/* Cleanup only in case of failure */
|
|
if(ret == FALSE)
|
|
{
|
|
ReleaseCapturedUnicodeString(&strDllNameSafe, UserMode);
|
|
ReleaseCapturedUnicodeString(&strFuncNameSafe, UserMode);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
BOOL
|
|
APIENTRY
|
|
NtUserUnregisterUserApiHook(VOID)
|
|
{
|
|
BOOL ret;
|
|
|
|
UserEnterExclusive();
|
|
ret = UserUnregisterUserApiHook();
|
|
UserLeave();
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* EOF */
|