From dbb2a95b6530ceac230dfa7bad09ae0e1bcdbffc Mon Sep 17 00:00:00 2001 From: zeffy Date: Thu, 8 Feb 2018 02:33:56 -0800 Subject: [PATCH] new hybrid method (improved group heuristic method + NotifyServiceStatusChange API) --- src/wufuc/callbacks.c | 121 +++++++++++++++++------------------------ src/wufuc/callbacks.h | 23 +------- src/wufuc/dllmain.c | 1 - src/wufuc/hlpmem.c | 85 ++++++++++++++++++----------- src/wufuc/hlpmem.h | 24 ++++++++- src/wufuc/hlpmisc.c | 43 +++++++++++++++ src/wufuc/hlpmisc.h | 2 + src/wufuc/hlpsvc.c | 123 ++++++++++++++++++++++++++++++++++++------ src/wufuc/hlpsvc.h | 13 +++-- src/wufuc/hlpver.c | 2 +- src/wufuc/hlpver.h | 2 +- src/wufuc/hooks.c | 77 ++++++++++++++++++++++---- src/wufuc/hooks.h | 3 ++ src/wufuc/rundll32.c | 20 +++++-- 14 files changed, 376 insertions(+), 163 deletions(-) diff --git a/src/wufuc/callbacks.c b/src/wufuc/callbacks.c index 6fae5c2..d7d59dc 100644 --- a/src/wufuc/callbacks.c +++ b/src/wufuc/callbacks.c @@ -5,66 +5,23 @@ #include "hlpmem.h" #include "hlpsvc.h" -bool DuplicateContextHandles(HANDLE hSrcProcess, ContextHandles *pSrcContext, HANDLE hAuxiliaryMutex, HANDLE hTargetProcess, ContextHandles *pTargetContext) -{ - return - DuplicateHandle(hSrcProcess, pSrcContext->hMainMutex, - hTargetProcess, &pTargetContext->hMainMutex, SYNCHRONIZE, FALSE, 0) - && DuplicateHandle(hSrcProcess, pSrcContext->hUnloadEvent, - hTargetProcess, &pTargetContext->hUnloadEvent, SYNCHRONIZE, FALSE, 0) - && DuplicateHandle(hSrcProcess, hAuxiliaryMutex, - hTargetProcess, &pTargetContext->hAuxiliaryMutex, 0, FALSE, DUPLICATE_SAME_ACCESS); -} - VOID CALLBACK ServiceNotifyCallback(PSERVICE_NOTIFYW pNotifyBuffer) { - HANDLE hProcess; - wchar_t MutexName[44]; - HANDLE hAuxiliaryMutex; - ContextHandles TargetContext; - + trace(L"Enter service notify callback. (NotifyStatus=%ld ServiceStatus=%ld)", pNotifyBuffer->dwNotificationStatus, pNotifyBuffer->ServiceStatus); switch ( pNotifyBuffer->dwNotificationStatus ) { case ERROR_SUCCESS: - if ( !pNotifyBuffer->ServiceStatus.dwProcessId - || swprintf_s(MutexName, _countof(MutexName), - L"Global\\%08x-7132-44a8-be15-56698979d2f3", - pNotifyBuffer->ServiceStatus.dwProcessId) == -1 - || !InitializeMutex(false, MutexName, &hAuxiliaryMutex) ) - break; - - hProcess = OpenProcess(PROCESS_ALL_ACCESS, - FALSE, - pNotifyBuffer->ServiceStatus.dwProcessId); - if ( !hProcess ) { - trace(L"Failed to open target process! (GetLastError=%lu)", GetLastError()); - break; - }; - - if ( !DuplicateContextHandles(GetCurrentProcess(), pNotifyBuffer->pContext, hAuxiliaryMutex, hProcess, &TargetContext) ) { - trace(L"Failed to duplicate handles into target process." - L"%p %p %p (GetLastError=%lu)", - TargetContext.hMainMutex, - TargetContext.hUnloadEvent, - TargetContext.hAuxiliaryMutex, - GetLastError()); - break; - }; - InjectLibraryAndCreateRemoteThread( - hProcess, - PIMAGEBASE, - StartAddress, - &TargetContext, - sizeof TargetContext); + if ( pNotifyBuffer->ServiceStatus.dwProcessId ) + wufuc_InjectLibrary(pNotifyBuffer->ServiceStatus.dwProcessId, (ContextHandles *)pNotifyBuffer->pContext); break; case ERROR_SERVICE_MARKED_FOR_DELETE: - SetEvent((HANDLE)pNotifyBuffer->pContext); + SetEvent(((ContextHandles *)pNotifyBuffer->pContext)->hUnloadEvent); break; } if ( pNotifyBuffer->pszServiceNames ) LocalFree((HLOCAL)pNotifyBuffer->pszServiceNames); } -DWORD WINAPI StartAddress(LPVOID pParam) +DWORD WINAPI ThreadStartCallback(LPVOID pParam) { ContextHandles ctx; SC_HANDLE hSCM; @@ -80,12 +37,12 @@ DWORD WINAPI StartAddress(LPVOID pParam) } ctx = *(ContextHandles *)pParam; if ( !VirtualFree(pParam, 0, MEM_RELEASE) ) - trace(L"Failed to free context parameter. pParam=%p GetLastError=%lu", + trace(L"Failed to free context parameter. (%p, GetLastError=%lu)", pParam, GetLastError()); // acquire child mutex, should be immediate. - if ( WaitForSingleObject(ctx.hAuxiliaryMutex, 5000) != WAIT_OBJECT_0 ) { - trace(L"Failed to acquire aux mutex within five seconds. hAuxiliaryMutex=%p", ctx.hAuxiliaryMutex); + if ( WaitForSingleObject(ctx.hChildMutex, 5000) != WAIT_OBJECT_0 ) { + trace(L"Failed to acquire aux mutex within five seconds. (%p)", ctx.hChildMutex); goto close_handles; } @@ -95,15 +52,13 @@ DWORD WINAPI StartAddress(LPVOID pParam) goto release; } - dwProcessId = QueryServiceProcessId(hSCM, L"wuauserv"); + dwProcessId = HeuristicServiceProcessIdByName(hSCM, L"wuauserv"); CloseServiceHandle(hSCM); if ( dwProcessId != GetCurrentProcessId() ) { - trace(L"Injected into wrong process! CurrentProcessId=%lu wuauserv ProcessId=%lu", - GetCurrentProcessId, dwProcessId); + trace(L"Injected into wrong process!", GetCurrentProcessId(), dwProcessId); goto release; } - // hook IsDeviceServiceable g_pszWUServiceDll = RegGetValueAlloc(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\services\\wuauserv\\Parameters", L"ServiceDll", @@ -111,6 +66,8 @@ DWORD WINAPI StartAddress(LPVOID pParam) NULL, &cbData); + trace(L"Installing hooks..."); + DetourTransactionBegin(); DetourUpdateThread(GetCurrentThread()); @@ -119,16 +76,32 @@ DWORD WINAPI StartAddress(LPVOID pParam) if ( g_pfnLoadLibraryExW ) DetourAttach(&(PVOID)g_pfnLoadLibraryExW, LoadLibraryExW_hook); + // hook IsDeviceServiceable if ( g_pszWUServiceDll ) { hModule = GetModuleHandleW(g_pszWUServiceDll); - if ( hModule && FindIsDeviceServiceablePtr(hModule, - &(PVOID)g_pfnIsDeviceServiceable) ) { - - DetourAttach(&(PVOID)g_pfnIsDeviceServiceable, IsDeviceServiceable_hook); + if ( hModule ) { + if ( FindIDSFunctionPointer(hModule, &(PVOID)g_pfnIsDeviceServiceable) ) { + trace(L"Matched pattern for %ls!IsDeviceServiceable. (%p)", + PathFindFileNameW(g_pszWUServiceDll), + g_pfnIsDeviceServiceable); + DetourAttach(&(PVOID)g_pfnIsDeviceServiceable, IsDeviceServiceable_hook); + } else { + trace(L"No pattern matched!"); + } + } else { + // assume wuaueng.dll hasn't been loaded yet, apply + // RegQueryValueExW hook to fix incompatibility with + // UpdatePack7R2 and other patches that work by + // modifying the Windows Update ServiceDll path in the + // registry. + g_pfnRegQueryValueExW = DetourFindFunction("advapi.dll", "RegQueryValueExW"); + if ( g_pfnRegQueryValueExW ) + DetourAttach(&(PVOID)g_pfnRegQueryValueExW, RegQueryValueExW_hook); } - } - DetourTransactionCommit(); + } + + DetourTransactionCommit(); // wait for unload event or parent mutex to be abandoned. // for example if the user killed rundll32.exe with task manager. @@ -136,30 +109,34 @@ DWORD WINAPI StartAddress(LPVOID pParam) // which point it becomes abandoned again. result = WaitForMultipleObjects(_countof(ctx.handles), ctx.handles, FALSE, INFINITE); - trace(L"Unloading!"); + trace(L"Unload condition has been met."); // unhook - if ( g_pfnLoadLibraryExW || g_pfnIsDeviceServiceable ) { + if ( g_pfnLoadLibraryExW || g_pfnIsDeviceServiceable || g_pfnRegQueryValueExW) { trace(L"Removing hooks..."); - trace(L"DetourTransactionBegin %lu", DetourTransactionBegin()); - trace(L"DetourUpdateThread %lu", DetourUpdateThread(GetCurrentThread())); + DetourTransactionBegin(); + DetourUpdateThread(GetCurrentThread()); if ( g_pfnLoadLibraryExW ) - trace(L"DetourDetach LoadLibraryExW %lu", DetourDetach(&(PVOID)g_pfnLoadLibraryExW, LoadLibraryExW_hook)); + DetourDetach(&(PVOID)g_pfnLoadLibraryExW, LoadLibraryExW_hook); if ( g_pfnIsDeviceServiceable ) - trace(L"DetourDetach IsDeviceServiceable %lu", DetourDetach(&(PVOID)g_pfnIsDeviceServiceable, IsDeviceServiceable_hook)); + DetourDetach(&(PVOID)g_pfnIsDeviceServiceable, IsDeviceServiceable_hook); - trace(L"DetourTransactionCommit %lu", DetourTransactionCommit()); + if ( g_pfnRegQueryValueExW ) + DetourDetach(&(PVOID)g_pfnRegQueryValueExW, RegQueryValueExW_hook); + + DetourTransactionCommit(); } free(g_pszWUServiceDll); release: - ReleaseMutex(ctx.hAuxiliaryMutex); + ReleaseMutex(ctx.hChildMutex); close_handles: - CloseHandle(ctx.hAuxiliaryMutex); - CloseHandle(ctx.hMainMutex); + CloseHandle(ctx.hChildMutex); + CloseHandle(ctx.hParentMutex); CloseHandle(ctx.hUnloadEvent); unload: + trace(L"Freeing library and exiting main thread."); FreeLibraryAndExitThread(PIMAGEBASE, 0); -} \ No newline at end of file +} diff --git a/src/wufuc/callbacks.h b/src/wufuc/callbacks.h index ce8013d..815ed13 100644 --- a/src/wufuc/callbacks.h +++ b/src/wufuc/callbacks.h @@ -1,25 +1,4 @@ #pragma once -#pragma pack(push, 1) -typedef struct -{ - HANDLE hAuxiliaryMutex; - union - { - struct - { - HANDLE hMainMutex; - HANDLE hUnloadEvent; - } DUMMYSTRUCTNAME; - struct - { - HANDLE hMainMutex; - HANDLE hUnloadEvent; - } u; - HANDLE handles[2]; - }; -} ContextHandles; -#pragma pack(pop) - VOID CALLBACK ServiceNotifyCallback(PSERVICE_NOTIFYW pNotifyBuffer); -DWORD WINAPI StartAddress(LPVOID pParam); +DWORD WINAPI ThreadStartCallback(LPVOID pParam); diff --git a/src/wufuc/dllmain.c b/src/wufuc/dllmain.c index 6c2f5d5..805ba47 100644 --- a/src/wufuc/dllmain.c +++ b/src/wufuc/dllmain.c @@ -6,7 +6,6 @@ BOOL APIENTRY DllMain(HMODULE hModule, { switch ( ul_reason_for_call ) { case DLL_PROCESS_ATTACH: - break; case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: diff --git a/src/wufuc/hlpmem.c b/src/wufuc/hlpmem.c index 2967d82..bd54c5f 100644 --- a/src/wufuc/hlpmem.c +++ b/src/wufuc/hlpmem.c @@ -1,10 +1,11 @@ #include "stdafx.h" +#include "hlpmisc.h" #include "hlpmem.h" #include "hlpver.h" #include "hooks.h" -#include +#include "callbacks.h" -bool FindIsDeviceServiceablePtr(HMODULE hModule, PVOID *ppfnIsDeviceServiceable) +bool FindIDSFunctionPointer(HMODULE hModule, PVOID *ppfnIsDeviceServiceable) { bool result = false; bool is_win7 = false; @@ -21,13 +22,11 @@ bool FindIsDeviceServiceablePtr(HMODULE hModule, PVOID *ppfnIsDeviceServiceable) MODULEINFO modinfo; size_t offset; - is_win7 = IsWindowsVersion(6, 1, 1); - if ( !is_win7 ) { - is_win81 = IsWindowsVersion(6, 3, 0); - if ( !is_win81 ) { - trace(L"Unsupported operating system."); - return result; - } + if ( !((is_win7 = IsWindowsVersion(6, 1, 1)) + || (is_win81 = IsWindowsVersion(6, 3, 0))) ) { + + trace(L"Unsupported operating system."); + return result; } ptl = GetVersionInfoFromHModuleAlloc(hModule, L"\\VarFileInfo\\Translation", &cbtl); @@ -54,7 +53,7 @@ bool FindIsDeviceServiceablePtr(HMODULE hModule, PVOID *ppfnIsDeviceServiceable) // identify wuaueng.dll by its resource data tmp = _wcsicmp(pInternalName, L"wuaueng.dll"); if ( tmp ) - trace(L"Module internal name does not match. pInternalName=%ls cbInternalName=%lu", pInternalName, cbInternalName); + trace(L"Module internal name does not match. (%ls)", pInternalName); free(pInternalName); if ( tmp ) continue; @@ -66,8 +65,8 @@ bool FindIsDeviceServiceablePtr(HMODULE hModule, PVOID *ppfnIsDeviceServiceable) } // assure wuaueng.dll is at least the minimum supported version - tmp = ((is_win7 && FileInfoVerCompare(pffi, 7, 6, 7601, 23714) != -1) - || (is_win81 && FileInfoVerCompare(pffi, 7, 9, 9600, 18621) != -1)); + tmp = ((is_win7 && ProductVersionCompare(pffi, 7, 6, 7601, 23714) != -1) + || (is_win81 && ProductVersionCompare(pffi, 7, 9, 9600, 18621) != -1)); free(pffi); if ( !tmp ) { trace(L"Module does not meet the minimum supported version."); @@ -89,10 +88,7 @@ bool FindIsDeviceServiceablePtr(HMODULE hModule, PVOID *ppfnIsDeviceServiceable) ); if ( offset != -1 ) { *ppfnIsDeviceServiceable = (PVOID)((uint8_t *)modinfo.lpBaseOfDll + offset); - trace(L"Found IsDeviceServiceable address: %p", *ppfnIsDeviceServiceable); result = true; - } else { - trace(L"IsDeviceServiceable function pattern not found."); } break; } @@ -128,10 +124,9 @@ bool InjectLibraryAndCreateRemoteThread( Status = NtSuspendProcess(hProcess); if ( !NT_SUCCESS(Status) ) return result; - trace(L"Suspended process. hProcess=%p", hProcess); if ( pParam ) { - // this gets freed by pStartAddress + // this will be VirtualFree()'d by the function at pStartAddress pBaseAddress = VirtualAllocEx(hProcess, NULL, cbParam, @@ -143,7 +138,7 @@ bool InjectLibraryAndCreateRemoteThread( goto vfree; } if ( InjectLibrary(hProcess, hModule, &hRemoteModule) ) { - trace(L"Injected library. hRemoteModule=%p", hRemoteModule); + trace(L"Injected library. (%p)", hRemoteModule); hThread = CreateRemoteThread(hProcess, NULL, @@ -154,7 +149,6 @@ bool InjectLibraryAndCreateRemoteThread( NULL); if ( hThread ) { - trace(L"Created remote thread. hThread=%p", hThread); CloseHandle(hThread); result = true; } @@ -173,9 +167,6 @@ bool InjectLibrary(HANDLE hProcess, HMODULE hModule, HMODULE *phRemoteModule) nLength = GetModuleFileNameW(hModule, Filename, _countof(Filename)); if ( nLength ) { - trace(L"Got module filename for local module. " - L"hModule=%p Filename=%ls nLength=%lu", - hModule, Filename, nLength); return InjectLibraryByFilename(hProcess, Filename, nLength, @@ -200,13 +191,11 @@ bool InjectLibraryByFilename( Status = NtSuspendProcess(hProcess); if ( !NT_SUCCESS(Status) ) return result; - trace(L"Suspended process. hProcess=%p", hProcess); dwProcessId = GetProcessId(hProcess); hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwProcessId); if ( !hSnapshot ) goto resume; - trace(L"Created TH32 module snapshot. dwProcessId=%lu", dwProcessId); *phRemoteModule = GetRemoteHModuleFromTh32ModuleSnapshot(hSnapshot, pLibFilename); @@ -224,13 +213,9 @@ bool InjectLibraryByFilename( PAGE_READWRITE); if ( !pBaseAddress ) goto resume; - trace(L"Allocated virtual memory in process. hProcess=%p pBaseAddress=%p nSize=%Iu", - hProcess, pBaseAddress, nSize); if ( !WriteProcessMemory(hProcess, pBaseAddress, pLibFilename, nSize, NULL) ) goto vfree; - trace(L"Wrote to process memory. hProcess=%p pBaseAddress=%p pLibFileName=%.*ls nSize=%Iu", - hProcess, pBaseAddress, cchLibFilename, pLibFilename, nSize); hThread = CreateRemoteThread(hProcess, NULL, @@ -240,10 +225,8 @@ bool InjectLibraryByFilename( 0, NULL); if ( !hThread ) goto vfree; - trace(L"Created remote thread. hThread=%p", hThread); WaitForSingleObject(hThread, INFINITE); - trace(L"Created thread finished running. hThread=%p", hThread); if ( sizeof *phRemoteModule > sizeof(DWORD) ) { hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwProcessId); @@ -253,13 +236,51 @@ bool InjectLibraryByFilename( pLibFilename); CloseHandle(hSnapshot); - result = !!*phRemoteModule; + result = *phRemoteModule != NULL; } } else { - result = !!GetExitCodeThread(hThread, (LPDWORD)phRemoteModule); + result = GetExitCodeThread(hThread, (LPDWORD)phRemoteModule) != FALSE; } CloseHandle(hThread); vfree: VirtualFreeEx(hProcess, pBaseAddress, 0, MEM_RELEASE); resume: NtResumeProcess(hProcess); return result; } + +bool wufuc_InjectLibrary(DWORD dwProcessId, ContextHandles *pContext) +{ + bool result = false; + HANDLE hProcess; + wchar_t MutexName[44]; + HANDLE hChildMutex; + HANDLE hSrcProcess; + ContextHandles param = { 0 }; + + if ( swprintf_s(MutexName, _countof(MutexName), L"Global\\%08x-7132-44a8-be15-56698979d2f3", dwProcessId) == -1 ) { + trace(L"Failed to print mutex name to string! (%lu)", dwProcessId); + return result; + } + if ( !InitializeMutex(false, MutexName, &hChildMutex) ) { + return result; + } + hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId); + + if ( !hProcess ) { + trace(L"Failed to open target process! (GetLastError=%lu)", GetLastError()); + goto close_mutex; + }; + hSrcProcess = GetCurrentProcess(); + + if ( DuplicateHandle(hSrcProcess, pContext->hParentMutex, hProcess, ¶m.hParentMutex, SYNCHRONIZE, FALSE, 0) + && DuplicateHandle(hSrcProcess, pContext->hUnloadEvent, hProcess, ¶m.hUnloadEvent, SYNCHRONIZE, FALSE, 0) + && DuplicateHandle(hSrcProcess, hChildMutex, hProcess, ¶m.hChildMutex, 0, FALSE, DUPLICATE_SAME_ACCESS) ) { + + InjectLibraryAndCreateRemoteThread(hProcess, PIMAGEBASE, ThreadStartCallback, ¶m, sizeof param); + } else { + trace(L"Failed to duplicate context handles! (GetLastError=%lu", GetLastError()); + } + CloseHandle(hProcess); +close_mutex: + CloseHandle(hChildMutex); + return result; +} diff --git a/src/wufuc/hlpmem.h b/src/wufuc/hlpmem.h index ed53bc9..a7a3a9a 100644 --- a/src/wufuc/hlpmem.h +++ b/src/wufuc/hlpmem.h @@ -1,12 +1,33 @@ #pragma once +#pragma pack(push, 1) +typedef struct +{ + HANDLE hChildMutex; + union + { + struct + { + HANDLE hParentMutex; + HANDLE hUnloadEvent; + } DUMMYSTRUCTNAME; + struct + { + HANDLE hMainMutex; + HANDLE hUnloadEvent; + } u; + HANDLE handles[2]; + }; +} ContextHandles; +#pragma pack(pop) + typedef struct { WORD wLanguage; WORD wCodePage; } LANGANDCODEPAGE, *PLANGANDCODEPAGE; -bool FindIsDeviceServiceablePtr(HMODULE hModule, PVOID *ppfnIsDeviceServiceable); +bool FindIDSFunctionPointer(HMODULE hModule, PVOID *ppfnIsDeviceServiceable); HANDLE GetRemoteHModuleFromTh32ModuleSnapshot(HANDLE hSnapshot, const wchar_t *pLibFileName); bool InjectLibraryAndCreateRemoteThread( HANDLE hProcess, @@ -20,3 +41,4 @@ bool InjectLibraryByFilename( const wchar_t *pLibFilename, size_t cchLibFilename, HMODULE *phRemoteModule); +bool wufuc_InjectLibrary(DWORD dwProcessId, ContextHandles *pContext); diff --git a/src/wufuc/hlpmisc.c b/src/wufuc/hlpmisc.c index 8e72371..f85af7a 100644 --- a/src/wufuc/hlpmisc.c +++ b/src/wufuc/hlpmisc.c @@ -14,6 +14,8 @@ bool InitializeMutex(bool InitialOwner, const wchar_t *pMutexName, HANDLE *phMut } *phMutex = hMutex; return true; + } else { + trace(L"Failed to create mutex: %ls (GetLastError=%ld)", pMutexName, GetLastError()); } return false; } @@ -68,3 +70,44 @@ PVOID RegGetValueAlloc( } return result; } + +PVOID NtQueryKeyAlloc(HANDLE KeyHandle, KEY_INFORMATION_CLASS KeyInformationClass, PULONG pResultLength) +{ + NTSTATUS Status; + ULONG ResultLength; + PVOID result = NULL; + + Status = NtQueryKey(KeyHandle, KeyInformationClass, NULL, 0, &ResultLength); + if ( Status != STATUS_BUFFER_OVERFLOW && Status != STATUS_BUFFER_TOO_SMALL ) + return result; + + result = malloc(ResultLength); + if ( !result ) return result; + + Status = NtQueryKey(KeyHandle, KeyInformationClass, result, ResultLength, &ResultLength); + if ( NT_SUCCESS(Status) ) { + *pResultLength = ResultLength; + } else { + free(result); + result = NULL; + } + return result; +} + + +bool FileExistsExpandEnvironmentStrings(const wchar_t *path) +{ + bool result; + LPWSTR dst; + DWORD buffersize; + DWORD size; + + buffersize = ExpandEnvironmentStringsW(path, NULL, 0); + dst = calloc(buffersize, sizeof *dst); + size = ExpandEnvironmentStringsW(path, dst, buffersize); + if ( !size || size > buffersize ) + return false; + result = PathFileExistsW(dst); + free(dst); + return result; +} diff --git a/src/wufuc/hlpmisc.h b/src/wufuc/hlpmisc.h index 4d6e27c..e613823 100644 --- a/src/wufuc/hlpmisc.h +++ b/src/wufuc/hlpmisc.h @@ -14,3 +14,5 @@ PVOID RegGetValueAlloc( DWORD dwFlags, LPDWORD pdwType, LPDWORD pcbData); +PVOID NtQueryKeyAlloc(HANDLE KeyHandle, KEY_INFORMATION_CLASS KeyInformationClass, PULONG pResultLength); +bool FileExistsExpandEnvironmentStrings(const wchar_t *path); diff --git a/src/wufuc/hlpsvc.c b/src/wufuc/hlpsvc.c index 4007132..112685f 100644 --- a/src/wufuc/hlpsvc.c +++ b/src/wufuc/hlpsvc.c @@ -8,12 +8,25 @@ LPQUERY_SERVICE_CONFIGW QueryServiceConfigByNameAlloc( LPDWORD pcbBufSize) { SC_HANDLE hService; - DWORD cbBytesNeeded; LPQUERY_SERVICE_CONFIGW result = NULL; hService = OpenServiceW(hSCM, pServiceName, SERVICE_QUERY_CONFIG); if ( !hService ) return result; + result = QueryServiceConfigAlloc(hSCM, hService, pcbBufSize); + + CloseServiceHandle(hService); + return result; +} + +LPQUERY_SERVICE_CONFIGW QueryServiceConfigAlloc( + SC_HANDLE hSCM, + SC_HANDLE hService, + LPDWORD pcbBufSize) +{ + DWORD cbBytesNeeded; + LPQUERY_SERVICE_CONFIGW result = NULL; + if ( !QueryServiceConfigW(hService, NULL, 0, &cbBytesNeeded) && GetLastError() == ERROR_INSUFFICIENT_BUFFER ) { @@ -27,7 +40,6 @@ LPQUERY_SERVICE_CONFIGW QueryServiceConfigByNameAlloc( } } } - CloseServiceHandle(hService); return result; } @@ -55,7 +67,7 @@ bool QueryServiceStatusProcessInfoByName( return result; } -bool QueryServiceGroupName(const LPQUERY_SERVICE_CONFIGW pServiceConfig, wchar_t *pGroupName, size_t nSize) +bool QueryServiceGroupName(const LPQUERY_SERVICE_CONFIGW pServiceConfig, LPWSTR *pGroupName, HLOCAL *hMem) { bool result = false; int NumArgs; @@ -66,16 +78,36 @@ bool QueryServiceGroupName(const LPQUERY_SERVICE_CONFIGW pServiceConfig, wchar_t if ( !_wcsicmp(PathFindFileNameW(argv[0]), L"svchost.exe") ) { for ( int i = 1; (i + 1) < NumArgs; i++ ) { - if ( !_wcsicmp(argv[i], L"-k") ) - return !wcscpy_s(pGroupName, nSize, argv[++i]); + if ( !_wcsicmp(argv[i], L"-k") ) { + *pGroupName = argv[++i]; + *hMem = (HLOCAL)argv; + return true; + } } } LocalFree((HLOCAL)argv); } + return false; +} + +DWORD QueryServiceProcessId(SC_HANDLE hSCM, SC_HANDLE hService) +{ + DWORD result = 0; + SERVICE_STATUS_PROCESS ServiceStatus; + DWORD cbBytesNeeded; + + if ( QueryServiceStatusEx(hService, + SC_STATUS_PROCESS_INFO, + (LPBYTE)&ServiceStatus, + sizeof ServiceStatus, + &cbBytesNeeded) ) { + + result = ServiceStatus.dwProcessId; + } return result; } -DWORD QueryServiceProcessId(SC_HANDLE hSCM, const wchar_t *pServiceName) +DWORD QueryServiceProcessIdByName(SC_HANDLE hSCM, const wchar_t *pServiceName) { SERVICE_STATUS_PROCESS ServiceStatusProcess; @@ -84,31 +116,44 @@ DWORD QueryServiceProcessId(SC_HANDLE hSCM, const wchar_t *pServiceName) return 0; } -DWORD InferSvchostGroupProcessId(SC_HANDLE hSCM, const wchar_t *pGroupName) +DWORD HeuristicServiceGroupProcessId(SC_HANDLE hSCM, const wchar_t *pGroupNameSearch) { - DWORD result = 0; - DWORD cbData; wchar_t *pData; + DWORD cbData; + DWORD result = 0; DWORD dwProcessId; DWORD cbBufSize; LPQUERY_SERVICE_CONFIGW pServiceConfig; bool success; - WCHAR GroupName[256]; + LPWSTR pGroupName; + HLOCAL hMem; + + pData = RegGetValueAlloc(HKEY_LOCAL_MACHINE, + L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Svchost", + pGroupNameSearch, + RRF_RT_REG_MULTI_SZ, + NULL, + &cbData); - pData = RegGetValueAlloc(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Svchost", pGroupName, RRF_RT_REG_MULTI_SZ, NULL, &cbData); if ( !pData ) return result; for ( wchar_t *pName = pData; *pName; pName += wcslen(pName) + 1 ) { - dwProcessId = QueryServiceProcessId(hSCM, pName); + dwProcessId = QueryServiceProcessIdByName(hSCM, pName); trace(L"pName=%ls dwProcessId=%lu", pName, dwProcessId); if ( !dwProcessId ) continue; pServiceConfig = QueryServiceConfigByNameAlloc(hSCM, pName, &cbBufSize); if ( !pServiceConfig ) continue; - success = QueryServiceGroupName(pServiceConfig, GroupName, _countof(GroupName)); + + success = QueryServiceGroupName(pServiceConfig, &pGroupName, &hMem); free(pServiceConfig); - if ( success && !_wcsicmp(pGroupName, GroupName) ) { - trace(L"found PID for group %ls: %lu", pGroupName, dwProcessId); + if ( !success ) + continue; + + success = !_wcsicmp(pGroupNameSearch, pGroupName); + LocalFree(hMem); + if ( success ) { + trace(L"Found PID: %lu", dwProcessId); result = dwProcessId; break; } @@ -116,3 +161,51 @@ DWORD InferSvchostGroupProcessId(SC_HANDLE hSCM, const wchar_t *pGroupName) free(pData); return result; } + +DWORD HeuristicServiceProcessId(SC_HANDLE hSCM, SC_HANDLE hService) +{ + DWORD result = 0; + LPQUERY_SERVICE_CONFIGW pServiceConfig; + LPWSTR pGroupName; + HLOCAL hMem; + DWORD cbBufSize; + + result = QueryServiceProcessId(hSCM, hService); + if ( result ) + return result; + + pServiceConfig = QueryServiceConfigAlloc(hSCM, hService, &cbBufSize); + if ( pServiceConfig ) { + switch ( pServiceConfig->dwServiceType ) { + case SERVICE_WIN32_OWN_PROCESS: + // if the service isn't already running there's no + // way to accurately guess the PID when it is set to + // run in its own process. returns 0 + break; + case SERVICE_WIN32_SHARE_PROCESS: + // when the service is configured to run in a shared + // process, it is possible to "guess" which svchost.exe + // it will eventually be loaded into by finding other + // services in the same group that are already running. + if ( QueryServiceGroupName(pServiceConfig, &pGroupName, &hMem) ) { + result = HeuristicServiceGroupProcessId(hSCM, pGroupName); + LocalFree(hMem); + return result; + } + break; + } + free(pServiceConfig); + } +} + +DWORD HeuristicServiceProcessIdByName(SC_HANDLE hSCM, const wchar_t *pServiceName) +{ + DWORD result = 0; + SC_HANDLE hService; + + hService = OpenServiceW(hSCM, pServiceName, SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG); + result = HeuristicServiceProcessId(hSCM, hService); + CloseServiceHandle(hService); + return result; + +} diff --git a/src/wufuc/hlpsvc.h b/src/wufuc/hlpsvc.h index 2245e96..b6530c0 100644 --- a/src/wufuc/hlpsvc.h +++ b/src/wufuc/hlpsvc.h @@ -4,10 +4,17 @@ LPQUERY_SERVICE_CONFIGW QueryServiceConfigByNameAlloc( SC_HANDLE hSCM, const wchar_t *pServiceName, LPDWORD pcbBufSize); +LPQUERY_SERVICE_CONFIGW QueryServiceConfigAlloc( + SC_HANDLE hSCM, + SC_HANDLE hService, + LPDWORD pcbBufSize); bool QueryServiceStatusProcessInfoByName( SC_HANDLE hSCM, const wchar_t *pServiceName, LPSERVICE_STATUS_PROCESS pServiceStatus); -bool QueryServiceGroupName(const LPQUERY_SERVICE_CONFIGW pServiceConfig, wchar_t *pGroupName, size_t nSize); -DWORD QueryServiceProcessId(SC_HANDLE hSCM, const wchar_t *pServiceName); -DWORD InferSvchostGroupProcessId(SC_HANDLE hSCM, const wchar_t *pGroupName); +bool QueryServiceGroupName(const LPQUERY_SERVICE_CONFIGW pServiceConfig, LPWSTR *pGroupName, HLOCAL *hMem); +DWORD QueryServiceProcessId(SC_HANDLE hSCM, SC_HANDLE hService); +DWORD QueryServiceProcessIdByName(SC_HANDLE hSCM, const wchar_t *pServiceName); +DWORD HeuristicServiceGroupProcessId(SC_HANDLE hSCM, const wchar_t *pGroupName); +DWORD HeuristicServiceProcessId(SC_HANDLE hSCM, SC_HANDLE hService); +DWORD HeuristicServiceProcessIdByName(SC_HANDLE hSCM, const wchar_t *pServiceName); \ No newline at end of file diff --git a/src/wufuc/hlpver.c b/src/wufuc/hlpver.c index 04010bf..b6fea42 100644 --- a/src/wufuc/hlpver.c +++ b/src/wufuc/hlpver.c @@ -1,7 +1,7 @@ #include "stdafx.h" #include "hlpver.h" -int FileInfoVerCompare(VS_FIXEDFILEINFO *pffi, WORD wMajor, WORD wMinor, WORD wBuild, WORD wRev) +int ProductVersionCompare(VS_FIXEDFILEINFO *pffi, WORD wMajor, WORD wMinor, WORD wBuild, WORD wRev) { if ( HIWORD(pffi->dwProductVersionMS) < wMajor ) return -1; if ( HIWORD(pffi->dwProductVersionMS) > wMajor ) return 1; diff --git a/src/wufuc/hlpver.h b/src/wufuc/hlpver.h index 6c45376..ae0905e 100644 --- a/src/wufuc/hlpver.h +++ b/src/wufuc/hlpver.h @@ -1,6 +1,6 @@ #pragma once -int FileInfoVerCompare(VS_FIXEDFILEINFO *pffi, WORD wMajor, WORD wMinor, WORD wBuild, WORD wRev); +int ProductVersionCompare(VS_FIXEDFILEINFO *pffi, WORD wMajor, WORD wMinor, WORD wBuild, WORD wRev); bool GetVersionInfoFromHModule(HMODULE hModule, LPCWSTR pszSubBlock, LPVOID pData, PUINT pcbData); LPVOID GetVersionInfoFromHModuleAlloc(HMODULE hModule, LPCWSTR pszSubBlock, PUINT pcbData); bool IsWindowsVersion(WORD wMajorVersion, WORD wMinorVersion, WORD wServicePackMajor); diff --git a/src/wufuc/hooks.c b/src/wufuc/hooks.c index 59e4ecc..6957c4d 100644 --- a/src/wufuc/hooks.c +++ b/src/wufuc/hooks.c @@ -1,13 +1,62 @@ #include "stdafx.h" #include "hooks.h" -#include "hlpmem.h" #include "hlpmisc.h" -#include "hlpsvc.h" +#include "hlpmem.h" LPWSTR g_pszWUServiceDll; -LPFN_ISDEVICESERVICEABLE g_pfnIsDeviceServiceable; +LPFN_REGQUERYVALUEEXW g_pfnRegQueryValueExW; LPFN_LOADLIBRARYEXW g_pfnLoadLibraryExW; +LPFN_ISDEVICESERVICEABLE g_pfnIsDeviceServiceable; + +LSTATUS WINAPI RegQueryValueExW_hook(HKEY hKey, LPCWSTR lpValueName, LPDWORD lpReserved, LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData) +{ + PWCH pBuffer; + DWORD MaximumLength = 0; + LSTATUS result; + ULONG ResultLength; + PKEY_NAME_INFORMATION pkni; + size_t NameCount; + int current; + int pos; + LPWSTR fname; + const WCHAR realpath[] = L"%systemroot%\\system32\\wuaueng.dll"; + + // save original buffer size + if ( lpData && lpcbData ) + MaximumLength = *lpcbData; + result = g_pfnRegQueryValueExW(hKey, lpValueName, lpReserved, lpType, lpData, lpcbData); + + if ( result != ERROR_SUCCESS || !MaximumLength || !lpValueName || _wcsicmp(lpValueName, L"ServiceDll") ) + return result; + + pBuffer = (PWCH)lpData; + + // get name of registry key being queried + pkni = NtQueryKeyAlloc((HANDLE)hKey, KeyNameInformation, &ResultLength); + if ( !pkni ) + return result; + + NameCount = pkni->NameLength / sizeof *pkni->Name; + // change key name to lower-case because there is no case-insensitive version of _snwscanf_s + if ( !_wcslwr_s(pkni->Name, NameCount) + && _snwscanf_s(pkni->Name, NameCount, L"\\registry\\machine\\system\\controlset%03d\\services\\wuauserv\\parameters%n", ¤t, &pos) == 1 + && pos == NameCount ) { + + fname = PathFindFileNameW(pBuffer); + + if ( (!_wcsicmp(fname, L"wuaueng2.dll") // UpdatePack7R2 + || !_wcsicmp(fname, L"WuaCpuFix64.dll") // WuaCpuFix + || !_wcsicmp(fname, L"WuaCpuFix.dll")) + && FileExistsExpandEnvironmentStrings(realpath) + && SUCCEEDED(StringCbCopyW(pBuffer, MaximumLength, realpath)) ) { + + *lpcbData = sizeof realpath; + } + } + free(pkni); + return result; +} HMODULE WINAPI LoadLibraryExW_hook(LPCWSTR lpFileName, HANDLE hFile, DWORD dwFlags) { @@ -15,21 +64,29 @@ HMODULE WINAPI LoadLibraryExW_hook(LPCWSTR lpFileName, HANDLE hFile, DWORD dwFla result = g_pfnLoadLibraryExW(lpFileName, hFile, dwFlags); if ( !result ) return result; - trace(L"Loaded library. lpFileName=%ls result=%p", lpFileName, result); + trace(L"Loaded library: %ls (%p)", lpFileName, result); if ( g_pszWUServiceDll - && !_wcsicmp(lpFileName, g_pszWUServiceDll) - && FindIsDeviceServiceablePtr(result, &(PVOID)g_pfnIsDeviceServiceable) ) { + && !_wcsicmp(lpFileName, g_pszWUServiceDll) ) { - trace(L"DetourTransactionBegin=%lu", DetourTransactionBegin()); - trace(L"DetourUpdateThread=%lu", DetourUpdateThread(GetCurrentThread())); - trace(L"DetourAttach=%lu", DetourAttach(&(PVOID)g_pfnIsDeviceServiceable, IsDeviceServiceable_hook)); - trace(L"DetourTransactionCommit=%lu", DetourTransactionCommit()); + if ( FindIDSFunctionPointer(result, &(PVOID)g_pfnIsDeviceServiceable) ) { + trace(L"Matched pattern for %ls!IsDeviceServiceable. (%p)", + PathFindFileNameW(lpFileName), + g_pfnIsDeviceServiceable); + + DetourTransactionBegin(); + DetourUpdateThread(GetCurrentThread()); + DetourAttach(&(PVOID)g_pfnIsDeviceServiceable, IsDeviceServiceable_hook); + DetourTransactionCommit(); + } else if ( !g_pfnIsDeviceServiceable ) { + trace(L"No pattern matched!"); + } } return result; } BOOL WINAPI IsDeviceServiceable_hook(void) { + trace(L"Entered stub function."); return TRUE; } diff --git a/src/wufuc/hooks.h b/src/wufuc/hooks.h index 05f494d..ef80057 100644 --- a/src/wufuc/hooks.h +++ b/src/wufuc/hooks.h @@ -1,13 +1,16 @@ #pragma once +typedef LSTATUS(WINAPI *LPFN_REGQUERYVALUEEXW)(HKEY, LPCWSTR, LPDWORD, LPDWORD, LPBYTE, LPDWORD); typedef HMODULE(WINAPI *LPFN_LOADLIBRARYEXW)(LPCWSTR, HANDLE, DWORD); typedef BOOL(WINAPI *LPFN_ISDEVICESERVICEABLE)(void); extern LPWSTR g_pszWUServiceDll; +extern LPFN_REGQUERYVALUEEXW g_pfnRegQueryValueExW; extern LPFN_LOADLIBRARYEXW g_pfnLoadLibraryExW; extern LPFN_ISDEVICESERVICEABLE g_pfnIsDeviceServiceable; +LSTATUS WINAPI RegQueryValueExW_hook(HKEY hKey, LPCWSTR lpValueName, LPDWORD lpReserved, LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData); HMODULE WINAPI LoadLibraryExW_hook(LPCWSTR lpFileName, HANDLE hFile, DWORD dwFlags); BOOL WINAPI IsDeviceServiceable_hook(void); diff --git a/src/wufuc/rundll32.c b/src/wufuc/rundll32.c index feff0d4..bb51a20 100644 --- a/src/wufuc/rundll32.c +++ b/src/wufuc/rundll32.c @@ -1,11 +1,14 @@ #include "stdafx.h" #include "callbacks.h" #include "hlpmisc.h" - +#include "hlpmem.h" +#include "hlpsvc.h" void CALLBACK RUNDLL32_StartW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) { ContextHandles ctx; + DWORD dwDesiredAccess; + DWORD dwProcessId; bool Unloading = false; bool Lagging; SC_HANDLE hSCM; @@ -14,7 +17,7 @@ void CALLBACK RUNDLL32_StartW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, in if ( !InitializeMutex(true, L"Global\\25020063-b5a7-4227-9fdf-25cb75e8c645", - &ctx.hMainMutex) ) { + &ctx.hParentMutex) ) { trace(L"Failed to initialize main mutex. (GetLastError=%ul)", GetLastError()); return; @@ -28,6 +31,7 @@ void CALLBACK RUNDLL32_StartW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, in trace(L"Failed to create unload event. (GetLastError=%ul)", GetLastError()); goto close_mutex; } + dwDesiredAccess = SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG; do { Lagging = false; hSCM = OpenSCManagerW(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE); @@ -36,12 +40,18 @@ void CALLBACK RUNDLL32_StartW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, in goto close_event; } - hService = OpenServiceW(hSCM, L"wuauserv", SERVICE_QUERY_STATUS); + hService = OpenServiceW(hSCM, L"wuauserv", dwDesiredAccess); if ( !hService ) { trace(L"Failed to open service. (GetLastError=%ul)", GetLastError()); goto close_scm; } + if ( (dwDesiredAccess & SERVICE_QUERY_CONFIG) == SERVICE_QUERY_CONFIG ) { + dwDesiredAccess &= ~SERVICE_QUERY_CONFIG; + dwProcessId = HeuristicServiceProcessId(hSCM, hService); + if ( dwProcessId ) + wufuc_InjectLibrary(dwProcessId, &ctx); + } ZeroMemory(&NotifyBuffer, sizeof NotifyBuffer); NotifyBuffer.dwVersion = SERVICE_NOTIFY_STATUS_CHANGE; NotifyBuffer.pfnNotifyCallback = ServiceNotifyCallback; @@ -68,8 +78,8 @@ close_scm: CloseServiceHandle(hSCM); close_event: CloseHandle(ctx.hUnloadEvent); close_mutex: - ReleaseMutex(ctx.hMainMutex); - CloseHandle(ctx.hMainMutex); + ReleaseMutex(ctx.hParentMutex); + CloseHandle(ctx.hParentMutex); } void CALLBACK RUNDLL32_UnloadW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow)