From 7950b8eb63bad61ee8ca61a98737e1ccc0f28b7c Mon Sep 17 00:00:00 2001 From: Giannis Adamopoulos Date: Tue, 12 Jul 2011 08:43:43 +0000 Subject: [PATCH] [win32k] - Implement NtUserRegisterUserApiHook and NtUserUnregisterUserApiHook svn path=/branches/GSoC_2011/ThemesSupport/; revision=52650 --- subsystems/win32/win32k/include/hook.h | 13 + subsystems/win32/win32k/include/msgqueue.h | 1 + subsystems/win32/win32k/include/win32.h | 1 + subsystems/win32/win32k/ntuser/hook.c | 272 ++++++++++++++++++++ subsystems/win32/win32k/ntuser/msgqueue.c | 6 + subsystems/win32/win32k/ntuser/ntstubs.c | 22 -- subsystems/win32/win32k/ntuser/simplecall.c | 3 + 7 files changed, 296 insertions(+), 22 deletions(-) diff --git a/subsystems/win32/win32k/include/hook.h b/subsystems/win32/win32k/include/hook.h index ce6d64e1b9f..a070868bf9c 100644 --- a/subsystems/win32/win32k/include/hook.h +++ b/subsystems/win32/win32k/include/hook.h @@ -5,6 +5,13 @@ #define HOOKID_TO_FLAG(HookId) (1 << ((HookId) + 1)) #define ISITHOOKED(HookId) (((PTHREADINFO)PsGetCurrentThreadWin32Thread())->fsHooks & HOOKID_TO_FLAG(HookId)) +/* NOTE: the following definition is not a real hook but + a pseudo-id that will be used only for + injecting user api hook module to all processes. + It is used internally in win32k */ +#define WH_APIHOOK WH_MAX + 1 + + typedef struct tagEVENTHOOK { THROBJHEAD head; @@ -43,4 +50,10 @@ PHOOK FASTCALL IntGetNextHook(PHOOK Hook); LRESULT FASTCALL UserCallNextHookEx( PHOOK pHook, int Code, WPARAM wParam, LPARAM lParam, BOOL Ansi); BOOL FASTCALL IntUnhookWindowsHook(int,HOOKPROC); +BOOL FASTCALL UserLoadApiHook(); +BOOL IntLoadHookModule(int iHookID, HHOOK hHook, BOOL Unload); +BOOL FASTCALL UserUnregisterUserApiHook(BOOL Block); + +extern PPROCESSINFO ppiUahServer; + /* EOF */ diff --git a/subsystems/win32/win32k/include/msgqueue.h b/subsystems/win32/win32k/include/msgqueue.h index 691c814f1ce..fbd11f9bf24 100644 --- a/subsystems/win32/win32k/include/msgqueue.h +++ b/subsystems/win32/win32k/include/msgqueue.h @@ -7,6 +7,7 @@ #define MSQ_NORMAL 0 #define MSQ_ISHOOK 1 #define MSQ_ISEVENT 2 +#define MSQ_INJECTMODULE 3 #define QSIDCOUNTS 6 diff --git a/subsystems/win32/win32k/include/win32.h b/subsystems/win32/win32k/include/win32.h index 0204ff96bf1..9af68661148 100644 --- a/subsystems/win32/win32k/include/win32.h +++ b/subsystems/win32/win32k/include/win32.h @@ -30,6 +30,7 @@ #define W32PF_NOWINDOWGHOSTING (0x01000000) #define W32PF_MANUALGUICHECK (0x02000000) #define W32PF_CREATEDWINORDC (0x04000000) +#define W32PF_APIHOOKLOADED (0x08000000) extern BOOL ClientPfnInit; extern HINSTANCE hModClient; diff --git a/subsystems/win32/win32k/ntuser/hook.c b/subsystems/win32/win32k/ntuser/hook.c index 466232d6b43..cb68c29b92b 100644 --- a/subsystems/win32/win32k/ntuser/hook.c +++ b/subsystems/win32/win32k/ntuser/hook.c @@ -24,8 +24,220 @@ typedef struct _HOOKPACK 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; + HMODULE hmod; + + ppi = PsGetCurrentProcessWin32Process(); + + DPRINT("IntLoadHookModule. Client PID: %d\n", PsGetProcessId(ppi->peProcess)); + + /* Check if this is the api hook */ + if(iHookID == WH_APIHOOK) + { + if(!Unload && !(ppi->W32PF_flags & W32PF_APIHOOKLOADED)) + { + /* Call ClientLoadLibrary in user32 */ + hmod = co_IntClientLoadLibrary(&strUahModule, &strUahInitFunc, Unload, TRUE); + if(hmod != 0) + { + ppi->W32PF_flags |= W32PF_APIHOOKLOADED; + return TRUE; + } + return FALSE; + } + else if(Unload && (ppi->W32PF_flags & W32PF_APIHOOKLOADED)) + { + /* Call ClientLoadLibrary in user32 */ + hmod = co_IntClientLoadLibrary(NULL, NULL, Unload, TRUE); + if(hmod != 0) + { + ppi->W32PF_flags &= ~W32PF_APIHOOKLOADED; + return TRUE; + } + return FALSE; + } + + return TRUE; + } + + UNIMPLEMENTED; + + 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, BOOL Block) +{ + PTHREADINFO ptiCurrent; + PLIST_ENTRY ListEntry; + ULONG_PTR Result; + PPROCESSINFO ppiCsr; + + DPRINT("IntHookModuleUnloaded: iHookID=%d\n", iHookID); + + ppiCsr = PsGetProcessWin32Process(CsrProcess); + + ListEntry = pdesk->PtiList.Flink; + while(ListEntry != &pdesk->PtiList) + { + ptiCurrent = CONTAINING_RECORD(ListEntry, THREADINFO, PtiLink); + + /* FIXME: do some more security checks here */ + + /* FIXME: 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) + { + DPRINT("IntHookModuleUnloaded: sending message to PID %d, ppi=0x%x\n", PsGetProcessId(ptiCurrent->ppi->peProcess), ptiCurrent->ppi); + co_MsqSendMessage( ptiCurrent->MessageQueue, + 0, + iHookID, + TRUE, + (LPARAM)hHook, + 0, + Block, + MSQ_INJECTMODULE, + &Result); + } + } + ListEntry = ListEntry->Flink; + } + + return TRUE; +} + +BOOL +FASTCALL +UserLoadApiHook() +{ + 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; + ULONG_PTR Result; + PPROCESSINFO ppiCsr; + + pti = PsGetCurrentThreadWin32Thread(); + ppiCsr = PsGetProcessWin32Process(CsrProcess); + + /* Fail if the api hook is already registered */ + if(gpsi->dwSRVIFlags & SRVINFO_APIHOOK) + { + return FALSE; + } + + DPRINT("UserRegisterUserApiHook. Server PID: %d\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_MsqSendMessage( ptiCurrent->MessageQueue, + 0, + WH_APIHOOK, + FALSE, /* load the module */ + 0, + 0, + TRUE, + MSQ_INJECTMODULE, + &Result); + if(Result == FALSE) + { + DPRINT1("Failed to inject module to process %d\n", PsGetProcessId(ptiCurrent->ppi->peProcess)); + } + } + ExFreePoolWithTag(List, USERTAG_WINDOWLIST); + } + + return TRUE; +} + +BOOL +FASTCALL +UserUnregisterUserApiHook(BOOL Block) +{ + 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; + } + + DPRINT1("UserUnregisterUserApiHook. Server PID: %d\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, TRUE); +} + static LRESULT FASTCALL @@ -1527,4 +1739,64 @@ CLEANUP: 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(TRUE); + UserLeave(); + + return ret; +} + + /* EOF */ diff --git a/subsystems/win32/win32k/ntuser/msgqueue.c b/subsystems/win32/win32k/ntuser/msgqueue.c index 35ed255edaa..42ef2d9b6d3 100644 --- a/subsystems/win32/win32k/ntuser/msgqueue.c +++ b/subsystems/win32/win32k/ntuser/msgqueue.c @@ -811,6 +811,12 @@ co_MsqDispatchOneSentMessage(PUSER_MESSAGE_QUEUE MessageQueue) Message->Msg.wParam, Message->Msg.lParam); } + else if(Message->HookMessage == MSQ_INJECTMODULE) + { + Result = IntLoadHookModule(Message->Msg.message, + (HHOOK)Message->Msg.lParam, + Message->Msg.wParam); + } else if ((Message->CompletionCallback) && (Message->CallBackSenderQueue == MessageQueue)) { /* Call the callback routine */ diff --git a/subsystems/win32/win32k/ntuser/ntstubs.c b/subsystems/win32/win32k/ntuser/ntstubs.c index f9168806404..80218476f73 100644 --- a/subsystems/win32/win32k/ntuser/ntstubs.c +++ b/subsystems/win32/win32k/ntuser/ntstubs.c @@ -889,20 +889,6 @@ NtUserRealWaitMessageEx( return 0; } -BOOL -APIENTRY -NtUserRegisterUserApiHook( - PUNICODE_STRING m_dllname1, - PUNICODE_STRING m_funname1, - DWORD dwUnknown3, - DWORD dwUnknown4) -{ - UserEnterExclusive(); - UNIMPLEMENTED; - UserLeave(); - return 0; -} - BOOL APIENTRY NtUserRegisterRawInputDevices( @@ -1036,14 +1022,6 @@ NtUserPaintMenuBar( return 0; } -BOOL -APIENTRY -NtUserUnregisterUserApiHook(VOID) -{ - UNIMPLEMENTED; - return 0; -} - BOOL APIENTRY NtUserGetLayeredWindowAttributes( diff --git a/subsystems/win32/win32k/ntuser/simplecall.c b/subsystems/win32/win32k/ntuser/simplecall.c index af25a1f1a97..c312dca3aab 100644 --- a/subsystems/win32/win32k/ntuser/simplecall.c +++ b/subsystems/win32/win32k/ntuser/simplecall.c @@ -122,6 +122,9 @@ NtUserCallNoParam(DWORD Routine) case NOPARAM_ROUTINE_RELEASECAPTURE: RETURN( (DWORD_PTR)IntReleaseCapture()); + case NOPARAM_ROUTINE_LOADUSERAPIHOOK: + RETURN(UserLoadApiHook()); + default: DPRINT1("Calling invalid routine number 0x%x in NtUserCallNoParam\n", Routine); EngSetLastError(ERROR_INVALID_PARAMETER);