diff --git a/README.md b/README.md index 20b4c3c..884b0d3 100644 --- a/README.md +++ b/README.md @@ -103,8 +103,9 @@ Advanced Installer's intuitive and friendly user interface allowed me to quickly ## Special thanks -- Wen Jia Liu ([@wj32](https://github.com/wj32)) for his awesome program [Process Hacker](https://github.com/processhacker2/processhacker) which has been absolutely instrumental in the development of wufuc, and also for his [`phnt`](https://github.com/processhacker2/processhacker/tree/master/phnt) headers. +- Wen Jia Liu ([@wj32](https://github.com/wj32)) for his awesome program [Process Hacker](https://github.com/processhacker2/processhacker), and also for his [`phnt` headers](https://github.com/processhacker2/processhacker/tree/master/phnt). - Duncan Ogilvie ([@mrexodia](https://github.com/mrexodia)) for his [`patternfind.cpp`](https://github.com/x64dbg/x64dbg/blob/development/src/dbg/patternfind.cpp) algorithm from [x64dbg](https://github.com/x64dbg/x64dbg). +- Tsuda Kageyu ([@TsudaKageyu](https://github.com/TsudaKageyu)) for his excellent [minhook](https://github.com/TsudaKageyu/minhook) library. [Latest]: https://github.com/zeffy/wufuc/releases/latest [AppVeyor]: https://ci.appveyor.com/project/zeffy/wufuc diff --git a/inc/minhook/minhook.h b/inc/minhook/minhook.h new file mode 100644 index 0000000..9f62e33 --- /dev/null +++ b/inc/minhook/minhook.h @@ -0,0 +1,184 @@ +/* + * MinHook - The Minimalistic API Hooking Library for x64/x86 + * Copyright (C) 2009-2017 Tsuda Kageyu. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if !(defined _M_IX86) && !(defined _M_X64) && !(defined __i386__) && !(defined __x86_64__) + #error MinHook supports only x86 and x64 systems. +#endif + +// MinHook Error Codes. +typedef enum MH_STATUS +{ + // Unknown error. Should not be returned. + MH_UNKNOWN = -1, + + // Successful. + MH_OK = 0, + + // MinHook is already initialized. + MH_ERROR_ALREADY_INITIALIZED, + + // MinHook is not initialized yet, or already uninitialized. + MH_ERROR_NOT_INITIALIZED, + + // The hook for the specified target function is already created. + MH_ERROR_ALREADY_CREATED, + + // The hook for the specified target function is not created yet. + MH_ERROR_NOT_CREATED, + + // The hook for the specified target function is already enabled. + MH_ERROR_ENABLED, + + // The hook for the specified target function is not enabled yet, or already + // disabled. + MH_ERROR_DISABLED, + + // The specified pointer is invalid. It points the address of non-allocated + // and/or non-executable region. + MH_ERROR_NOT_EXECUTABLE, + + // The specified target function cannot be hooked. + MH_ERROR_UNSUPPORTED_FUNCTION, + + // Failed to allocate memory. + MH_ERROR_MEMORY_ALLOC, + + // Failed to change the memory protection. + MH_ERROR_MEMORY_PROTECT, + + // The specified module is not loaded. + MH_ERROR_MODULE_NOT_FOUND, + + // The specified function is not found. + MH_ERROR_FUNCTION_NOT_FOUND +} +MH_STATUS; + +// Can be passed as a parameter to MH_EnableHook, MH_DisableHook, +// MH_QueueEnableHook or MH_QueueDisableHook. +#define MH_ALL_HOOKS NULL + +#ifdef __cplusplus +extern "C" { +#endif + + // Initialize the MinHook library. You must call this function EXACTLY ONCE + // at the beginning of your program. + MH_STATUS WINAPI MH_Initialize(VOID); + + // Uninitialize the MinHook library. You must call this function EXACTLY + // ONCE at the end of your program. + MH_STATUS WINAPI MH_Uninitialize(VOID); + + // Creates a Hook for the specified target function, in disabled state. + // Parameters: + // pTarget [in] A pointer to the target function, which will be + // overridden by the detour function. + // pDetour [in] A pointer to the detour function, which will override + // the target function. + // ppOriginal [out] A pointer to the trampoline function, which will be + // used to call the original target function. + // This parameter can be NULL. + MH_STATUS WINAPI MH_CreateHook(LPVOID pTarget, LPVOID pDetour, LPVOID *ppOriginal); + + // Creates a Hook for the specified API function, in disabled state. + // Parameters: + // pszModule [in] A pointer to the loaded module name which contains the + // target function. + // pszTarget [in] A pointer to the target function name, which will be + // overridden by the detour function. + // pDetour [in] A pointer to the detour function, which will override + // the target function. + // ppOriginal [out] A pointer to the trampoline function, which will be + // used to call the original target function. + // This parameter can be NULL. + MH_STATUS WINAPI MH_CreateHookApi( + LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, LPVOID *ppOriginal); + + // Creates a Hook for the specified API function, in disabled state. + // Parameters: + // pszModule [in] A pointer to the loaded module name which contains the + // target function. + // pszTarget [in] A pointer to the target function name, which will be + // overridden by the detour function. + // pDetour [in] A pointer to the detour function, which will override + // the target function. + // ppOriginal [out] A pointer to the trampoline function, which will be + // used to call the original target function. + // This parameter can be NULL. + // ppTarget [out] A pointer to the target function, which will be used + // with other functions. + // This parameter can be NULL. + MH_STATUS WINAPI MH_CreateHookApiEx( + LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, LPVOID *ppOriginal, LPVOID *ppTarget); + + // Removes an already created hook. + // Parameters: + // pTarget [in] A pointer to the target function. + MH_STATUS WINAPI MH_RemoveHook(LPVOID pTarget); + + // Enables an already created hook. + // Parameters: + // pTarget [in] A pointer to the target function. + // If this parameter is MH_ALL_HOOKS, all created hooks are + // enabled in one go. + MH_STATUS WINAPI MH_EnableHook(LPVOID pTarget); + + // Disables an already created hook. + // Parameters: + // pTarget [in] A pointer to the target function. + // If this parameter is MH_ALL_HOOKS, all created hooks are + // disabled in one go. + MH_STATUS WINAPI MH_DisableHook(LPVOID pTarget); + + // Queues to enable an already created hook. + // Parameters: + // pTarget [in] A pointer to the target function. + // If this parameter is MH_ALL_HOOKS, all created hooks are + // queued to be enabled. + MH_STATUS WINAPI MH_QueueEnableHook(LPVOID pTarget); + + // Queues to disable an already created hook. + // Parameters: + // pTarget [in] A pointer to the target function. + // If this parameter is MH_ALL_HOOKS, all created hooks are + // queued to be disabled. + MH_STATUS WINAPI MH_QueueDisableHook(LPVOID pTarget); + + // Applies all queued changes in one go. + MH_STATUS WINAPI MH_ApplyQueued(VOID); + + // Translates the MH_STATUS to its name as a string. + const char * WINAPI MH_StatusToString(MH_STATUS status); + +#ifdef __cplusplus +} +#endif + diff --git a/lib/minhook/libMinHook.x64.MT.lib b/lib/minhook/libMinHook.x64.MT.lib new file mode 100644 index 0000000..9bed8ad Binary files /dev/null and b/lib/minhook/libMinHook.x64.MT.lib differ diff --git a/lib/minhook/libMinHook.x64.MTd.lib b/lib/minhook/libMinHook.x64.MTd.lib new file mode 100644 index 0000000..7ce78df Binary files /dev/null and b/lib/minhook/libMinHook.x64.MTd.lib differ diff --git a/lib/minhook/libMinHook.x86.MT.lib b/lib/minhook/libMinHook.x86.MT.lib new file mode 100644 index 0000000..8a9d7f3 Binary files /dev/null and b/lib/minhook/libMinHook.x86.MT.lib differ diff --git a/lib/minhook/libMinHook.x86.MTd.lib b/lib/minhook/libMinHook.x86.MTd.lib new file mode 100644 index 0000000..36c32ba Binary files /dev/null and b/lib/minhook/libMinHook.x86.MTd.lib differ diff --git a/src/wufuc/callbacks.c b/src/wufuc/callbacks.c index fa8eefd..422eaf6 100644 --- a/src/wufuc/callbacks.c +++ b/src/wufuc/callbacks.c @@ -1,52 +1,36 @@ #include "stdafx.h" -#include "callbacks.h" -#include "hlpmem.h" -#include "hlpmisc.h" -#include "hlpsvc.h" -#include "hooks.h" -VOID CALLBACK ServiceNotifyCallback(PSERVICE_NOTIFYW pNotifyBuffer) +#include "context.h" +#include "callbacks.h" +#include "hooks.h" +#include "log.h" +#include "modulehelper.h" +#include "registryhelper.h" +#include "servicehelper.h" +#include "wufuc.h" + +#include + +VOID CALLBACK cb_service_notify(PSERVICE_NOTIFYW pNotifyBuffer) { switch ( pNotifyBuffer->dwNotificationStatus ) { case ERROR_SUCCESS: if ( pNotifyBuffer->ServiceStatus.dwProcessId ) - wufuc_InjectLibrary( + wufuc_inject( pNotifyBuffer->ServiceStatus.dwProcessId, - (ContextHandles *)pNotifyBuffer->pContext); + (LPTHREAD_START_ROUTINE)cb_start, + (context *)pNotifyBuffer->pContext); break; case ERROR_SERVICE_MARKED_FOR_DELETE: - SetEvent(((ContextHandles *)pNotifyBuffer->pContext)->hUnloadEvent); + SetEvent(((context *)pNotifyBuffer->pContext)->uevent); break; } if ( pNotifyBuffer->pszServiceNames ) LocalFree((HLOCAL)pNotifyBuffer->pszServiceNames); } -DWORD WINAPI PipeLoopThreadCallback(LPVOID pParam) +DWORD WINAPI cb_start(context *ctx) { - HANDLE hPipe = (HANDLE)pParam; - BOOL fSuccess; - wchar_t chBuf[512]; - while (true) { - // Read from the pipe. - - fSuccess = ReadFile( - hPipe, // pipe handle - chBuf, // buffer to receive reply - BUFSIZE * sizeof(wchar_t), // size of buffer - &cbRead, // number of bytes read - NULL); // not overlapped - - if ( !fSuccess && GetLastError() != ERROR_MORE_DATA ) - break;; - - _tprintf(TEXT("\"%s\"\n"), chBuf); - } -} - -DWORD WINAPI StartThreadCallback(LPVOID pParam) -{ - ContextHandles ctx; SC_HANDLE hSCM; SC_HANDLE hService; DWORD dwProcessId; @@ -54,22 +38,17 @@ DWORD WINAPI StartThreadCallback(LPVOID pParam) DWORD dwServiceType; wchar_t *str; HMODULE hModule; - wchar_t Filename[MAX_PATH]; DWORD result; // get mutex and unload event handles from virtual memory - if ( !pParam ) { + if ( !ctx ) { trace(L"Context parameter is null!"); goto unload; } - ctx = *(ContextHandles *)pParam; - if ( !VirtualFree(pParam, 0, MEM_RELEASE) ) - trace(L"Failed to free context parameter. (%p, GetLastError=%lu)", - pParam, GetLastError()); - // acquire child mutex, should be immediate. - if ( WaitForSingleObject(ctx.hChildMutex, 5000) != WAIT_OBJECT_0 ) { - trace(L"Failed to acquire child mutex within five seconds. (%p)", ctx.hChildMutex); + // acquire child mutex, this should be immediate. + if ( WaitForSingleObject(ctx->handles[ctx->mutex_tag], 5000) != WAIT_OBJECT_0 ) { + trace(L"Failed to acquire child mutex within five seconds. (%p)", ctx->handles[ctx->mutex_tag]); goto close_handles; } @@ -79,112 +58,78 @@ DWORD WINAPI StartThreadCallback(LPVOID pParam) goto release; } - hService = OpenServiceW(hSCM, L"wuauserv", SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG); - dwProcessId = HeuristicServiceProcessId(hSCM, hService); - pServiceConfig = QueryServiceConfigAlloc(hSCM, hService, NULL); + hService = OpenServiceW(hSCM, L"wuauserv", + SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG); + dwProcessId = svc_heuristic_process_id(hSCM, hService); + pServiceConfig = svc_query_config_alloc(hSCM, hService, NULL); dwServiceType = pServiceConfig->dwServiceType; free(pServiceConfig); CloseServiceHandle(hService); CloseServiceHandle(hSCM); if ( dwProcessId != GetCurrentProcessId() ) { - trace(L"Injected into wrong process!", GetCurrentProcessId(), dwProcessId); + trace(L"Injected into wrong process!"); goto release; } - trace(L"Installing hooks..."); - if ( dwServiceType == SERVICE_WIN32_SHARE_PROCESS ) { - DetourTransactionBegin(); - DetourUpdateThread(GetCurrentThread()); - // 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("kernel32.dll", "RegQueryValueExW"); - if ( g_pfnRegQueryValueExW ) - DetourAttach(&(PVOID)g_pfnRegQueryValueExW, RegQueryValueExW_hook); - DetourTransactionCommit(); + // UpdatePack7R2 and other patches that modify the + // Windows Update ServiceDll path in the registry. + MH_CreateHookApi(L"kernel32.dll", + "RegQueryValueExW", + RegQueryValueExW_hook, + &(PVOID)g_pfnRegQueryValueExW); + MH_EnableHook(g_pfnRegQueryValueExW); } // query the ServiceDll path after applying our compat hook so that it // is correct - str = (wchar_t *)RegQueryValueExAlloc(HKEY_LOCAL_MACHINE, + str = (wchar_t *)reg_query_value_alloc(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\services\\wuauserv\\Parameters", L"ServiceDll", NULL, NULL); - g_pszWUServiceDll = ExpandEnvironmentStringsAlloc(str, NULL); + g_pszWUServiceDll = env_expand_strings_alloc(str, NULL); free(str); - DetourTransactionBegin(); - DetourUpdateThread(GetCurrentThread()); + MH_CreateHookApi(L"kernel32.dll", + "LoadLibraryExW", + LoadLibraryExW_hook, + &(PVOID)g_pfnLoadLibraryExW); - g_pfnLoadLibraryExW = DetourFindFunction("kernel32.dll", "LoadLibraryExW"); - if ( g_pfnLoadLibraryExW ) - DetourAttach(&(PVOID)g_pfnLoadLibraryExW, LoadLibraryExW_hook); + if ( g_pszWUServiceDll + && (GetModuleHandleExW(0, g_pszWUServiceDll, &hModule) + || GetModuleHandleExW(0, PathFindFileNameW(g_pszWUServiceDll), &hModule))) { - if ( g_pszWUServiceDll ) { - if ( GetModuleHandleExW(0, g_pszWUServiceDll, &hModule) ) { - if ( FindIDSFunctionAddress(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!"); - } - FreeLibrary(hModule); - } + // hook IsDeviceServiceable if wuaueng.dll is already loaded + wufuc_hook(hModule); + FreeLibrary(hModule); } - DetourTransactionCommit(); + MH_EnableHook(MH_ALL_HOOKS); // wait for unload event or parent mutex to be abandoned. // for example if the user killed rundll32.exe with task manager. - // intentionally leave parent mutex open until this thread ends, at - // which point it becomes abandoned again. - result = WaitForMultipleObjects(_countof(ctx.handles), ctx.handles, FALSE, INFINITE); + result = WaitForMultipleObjects(_countof(ctx->handles), ctx->handles, FALSE, INFINITE); trace(L"Unload condition has been met."); - // unhook - if ( g_pfnLoadLibraryExW || g_pfnIsDeviceServiceable || g_pfnRegQueryValueExW ) { - trace(L"Removing hooks..."); - DetourTransactionBegin(); - DetourUpdateThread(GetCurrentThread()); - - if ( g_pfnLoadLibraryExW ) - DetourDetach(&(PVOID)g_pfnLoadLibraryExW, LoadLibraryExW_hook); - - // check to see if the last known address of IsDeviceServiceable - // is still in the address space of wuaueng.dll before - // attempting to unhook the function. - if ( g_pfnIsDeviceServiceable - && GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, - (LPWSTR)g_pfnIsDeviceServiceableLastKnown, &hModule) ) { - - if ( GetModuleFileNameW(hModule, Filename, _countof(Filename)) - && (!_wcsicmp(Filename, g_pszWUServiceDll) - || !_wcsicmp(Filename, PathFindFileNameW(g_pszWUServiceDll))) ) - DetourDetach(&(PVOID)g_pfnIsDeviceServiceable, IsDeviceServiceable_hook); - FreeLibrary(hModule); - } - if ( g_pfnRegQueryValueExW ) - DetourDetach(&(PVOID)g_pfnRegQueryValueExW, RegQueryValueExW_hook); - - DetourTransactionCommit(); + switch ( result ) { + case ERROR_ABANDONED_WAIT_0: + ReleaseMutex(ctx->mutex); + break; } - free(g_pszWUServiceDll); + MH_DisableHook(MH_ALL_HOOKS); + free(g_pszWUServiceDll); release: - ReleaseMutex(ctx.hChildMutex); + ReleaseMutex(ctx->handles[ctx->mutex_tag]); close_handles: - CloseHandle(ctx.hChildMutex); - CloseHandle(ctx.hUnloadEvent); - CloseHandle(ctx.hParentMutex); - if ( g_hTracingMutex ) - CloseHandle(g_hTracingMutex); + CloseHandle(ctx->handles[ctx->mutex_tag]); + CloseHandle(ctx->mutex); + CloseHandle(ctx->uevent); + VirtualFree(ctx, 0, MEM_RELEASE); unload: - trace(L"Freeing library and exiting main thread."); + trace(L"Freeing library and exiting thread."); FreeLibraryAndExitThread(PIMAGEBASE, 0); } + diff --git a/src/wufuc/callbacks.h b/src/wufuc/callbacks.h index 815ed13..367c101 100644 --- a/src/wufuc/callbacks.h +++ b/src/wufuc/callbacks.h @@ -1,4 +1,4 @@ #pragma once -VOID CALLBACK ServiceNotifyCallback(PSERVICE_NOTIFYW pNotifyBuffer); -DWORD WINAPI ThreadStartCallback(LPVOID pParam); +VOID CALLBACK cb_service_notify(PSERVICE_NOTIFYW pNotifyBuffer); +DWORD WINAPI cb_start(context *ctx); diff --git a/src/wufuc/context.c b/src/wufuc/context.c new file mode 100644 index 0000000..ffd0233 --- /dev/null +++ b/src/wufuc/context.c @@ -0,0 +1,337 @@ +#include "stdafx.h" +#include "context.h" +#include + +static bool ctxp_remove_handle(context *ctx, unsigned Index) +{ + if ( !ctx || Index > _countof(ctx->handles) || Index > ctx->count ) + return false; + + EnterCriticalSection(&ctx->cs); + for ( unsigned i = Index; i < ctx->count - 1; i++ ) { + ctx->handles[i] = ctx->handles[i + 1]; + ctx->tags[i] = ctx->tags[i + 1]; + } + LeaveCriticalSection(&ctx->cs); + return true; +} + +static bool ctxp_create_new_mutex(bool InitialOwner, + const wchar_t *MutexName, + HANDLE *pMutexHandle) +{ + HANDLE hMutex; + + hMutex = CreateMutexW(NULL, InitialOwner, MutexName); + if ( hMutex ) { + if ( GetLastError() == ERROR_ALREADY_EXISTS ) { + CloseHandle(hMutex); + return false; + } + *pMutexHandle = hMutex; + return true; + } + return false; +} + +static bool ctxp_create_new_mutex_vfmt(bool InitialOwner, + HANDLE *pMutexHandle, + const wchar_t *const NameFormat, + va_list ArgList) +{ + wchar_t *buffer; + int ret; + bool result; + + ret = _vscwprintf(NameFormat, ArgList) + 1; + buffer = calloc(ret, sizeof *buffer); + if ( !buffer ) + return false; + + ret = vswprintf_s(buffer, ret, NameFormat, ArgList); + if ( ret == -1 ) + return false; + + result = ctxp_create_new_mutex(InitialOwner, buffer, pMutexHandle); + free(buffer); + return result; +} + +static bool ctxp_create_event_with_string_security_descriptor( + bool ManualReset, + bool InitialState, + const wchar_t *Name, + const wchar_t *StringSecurityDescriptor, + HANDLE *pEventHandle) +{ + SECURITY_ATTRIBUTES sa = { sizeof sa }; + HANDLE hEvent; + + if ( ConvertStringSecurityDescriptorToSecurityDescriptorW( + StringSecurityDescriptor, + SDDL_REVISION_1, + &sa.lpSecurityDescriptor, + NULL) ) { + + hEvent = CreateEventW(&sa, ManualReset, InitialState, Name); + if ( hEvent ) { + *pEventHandle = hEvent; + return true; + } + } + return false; +} + +static unsigned ctxp_find_handle_index(context *ctx, HANDLE Handle) +{ + unsigned result = -1; + + if ( !ctx || !Handle || Handle == INVALID_HANDLE_VALUE ) + return -1; + + EnterCriticalSection(&ctx->cs); + for ( unsigned i = 0; i < ctx->count; i++ ) { + if ( ctx->handles[i] == Handle ) { + result = i; + break; + } + } + LeaveCriticalSection(&ctx->cs); + return result; +} + +bool ctx_create(context *ctx, + const wchar_t *MutexName, + unsigned MutexTag, + const wchar_t *EventName, + const wchar_t *EventStringSecurityDescriptor, + unsigned EventTag) +{ + bool result = false; + + if ( !ctx ) return false; + + ZeroMemory(ctx, sizeof *ctx); + InitializeCriticalSection(&ctx->cs); + EnterCriticalSection(&ctx->cs); + + if ( ctxp_create_new_mutex(true, MutexName, &ctx->mutex) ) { + ctx->mutex_tag = MutexTag; + ctx->count++; + + result = ctx_add_event(ctx, EventName, EventStringSecurityDescriptor, EventTag, NULL); + if ( !result ) { + ReleaseMutex(ctx->mutex); + ctx_close_and_remove_handle(ctx, ctx->mutex); + } + } + LeaveCriticalSection(&ctx->cs); + return result; +} + +bool ctx_delete(context *ctx) +{ + if ( !ctx || ctx->count > _countof(ctx->handles) ) + return false; + + EnterCriticalSection(&ctx->cs); + while ( ctx->count-- ) { + CloseHandle(ctx->handles[ctx->count]); + ctx->handles[ctx->count] = INVALID_HANDLE_VALUE; + ctx->tags[ctx->count] = -1; + } + LeaveCriticalSection(&ctx->cs); + DeleteCriticalSection(&ctx->cs); + return true; +} + +unsigned ctx_add_handle(context *ctx, HANDLE Handle, unsigned Tag) +{ + unsigned result = -1; + + if ( !ctx || !Handle ) + return -1; + + EnterCriticalSection(&ctx->cs); + result = ctxp_find_handle_index(ctx, Handle); + if ( result != -1) { + ctx->tags[result] = Tag; + } else if ( ctx->count < _countof(ctx->handles) ) { + ctx->handles[ctx->count] = Handle; + ctx->tags[ctx->count] = Tag; + + result = ctx->count++; + } + LeaveCriticalSection(&ctx->cs); + return result; +} + +bool ctx_get_tag(context *ctx, HANDLE Handle, unsigned *pTag) +{ + bool result = false; + unsigned index; + + if ( !ctx || !Handle || !pTag ) + return false; + + EnterCriticalSection(&ctx->cs); + index = ctxp_find_handle_index(ctx, Handle); + if ( index != -1 ) { + if ( pTag ) { + *pTag = ctx->tags[index]; + result = true; + } + } + LeaveCriticalSection(&ctx->cs); + return result; +} + +unsigned ctx_add_new_mutex(context *ctx, + bool InitialOwner, + const wchar_t *Name, + unsigned Tag, + HANDLE *pMutexHandle) +{ + HANDLE hMutex; + unsigned result = -1; + + EnterCriticalSection(&ctx->cs); + if ( ctxp_create_new_mutex(InitialOwner, Name, &hMutex) ) { + result = ctx_add_handle(ctx, hMutex, Tag); + if ( result != -1 ) { + if ( pMutexHandle ) + *pMutexHandle = hMutex; + } else { + CloseHandle(hMutex); + } + } + LeaveCriticalSection(&ctx->cs); + return result; +} + +unsigned ctx_add_new_mutex_fmt(context *ctx, + bool InitialOwner, + unsigned Tag, + HANDLE *pMutexHandle, + const wchar_t *const NameFormat, + ...) +{ + HANDLE hMutex; + unsigned result = -1; + va_list arglist; + + EnterCriticalSection(&ctx->cs); + va_start(arglist, NameFormat); + if ( ctxp_create_new_mutex_vfmt(InitialOwner, &hMutex, NameFormat, arglist) ) { + result = ctx_add_handle(ctx, hMutex, Tag); + if ( result != -1 ) { + if ( pMutexHandle ) + *pMutexHandle = hMutex; + } else { + CloseHandle(hMutex); + } + } + va_end(arglist); + LeaveCriticalSection(&ctx->cs); + return result; +} + +unsigned ctx_add_event(context *ctx, + const wchar_t *Name, + const wchar_t *StringSecurityDescriptor, + unsigned Tag, + HANDLE *pEventHandle) +{ + unsigned result = -1; + HANDLE hEvent; + + EnterCriticalSection(&ctx->cs); + if ( ctxp_create_event_with_string_security_descriptor( + true, + false, + Name, + StringSecurityDescriptor, + &hEvent) ) { + result = ctx_add_handle(ctx, hEvent, Tag); + if ( result != -1 ) { + if ( pEventHandle ) + *pEventHandle = hEvent; + } else { + CloseHandle(hEvent); + } + } + LeaveCriticalSection(&ctx->cs); + return result; +} + +bool ctx_wait(context *ctx, bool WaitAll, bool Alertable, DWORD *pResult) +{ + int ret; + unsigned count; + HANDLE handles[MAXIMUM_WAIT_OBJECTS]; + + EnterCriticalSection(&ctx->cs); + count = ctx->count; + ret = memcpy_s(handles, sizeof(handles), ctx->handles, count * (sizeof *handles)); + LeaveCriticalSection(&ctx->cs); + + if ( !ret ) { + *pResult = WaitForMultipleObjectsEx(count, + handles, + WaitAll, + INFINITE, + Alertable); + return true; + } + return false; +} + +bool ctx_close_and_remove_handle(context *ctx, + HANDLE Handle) +{ + bool result = false; + unsigned index; + + EnterCriticalSection(&ctx->cs); + index = ctxp_find_handle_index(ctx, Handle); + result = index != -1 + && CloseHandle(Handle) + && ctxp_remove_handle(ctx, index); + LeaveCriticalSection(&ctx->cs); + return result; +} + +bool ctx_duplicate_context(const context *pSrc, + HANDLE hProcess, + context *pDst, + HANDLE Handle, + DWORD DesiredAccess, + unsigned Tag) +{ + bool result = false; + HANDLE hSrcProcess; + HANDLE hTarget; + size_t index; + + hSrcProcess = GetCurrentProcess(); + + if ( !DuplicateHandle(hSrcProcess, pSrc->mutex, hProcess, &pDst->mutex, SYNCHRONIZE, FALSE, 0) ) + return false; + + if ( !DuplicateHandle(hSrcProcess, pSrc->uevent, hProcess, &pDst->uevent, SYNCHRONIZE, FALSE, 0) ) { +close_mutex: + CloseHandle(pDst->mutex); + pDst->mutex = INVALID_HANDLE_VALUE; + return false; + } + if ( !DuplicateHandle(hSrcProcess, Handle, hProcess, &hTarget, 0, FALSE, DesiredAccess) + || (index = ctx_add_handle(pDst, hTarget, Tag)) == -1 ) { + + CloseHandle(pDst->uevent); + pDst->uevent = INVALID_HANDLE_VALUE; + goto close_mutex; + + } + pDst->tags[0] = index; + return true; +} diff --git a/src/wufuc/context.h b/src/wufuc/context.h new file mode 100644 index 0000000..0de5d10 --- /dev/null +++ b/src/wufuc/context.h @@ -0,0 +1,71 @@ +#pragma once + +#pragma pack(push, 1) +typedef struct +{ + CRITICAL_SECTION cs; + unsigned count; + union + { + struct + { + HANDLE mutex; + HANDLE uevent; + }; + HANDLE handles[MAXIMUM_WAIT_OBJECTS]; + }; + union + { + struct + { + unsigned mutex_tag; + unsigned uevent_tag; + }; + unsigned tags[MAXIMUM_WAIT_OBJECTS]; + }; +} context; +#pragma pack(pop) + +bool ctx_create(context *ctx, + const wchar_t *MutexName, + unsigned MutexTag, + const wchar_t *EventName, + const wchar_t *EventStringSecurityDescriptor, + unsigned EventTag); + +bool ctx_delete(context *ctx); + +unsigned ctx_add_handle(context *ctx, HANDLE Handle, unsigned Tag); + +bool ctx_get_tag(context *ctx, HANDLE Handle, unsigned *pTag); + +unsigned ctx_add_new_mutex(context *ctx, + bool InitialOwner, + const wchar_t *Name, + unsigned Tag, + HANDLE *pMutexHandle); + +unsigned ctx_add_new_mutex_fmt(context *ctx, + bool InitialOwner, + unsigned Tag, + HANDLE *pMutexHandle, + const wchar_t *const NameFormat, + ...); + +unsigned ctx_add_event(context *ctx, + const wchar_t *Name, + const wchar_t *StringSecurityDescriptor, + unsigned Tag, + HANDLE *pEventHandle); + +bool ctx_wait(context *ctx, bool WaitAll, bool Alertable, DWORD *pResult); + +bool ctx_close_and_remove_handle(context *ctx, + HANDLE Handle); + +bool ctx_duplicate_context(const context *pSrc, + HANDLE hProcess, + context *pDst, + HANDLE Handle, + DWORD DesiredAccess, + unsigned Tag); diff --git a/src/wufuc/dllmain.c b/src/wufuc/dllmain.c index 805ba47..6612e6e 100644 --- a/src/wufuc/dllmain.c +++ b/src/wufuc/dllmain.c @@ -1,4 +1,5 @@ #include "stdafx.h" +#include BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, @@ -6,9 +7,12 @@ BOOL APIENTRY DllMain(HMODULE hModule, { switch ( ul_reason_for_call ) { case DLL_PROCESS_ATTACH: + MH_Initialize(); + break; case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: + MH_Uninitialize(); break; } return TRUE; diff --git a/src/wufuc/hlpmem.c b/src/wufuc/hlpmem.c deleted file mode 100644 index 2b028d4..0000000 --- a/src/wufuc/hlpmem.c +++ /dev/null @@ -1,291 +0,0 @@ -#include "stdafx.h" -#include "callbacks.h" -#include "hlpmem.h" -#include "hlpmisc.h" -#include "hlpver.h" -#include "hooks.h" - -bool FindIDSFunctionAddress(HMODULE hModule, PVOID *ppfnIsDeviceServiceable) -{ - bool result = false; - bool is_win7 = false; - bool is_win81 = false; - PLANGANDCODEPAGE ptl; - HANDLE hProcess; - int tmp; - UINT cbtl; - wchar_t SubBlock[38]; - UINT cbInternalName; - wchar_t *pInternalName; - UINT cbffi; - VS_FIXEDFILEINFO *pffi; - MODULEINFO modinfo; - size_t offset; - - 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); - if ( !ptl ) { - trace(L"Failed to allocate version translation information from hmodule."); - return result; - } - hProcess = GetCurrentProcess(); - - for ( size_t i = 0, count = (cbtl / sizeof *ptl); i < count; i++ ) { - if ( swprintf_s(SubBlock, - _countof(SubBlock), - L"\\StringFileInfo\\%04x%04x\\InternalName", - ptl[i].wLanguage, - ptl[i].wCodePage) == -1 ) - continue; - - pInternalName = GetVersionInfoFromHModuleAlloc(hModule, SubBlock, &cbInternalName); - if ( !pInternalName ) { - trace(L"Failed to allocate version internal name from hmodule."); - continue; - } - - // identify wuaueng.dll by its resource data - tmp = _wcsicmp(pInternalName, L"wuaueng.dll"); - if ( tmp ) - trace(L"Module internal name does not match. (%ls)", pInternalName); - free(pInternalName); - if ( tmp ) - continue; - - pffi = GetVersionInfoFromHModuleAlloc(hModule, L"\\", &cbffi); - if ( !pffi ) { - trace(L"Failed to allocate version information from hmodule."); - break; - } - trace(L"Windows Update Agent version: %hu.%hu.%hu.%hu", - HIWORD(pffi->dwProductVersionMS), - LOWORD(pffi->dwProductVersionMS), - HIWORD(pffi->dwProductVersionLS), - LOWORD(pffi->dwProductVersionLS)); - - // assure wuaueng.dll is at least the minimum supported version - 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"Windows Update Agent does not meet the minimum supported version."); - break; - } - if ( !GetModuleInformation(hProcess, hModule, &modinfo, sizeof modinfo) ) - break; - - offset = patternfind(modinfo.lpBaseOfDll, modinfo.SizeOfImage, -#ifdef _WIN64 - "FFF3 4883EC?? 33DB 391D???????? 7508 8B05????????" -#else - is_win7 - ? "833D????????00 743E E8???????? A3????????" - : "8BFF 51 833D????????00 7507 A1????????" -#endif - ); - if ( offset != -1 ) { - g_pfnIsDeviceServiceableLastKnown = (PVOID)((uint8_t *)modinfo.lpBaseOfDll + offset); - *ppfnIsDeviceServiceable = g_pfnIsDeviceServiceableLastKnown; - result = true; - } - break; - } - free(ptl); - return result; -} - -HANDLE GetRemoteHModuleFromTh32ModuleSnapshot(HANDLE hSnapshot, const wchar_t *pLibFileName) -{ - MODULEENTRY32W me = { sizeof me }; - if ( !Module32FirstW(hSnapshot, &me) ) - return NULL; - do { - if ( !_wcsicmp(me.szExePath, pLibFileName) ) - return me.hModule; - } while ( Module32NextW(hSnapshot, &me) ); - return NULL; -} - -bool InjectLibraryAndCreateRemoteThread( - HANDLE hProcess, - HMODULE hModule, - LPTHREAD_START_ROUTINE pStartAddress, - const void *pParam, - size_t cbParam) -{ - bool result = false; - NTSTATUS Status; - LPVOID pBaseAddress = NULL; - SIZE_T cb; - HMODULE hRemoteModule = NULL; - HANDLE hThread; - - Status = NtSuspendProcess(hProcess); - if ( !NT_SUCCESS(Status) ) return result; - - if ( pParam ) { - // this will be VirtualFree()'d by the function at pStartAddress - pBaseAddress = VirtualAllocEx(hProcess, - NULL, - cbParam, - MEM_RESERVE | MEM_COMMIT, - PAGE_READWRITE); - if ( !pBaseAddress ) goto resume; - - if ( !WriteProcessMemory(hProcess, pBaseAddress, pParam, cbParam, &cb) ) - goto vfree; - } - if ( InjectLibrary(hProcess, hModule, &hRemoteModule) ) { - hThread = CreateRemoteThread(hProcess, - NULL, - 0, - (LPTHREAD_START_ROUTINE)((uint8_t *)hRemoteModule + ((uint8_t *)pStartAddress - (uint8_t *)hModule)), - pBaseAddress, - 0, - NULL); - - if ( hThread ) { - CloseHandle(hThread); - result = true; - } - } -vfree: - if ( !result && pBaseAddress ) - VirtualFreeEx(hProcess, pBaseAddress, 0, MEM_RELEASE); -resume: NtResumeProcess(hProcess); - return result; -} - -bool InjectLibrary(HANDLE hProcess, HMODULE hModule, HMODULE *phRemoteModule) -{ - WCHAR Filename[MAX_PATH]; - DWORD nLength; - - nLength = GetModuleFileNameW(hModule, Filename, _countof(Filename)); - if ( nLength ) { - return InjectLibraryByFilename(hProcess, - Filename, - nLength, - phRemoteModule); - } - return false; -} - -bool InjectLibraryByFilename( - HANDLE hProcess, - const wchar_t *pLibFilename, - size_t cchLibFilename, - HMODULE *phRemoteModule) -{ - bool result = false; - DWORD dwProcessId; - NTSTATUS Status; - HANDLE hSnapshot; - SIZE_T nSize; - LPVOID pBaseAddress; - HANDLE hThread; - - Status = NtSuspendProcess(hProcess); - if ( !NT_SUCCESS(Status) ) return result; - - dwProcessId = GetProcessId(hProcess); - - hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwProcessId); - if ( !hSnapshot ) goto resume; - - *phRemoteModule = GetRemoteHModuleFromTh32ModuleSnapshot(hSnapshot, - pLibFilename); - - CloseHandle(hSnapshot); - - // already injected... still sets *phRemoteModule - if ( *phRemoteModule ) goto resume; - - nSize = (cchLibFilename + 1) * sizeof *pLibFilename; - pBaseAddress = VirtualAllocEx(hProcess, - NULL, - nSize, - MEM_RESERVE | MEM_COMMIT, - PAGE_READWRITE); - - if ( !pBaseAddress ) goto resume; - - if ( !WriteProcessMemory(hProcess, pBaseAddress, pLibFilename, nSize, NULL) ) - goto vfree; - - hThread = CreateRemoteThread(hProcess, - NULL, - 0, - (LPTHREAD_START_ROUTINE)LoadLibraryW, - pBaseAddress, - 0, - NULL); - if ( !hThread ) goto vfree; - - WaitForSingleObject(hThread, INFINITE); - - if ( sizeof *phRemoteModule > sizeof(DWORD) ) { - hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwProcessId); - if ( hSnapshot ) { - *phRemoteModule = GetRemoteHModuleFromTh32ModuleSnapshot( - hSnapshot, - pLibFilename); - - CloseHandle(hSnapshot); - result = *phRemoteModule != NULL; - } - } else { - 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) ) { - - if ( InjectLibraryAndCreateRemoteThread(hProcess, PIMAGEBASE, ThreadStartCallback, ¶m, sizeof param) ) - trace(L"Injected into process. (%lu)", dwProcessId); - } 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 deleted file mode 100644 index 4734d43..0000000 --- a/src/wufuc/hlpmem.h +++ /dev/null @@ -1,44 +0,0 @@ -#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 FindIDSFunctionAddress(HMODULE hModule, PVOID *ppfnIsDeviceServiceable); -HANDLE GetRemoteHModuleFromTh32ModuleSnapshot(HANDLE hSnapshot, const wchar_t *pLibFileName); -bool InjectLibraryAndCreateRemoteThread( - HANDLE hProcess, - HMODULE hModule, - LPTHREAD_START_ROUTINE pStartAddress, - const void *pParam, - size_t cbParam); -bool InjectLibrary(HANDLE hProcess, HMODULE hModule, HMODULE *phRemoteModule); -bool InjectLibraryByFilename( - HANDLE hProcess, - 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 deleted file mode 100644 index 3a2a903..0000000 --- a/src/wufuc/hlpmisc.c +++ /dev/null @@ -1,155 +0,0 @@ -#include "stdafx.h" -#include "hlpmisc.h" -#include - -bool InitializeMutex(bool InitialOwner, const wchar_t *pMutexName, HANDLE *phMutex) -{ - HANDLE hMutex; - - hMutex = CreateMutexW(NULL, InitialOwner, pMutexName); - if ( hMutex ) { - if ( GetLastError() == ERROR_ALREADY_EXISTS ) { - CloseHandle(hMutex); - return false; - } - *phMutex = hMutex; - return true; - } else { - trace(L"Failed to create mutex: %ls (GetLastError=%ld)", - pMutexName, GetLastError()); - } - return false; -} - -bool CreateEventWithStringSecurityDescriptor( - const wchar_t *pStringSecurityDescriptor, - bool ManualReset, - bool InitialState, - const wchar_t *pName, - HANDLE *phEvent) -{ - SECURITY_ATTRIBUTES sa = { sizeof sa }; - HANDLE event; - - if ( ConvertStringSecurityDescriptorToSecurityDescriptorW( - pStringSecurityDescriptor, - SDDL_REVISION_1, - &sa.lpSecurityDescriptor, - NULL) ) { - - event = CreateEventW(&sa, ManualReset, InitialState, pName); - if ( event ) { - *phEvent = event; - return true; - } - } - return false; -} - -PVOID RegGetValueAlloc( - HKEY hkey, - const wchar_t *pSubKey, - const wchar_t *pValue, - DWORD dwFlags, - LPDWORD pdwType, - LPDWORD pcbData) -{ - DWORD cbData = 0; - PVOID result = NULL; - - if ( RegGetValueW(hkey, pSubKey, pValue, dwFlags, pdwType, NULL, &cbData) != ERROR_SUCCESS ) - return result; - - result = malloc(cbData); - if ( !result ) return result; - - if ( RegGetValueW(hkey, pSubKey, pValue, dwFlags, pdwType, result, &cbData) == ERROR_SUCCESS ) { - if ( pcbData ) - *pcbData = cbData; - } else { - free(result); - result = NULL; - } - return result; -} - -LPBYTE RegQueryValueExAlloc( - HKEY hKey, - const wchar_t *pSubKey, - const wchar_t *pValueName, - LPDWORD pType, - LPDWORD pcbData) -{ - HKEY hSubKey; - DWORD cbData = 0; - size_t length; - LPBYTE result = NULL; - - if ( pSubKey && *pSubKey ) { - if ( RegOpenKeyW(hKey, pSubKey, &hSubKey) != ERROR_SUCCESS ) - return result; - } else { - hSubKey = hKey; - } - if ( RegQueryValueExW(hSubKey, pValueName, NULL, pType, result, &cbData) != ERROR_SUCCESS ) - return result; - - length = cbData + (sizeof UNICODE_NULL * 2); - result = malloc(length); - - if ( !result ) return result; - ZeroMemory(result, length); - - if ( RegQueryValueExW(hSubKey, pValueName, NULL, pType, result, &cbData) == ERROR_SUCCESS ) { - if ( pcbData ) - *pcbData = cbData; - } else { - free(result); - result = NULL; - } - 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; -} - -wchar_t *ExpandEnvironmentStringsAlloc(const wchar_t *src, LPDWORD pcchLength) -{ - wchar_t *result; - DWORD buffersize; - DWORD size; - - buffersize = ExpandEnvironmentStringsW(src, NULL, 0); - result = calloc(buffersize, sizeof *result); - size = ExpandEnvironmentStringsW(src, result, buffersize); - if ( !size || size > buffersize ) { - free(result); - result = NULL; - } else if ( pcchLength ) { - *pcchLength = buffersize; - } - return result; -} diff --git a/src/wufuc/hlpmisc.h b/src/wufuc/hlpmisc.h deleted file mode 100644 index 37629d9..0000000 --- a/src/wufuc/hlpmisc.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -bool InitializeMutex(bool InitialOwner, const wchar_t *pMutexName, HANDLE *phMutex); -bool CreateEventWithStringSecurityDescriptor( - const wchar_t *pStringSecurityDescriptor, - bool ManualReset, - bool InitialState, - const wchar_t *pName, - HANDLE *phEvent); -PVOID RegGetValueAlloc( - HKEY hkey, - const wchar_t *pSubKey, - const wchar_t *pValue, - DWORD dwFlags, - LPDWORD pdwType, - LPDWORD pcbData); -LPBYTE RegQueryValueExAlloc( - HKEY hKey, - const wchar_t *pSubKey, - const wchar_t *pValueName, - LPDWORD pType, - LPDWORD pcbData); -PVOID NtQueryKeyAlloc( - HANDLE KeyHandle, - KEY_INFORMATION_CLASS KeyInformationClass, - PULONG pResultLength); -wchar_t *ExpandEnvironmentStringsAlloc(const wchar_t *src, LPDWORD pcchLength); diff --git a/src/wufuc/hlpsvc.h b/src/wufuc/hlpsvc.h deleted file mode 100644 index 6ab70fe..0000000 --- a/src/wufuc/hlpsvc.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -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, - 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); diff --git a/src/wufuc/hlpver.h b/src/wufuc/hlpver.h deleted file mode 100644 index 240c605..0000000 --- a/src/wufuc/hlpver.h +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - -int ProductVersionCompare(VS_FIXEDFILEINFO *pffi, WORD wMajor, WORD wMinor, WORD wBuild, WORD wRev); -bool GetVersionInfoFromHModule(HMODULE hModule, const wchar_t *pszSubBlock, LPVOID pData, PUINT pcbData); -LPVOID GetVersionInfoFromHModuleAlloc(HMODULE hModule, const wchar_t *pszSubBlock, PUINT pcbData); -bool IsWindowsVersion(WORD wMajorVersion, WORD wMinorVersion, WORD wServicePackMajor); diff --git a/src/wufuc/hooks.c b/src/wufuc/hooks.c index 6f83cc1..a3d039c 100644 --- a/src/wufuc/hooks.c +++ b/src/wufuc/hooks.c @@ -1,14 +1,16 @@ #include "stdafx.h" -#include "hlpmem.h" -#include "hlpmisc.h" + #include "hooks.h" +#include "log.h" +#include "registryhelper.h" +#include "context.h" +#include "wufuc.h" wchar_t *g_pszWUServiceDll; LPFN_REGQUERYVALUEEXW g_pfnRegQueryValueExW; LPFN_LOADLIBRARYEXW g_pfnLoadLibraryExW; LPFN_ISDEVICESERVICEABLE g_pfnIsDeviceServiceable; -LPFN_ISDEVICESERVICEABLE g_pfnIsDeviceServiceableLastKnown; LSTATUS WINAPI RegQueryValueExW_hook(HKEY hKey, LPCWSTR lpValueName, LPDWORD lpReserved, LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData) { @@ -38,10 +40,10 @@ LSTATUS WINAPI RegQueryValueExW_hook(HKEY hKey, LPCWSTR lpValueName, LPDWORD lpR || _wcsicmp(lpValueName, L"ServiceDll") ) return result; - pBuffer = (PWCH)lpData; + pBuffer = (wchar_t *)lpData; // get name of registry key being queried - pkni = NtQueryKeyAlloc((HANDLE)hKey, KeyNameInformation, &ResultLength); + pkni = reg_query_key_alloc((HANDLE)hKey, KeyNameInformation, &ResultLength); if ( !pkni ) return result; NameCount = pkni->NameLength / sizeof *pkni->Name; @@ -59,7 +61,7 @@ LSTATUS WINAPI RegQueryValueExW_hook(HKEY hKey, LPCWSTR lpValueName, LPDWORD lpR || !_wcsicmp(fname, L"WuaCpuFix64.dll") // WuaCpuFix || !_wcsicmp(fname, L"WuaCpuFix.dll")) ) { - expandedpath = ExpandEnvironmentStringsAlloc(realpath, &cchLength); + expandedpath = env_expand_strings_alloc(realpath, &cchLength); if ( expandedpath ) { if ( PathFileExistsW(expandedpath) && SUCCEEDED(StringCbCopyW(pBuffer, MaximumLength, expandedpath)) ) { @@ -87,18 +89,7 @@ HMODULE WINAPI LoadLibraryExW_hook(LPCWSTR lpFileName, HANDLE hFile, DWORD dwFla && (!_wcsicmp(lpFileName, g_pszWUServiceDll) || !_wcsicmp(lpFileName, PathFindFileNameW(g_pszWUServiceDll))) ) { - if ( FindIDSFunctionAddress(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!"); - } + wufuc_hook(result); } return result; } diff --git a/src/wufuc/hooks.h b/src/wufuc/hooks.h index e752d34..3fb53c3 100644 --- a/src/wufuc/hooks.h +++ b/src/wufuc/hooks.h @@ -10,7 +10,10 @@ extern wchar_t *g_pszWUServiceDll; extern LPFN_REGQUERYVALUEEXW g_pfnRegQueryValueExW; extern LPFN_LOADLIBRARYEXW g_pfnLoadLibraryExW; extern LPFN_ISDEVICESERVICEABLE g_pfnIsDeviceServiceable; -extern LPFN_ISDEVICESERVICEABLE g_pfnIsDeviceServiceableLastKnown; + +extern PVOID g_ptRegQueryValueExW; +extern PVOID g_ptLoadLibraryExW; +extern PVOID g_ptIsDeviceServiceable; 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); diff --git a/src/wufuc/log.c b/src/wufuc/log.c new file mode 100644 index 0000000..70378e0 --- /dev/null +++ b/src/wufuc/log.c @@ -0,0 +1,292 @@ +#include "stdafx.h" +#include "log.h" + +void logp_debug_write(const wchar_t *const format, ...) +{ + DWORD pid; + wchar_t exepath[MAX_PATH]; + wchar_t *exename; + va_list argptr; + int count; + wchar_t *buffer1; + wchar_t *buffer2; + wchar_t datebuf[9]; + wchar_t timebuf[9]; + const wchar_t fmt[] = L"%ls %ls [%ls:%lu] %ls"; + + pid = GetCurrentProcessId(); + GetModuleFileNameW(NULL, exepath, _countof(exepath)); + exename = PathFindFileNameW(exepath); + + va_start(argptr, format); + count = _vscwprintf(format, argptr) + 1; + buffer1 = calloc(count, sizeof *buffer1); + vswprintf_s(buffer1, count, format, argptr); + va_end(argptr); + + _wstrdate_s(datebuf, _countof(datebuf)); + _wstrtime_s(timebuf, _countof(timebuf)); + count = _scwprintf(fmt, datebuf, timebuf, exename, pid, buffer1) + 1; + + buffer2 = calloc(count, sizeof *buffer2); + swprintf_s(buffer1, count, fmt, datebuf, timebuf, exename, pid, buffer1); + free(buffer1); + OutputDebugStringW(buffer2); + free(buffer2); +} +#define log_debug_write(format, ...) \ + logp_debug_write(__FUNCTIONW__ L": " format L"\r\n", ##__VA_ARGS__) +#define trace log_debug_write + +//#include "stdafx.h" +//#include "tracing.h" +//#include +// +//wchar_t path[MAX_PATH]; +//wchar_t exepath[MAX_PATH]; +// +//bool tracec(bool condition, const wchar_t *const format, ...) +//{ +// va_list argptr; +// int count; +// wchar_t *buffer; +// +// if ( condition ) { +// va_start(argptr, format); +// +// count = _vscwprintf(format, argptr) + 1; +// buffer = calloc(count, sizeof *buffer); +// vswprintf_s(buffer, count, format, argptr); +// +// va_end(argptr); +// OutputDebugStringW(buffer); +// free(buffer); +// } +// return condition; +//} +//void trace(const wchar_t *const format, ...) +//{ +// va_list argptr; +// int count; +// wchar_t *buffer; +// +// va_start(argptr, format); +// +// count = _vscwprintf(format, argptr) + 1; +// buffer = calloc(count, sizeof *buffer); +// vswprintf_s(buffer, count, format, argptr); +// +// va_end(argptr); +// OutputDebugStringW(buffer); +// free(buffer); +//} +// +//DWORD WINAPI tracing_thread(LPVOID pParam) +//{ +// wchar_t *folder; +// HANDLE file; +// DWORD dwProcessId; +// wchar_t *exename; +// int count; +// +// if ( !*path ) { +// SHGetKnownFolderPath(&FOLDERID_ProgramData, 0, NULL, &folder); +// wcscpy_s(path, _countof(path), folder); +// CoTaskMemFree(folder); +// PathAppendW(path, L"wufuc"); +// CreateDirectoryW(path, NULL); +// PathAppendW(path, L"wufuc.log"); +// } +// if ( !*exepath ) +// GetModuleFileNameW(NULL, exepath, _countof(exepath)); +// +// file = CreateFileW(path, FILE_APPEND_DATA, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); +// itrace(L"CreateFileW=%p", file); +// dwProcessId = GetCurrentProcessId(); +// exename = PathFindFileNameW(exepath); +// +// va_start(argptr, format); +// count = _vscwprintf(format, argptr) + 1; +// buf1 = calloc(count, sizeof *buf1); +// vswprintf_s(buf1, count, format, argptr); +// va_end(argptr); +// +// count = _scwprintf(fmt, datebuf, timebuf, dwProcessId, exename, buf1); +// buf2 = calloc(count + 1, sizeof *buf2); +// swprintf_s(buf2, count + 1, fmt, datebuf, timebuf, dwProcessId, exename, buf1); +// free(buf1); +// itrace(L"WriteFile=%d", WriteFile(file, buf2, count * (sizeof *buf2), &written, NULL)); +// free(buf2); +// itrace(L"FlushFileBuffers=%d", FlushFileBuffers(file)); +// itrace(L"CloseHandle=%d", CloseHandle(file)); +// itrace(L"ReleaseMutex=%d", ReleaseMutex(g_hTracingMutex)); +// +// +// HANDLE hHeap = GetProcessHeap(); +// TCHAR* pchRequest = (TCHAR*)HeapAlloc(hHeap, 0, 512 * sizeof(TCHAR)); +// +// DWORD cbBytesRead = 0, cbReplyBytes = 0, cbWritten = 0; +// BOOL fSuccess = FALSE; +// HANDLE hPipe = NULL; +// +// // Do some extra error checking since the app will keep running even if this +// // thread fails. +// +// if ( pParam == NULL ) { +// itrace(L"\nERROR - Pipe Server Failure:"); +// itrace(L" InstanceThread got an unexpected NULL value in lpvParam."); +// itrace(L" InstanceThread exitting."); +// if ( pchRequest != NULL ) HeapFree(hHeap, 0, pchRequest); +// return -1; +// } +// +// // Print verbose messages. In production code, this should be for debugging only. +// printf("InstanceThread created, receiving and processing messages.\n"); +// +// // The thread's parameter is a handle to a pipe object instance. +// +// hPipe = (HANDLE)pParam; +// +// // Loop until done reading +// while ( true ) { +// // Read client requests from the pipe. This simplistic code only allows messages +// // up to BUFSIZE characters in length. +// fSuccess = ReadFile( +// hPipe, // handle to pipe +// pchRequest, // buffer to receive data +// 512 * sizeof(TCHAR), // size of buffer +// &cbBytesRead, // number of bytes read +// NULL); // not overlapped I/O +// +// if ( !fSuccess || cbBytesRead == 0 ) { +// if ( GetLastError() == ERROR_BROKEN_PIPE ) { +// itrace(L"InstanceThread: client disconnected."); +// } else { +// itrace(L"InstanceThread ReadFile failed, GLE=%d.", GetLastError()); +// } +// break; +// } +// +// _wstrdate_s(datebuf, _countof(datebuf)); +// _wstrtime_s(timebuf, _countof(timebuf)); +// +// if ( !fSuccess ) { +// itrace(L"InstanceThread WriteFile failed, GLE=%d.", GetLastError()); +// break; +// } +// } +// +// // Flush the pipe to allow the client to read the pipe's contents +// // before disconnecting. Then disconnect the pipe, and close the +// // handle to this pipe instance. +// +// FlushFileBuffers(hPipe); +// DisconnectNamedPipe(hPipe); +// CloseHandle(hPipe); +// +// HeapFree(hHeap, 0, pchRequest); +// +// printf("InstanceThread exitting.\n"); +// return 1; +//} +// +//bool start_tracing(const wchar_t *pipename) +//{ +// HANDLE hPipe; +// HANDLE hPipe = INVALID_HANDLE_VALUE; +// HANDLE hThread = NULL; +// +// while ( true ) { +// hPipe = CreateNamedPipeW(pipename, +// PIPE_ACCESS_INBOUND, 0, PIPE_UNLIMITED_INSTANCES, 512, 0, 512, NULL); +// if ( hPipe == INVALID_HANDLE_VALUE ) { +// itrace(L"CreateNamedPipe failed, GLE=%d.\n"), GetLastError(); +// return false; +// } +// +// if ( ConnectNamedPipe(hPipe, NULL) || +// GetLastError() == ERROR_PIPE_CONNECTED ) { +// itrace(L"Client connected, creating a processing thread."); +// +// // Create a thread for this client. +// hThread = CreateThread( +// NULL, // no security attribute +// 0, // default stack size +// tracing_thread, // thread proc +// (LPVOID)hPipe, // thread parameter +// 0, // not suspended +// NULL); // returns thread ID +// +// if ( hThread == NULL ) { +// itrace(L"CreateThread failed, GLE=%d.\n"), GetLastError(); +// return false; +// } else { +// CloseHandle(hThread); +// } +// } else { +// // The client could not connect, so close the pipe. +// CloseHandle(hPipe); +// } +// } +// return true; +//} +// +//void trace_(const wchar_t *const format, ...) +//{ +// va_list argptr; +// wchar_t *folder; +// wchar_t datebuf[9]; +// wchar_t timebuf[9]; +// HANDLE file; +// DWORD dwProcessId; +// wchar_t *exename; +// int count; +// DWORD written; +// const wchar_t fmt[] = L"%ls %ls [%lu] Exe(%ls) %ls"; +// wchar_t *buf1; +// wchar_t *buf2; +// +// va_start(argptr, format); +// itrace_(format); +// va_end(argptr); +// +// if ( !*path ) { +// SHGetKnownFolderPath(&FOLDERID_ProgramData, 0, NULL, &folder); +// wcscpy_s(path, _countof(path), folder); +// CoTaskMemFree(folder); +// PathAppendW(path, L"wufuc"); +// CreateDirectoryW(path, NULL); +// PathAppendW(path, L"wufuc.log"); +// } +// if ( !*exepath ) +// GetModuleFileNameW(NULL, exepath, _countof(exepath)); +// +// if ( !g_hTracingMutex ) +// g_hTracingMutex = CreateMutexW(NULL, FALSE, L"Global\\6b2f5740-7435-47f7-865c-dbd825292f32"); +// +// itrace(L"WaitForSingleObject=%lu", WaitForSingleObject(g_hTracingMutex, INFINITE)); +// +// _wstrdate_s(datebuf, _countof(datebuf)); +// _wstrtime_s(timebuf, _countof(timebuf)); +// +// file = CreateFileW(path, FILE_APPEND_DATA, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); +// itrace(L"CreateFileW=%p", file); +// dwProcessId = GetCurrentProcessId(); +// exename = PathFindFileNameW(exepath); +// +// va_start(argptr, format); +// count = _vscwprintf(format, argptr) + 1; +// buf1 = calloc(count, sizeof *buf1); +// vswprintf_s(buf1, count, format, argptr); +// va_end(argptr); +// +// count = _scwprintf(fmt, datebuf, timebuf, dwProcessId, exename, buf1); +// buf2 = calloc(count + 1, sizeof *buf2); +// swprintf_s(buf2, count + 1, fmt, datebuf, timebuf, dwProcessId, exename, buf1); +// free(buf1); +// itrace(L"WriteFile=%d", WriteFile(file, buf2, count * (sizeof *buf2), &written, NULL)); +// free(buf2); +// itrace(L"FlushFileBuffers=%d", FlushFileBuffers(file)); +// itrace(L"CloseHandle=%d", CloseHandle(file)); +// itrace(L"ReleaseMutex=%d", ReleaseMutex(g_hTracingMutex)); +//} diff --git a/src/wufuc/log.h b/src/wufuc/log.h new file mode 100644 index 0000000..30397c1 --- /dev/null +++ b/src/wufuc/log.h @@ -0,0 +1,6 @@ +#pragma once + +void logp_debug_write(const wchar_t *const format, ...); +#define log_debug_write(format, ...) \ + logp_debug_write(__FUNCTIONW__ L": " format L"\r\n", ##__VA_ARGS__) +#define trace log_debug_write diff --git a/src/wufuc/modulehelper.c b/src/wufuc/modulehelper.c new file mode 100644 index 0000000..7c5f901 --- /dev/null +++ b/src/wufuc/modulehelper.c @@ -0,0 +1,151 @@ +#include "stdafx.h" +#include "modulehelper.h" + +HMODULE mod_get_from_th32_snapshot(HANDLE hSnapshot, const wchar_t *pLibFileName) +{ + MODULEENTRY32W me = { sizeof me }; + if ( !Module32FirstW(hSnapshot, &me) ) + return NULL; + do { + if ( !_wcsicmp(me.szExePath, pLibFileName) ) + return me.hModule; + } while ( Module32NextW(hSnapshot, &me) ); + return NULL; +} + +bool mod_inject_and_begin_thread( + HANDLE hProcess, + HMODULE hModule, + LPTHREAD_START_ROUTINE pStartAddress, + const void *pParam, + size_t cbParam) +{ + bool result = false; + NTSTATUS Status; + LPVOID pBaseAddress = NULL; + SIZE_T cb; + HMODULE hRemoteModule = NULL; + HANDLE hThread; + + Status = NtSuspendProcess(hProcess); + if ( !NT_SUCCESS(Status) ) return result; + + if ( pParam ) { + // this will be VirtualFree()'d by the function at pStartAddress + pBaseAddress = VirtualAllocEx(hProcess, + NULL, + cbParam, + MEM_RESERVE | MEM_COMMIT, + PAGE_READWRITE); + if ( !pBaseAddress ) goto resume; + + if ( !WriteProcessMemory(hProcess, pBaseAddress, pParam, cbParam, &cb) ) + goto vfree; + } + if ( mod_inject_by_hmodule(hProcess, hModule, &hRemoteModule) ) { + hThread = CreateRemoteThread(hProcess, + NULL, + 0, + (LPTHREAD_START_ROUTINE)((uint8_t *)hRemoteModule + ((uint8_t *)pStartAddress - (uint8_t *)hModule)), + pBaseAddress, + 0, + NULL); + + if ( hThread ) { + CloseHandle(hThread); + result = true; + } + } +vfree: + if ( !result && pBaseAddress ) + VirtualFreeEx(hProcess, pBaseAddress, 0, MEM_RELEASE); +resume: NtResumeProcess(hProcess); + return result; +} + +bool mod_inject_by_hmodule(HANDLE hProcess, HMODULE hModule, HMODULE *phRemoteModule) +{ + WCHAR Filename[MAX_PATH]; + DWORD nLength; + + nLength = GetModuleFileNameW(hModule, Filename, _countof(Filename)); + if ( nLength ) { + return mod_inject(hProcess, + Filename, + nLength, + phRemoteModule); + } + return false; +} + +bool mod_inject( + HANDLE hProcess, + const wchar_t *pLibFilename, + size_t cchLibFilename, + HMODULE *phRemoteModule) +{ + bool result = false; + DWORD dwProcessId; + NTSTATUS Status; + HANDLE hSnapshot; + SIZE_T nSize; + LPVOID pBaseAddress; + HANDLE hThread; + + Status = NtSuspendProcess(hProcess); + if ( !NT_SUCCESS(Status) ) return result; + + dwProcessId = GetProcessId(hProcess); + + hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwProcessId); + if ( !hSnapshot ) goto resume; + + *phRemoteModule = mod_get_from_th32_snapshot(hSnapshot, + pLibFilename); + + CloseHandle(hSnapshot); + + // already injected... still sets *phRemoteModule + if ( *phRemoteModule ) goto resume; + + nSize = (cchLibFilename + 1) * sizeof *pLibFilename; + pBaseAddress = VirtualAllocEx(hProcess, + NULL, + nSize, + MEM_RESERVE | MEM_COMMIT, + PAGE_READWRITE); + + if ( !pBaseAddress ) goto resume; + + if ( !WriteProcessMemory(hProcess, pBaseAddress, pLibFilename, nSize, NULL) ) + goto vfree; + + hThread = CreateRemoteThread(hProcess, + NULL, + 0, + (LPTHREAD_START_ROUTINE)LoadLibraryW, + pBaseAddress, + 0, + NULL); + if ( !hThread ) goto vfree; + + WaitForSingleObject(hThread, INFINITE); + + if ( sizeof *phRemoteModule > sizeof(DWORD) ) { + hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwProcessId); + if ( hSnapshot ) { + *phRemoteModule = mod_get_from_th32_snapshot( + hSnapshot, + pLibFilename); + + CloseHandle(hSnapshot); + result = *phRemoteModule != NULL; + } + } else { + result = GetExitCodeThread(hThread, (LPDWORD)phRemoteModule) != FALSE; + } + CloseHandle(hThread); +vfree: VirtualFreeEx(hProcess, pBaseAddress, 0, MEM_RELEASE); +resume: NtResumeProcess(hProcess); + return result; +} diff --git a/src/wufuc/modulehelper.h b/src/wufuc/modulehelper.h new file mode 100644 index 0000000..2e88a78 --- /dev/null +++ b/src/wufuc/modulehelper.h @@ -0,0 +1,15 @@ +#pragma once + +HMODULE mod_get_from_th32_snapshot(HANDLE hSnapshot, const wchar_t *pLibFileName); +bool mod_inject_and_begin_thread( + HANDLE hProcess, + HMODULE hModule, + LPTHREAD_START_ROUTINE pStartAddress, + const void *pParam, + size_t cbParam); +bool mod_inject_by_hmodule(HANDLE hProcess, HMODULE hModule, HMODULE *phRemoteModule); +bool mod_inject( + HANDLE hProcess, + const wchar_t *pLibFilename, + size_t cchLibFilename, + HMODULE *phRemoteModule); diff --git a/src/wufuc/registryhelper.c b/src/wufuc/registryhelper.c new file mode 100644 index 0000000..1c458ff --- /dev/null +++ b/src/wufuc/registryhelper.c @@ -0,0 +1,119 @@ +#include "stdafx.h" +#include "registryhelper.h" +#include + +PVOID reg_get_value_alloc( + HKEY hKey, + const wchar_t *SubKey, + const wchar_t *Value, + DWORD dwFlags, + LPDWORD pdwType, + LPDWORD pcbData) +{ + DWORD cbData = 0; + PVOID result = NULL; + + if ( RegGetValueW(hKey, SubKey, Value, dwFlags, pdwType, NULL, &cbData) != ERROR_SUCCESS ) + return result; + + result = malloc(cbData); + if ( !result ) return result; + + if ( RegGetValueW(hKey, SubKey, Value, dwFlags, pdwType, result, &cbData) == ERROR_SUCCESS ) { + if ( pcbData ) + *pcbData = cbData; + } else { + free(result); + result = NULL; + } + return result; +} + +LPBYTE reg_query_value_alloc( + HKEY hKey, + const wchar_t *SubKey, + const wchar_t *Value, + LPDWORD pdwType, + LPDWORD pcbData) +{ + HKEY hSubKey; + DWORD cbData = 0; + DWORD dwType; + LPBYTE result = NULL; + + if ( SubKey && *SubKey ) { + if ( RegOpenKeyW(hKey, SubKey, &hSubKey) != ERROR_SUCCESS ) + return result; + } else { + hSubKey = hKey; + } + if ( RegQueryValueExW(hSubKey, Value, NULL, &dwType, result, &cbData) != ERROR_SUCCESS ) + return result; + + switch ( dwType ) { + case REG_SZ: + case REG_EXPAND_SZ: + cbData += sizeof UNICODE_NULL; + break; + case REG_MULTI_SZ: + cbData += (sizeof UNICODE_NULL) * 2; + break; + } + result = malloc(cbData); + + if ( !result ) return result; + ZeroMemory(result, cbData); + + if ( RegQueryValueExW(hSubKey, Value, NULL, pdwType, result, &cbData) == ERROR_SUCCESS ) { + if ( pcbData ) + *pcbData = cbData; + } else { + free(result); + result = NULL; + } + return result; +} + +PVOID reg_query_key_alloc( + 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; +} + +wchar_t *env_expand_strings_alloc(const wchar_t *src, LPDWORD pcchLength) +{ + wchar_t *result; + DWORD buffersize; + DWORD size; + + buffersize = ExpandEnvironmentStringsW(src, NULL, 0); + result = calloc(buffersize, sizeof *result); + size = ExpandEnvironmentStringsW(src, result, buffersize); + if ( !size || size > buffersize ) { + free(result); + result = NULL; + } else if ( pcchLength ) { + *pcchLength = buffersize; + } + return result; +} diff --git a/src/wufuc/registryhelper.h b/src/wufuc/registryhelper.h new file mode 100644 index 0000000..a02c69c --- /dev/null +++ b/src/wufuc/registryhelper.h @@ -0,0 +1,20 @@ +#pragma once + +PVOID reg_get_value_alloc( + HKEY hkey, + const wchar_t *pSubKey, + const wchar_t *pValue, + DWORD dwFlags, + LPDWORD pdwType, + LPDWORD pcbData); +LPBYTE reg_query_value_alloc( + HKEY hKey, + const wchar_t *pSubKey, + const wchar_t *pValueName, + LPDWORD pType, + LPDWORD pcbData); +PVOID reg_query_key_alloc( + HANDLE KeyHandle, + KEY_INFORMATION_CLASS KeyInformationClass, + PULONG pResultLength); +wchar_t *env_expand_strings_alloc(const wchar_t *src, LPDWORD pcchLength); diff --git a/src/wufuc/rundll32.c b/src/wufuc/rundll32.c index 8e68f4b..b326d13 100644 --- a/src/wufuc/rundll32.c +++ b/src/wufuc/rundll32.c @@ -1,12 +1,15 @@ #include "stdafx.h" +#include "context.h" #include "callbacks.h" -#include "hlpmem.h" -#include "hlpmisc.h" -#include "hlpsvc.h" +#include "modulehelper.h" +#include "registryhelper.h" +#include "servicehelper.h" +#include "log.h" +#include "wufuc.h" void CALLBACK RUNDLL32_StartW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) { - ContextHandles ctx; + context ctx; DWORD dwDesiredAccess; DWORD dwProcessId; bool Unloading = false; @@ -15,55 +18,37 @@ void CALLBACK RUNDLL32_StartW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, in SC_HANDLE hService; SERVICE_NOTIFYW NotifyBuffer; - if ( !InitializeMutex(true, - L"Global\\25020063-b5a7-4227-9fdf-25cb75e8c645", - &ctx.hParentMutex) ) + if ( !ctx_create(&ctx, + L"Global\\25020063-b5a7-4227-9fdf-25cb75e8c645", 0, + L"Global\\wufuc_UnloadEvent", L"D:(A;;0x001F0003;;;BA)", 0) ) return; - if ( !start_traing() ) - itrace(L"Could not create tracing pipe!"); - return; - - if ( !CreateEventWithStringSecurityDescriptor(L"D:(A;;0x001F0003;;;BA)", - true, - false, - L"Global\\wufuc_UnloadEvent", - &ctx.hUnloadEvent) ) { - - trace(L"Failed to create unload event. (GetLastError=%lu)", GetLastError()); - goto close_mutex; - } dwDesiredAccess = SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG; do { Lagging = false; hSCM = OpenSCManagerW(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE); - if ( !hSCM ) { - trace(L"Failed to open SCM. (GetLastError=%lu)", GetLastError()); - goto close_event; - } + if ( !hSCM ) goto delete_ctx; hService = OpenServiceW(hSCM, L"wuauserv", dwDesiredAccess); - if ( !hService ) { - trace(L"Failed to open service. (GetLastError=%lu)", GetLastError()); - goto close_scm; - } + if ( !hService ) goto close_scm; + if ( (dwDesiredAccess & SERVICE_QUERY_CONFIG) == SERVICE_QUERY_CONFIG ) { dwDesiredAccess &= ~SERVICE_QUERY_CONFIG; - dwProcessId = HeuristicServiceProcessId(hSCM, hService); + dwProcessId = svc_heuristic_process_id(hSCM, hService); if ( dwProcessId ) - wufuc_InjectLibrary(dwProcessId, &ctx); + wufuc_inject(dwProcessId, (LPTHREAD_START_ROUTINE)cb_start, &ctx); } ZeroMemory(&NotifyBuffer, sizeof NotifyBuffer); NotifyBuffer.dwVersion = SERVICE_NOTIFY_STATUS_CHANGE; - NotifyBuffer.pfnNotifyCallback = ServiceNotifyCallback; + NotifyBuffer.pfnNotifyCallback = cb_service_notify; NotifyBuffer.pContext = (PVOID)&ctx; while ( !Unloading && !Lagging ) { switch ( NotifyServiceStatusChangeW(hService, SERVICE_NOTIFY_START_PENDING | SERVICE_NOTIFY_RUNNING, &NotifyBuffer) ) { case ERROR_SUCCESS: - Unloading = WaitForSingleObjectEx(ctx.hUnloadEvent, INFINITE, TRUE) == WAIT_OBJECT_0; + Unloading = WaitForSingleObjectEx(ctx.uevent, INFINITE, TRUE) == WAIT_OBJECT_0; break; case ERROR_SERVICE_NOTIFY_CLIENT_LAGGING: trace(L"Client lagging!"); @@ -75,15 +60,11 @@ void CALLBACK RUNDLL32_StartW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, in } } CloseServiceHandle(hService); -close_scm: CloseServiceHandle(hSCM); +close_scm: + CloseServiceHandle(hSCM); } while ( Lagging ); -close_event: - CloseHandle(ctx.hUnloadEvent); -close_mutex: - ReleaseMutex(ctx.hParentMutex); - if ( g_hTracingMutex ) - CloseHandle(g_hTracingMutex); - CloseHandle(ctx.hParentMutex); +delete_ctx: + ctx_delete(&ctx); } void CALLBACK RUNDLL32_UnloadW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) diff --git a/src/wufuc/hlpsvc.c b/src/wufuc/servicehelper.c similarity index 79% rename from src/wufuc/hlpsvc.c rename to src/wufuc/servicehelper.c index cfa7761..0e94113 100644 --- a/src/wufuc/hlpsvc.c +++ b/src/wufuc/servicehelper.c @@ -1,8 +1,8 @@ #include "stdafx.h" -#include "hlpmisc.h" -#include "hlpsvc.h" +#include "servicehelper.h" +#include "registryhelper.h" -LPQUERY_SERVICE_CONFIGW QueryServiceConfigByNameAlloc( +LPQUERY_SERVICE_CONFIGW svc_query_config_by_name_alloc( SC_HANDLE hSCM, const wchar_t *pServiceName, LPDWORD pcbBufSize) @@ -13,13 +13,13 @@ LPQUERY_SERVICE_CONFIGW QueryServiceConfigByNameAlloc( hService = OpenServiceW(hSCM, pServiceName, SERVICE_QUERY_CONFIG); if ( !hService ) return result; - result = QueryServiceConfigAlloc(hSCM, hService, pcbBufSize); + result = svc_query_config_alloc(hSCM, hService, pcbBufSize); CloseServiceHandle(hService); return result; } -LPQUERY_SERVICE_CONFIGW QueryServiceConfigAlloc( +LPQUERY_SERVICE_CONFIGW svc_query_config_alloc( SC_HANDLE hSCM, SC_HANDLE hService, LPDWORD pcbBufSize) @@ -44,7 +44,7 @@ LPQUERY_SERVICE_CONFIGW QueryServiceConfigAlloc( return result; } -bool QueryServiceStatusProcessInfoByName( +bool svc_query_process_info_by_name( SC_HANDLE hSCM, const wchar_t *pServiceName, LPSERVICE_STATUS_PROCESS pServiceStatus) @@ -66,7 +66,7 @@ bool QueryServiceStatusProcessInfoByName( return result; } -bool QueryServiceGroupName( +bool svc_query_group_name( const LPQUERY_SERVICE_CONFIGW pServiceConfig, wchar_t **pGroupName, HLOCAL *hMem) @@ -92,7 +92,7 @@ bool QueryServiceGroupName( return false; } -DWORD QueryServiceProcessId(SC_HANDLE hSCM, SC_HANDLE hService) +DWORD svc_query_process_id(SC_HANDLE hSCM, SC_HANDLE hService) { DWORD result = 0; SERVICE_STATUS_PROCESS ServiceStatus; @@ -109,16 +109,16 @@ DWORD QueryServiceProcessId(SC_HANDLE hSCM, SC_HANDLE hService) return result; } -DWORD QueryServiceProcessIdByName(SC_HANDLE hSCM, const wchar_t *pServiceName) +DWORD svc_query_process_id_by_name(SC_HANDLE hSCM, const wchar_t *pServiceName) { SERVICE_STATUS_PROCESS ServiceStatusProcess; - if ( QueryServiceStatusProcessInfoByName(hSCM, pServiceName, &ServiceStatusProcess) ) + if ( svc_query_process_info_by_name(hSCM, pServiceName, &ServiceStatusProcess) ) return ServiceStatusProcess.dwProcessId; return 0; } -DWORD HeuristicServiceGroupProcessId(SC_HANDLE hSCM, const wchar_t *pGroupNameSearch) +DWORD svc_heuristic_group_process_id(SC_HANDLE hSCM, const wchar_t *pGroupNameSearch) { wchar_t *pData; DWORD result = 0; @@ -129,7 +129,7 @@ DWORD HeuristicServiceGroupProcessId(SC_HANDLE hSCM, const wchar_t *pGroupNameSe wchar_t *pGroupName; HLOCAL hMem; - pData = RegGetValueAlloc(HKEY_LOCAL_MACHINE, + pData = reg_get_value_alloc(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Svchost", pGroupNameSearch, RRF_RT_REG_MULTI_SZ, @@ -139,14 +139,14 @@ DWORD HeuristicServiceGroupProcessId(SC_HANDLE hSCM, const wchar_t *pGroupNameSe if ( !pData ) return result; for ( wchar_t *pName = pData; *pName; pName += wcslen(pName) + 1 ) { - dwProcessId = QueryServiceProcessIdByName(hSCM, pName); + dwProcessId = svc_query_process_id_by_name(hSCM, pName); if ( !dwProcessId ) continue; - pServiceConfig = QueryServiceConfigByNameAlloc(hSCM, pName, &cbBufSize); + pServiceConfig = svc_query_config_by_name_alloc(hSCM, pName, &cbBufSize); if ( !pServiceConfig ) continue; if ( pServiceConfig->dwServiceType == SERVICE_WIN32_SHARE_PROCESS - && QueryServiceGroupName(pServiceConfig, &pGroupName, &hMem) ) { + && svc_query_group_name(pServiceConfig, &pGroupName, &hMem) ) { success = !_wcsicmp(pGroupNameSearch, pGroupName); LocalFree(hMem); @@ -161,18 +161,18 @@ DWORD HeuristicServiceGroupProcessId(SC_HANDLE hSCM, const wchar_t *pGroupNameSe return result; } -DWORD HeuristicServiceProcessId(SC_HANDLE hSCM, SC_HANDLE hService) +DWORD svc_heuristic_process_id(SC_HANDLE hSCM, SC_HANDLE hService) { DWORD result = 0; LPQUERY_SERVICE_CONFIGW pServiceConfig; wchar_t *pGroupName; HLOCAL hMem; - result = QueryServiceProcessId(hSCM, hService); + result = svc_query_process_id(hSCM, hService); if ( result ) return result; - pServiceConfig = QueryServiceConfigAlloc(hSCM, hService, NULL); + pServiceConfig = svc_query_config_alloc(hSCM, hService, NULL); if ( pServiceConfig ) { switch ( pServiceConfig->dwServiceType ) { case SERVICE_WIN32_OWN_PROCESS: @@ -185,8 +185,8 @@ DWORD HeuristicServiceProcessId(SC_HANDLE hSCM, SC_HANDLE hService) // 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); + if ( svc_query_group_name(pServiceConfig, &pGroupName, &hMem) ) { + result = svc_heuristic_group_process_id(hSCM, pGroupName); LocalFree(hMem); } break; @@ -196,13 +196,13 @@ DWORD HeuristicServiceProcessId(SC_HANDLE hSCM, SC_HANDLE hService) return result; } -DWORD HeuristicServiceProcessIdByName(SC_HANDLE hSCM, const wchar_t *pServiceName) +DWORD svc_heuristic_process_id_by_name(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); + result = svc_heuristic_process_id(hSCM, hService); CloseServiceHandle(hService); return result; diff --git a/src/wufuc/servicehelper.h b/src/wufuc/servicehelper.h new file mode 100644 index 0000000..7342705 --- /dev/null +++ b/src/wufuc/servicehelper.h @@ -0,0 +1,23 @@ +#pragma once + +LPQUERY_SERVICE_CONFIGW svc_query_config_by_name_alloc( + SC_HANDLE hSCM, + const wchar_t *pServiceName, + LPDWORD pcbBufSize); +LPQUERY_SERVICE_CONFIGW svc_query_config_alloc( + SC_HANDLE hSCM, + SC_HANDLE hService, + LPDWORD pcbBufSize); +bool svc_query_process_info_by_name( + SC_HANDLE hSCM, + const wchar_t *pServiceName, + LPSERVICE_STATUS_PROCESS pServiceStatus); +bool svc_query_group_name( + const LPQUERY_SERVICE_CONFIGW pServiceConfig, + wchar_t **pGroupName, + HLOCAL *hMem); +DWORD svc_query_process_id(SC_HANDLE hSCM, SC_HANDLE hService); +DWORD svc_query_process_id_by_name(SC_HANDLE hSCM, const wchar_t *pServiceName); +DWORD svc_heuristic_group_process_id(SC_HANDLE hSCM, const wchar_t *pGroupName); +DWORD svc_heuristic_process_id(SC_HANDLE hSCM, SC_HANDLE hService); +DWORD svc_heuristic_process_id_by_name(SC_HANDLE hSCM, const wchar_t *pServiceName); diff --git a/src/wufuc/stdafx.h b/src/wufuc/stdafx.h index 8664869..51cad5d 100644 --- a/src/wufuc/stdafx.h +++ b/src/wufuc/stdafx.h @@ -27,10 +27,6 @@ #include #include -#include -#include "patternfind.h" -#include "tracing.h" - extern IMAGE_DOS_HEADER __ImageBase; #define PIMAGEBASE ((HMODULE)&__ImageBase) diff --git a/src/wufuc/tracing.c b/src/wufuc/tracing.c deleted file mode 100644 index 667d0e2..0000000 --- a/src/wufuc/tracing.c +++ /dev/null @@ -1,234 +0,0 @@ -#include "stdafx.h" -#include "tracing.h" -#include - -void itrace_(const wchar_t *const format, ...) -{ - va_list argptr; - int count; - wchar_t *buffer; - - va_start(argptr, format); - - count = _vscwprintf(format, argptr) + 1; - buffer = calloc(count, sizeof *buffer); - vswprintf_s(buffer, count, format, argptr); - - va_end(argptr); - OutputDebugStringW(buffer); - free(buffer); -} - -HANDLE g_hTracingMutex; -wchar_t path[MAX_PATH]; -wchar_t exepath[MAX_PATH]; - -DWORD WINAPI tracing_thread(LPVOID pParam) -{ - wchar_t *folder; - HANDLE file; - DWORD dwProcessId; - wchar_t *exename; - int count; - - if ( !*path ) { - SHGetKnownFolderPath(&FOLDERID_ProgramData, 0, NULL, &folder); - wcscpy_s(path, _countof(path), folder); - CoTaskMemFree(folder); - PathAppendW(path, L"wufuc"); - CreateDirectoryW(path, NULL); - PathAppendW(path, L"wufuc.log"); - } - if ( !*exepath ) - GetModuleFileNameW(NULL, exepath, _countof(exepath)); - - file = CreateFileW(path, FILE_APPEND_DATA, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - itrace(L"CreateFileW=%p", file); - dwProcessId = GetCurrentProcessId(); - exename = PathFindFileNameW(exepath); - - va_start(argptr, format); - count = _vscwprintf(format, argptr) + 1; - buf1 = calloc(count, sizeof *buf1); - vswprintf_s(buf1, count, format, argptr); - va_end(argptr); - - count = _scwprintf(fmt, datebuf, timebuf, dwProcessId, exename, buf1); - buf2 = calloc(count + 1, sizeof *buf2); - swprintf_s(buf2, count + 1, fmt, datebuf, timebuf, dwProcessId, exename, buf1); - free(buf1); - itrace(L"WriteFile=%d", WriteFile(file, buf2, count * (sizeof *buf2), &written, NULL)); - free(buf2); - itrace(L"FlushFileBuffers=%d", FlushFileBuffers(file)); - itrace(L"CloseHandle=%d", CloseHandle(file)); - itrace(L"ReleaseMutex=%d", ReleaseMutex(g_hTracingMutex)); - - - HANDLE hHeap = GetProcessHeap(); - TCHAR* pchRequest = (TCHAR*)HeapAlloc(hHeap, 0, 512 * sizeof(TCHAR)); - - DWORD cbBytesRead = 0, cbReplyBytes = 0, cbWritten = 0; - BOOL fSuccess = FALSE; - HANDLE hPipe = NULL; - - // Do some extra error checking since the app will keep running even if this - // thread fails. - - if ( pParam == NULL ) { - itrace(L"\nERROR - Pipe Server Failure:"); - itrace(L" InstanceThread got an unexpected NULL value in lpvParam."); - itrace(L" InstanceThread exitting."); - if ( pchRequest != NULL ) HeapFree(hHeap, 0, pchRequest); - return -1; - } - - // Print verbose messages. In production code, this should be for debugging only. - printf("InstanceThread created, receiving and processing messages.\n"); - - // The thread's parameter is a handle to a pipe object instance. - - hPipe = (HANDLE)pParam; - - // Loop until done reading - while ( true ) { - // Read client requests from the pipe. This simplistic code only allows messages - // up to BUFSIZE characters in length. - fSuccess = ReadFile( - hPipe, // handle to pipe - pchRequest, // buffer to receive data - 512 * sizeof(TCHAR), // size of buffer - &cbBytesRead, // number of bytes read - NULL); // not overlapped I/O - - if ( !fSuccess || cbBytesRead == 0 ) { - if ( GetLastError() == ERROR_BROKEN_PIPE ) { - itrace(L"InstanceThread: client disconnected."); - } else { - itrace(L"InstanceThread ReadFile failed, GLE=%d.", GetLastError()); - } - break; - } - - _wstrdate_s(datebuf, _countof(datebuf)); - _wstrtime_s(timebuf, _countof(timebuf)); - - if ( !fSuccess ) { - itrace(L"InstanceThread WriteFile failed, GLE=%d.", GetLastError()); - break; - } - } - - // Flush the pipe to allow the client to read the pipe's contents - // before disconnecting. Then disconnect the pipe, and close the - // handle to this pipe instance. - - FlushFileBuffers(hPipe); - DisconnectNamedPipe(hPipe); - CloseHandle(hPipe); - - HeapFree(hHeap, 0, pchRequest); - - printf("InstanceThread exitting.\n"); - return 1; -} - -bool start_tracing(const wchar_t *pipename) -{ - HANDLE hPipe; - HANDLE hPipe = INVALID_HANDLE_VALUE; - HANDLE hThread = NULL; - - while ( true ) { - hPipe = CreateNamedPipeW(pipename, - PIPE_ACCESS_INBOUND, 0, PIPE_UNLIMITED_INSTANCES, 512, 0, 512, NULL); - if ( hPipe == INVALID_HANDLE_VALUE ) { - itrace(L"CreateNamedPipe failed, GLE=%d.\n"), GetLastError(); - return false; - } - - if ( ConnectNamedPipe(hPipe, NULL) || - GetLastError() == ERROR_PIPE_CONNECTED ) { - itrace(L"Client connected, creating a processing thread."); - - // Create a thread for this client. - hThread = CreateThread( - NULL, // no security attribute - 0, // default stack size - tracing_thread, // thread proc - (LPVOID)hPipe, // thread parameter - 0, // not suspended - NULL); // returns thread ID - - if ( hThread == NULL ) { - itrace(L"CreateThread failed, GLE=%d.\n"), GetLastError(); - return false; - } else { - CloseHandle(hThread); - } - } else { - // The client could not connect, so close the pipe. - CloseHandle(hPipe); - } - } - return true; -} - -void trace_(const wchar_t *const format, ...) -{ - va_list argptr; - wchar_t *folder; - wchar_t datebuf[9]; - wchar_t timebuf[9]; - HANDLE file; - DWORD dwProcessId; - wchar_t *exename; - int count; - DWORD written; - const wchar_t fmt[] = L"%ls %ls [%lu] Exe(%ls) %ls"; - wchar_t *buf1; - wchar_t *buf2; - - va_start(argptr, format); - itrace_(format); - va_end(argptr); - - if ( !*path ) { - SHGetKnownFolderPath(&FOLDERID_ProgramData, 0, NULL, &folder); - wcscpy_s(path, _countof(path), folder); - CoTaskMemFree(folder); - PathAppendW(path, L"wufuc"); - CreateDirectoryW(path, NULL); - PathAppendW(path, L"wufuc.log"); - } - if ( !*exepath ) - GetModuleFileNameW(NULL, exepath, _countof(exepath)); - - if ( !g_hTracingMutex ) - g_hTracingMutex = CreateMutexW(NULL, FALSE, L"Global\\6b2f5740-7435-47f7-865c-dbd825292f32"); - - itrace(L"WaitForSingleObject=%lu", WaitForSingleObject(g_hTracingMutex, INFINITE)); - - _wstrdate_s(datebuf, _countof(datebuf)); - _wstrtime_s(timebuf, _countof(timebuf)); - - file = CreateFileW(path, FILE_APPEND_DATA, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - itrace(L"CreateFileW=%p", file); - dwProcessId = GetCurrentProcessId(); - exename = PathFindFileNameW(exepath); - - va_start(argptr, format); - count = _vscwprintf(format, argptr) + 1; - buf1 = calloc(count, sizeof *buf1); - vswprintf_s(buf1, count, format, argptr); - va_end(argptr); - - count = _scwprintf(fmt, datebuf, timebuf, dwProcessId, exename, buf1); - buf2 = calloc(count + 1, sizeof *buf2); - swprintf_s(buf2, count + 1, fmt, datebuf, timebuf, dwProcessId, exename, buf1); - free(buf1); - itrace(L"WriteFile=%d", WriteFile(file, buf2, count * (sizeof *buf2), &written, NULL)); - free(buf2); - itrace(L"FlushFileBuffers=%d", FlushFileBuffers(file)); - itrace(L"CloseHandle=%d", CloseHandle(file)); - itrace(L"ReleaseMutex=%d", ReleaseMutex(g_hTracingMutex)); -} diff --git a/src/wufuc/tracing.h b/src/wufuc/tracing.h deleted file mode 100644 index 8069508..0000000 --- a/src/wufuc/tracing.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include - -extern HANDLE g_hTracingMutex; - -void itrace_(const wchar_t *const format, ...); -void trace_(const wchar_t *const format, ...); - -#define STRINGIZEW_(x) L#x -#define STRINGIZEW(x) STRINGIZEW_(x) - -#define LINEWSTR STRINGIZEW(__LINE__) -#define itrace(format, ...) itrace_(__FILEW__ L":" LINEWSTR L"(" __FUNCTIONW__ L"): " format L"\r\n", ##__VA_ARGS__) -#define trace(format, ...) trace_(__FILEW__ L":" LINEWSTR L"(" __FUNCTIONW__ L"): " format L"\r\n", ##__VA_ARGS__) diff --git a/src/wufuc/hlpver.c b/src/wufuc/versionhelper.c similarity index 76% rename from src/wufuc/hlpver.c rename to src/wufuc/versionhelper.c index dfa0fbb..b46c77d 100644 --- a/src/wufuc/hlpver.c +++ b/src/wufuc/versionhelper.c @@ -1,7 +1,7 @@ #include "stdafx.h" -#include "hlpver.h" +#include "versionhelper.h" -int ProductVersionCompare(VS_FIXEDFILEINFO *pffi, WORD wMajor, WORD wMinor, WORD wBuild, WORD wRev) +int ver_compare_product_version(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; @@ -14,7 +14,7 @@ int ProductVersionCompare(VS_FIXEDFILEINFO *pffi, WORD wMajor, WORD wMinor, WORD return 0; } -bool GetVersionInfoFromHModule(HMODULE hModule, const wchar_t *pszSubBlock, LPVOID pData, PUINT pcbData) +bool ver_get_version_info_from_hmodule(HMODULE hModule, const wchar_t *pszSubBlock, LPVOID pData, PUINT pcbData) { bool result = false; UINT cbData; @@ -44,8 +44,9 @@ bool GetVersionInfoFromHModule(HMODULE hModule, const wchar_t *pszSubBlock, LPVO if ( !pRes ) return result; pCopy = malloc(dwSize); - if ( !pCopy - || memcpy_s(pCopy, dwSize, pRes, dwSize) + if ( !pCopy ) return result; + + if ( memcpy_s(pCopy, dwSize, pRes, dwSize) || !VerQueryValueW(pCopy, pszSubBlock, &pBuffer, &uLen) ) goto cleanup; @@ -68,18 +69,18 @@ cleanup: return result; } -LPVOID GetVersionInfoFromHModuleAlloc(HMODULE hModule, const wchar_t *pszSubBlock, PUINT pcbData) +LPVOID ver_get_version_info_from_hmodule_alloc(HMODULE hModule, const wchar_t *pszSubBlock, PUINT pcbData) { UINT cbData = 0; LPVOID result = NULL; - if ( !GetVersionInfoFromHModule(hModule, pszSubBlock, NULL, &cbData) ) + if ( !ver_get_version_info_from_hmodule(hModule, pszSubBlock, NULL, &cbData) ) return result; result = malloc(cbData); if ( !result ) return result; - if ( GetVersionInfoFromHModule(hModule, pszSubBlock, result, &cbData) ) { + if ( ver_get_version_info_from_hmodule(hModule, pszSubBlock, result, &cbData) ) { *pcbData = cbData; } else { free(result); @@ -88,7 +89,7 @@ LPVOID GetVersionInfoFromHModuleAlloc(HMODULE hModule, const wchar_t *pszSubBloc return result; } -bool IsWindowsVersion(WORD wMajorVersion, WORD wMinorVersion, WORD wServicePackMajor) +bool ver_verify_version_info(WORD wMajorVersion, WORD wMinorVersion, WORD wServicePackMajor) { OSVERSIONINFOEXW osvi = { sizeof osvi }; @@ -105,3 +106,13 @@ bool IsWindowsVersion(WORD wMajorVersion, WORD wMinorVersion, WORD wServicePackM VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR, dwlConditionMask) != FALSE; } + +bool ver_verify_windows_7_sp1(void) +{ + return ver_verify_version_info(6, 1, 1); +} + +bool ver_verify_windows_8_1(void) +{ + return ver_verify_version_info(6, 3, 0); +} diff --git a/src/wufuc/versionhelper.h b/src/wufuc/versionhelper.h new file mode 100644 index 0000000..7fc954e --- /dev/null +++ b/src/wufuc/versionhelper.h @@ -0,0 +1,8 @@ +#pragma once + +int ver_compare_product_version(VS_FIXEDFILEINFO *pffi, WORD wMajor, WORD wMinor, WORD wBuild, WORD wRev); +bool ver_get_version_info_from_hmodule(HMODULE hModule, const wchar_t *pszSubBlock, LPVOID pData, PUINT pcbData); +LPVOID ver_get_version_info_from_hmodule_alloc(HMODULE hModule, const wchar_t *pszSubBlock, PUINT pcbData); +bool ver_verify_version_info(WORD wMajorVersion, WORD wMinorVersion, WORD wServicePackMajor); +bool ver_verify_windows_7_sp1(void); +bool ver_verify_windows_8_1(void); \ No newline at end of file diff --git a/src/wufuc/wufuc.c b/src/wufuc/wufuc.c new file mode 100644 index 0000000..856fa77 --- /dev/null +++ b/src/wufuc/wufuc.c @@ -0,0 +1,126 @@ +#include "stdafx.h" + +#include "context.h" +#include "wufuc.h" +#include "modulehelper.h" +#include "versionhelper.h" +#include "hooks.h" +#include "log.h" +#include "patternfind.h" + +#include + +bool wufuc_inject(DWORD dwProcessId, + LPTHREAD_START_ROUTINE pfnStart, + context *pContext) +{ + bool result = false; + HANDLE hProcess; + HANDLE hMutex; + context ctx; + + if ( !ctx_add_new_mutex_fmt(pContext, + false, + dwProcessId, + &hMutex, + L"Global\\%08x-7132-44a8-be15-56698979d2f3", dwProcessId) ) + return false; + + hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId); + if ( !hProcess ) return false; + + result = ctx_duplicate_context(pContext, hProcess, &ctx, hMutex, DUPLICATE_SAME_ACCESS, dwProcessId) + && mod_inject_and_begin_thread(hProcess, PIMAGEBASE, pfnStart, &ctx, sizeof ctx); + CloseHandle(hProcess); + return result; +} + +bool wufuc_hook(HMODULE hModule) +{ + bool result = false; + PLANGANDCODEPAGE ptl; + HANDLE hProcess; + int tmp; + UINT cbtl; + wchar_t SubBlock[38]; + UINT cbInternalName; + wchar_t *pInternalName; + UINT cbffi; + VS_FIXEDFILEINFO *pffi; + MODULEINFO modinfo; + size_t offset; + + if ( !ver_verify_windows_7_sp1() && !ver_verify_windows_8_1() ) + return false; + + ptl = ver_get_version_info_from_hmodule_alloc(hModule, L"\\VarFileInfo\\Translation", &cbtl); + if ( !ptl ) { + trace(L"Failed to allocate version translation information from hmodule."); + return false; + } + hProcess = GetCurrentProcess(); + + for ( size_t i = 0, count = (cbtl / sizeof *ptl); i < count; i++ ) { + if ( swprintf_s(SubBlock, + _countof(SubBlock), + L"\\StringFileInfo\\%04x%04x\\InternalName", + ptl[i].wLanguage, + ptl[i].wCodePage) == -1 ) + continue; + + pInternalName = ver_get_version_info_from_hmodule_alloc(hModule, SubBlock, &cbInternalName); + if ( !pInternalName ) { + trace(L"Failed to allocate version internal name from hmodule."); + continue; + } + + // identify wuaueng.dll by its resource data + if ( !_wcsicmp(pInternalName, L"wuaueng.dll") ) { + pffi = ver_get_version_info_from_hmodule_alloc(hModule, L"\\", &cbffi); + if ( !pffi ) { + trace(L"Failed to allocate version information from hmodule."); + break; + } + trace(L"Windows Update Agent version: %hu.%hu.%hu.%hu"), + HIWORD(pffi->dwProductVersionMS), + LOWORD(pffi->dwProductVersionMS), + HIWORD(pffi->dwProductVersionLS), + LOWORD(pffi->dwProductVersionLS); + + // assure wuaueng.dll is at least the minimum supported version + tmp = ((ver_verify_windows_7_sp1() && ver_compare_product_version(pffi, 7, 6, 7601, 23714) != -1) + || (ver_verify_windows_8_1() && ver_compare_product_version(pffi, 7, 9, 9600, 18621) != -1)); + free(pffi); + if ( !tmp ) { + trace(L"Windows Update Agent does not meet the minimum supported version."); + break; + } + if ( !GetModuleInformation(hProcess, hModule, &modinfo, sizeof modinfo) ) { + trace(L"Failed to get module information (%p)", hModule); + break; + } + offset = patternfind(modinfo.lpBaseOfDll, modinfo.SizeOfImage, +#ifdef _WIN64 + "FFF3 4883EC?? 33DB 391D???????? 7508 8B05????????" +#else + ver_verify_windows_7_sp1() + ? "833D????????00 743E E8???????? A3????????" + : "8BFF 51 833D????????00 7507 A1????????" +#endif + ); + + if ( offset == -1 ) { + trace(L"Could not locate pattern offset!"); + break; + } else { + result = MH_CreateHook((PVOID)((uint8_t *)modinfo.lpBaseOfDll + offset), + IsDeviceServiceable_hook, + NULL) == MH_OK; + } + break; + } else trace(L"Module internal name does not match. (%ls)", pInternalName); + free(pInternalName); + } + free(ptl); + return result; + } diff --git a/src/wufuc/wufuc.h b/src/wufuc/wufuc.h new file mode 100644 index 0000000..d78a396 --- /dev/null +++ b/src/wufuc/wufuc.h @@ -0,0 +1,12 @@ +#pragma once + +typedef struct +{ + WORD wLanguage; + WORD wCodePage; +} LANGANDCODEPAGE, *PLANGANDCODEPAGE; + +bool wufuc_inject(DWORD dwProcessId, + LPTHREAD_START_ROUTINE pfnStart, + context *pContext); +bool wufuc_hook(HMODULE hModule); \ No newline at end of file diff --git a/src/wufuc/wufuc.vcxproj b/src/wufuc/wufuc.vcxproj index f078f25..fcc3c9f 100644 --- a/src/wufuc/wufuc.vcxproj +++ b/src/wufuc/wufuc.vcxproj @@ -20,24 +20,28 @@ - - - - + + + + + + - + + - - - - + + + + + Create @@ -46,7 +50,7 @@ Create - + @@ -115,8 +119,8 @@ $(ProjectDir)$(BaseIntermediateOutputPath)$(Configuration)\$(PlatformShortName)\ $(ProjectName)$(PlatformArchitecture) false - $(SolutionDir)..\inc\phnt;$(SolutionDir)..\inc\detours;$(IncludePath) - $(SolutionDir)..\lib\detours;$(LibraryPath) + $(SolutionDir)..\inc\phnt;$(SolutionDir)..\inc\minhook;$(IncludePath) + $(SolutionDir)..\lib\minhook;$(LibraryPath) true @@ -124,26 +128,26 @@ $(ProjectDir)$(BaseIntermediateOutputPath)$(Configuration)\$(PlatformShortName)\ $(ProjectName)$(PlatformArchitecture) false - $(SolutionDir)..\inc\phnt;$(SolutionDir)..\inc\detours;$(IncludePath) - $(SolutionDir)..\lib\detours;$(LibraryPath) + $(SolutionDir)..\inc\phnt;$(SolutionDir)..\inc\minhook;$(IncludePath) + $(SolutionDir)..\lib\minhook;$(LibraryPath) false $(ProjectDir)bin\$(Configuration)\$(PlatformShortName)\ - $(ProjectDir)$(BaseIntermediateOutputPath)$(Configuration)\$(PlatformShortName)\ + $(ProjectDir)obj\$(Configuration)\$(PlatformShortName)\ $(ProjectName)$(PlatformArchitecture) false - $(SolutionDir)..\inc\phnt;$(SolutionDir)..\inc\detours;$(IncludePath) - $(SolutionDir)..\lib\detours;$(LibraryPath) + $(SolutionDir)..\inc\phnt;$(SolutionDir)..\inc\minhook;$(IncludePath) + $(SolutionDir)..\lib\minhook;$(LibraryPath) false $(ProjectDir)bin\$(Configuration)\$(PlatformShortName)\ - $(ProjectDir)$(BaseIntermediateOutputPath)$(Configuration)\$(PlatformShortName)\ + $(ProjectDir)obj\$(Configuration)\$(PlatformShortName)\ $(ProjectName)$(PlatformArchitecture) false - $(SolutionDir)..\inc\phnt;$(SolutionDir)..\inc\detours;$(IncludePath) - $(SolutionDir)..\lib\detours;$(LibraryPath) + $(SolutionDir)..\inc\phnt;$(SolutionDir)..\inc\minhook;$(IncludePath) + $(SolutionDir)..\lib\minhook;$(LibraryPath) @@ -162,7 +166,7 @@ exports.def - version.lib;Shlwapi.lib;detours.X86.MTd.lib;%(AdditionalDependencies);ntdll.lib + version.lib;Shlwapi.lib;libMinHook.x86.MTd.lib;%(AdditionalDependencies);ntdll.lib X86;%(PreprocessorDefinitions) @@ -185,7 +189,7 @@ exports.def - version.lib;Shlwapi.lib;detours.X64.MTd.lib;%(AdditionalDependencies);ntdll.lib + version.lib;Shlwapi.lib;libMinHook.x64.MTd.lib;%(AdditionalDependencies);ntdll.lib X64;%(PreprocessorDefinitions) @@ -216,7 +220,7 @@ exports.def - version.lib;Shlwapi.lib;detours.X86.MT.lib;%(AdditionalDependencies);ntdll.lib + version.lib;Shlwapi.lib;libMinHook.x86.MT.lib;%(AdditionalDependencies);ntdll.lib true @@ -253,7 +257,7 @@ copy /Y "$(TargetPath)" "$(SolutionDir)wufuc_setup\" true false exports.def - version.lib;Shlwapi.lib;detours.X64.MT.lib;%(AdditionalDependencies);ntdll.lib + version.lib;Shlwapi.lib;libMinHook.x64.MT.lib;%(AdditionalDependencies);ntdll.lib true diff --git a/src/wufuc/wufuc.vcxproj.filters b/src/wufuc/wufuc.vcxproj.filters index 5b1276a..1c95330 100644 --- a/src/wufuc/wufuc.vcxproj.filters +++ b/src/wufuc/wufuc.vcxproj.filters @@ -21,9 +21,6 @@ Header Files - - Header Files - Header Files @@ -33,16 +30,25 @@ Header Files - + Header Files - + Header Files - + Header Files - + + Header Files + + + Header Files + + + Header Files + + Header Files @@ -56,9 +62,6 @@ Source Files - - Source Files - Source Files @@ -68,16 +71,25 @@ Source Files - + Source Files - + Source Files - + Source Files - + + Source Files + + + Source Files + + + Source Files + + Source Files