| /************************************************************************** |
| * |
| * Copyright 2016-2018 VMware, Inc. |
| * Copyright 2011-2012 Jose Fonseca |
| * All Rights Reserved. |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a copy |
| * of this software and associated documentation files (the "Software"), to deal |
| * in the Software without restriction, including without limitation the rights |
| * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| * copies of the Software, and to permit persons to whom the Software is |
| * furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be included in |
| * all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| * THE SOFTWARE. |
| * |
| **************************************************************************/ |
| |
| |
| /* |
| * Code for the DLL that will be injected in the target process. |
| * |
| * The injected DLL will manipulate the import tables to hook the |
| * modules/functions of interest. |
| * |
| * See also: |
| * - http://www.codeproject.com/KB/system/api_spying_hack.aspx |
| * - http://www.codeproject.com/KB/threads/APIHooking.aspx |
| * - http://msdn.microsoft.com/en-us/magazine/cc301808.aspx |
| */ |
| |
| |
| #include <assert.h> |
| #include <stdio.h> |
| #include <stdarg.h> |
| #include <string.h> |
| |
| #include <algorithm> |
| #include <set> |
| #include <map> |
| #include <functional> |
| #include <iterator> |
| |
| #include <windows.h> |
| #include <tlhelp32.h> |
| #include <delayimp.h> |
| |
| #include "inject.h" |
| |
| |
| static int VERBOSITY = 0; |
| #define NOOP 0 |
| |
| |
| static CRITICAL_SECTION g_Mutex; |
| |
| |
| |
| static HMODULE g_hThisModule = NULL; |
| static HMODULE g_hHookModule = NULL; |
| |
| |
| static void |
| debugPrintf(const char *format, ...) |
| { |
| char buf[512]; |
| |
| va_list ap; |
| va_start(ap, format); |
| _vsnprintf(buf, sizeof buf, format, ap); |
| va_end(ap); |
| |
| OutputDebugStringA(buf); |
| } |
| |
| |
| EXTERN_C void |
| _assert(const char *_Message, const char *_File, unsigned _Line) |
| { |
| debugPrintf("Assertion failed: %s, file %s, line %u\n", _Message, _File, _Line); |
| TerminateProcess(GetCurrentProcess(), 1); |
| } |
| |
| |
| #ifndef __MINGW32__ |
| |
| EXTERN_C void |
| _wassert(const wchar_t * _Message, const wchar_t *_File, unsigned _Line) |
| { |
| debugPrintf("Assertion failed: %S, file %S, line %u\n", _Message, _File, _Line); |
| TerminateProcess(GetCurrentProcess(), 1); |
| } |
| |
| #endif /* !__MINGW32__ */ |
| |
| |
| static HMODULE WINAPI |
| MyLoadLibraryA(LPCSTR lpLibFileName); |
| |
| static HMODULE WINAPI |
| MyLoadLibraryW(LPCWSTR lpLibFileName); |
| |
| static HMODULE WINAPI |
| MyLoadLibraryExA(LPCSTR lpFileName, HANDLE hFile, DWORD dwFlags); |
| |
| static HMODULE WINAPI |
| MyLoadLibraryExW(LPCWSTR lpFileName, HANDLE hFile, DWORD dwFlags); |
| |
| static FARPROC WINAPI |
| MyGetProcAddress(HMODULE hModule, LPCSTR lpProcName); |
| |
| |
| static void |
| MyCreateProcessCommon(BOOL bRet, |
| DWORD dwCreationFlags, |
| LPPROCESS_INFORMATION lpProcessInformation) |
| { |
| if (!bRet) { |
| debugPrintf("inject: warning: failed to create child process\n"); |
| return; |
| } |
| |
| DWORD dwLastError = GetLastError(); |
| |
| if (isDifferentArch(lpProcessInformation->hProcess)) { |
| debugPrintf("inject: error: child process %lu has different architecture\n", |
| GetProcessId(lpProcessInformation->hProcess)); |
| } else { |
| char szDllPath[MAX_PATH]; |
| GetModuleFileNameA(g_hThisModule, szDllPath, sizeof szDllPath); |
| |
| if (!injectDll(lpProcessInformation->hProcess, szDllPath)) { |
| debugPrintf("inject: warning: failed to inject into child process %lu\n", |
| GetProcessId(lpProcessInformation->hProcess)); |
| } |
| } |
| |
| if (!(dwCreationFlags & CREATE_SUSPENDED)) { |
| ResumeThread(lpProcessInformation->hThread); |
| } |
| |
| SetLastError(dwLastError); |
| } |
| |
| static BOOL WINAPI |
| MyCreateProcessA(LPCSTR lpApplicationName, |
| LPSTR lpCommandLine, |
| LPSECURITY_ATTRIBUTES lpProcessAttributes, |
| LPSECURITY_ATTRIBUTES lpThreadAttributes, |
| BOOL bInheritHandles, |
| DWORD dwCreationFlags, |
| LPVOID lpEnvironment, |
| LPCSTR lpCurrentDirectory, |
| LPSTARTUPINFOA lpStartupInfo, |
| LPPROCESS_INFORMATION lpProcessInformation) |
| { |
| if (VERBOSITY >= 2) { |
| debugPrintf("inject: intercepting %s(\"%s\", \"%s\", ...)\n", |
| __FUNCTION__, |
| lpApplicationName, |
| lpCommandLine); |
| } |
| |
| BOOL bRet; |
| bRet = CreateProcessA(lpApplicationName, |
| lpCommandLine, |
| lpProcessAttributes, |
| lpThreadAttributes, |
| bInheritHandles, |
| dwCreationFlags | CREATE_SUSPENDED, |
| lpEnvironment, |
| lpCurrentDirectory, |
| lpStartupInfo, |
| lpProcessInformation); |
| |
| MyCreateProcessCommon(bRet, dwCreationFlags, lpProcessInformation); |
| |
| return bRet; |
| } |
| |
| static BOOL WINAPI |
| MyCreateProcessW(LPCWSTR lpApplicationName, |
| LPWSTR lpCommandLine, |
| LPSECURITY_ATTRIBUTES lpProcessAttributes, |
| LPSECURITY_ATTRIBUTES lpThreadAttributes, |
| BOOL bInheritHandles, |
| DWORD dwCreationFlags, |
| LPVOID lpEnvironment, |
| LPCWSTR lpCurrentDirectory, |
| LPSTARTUPINFOW lpStartupInfo, |
| LPPROCESS_INFORMATION lpProcessInformation) |
| { |
| if (VERBOSITY >= 2) { |
| debugPrintf("inject: intercepting %s(\"%S\", \"%S\", ...)\n", |
| __FUNCTION__, |
| lpApplicationName, |
| lpCommandLine); |
| } |
| |
| BOOL bRet; |
| bRet = CreateProcessW(lpApplicationName, |
| lpCommandLine, |
| lpProcessAttributes, |
| lpThreadAttributes, |
| bInheritHandles, |
| dwCreationFlags | CREATE_SUSPENDED, |
| lpEnvironment, |
| lpCurrentDirectory, |
| lpStartupInfo, |
| lpProcessInformation); |
| |
| MyCreateProcessCommon(bRet, dwCreationFlags, lpProcessInformation); |
| |
| return bRet; |
| } |
| |
| typedef BOOL |
| (WINAPI *PFNCREATEPROCESSASUSERW) (HANDLE, LPCWSTR, LPWSTR, |
| LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES, BOOL, DWORD, LPVOID, |
| LPCWSTR, LPSTARTUPINFOW, LPPROCESS_INFORMATION); |
| |
| static PFNCREATEPROCESSASUSERW pfnCreateProcessAsUserW; |
| |
| static BOOL WINAPI |
| MyCreateProcessAsUserW(HANDLE hToken, |
| LPCWSTR lpApplicationName, |
| LPWSTR lpCommandLine, |
| LPSECURITY_ATTRIBUTES lpProcessAttributes, |
| LPSECURITY_ATTRIBUTES lpThreadAttributes, |
| BOOL bInheritHandles, |
| DWORD dwCreationFlags, |
| LPVOID lpEnvironment, |
| LPCWSTR lpCurrentDirectory, |
| LPSTARTUPINFOW lpStartupInfo, |
| LPPROCESS_INFORMATION lpProcessInformation) |
| { |
| if (VERBOSITY >= 2) { |
| debugPrintf("inject: intercepting %s(\"%S\", \"%S\", ...)\n", |
| __FUNCTION__, |
| lpApplicationName, |
| lpCommandLine); |
| } |
| |
| // Certain WINE versions (at least 1.6.2) don't export |
| // kernel32.dll!CreateProcessAsUserW |
| assert(pfnCreateProcessAsUserW); |
| |
| BOOL bRet; |
| bRet = pfnCreateProcessAsUserW(hToken, |
| lpApplicationName, |
| lpCommandLine, |
| lpProcessAttributes, |
| lpThreadAttributes, |
| bInheritHandles, |
| dwCreationFlags | CREATE_SUSPENDED, |
| lpEnvironment, |
| lpCurrentDirectory, |
| lpStartupInfo, |
| lpProcessInformation); |
| |
| MyCreateProcessCommon(bRet, dwCreationFlags, lpProcessInformation); |
| |
| return bRet; |
| } |
| |
| static BOOL WINAPI |
| MyCreateProcessWithTokenW(HANDLE hToken, |
| DWORD dwLogonFlags, |
| LPCWSTR lpApplicationName, |
| LPWSTR lpCommandLine, |
| DWORD dwCreationFlags, |
| LPVOID lpEnvironment, |
| LPCWSTR lpCurrentDirectory, |
| LPSTARTUPINFOW lpStartupInfo, |
| LPPROCESS_INFORMATION lpProcessInformation) |
| { |
| if (VERBOSITY >= 2) { |
| debugPrintf("inject: intercepting %s(\"%S\", \"%S\", ...)\n", |
| __FUNCTION__, |
| lpApplicationName, |
| lpCommandLine); |
| } |
| |
| BOOL bRet; |
| bRet = CreateProcessWithTokenW(hToken, |
| dwLogonFlags, |
| lpApplicationName, |
| lpCommandLine, |
| dwCreationFlags | CREATE_SUSPENDED, |
| lpEnvironment, |
| lpCurrentDirectory, |
| lpStartupInfo, |
| lpProcessInformation); |
| |
| MyCreateProcessCommon(bRet, dwCreationFlags, lpProcessInformation); |
| |
| return bRet; |
| } |
| |
| static BOOL WINAPI |
| MyCreateProcessWithLogonW(LPCWSTR lpUsername, |
| LPCWSTR lpDomain, |
| LPCWSTR lpPassword, |
| DWORD dwLogonFlags, |
| LPCWSTR lpApplicationName, |
| LPWSTR lpCommandLine, |
| DWORD dwCreationFlags, |
| LPVOID lpEnvironment, |
| LPCWSTR lpCurrentDirectory, |
| LPSTARTUPINFOW lpStartupInfo, |
| LPPROCESS_INFORMATION lpProcessInformation) |
| { |
| if (VERBOSITY >= 2) { |
| debugPrintf("inject: intercepting %s(\"%S\", \"%S\", ...)\n", |
| __FUNCTION__, |
| lpApplicationName, |
| lpCommandLine); |
| } |
| |
| BOOL bRet; |
| bRet = CreateProcessWithLogonW(lpUsername, |
| lpDomain, |
| lpPassword, |
| dwLogonFlags, |
| lpApplicationName, |
| lpCommandLine, |
| dwCreationFlags | CREATE_SUSPENDED, |
| lpEnvironment, |
| lpCurrentDirectory, |
| lpStartupInfo, |
| lpProcessInformation); |
| |
| MyCreateProcessCommon(bRet, dwCreationFlags, lpProcessInformation); |
| |
| return bRet; |
| } |
| |
| |
| template< class T, class I > |
| inline T * |
| rvaToVa(HMODULE hModule, I rva) |
| { |
| assert(rva != 0); |
| return reinterpret_cast<T *>(reinterpret_cast<PBYTE>(hModule) + rva); |
| } |
| |
| |
| static const char * |
| getDescriptorName(HMODULE hModule, |
| const PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor) |
| { |
| return rvaToVa<const char>(hModule, pImportDescriptor->Name); |
| } |
| |
| static const char * |
| getDescriptorName(HMODULE hModule, |
| const PImgDelayDescr pDelayDescriptor) |
| { |
| if (pDelayDescriptor->grAttrs & dlattrRva) { |
| return rvaToVa<const char>(hModule, pDelayDescriptor->rvaDLLName); |
| } else { |
| #ifdef _WIN64 |
| assert(pDelayDescriptor->grAttrs & dlattrRva); |
| return "???"; |
| #else |
| // old-style, with ImgDelayDescr::szName being a LPCSTR |
| return reinterpret_cast<LPCSTR>(pDelayDescriptor->rvaDLLName); |
| #endif |
| } |
| } |
| |
| |
| static PIMAGE_OPTIONAL_HEADER |
| getOptionalHeader(HMODULE hModule, |
| const char *szModule) |
| { |
| PIMAGE_DOS_HEADER pDosHeader = reinterpret_cast<PIMAGE_DOS_HEADER>(hModule); |
| if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) { |
| debugPrintf("inject: warning: %s: unexpected DOS header magic (0x%04x)\n", |
| szModule, pDosHeader->e_magic); |
| return NULL; |
| } |
| PIMAGE_NT_HEADERS pNtHeaders = rvaToVa<IMAGE_NT_HEADERS>(hModule, pDosHeader->e_lfanew); |
| if (pNtHeaders->Signature != IMAGE_NT_SIGNATURE) { |
| debugPrintf("inject: warning: %s: unexpected NT header signature (0x%08lx)\n", |
| szModule, pNtHeaders->Signature); |
| return NULL; |
| } |
| |
| /* |
| * Handle gracefully DLL might have been loaded for resources, |
| * LOAD_LIBRARY_AS_DATAFILE. |
| */ |
| const WORD Machine = |
| #ifdef _WIN64 |
| IMAGE_FILE_MACHINE_AMD64 |
| #else |
| IMAGE_FILE_MACHINE_I386 |
| #endif |
| ; |
| if (pNtHeaders->FileHeader.Machine != Machine) { |
| debugPrintf("inject: warning: %s: ignoring different machine (0x%02x)\n", |
| szModule, pNtHeaders->FileHeader.Machine); |
| return nullptr; |
| } |
| if (pNtHeaders->FileHeader.SizeOfOptionalHeader < sizeof pNtHeaders->OptionalHeader) { |
| debugPrintf("inject: warning: %s: SizeOfOptionalHeader too small (%u)\n", |
| szModule, pNtHeaders->FileHeader.SizeOfOptionalHeader); |
| return nullptr; |
| } |
| |
| PIMAGE_OPTIONAL_HEADER pOptionalHeader = &pNtHeaders->OptionalHeader; |
| return pOptionalHeader; |
| } |
| |
| template< typename T > |
| static bool |
| getImageDirectoryEntry(HMODULE hModule, |
| const char *szModule, |
| UINT Entry, |
| T ** ppEntry) |
| { |
| MEMORY_BASIC_INFORMATION MemoryInfo; |
| if (VirtualQuery(hModule, &MemoryInfo, sizeof MemoryInfo) != sizeof MemoryInfo) { |
| debugPrintf("inject: warning: %s: VirtualQuery failed\n", szModule); |
| return false; |
| } |
| if (MemoryInfo.Protect & (PAGE_NOACCESS | PAGE_EXECUTE)) { |
| debugPrintf("inject: warning: %s: no read access (Protect = 0x%08lx)\n", szModule, MemoryInfo.Protect); |
| return false; |
| } |
| |
| PIMAGE_OPTIONAL_HEADER pOptionalHeader = getOptionalHeader(hModule, szModule); |
| if (!pOptionalHeader) { |
| return false; |
| } |
| |
| assert(pOptionalHeader->NumberOfRvaAndSizes > Entry); |
| if (pOptionalHeader->DataDirectory[Entry].Size == 0) { |
| return false; |
| } |
| |
| assert(pOptionalHeader->DataDirectory[Entry].Size >= sizeof(T)); |
| UINT_PTR ImportAddress = pOptionalHeader->DataDirectory[Entry].VirtualAddress; |
| if (!ImportAddress) { |
| return false; |
| } |
| |
| *ppEntry = rvaToVa<T>(hModule, ImportAddress); |
| return true; |
| } |
| |
| |
| static PIMAGE_IMPORT_DESCRIPTOR |
| getFirstImportDescriptor(HMODULE hModule, const char *szModule) |
| { |
| PIMAGE_IMPORT_DESCRIPTOR pEntry = nullptr; |
| return getImageDirectoryEntry(hModule, szModule, IMAGE_DIRECTORY_ENTRY_IMPORT, &pEntry) ? pEntry : nullptr; |
| } |
| |
| |
| static PImgDelayDescr |
| getDelayImportDescriptor(HMODULE hModule, const char *szModule) |
| { |
| PImgDelayDescr pEntry = nullptr; |
| return getImageDirectoryEntry(hModule, szModule, IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT, &pEntry) ? pEntry : nullptr; |
| } |
| |
| |
| static PIMAGE_EXPORT_DIRECTORY |
| getExportDescriptor(HMODULE hModule) |
| { |
| PIMAGE_EXPORT_DIRECTORY pEntry = nullptr; |
| return getImageDirectoryEntry(hModule, "(wrapper)", IMAGE_DIRECTORY_ENTRY_EXPORT, &pEntry) ? pEntry : nullptr; |
| } |
| |
| |
| static BOOL |
| replaceAddress(LPVOID *lpOldAddress, LPVOID lpNewAddress) |
| { |
| DWORD flOldProtect; |
| |
| if (*lpOldAddress == lpNewAddress) { |
| return TRUE; |
| } |
| |
| EnterCriticalSection(&g_Mutex); |
| |
| if (!(VirtualProtect(lpOldAddress, sizeof *lpOldAddress, PAGE_READWRITE, &flOldProtect))) { |
| LeaveCriticalSection(&g_Mutex); |
| return FALSE; |
| } |
| |
| *lpOldAddress = lpNewAddress; |
| |
| if (!(VirtualProtect(lpOldAddress, sizeof *lpOldAddress, flOldProtect, &flOldProtect))) { |
| LeaveCriticalSection(&g_Mutex); |
| return FALSE; |
| } |
| |
| LeaveCriticalSection(&g_Mutex); |
| return TRUE; |
| } |
| |
| |
| /* Return pointer to patcheable function address. |
| * |
| * See also: |
| * |
| * - An In-Depth Look into the Win32 Portable Executable File Format, Part 2, Matt Pietrek, |
| * http://msdn.microsoft.com/en-gb/magazine/cc301808.aspx |
| * |
| * - http://www.microsoft.com/msj/1298/hood/hood1298.aspx |
| * |
| */ |
| static LPVOID * |
| getPatchAddress(HMODULE hModule, |
| const char *szDescriptorName, |
| DWORD OriginalFirstThunk, |
| DWORD FirstThunk, |
| const char* pszFunctionName, |
| LPVOID lpOldAddress) |
| { |
| if (VERBOSITY >= 4) { |
| debugPrintf("inject: %s(%s, %s)\n", __FUNCTION__, |
| szDescriptorName, |
| pszFunctionName); |
| } |
| |
| PIMAGE_THUNK_DATA pThunkIAT = rvaToVa<IMAGE_THUNK_DATA>(hModule, FirstThunk); |
| |
| UINT_PTR pOldFunction = (UINT_PTR)lpOldAddress; |
| |
| PIMAGE_THUNK_DATA pThunk; |
| if (OriginalFirstThunk) { |
| pThunk = rvaToVa<IMAGE_THUNK_DATA>(hModule, OriginalFirstThunk); |
| } else { |
| pThunk = pThunkIAT; |
| } |
| |
| while (pThunk->u1.Function) { |
| if (OriginalFirstThunk == 0 || |
| pThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG) { |
| // No name -- search by the real function address |
| if (!pOldFunction) { |
| return NULL; |
| } |
| if (pThunkIAT->u1.Function == pOldFunction) { |
| return (LPVOID *)(&pThunkIAT->u1.Function); |
| } |
| } else { |
| // Search by name |
| PIMAGE_IMPORT_BY_NAME pImport = rvaToVa<IMAGE_IMPORT_BY_NAME>(hModule, pThunk->u1.AddressOfData); |
| const char* szName = (const char* )pImport->Name; |
| if (strcmp(pszFunctionName, szName) == 0) { |
| return (LPVOID *)(&pThunkIAT->u1.Function); |
| } |
| } |
| ++pThunk; |
| ++pThunkIAT; |
| } |
| |
| return NULL; |
| } |
| |
| |
| static LPVOID * |
| getPatchAddress(HMODULE hModule, |
| PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor, |
| const char* pszFunctionName, |
| LPVOID lpOldAddress) |
| { |
| assert(pImportDescriptor->TimeDateStamp != 0 || pImportDescriptor->Name != 0); |
| |
| return getPatchAddress(hModule, |
| getDescriptorName(hModule, pImportDescriptor), |
| pImportDescriptor->OriginalFirstThunk, |
| pImportDescriptor->FirstThunk, |
| pszFunctionName, |
| lpOldAddress); |
| } |
| |
| |
| // See |
| // http://www.microsoft.com/msj/1298/hood/hood1298.aspx |
| // http://msdn.microsoft.com/en-us/library/16b2dyk5.aspx |
| static LPVOID * |
| getPatchAddress(HMODULE hModule, |
| PImgDelayDescr pDelayDescriptor, |
| const char* pszFunctionName, |
| LPVOID lpOldAddress) |
| { |
| assert(pDelayDescriptor->rvaDLLName != 0); |
| |
| return getPatchAddress(hModule, |
| getDescriptorName(hModule, pDelayDescriptor), |
| pDelayDescriptor->rvaINT, |
| pDelayDescriptor->rvaIAT, |
| pszFunctionName, |
| lpOldAddress); |
| } |
| |
| |
| template< class T > |
| static BOOL |
| patchFunction(HMODULE hModule, |
| const char *szModule, |
| const char *pszDllName, |
| T pImportDescriptor, |
| const char *pszFunctionName, |
| LPVOID lpOldAddress, |
| LPVOID lpNewAddress) |
| { |
| LPVOID* lpPatchAddress = getPatchAddress(hModule, pImportDescriptor, pszFunctionName, lpOldAddress); |
| if (lpPatchAddress == NULL) { |
| return FALSE; |
| } |
| |
| if (*lpPatchAddress == lpNewAddress) { |
| return TRUE; |
| } |
| |
| DWORD Offset = (DWORD)(UINT_PTR)lpPatchAddress - (UINT_PTR)hModule; |
| if (VERBOSITY > 0) { |
| debugPrintf("inject: patching %s!0x%lx -> %s!%s\n", szModule, Offset, pszDllName, pszFunctionName); |
| } |
| |
| BOOL bRet; |
| bRet = replaceAddress(lpPatchAddress, lpNewAddress); |
| if (!bRet) { |
| debugPrintf("inject: failed to patch %s!0x%lx -> %s!%s\n", szModule, Offset, pszDllName, pszFunctionName); |
| } |
| |
| return bRet; |
| } |
| |
| |
| |
| struct StrCompare : public std::binary_function<const char *, const char *, bool> { |
| bool operator() (const char * s1, const char * s2) const { |
| return strcmp(s1, s2) < 0; |
| } |
| }; |
| |
| typedef std::map<const char *, LPVOID, StrCompare> FunctionMap; |
| |
| struct StrICompare : public std::binary_function<const char *, const char *, bool> { |
| bool operator() (const char * s1, const char * s2) const { |
| return stricmp(s1, s2) < 0; |
| } |
| }; |
| |
| struct Module { |
| bool bInternal; |
| FunctionMap functionMap; |
| }; |
| |
| typedef std::map<const char *, Module, StrICompare> ModulesMap; |
| |
| /* This is only modified at DLL_PROCESS_ATTACH time. */ |
| static ModulesMap modulesMap; |
| |
| |
| static inline bool |
| isMatchModuleName(const char *szModuleName) |
| { |
| ModulesMap::const_iterator modIt = modulesMap.find(szModuleName); |
| return modIt != modulesMap.end(); |
| } |
| |
| |
| /* Set of previously hooked modules */ |
| static std::set<HMODULE> |
| g_hHookedModules; |
| |
| |
| enum Action { |
| ACTION_HOOK, |
| ACTION_UNHOOK, |
| |
| }; |
| |
| |
| template< class T > |
| void |
| patchDescriptor(HMODULE hModule, |
| const char *szModule, |
| T pImportDescriptor, |
| Action action) |
| { |
| const char* szDescriptorName = getDescriptorName(hModule, pImportDescriptor); |
| |
| ModulesMap::const_iterator modIt = modulesMap.find(szDescriptorName); |
| if (modIt != modulesMap.end()) { |
| const char *szMatchModule = modIt->first; // same as szDescriptorName |
| const Module & module = modIt->second; |
| |
| const FunctionMap & functionMap = module.functionMap; |
| FunctionMap::const_iterator fnIt; |
| for (fnIt = functionMap.begin(); fnIt != functionMap.end(); ++fnIt) { |
| const char *szFunctionName = fnIt->first; |
| LPVOID lpHookAddress = fnIt->second; |
| |
| // Knowning the real address is useful when patching imports by ordinal |
| LPVOID lpRealAddress = NULL; |
| HMODULE hRealModule = GetModuleHandleA(szDescriptorName); |
| if (hRealModule) { |
| // FIXME: this assertion can fail when the wrapper name is the same as the original DLL |
| //assert(hRealModule != g_hHookModule); |
| if (hRealModule != g_hHookModule) { |
| lpRealAddress = (LPVOID)GetProcAddress(hRealModule, szFunctionName); |
| } |
| } |
| |
| LPVOID lpOldAddress = lpRealAddress; |
| LPVOID lpNewAddress = lpHookAddress; |
| |
| if (action == ACTION_UNHOOK) { |
| std::swap(lpOldAddress, lpNewAddress); |
| } |
| |
| BOOL bPatched; |
| bPatched = patchFunction(hModule, szModule, szMatchModule, pImportDescriptor, szFunctionName, lpOldAddress, lpNewAddress); |
| if (action == ACTION_HOOK && bPatched && !module.bInternal && pSharedMem) { |
| pSharedMem->bReplaced = TRUE; |
| } |
| } |
| } |
| } |
| |
| |
| static void |
| patchModule(HMODULE hModule, |
| const char *szModule, |
| Action action) |
| { |
| /* Never patch this module */ |
| if (hModule == g_hThisModule) { |
| return; |
| } |
| |
| /* Never patch our hook module */ |
| if (hModule == g_hHookModule) { |
| return; |
| } |
| |
| /* Hook modules only once */ |
| if (action == ACTION_HOOK) { |
| std::pair< std::set<HMODULE>::iterator, bool > ret; |
| EnterCriticalSection(&g_Mutex); |
| ret = g_hHookedModules.insert(hModule); |
| LeaveCriticalSection(&g_Mutex); |
| if (!ret.second) { |
| return; |
| } |
| } |
| |
| const char *szBaseName = getBaseName(szModule); |
| |
| /* Don't hook our replacement modules to avoid tracing internal APIs */ |
| /* XXX: is this really a good idea? */ |
| if (isMatchModuleName(szBaseName)) { |
| return; |
| } |
| |
| /* Leave these modules alone. |
| * |
| * Hooking other injection DLLs easily leads to infinite recursion (and |
| * stack overflow), especially when those libraries use techniques like |
| * modifying the hooked functions prolog (instead of patching IAT like we |
| * do). |
| * |
| * See also: |
| * - http://www.nynaeve.net/?p=62 |
| */ |
| if (stricmp(szBaseName, "kernel32.dll") == 0 || |
| stricmp(szBaseName, "AcLayers.dll") == 0 || |
| stricmp(szBaseName, "ConEmuHk.dll") == 0 || |
| stricmp(szBaseName, "gameoverlayrenderer.dll") == 0 || |
| stricmp(szBaseName, "gameoverlayrenderer64.dll") == 0) { |
| return; |
| } |
| |
| if (VERBOSITY > 0) { |
| debugPrintf("inject: found module %s\n", szModule); |
| } |
| |
| PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor = getFirstImportDescriptor(hModule, szModule); |
| if (pImportDescriptor) { |
| while (pImportDescriptor->FirstThunk) { |
| |
| patchDescriptor(hModule, szModule, pImportDescriptor, action); |
| |
| ++pImportDescriptor; |
| } |
| } |
| |
| PImgDelayDescr pDelayDescriptor = getDelayImportDescriptor(hModule, szModule); |
| if (pDelayDescriptor) { |
| while (pDelayDescriptor->rvaDLLName) { |
| if (pDelayDescriptor->grAttrs > 1) { |
| debugPrintf("inject: warning: ignoring delay import section (grAttrs = 0x%08lx)\n", |
| pDelayDescriptor->grAttrs); |
| break; |
| } |
| |
| if (VERBOSITY > 1) { |
| const char* szName = getDescriptorName(hModule, pDelayDescriptor); |
| debugPrintf("inject: found %sdelay-load import entry for module %s\n", |
| pDelayDescriptor->grAttrs & dlattrRva ? "" : "old-style ", |
| szName); |
| } |
| |
| #ifdef _WIN64 |
| assert(pDelayDescriptor->grAttrs & dlattrRva); |
| #endif |
| |
| patchDescriptor(hModule, szModule, pDelayDescriptor, action); |
| |
| ++pDelayDescriptor; |
| } |
| } |
| } |
| |
| |
| static void |
| patchAllModules(Action action) |
| { |
| HANDLE hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetCurrentProcessId()); |
| if (hModuleSnap == INVALID_HANDLE_VALUE) { |
| return; |
| } |
| |
| MODULEENTRY32 me32; |
| me32.dwSize = sizeof me32; |
| if (Module32First(hModuleSnap, &me32)) { |
| do { |
| patchModule(me32.hModule, me32.szExePath, action); |
| } while (Module32Next(hModuleSnap, &me32)); |
| } |
| |
| CloseHandle(hModuleSnap); |
| } |
| |
| |
| static HMODULE WINAPI |
| MyLoadLibraryA(LPCSTR lpLibFileName) |
| { |
| HMODULE hModule = LoadLibraryA(lpLibFileName); |
| DWORD dwLastError = GetLastError(); |
| |
| if (VERBOSITY >= 2) { |
| debugPrintf("inject: intercepting %s(\"%s\") = 0x%p\n", |
| __FUNCTION__ + 2, lpLibFileName, hModule); |
| } |
| |
| if (VERBOSITY > 0) { |
| const char *szBaseName = getBaseName(lpLibFileName); |
| if (isMatchModuleName(szBaseName)) { |
| if (VERBOSITY < 2) { |
| debugPrintf("inject: intercepting %s(\"%s\")\n", __FUNCTION__, lpLibFileName); |
| } |
| #ifdef __GNUC__ |
| void *caller = __builtin_return_address (0); |
| |
| HMODULE hModule = 0; |
| BOOL bRet = GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | |
| GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, |
| (LPCTSTR)caller, |
| &hModule); |
| assert(bRet); |
| char szCaller[MAX_PATH]; |
| DWORD dwRet = GetModuleFileNameA(hModule, szCaller, sizeof szCaller); |
| assert(dwRet); |
| debugPrintf("inject: called from %s\n", szCaller); |
| #endif |
| } |
| } |
| |
| // Hook all new modules (and not just this one, to pick up any dependencies) |
| patchAllModules(ACTION_HOOK); |
| |
| SetLastError(dwLastError); |
| return hModule; |
| } |
| |
| static HMODULE WINAPI |
| MyLoadLibraryW(LPCWSTR lpLibFileName) |
| { |
| HMODULE hModule = LoadLibraryW(lpLibFileName); |
| DWORD dwLastError = GetLastError(); |
| |
| if (VERBOSITY >= 2) { |
| debugPrintf("inject: intercepting %s(L\"%S\") = 0x%p\n", |
| __FUNCTION__ + 2, lpLibFileName, hModule); |
| } |
| |
| // Hook all new modules (and not just this one, to pick up any dependencies) |
| patchAllModules(ACTION_HOOK); |
| |
| SetLastError(dwLastError); |
| return hModule; |
| } |
| |
| #ifndef LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR |
| #define LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR 0x00000100 |
| #endif |
| #ifndef LOAD_LIBRARY_SEARCH_APPLICATION_DIR |
| #define LOAD_LIBRARY_SEARCH_APPLICATION_DIR 0x00000200 |
| #endif |
| #ifndef LOAD_LIBRARY_SEARCH_USER_DIRS |
| #define LOAD_LIBRARY_SEARCH_USER_DIRS 0x00000400 |
| #endif |
| #ifndef LOAD_LIBRARY_SEARCH_SYSTEM32 |
| #define LOAD_LIBRARY_SEARCH_SYSTEM32 0x00000800 |
| #endif |
| #ifndef LOAD_LIBRARY_SEARCH_DEFAULT_DIRS |
| #define LOAD_LIBRARY_SEARCH_DEFAULT_DIRS 0x00001000 |
| #endif |
| |
| static inline DWORD |
| adjustFlags(DWORD dwFlags) |
| { |
| /* |
| * XXX: LoadLibraryEx seems to interpret "application directory" in respect |
| * to the module that's calling it. So when the application restricts the |
| * search path to application directory via |
| * LOAD_LIBRARY_SEARCH_APPLICATION_DIR or LOAD_LIBRARY_SEARCH_DEFAULT_DIRS |
| * flags, kernel32.dll ends up searching on the directory of the inject.dll |
| * module. |
| * |
| * XXX: What about SetDefaultDllDirectories? |
| * |
| */ |
| if (dwFlags & (LOAD_LIBRARY_SEARCH_APPLICATION_DIR | |
| LOAD_LIBRARY_SEARCH_DEFAULT_DIRS)) { |
| dwFlags &= ~(LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | |
| LOAD_LIBRARY_SEARCH_APPLICATION_DIR | |
| LOAD_LIBRARY_SEARCH_USER_DIRS | |
| LOAD_LIBRARY_SEARCH_SYSTEM32 | |
| LOAD_LIBRARY_SEARCH_DEFAULT_DIRS); |
| } |
| |
| return dwFlags; |
| } |
| |
| static HMODULE WINAPI |
| MyLoadLibraryExA(LPCSTR lpLibFileName, HANDLE hFile, DWORD dwFlags) |
| { |
| HMODULE hModule = LoadLibraryExA(lpLibFileName, hFile, adjustFlags(dwFlags)); |
| DWORD dwLastError = GetLastError(); |
| |
| if (VERBOSITY >= 2) { |
| debugPrintf("inject: intercepting %s(\"%s\", 0x%p, 0x%lx) = 0x%p\n", |
| __FUNCTION__ + 2, lpLibFileName, hFile, dwFlags, hModule); |
| } |
| |
| // Hook all new modules (and not just this one, to pick up any dependencies) |
| if ((dwFlags & (DONT_RESOLVE_DLL_REFERENCES | |
| LOAD_LIBRARY_AS_DATAFILE | |
| LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE)) == 0) { |
| patchAllModules(ACTION_HOOK); |
| } |
| |
| SetLastError(dwLastError); |
| return hModule; |
| } |
| |
| static HMODULE WINAPI |
| MyLoadLibraryExW(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags) |
| { |
| HMODULE hModule = LoadLibraryExW(lpLibFileName, hFile, adjustFlags(dwFlags)); |
| DWORD dwLastError = GetLastError(); |
| |
| if (VERBOSITY >= 2) { |
| debugPrintf("inject: intercepting %s(L\"%S\", 0x%p, 0x%lx) = 0x%p\n", |
| __FUNCTION__ + 2, lpLibFileName, hFile, dwFlags, hModule); |
| } |
| |
| // Hook all new modules (and not just this one, to pick up any dependencies) |
| if ((dwFlags & (DONT_RESOLVE_DLL_REFERENCES | |
| LOAD_LIBRARY_AS_DATAFILE | |
| LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE)) == 0) { |
| patchAllModules(ACTION_HOOK); |
| } |
| |
| SetLastError(dwLastError); |
| return hModule; |
| } |
| |
| |
| static void |
| logGetProcAddress(HMODULE hModule, LPCSTR lpProcName) { |
| if (HIWORD(lpProcName) == 0) { |
| debugPrintf("inject: intercepting %s(0x%p, %u)\n", "GetProcAddress", hModule, LOWORD(lpProcName)); |
| } else { |
| debugPrintf("inject: intercepting %s(0x%p, \"%s\")\n", "GetProcAddress", hModule, lpProcName); |
| } |
| } |
| |
| static FARPROC WINAPI |
| MyGetProcAddress(HMODULE hModule, LPCSTR lpProcName) { |
| |
| if (VERBOSITY >= 3) { |
| /* XXX this can cause segmentation faults */ |
| logGetProcAddress(hModule, lpProcName); |
| } |
| |
| if (!NOOP) { |
| char szModule[MAX_PATH]; |
| DWORD dwRet = GetModuleFileNameA(hModule, szModule, sizeof szModule); |
| assert(dwRet); |
| const char *szBaseName = getBaseName(szModule); |
| |
| ModulesMap::const_iterator modIt; |
| modIt = modulesMap.find(szBaseName); |
| if (modIt != modulesMap.end()) { |
| if (VERBOSITY > 1 && VERBOSITY < 3) { |
| logGetProcAddress(hModule, lpProcName); |
| } |
| |
| const Module & module = modIt->second; |
| const FunctionMap & functionMap = module.functionMap; |
| FunctionMap::const_iterator fnIt; |
| |
| if (HIWORD(lpProcName) == 0) { |
| FARPROC proc = GetProcAddress(hModule, lpProcName); |
| if (!proc) { |
| return proc; |
| } |
| |
| for (fnIt = functionMap.begin(); fnIt != functionMap.end(); ++fnIt) { |
| FARPROC pRealProc = GetProcAddress(hModule, fnIt->first); |
| if (proc == pRealProc) { |
| if (VERBOSITY > 0) { |
| debugPrintf("inject: replacing %s!%s\n", szBaseName, lpProcName); |
| } |
| return (FARPROC)fnIt->second; |
| } |
| |
| } |
| |
| debugPrintf("inject: ignoring %s!@%u\n", szBaseName, LOWORD(lpProcName)); |
| |
| return proc; |
| } |
| |
| fnIt = functionMap.find(lpProcName); |
| |
| if (fnIt != functionMap.end()) { |
| LPVOID pProcAddress = fnIt->second; |
| if (VERBOSITY > 0) { |
| debugPrintf("inject: replacing %s!%s\n", szBaseName, lpProcName); |
| } |
| if (!module.bInternal && pSharedMem) { |
| pSharedMem->bReplaced = TRUE; |
| } |
| return (FARPROC)pProcAddress; |
| } else { |
| if (VERBOSITY > 0 && !module.bInternal) { |
| debugPrintf("inject: ignoring %s!%s\n", szBaseName, lpProcName); |
| } |
| } |
| } |
| } |
| |
| return GetProcAddress(hModule, lpProcName); |
| } |
| |
| |
| static BOOL WINAPI |
| MyFreeLibrary(HMODULE hModule) |
| { |
| if (VERBOSITY >= 2) { |
| debugPrintf("inject: intercepting %s(0x%p)\n", __FUNCTION__, hModule); |
| } |
| |
| BOOL bRet = FreeLibrary(hModule); |
| DWORD dwLastError = GetLastError(); |
| |
| std::set<HMODULE> hCurrentModules; |
| HANDLE hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetCurrentProcessId()); |
| if (hModuleSnap != INVALID_HANDLE_VALUE) { |
| MODULEENTRY32 me32; |
| me32.dwSize = sizeof me32; |
| if (Module32First(hModuleSnap, &me32)) { |
| do { |
| hCurrentModules.insert(me32.hModule); |
| } while (Module32Next(hModuleSnap, &me32)); |
| } |
| CloseHandle(hModuleSnap); |
| } |
| |
| // Clear the modules that have been freed |
| EnterCriticalSection(&g_Mutex); |
| std::set<HMODULE> hIntersectedModules; |
| std::set_intersection(g_hHookedModules.begin(), g_hHookedModules.end(), |
| hCurrentModules.begin(), hCurrentModules.end(), |
| std::inserter(hIntersectedModules, hIntersectedModules.begin())); |
| g_hHookedModules = std::move(hIntersectedModules); |
| LeaveCriticalSection(&g_Mutex); |
| |
| SetLastError(dwLastError); |
| return bRet; |
| } |
| |
| |
| static void |
| registerLibraryLoaderHooks(const char *szMatchModule) |
| { |
| Module & module = modulesMap[szMatchModule]; |
| module.bInternal = true; |
| FunctionMap & functionMap = module.functionMap; |
| functionMap["LoadLibraryA"] = (LPVOID)MyLoadLibraryA; |
| functionMap["LoadLibraryW"] = (LPVOID)MyLoadLibraryW; |
| functionMap["LoadLibraryExA"] = (LPVOID)MyLoadLibraryExA; |
| functionMap["LoadLibraryExW"] = (LPVOID)MyLoadLibraryExW; |
| functionMap["GetProcAddress"] = (LPVOID)MyGetProcAddress; |
| functionMap["FreeLibrary"] = (LPVOID)MyFreeLibrary; |
| } |
| |
| static void |
| registerProcessThreadsHooks(const char *szMatchModule) |
| { |
| Module & module = modulesMap[szMatchModule]; |
| module.bInternal = true; |
| FunctionMap & functionMap = module.functionMap; |
| functionMap["CreateProcessA"] = (LPVOID)MyCreateProcessA; |
| functionMap["CreateProcessW"] = (LPVOID)MyCreateProcessW; |
| // NOTE: CreateProcessAsUserA is implemented by advapi32.dll |
| functionMap["CreateProcessAsUserW"] = (LPVOID)MyCreateProcessAsUserW; |
| functionMap["CreateProcessWithTokenW"] = (LPVOID)MyCreateProcessWithTokenW; |
| functionMap["CreateProcessWithLogonW"] = (LPVOID)MyCreateProcessWithLogonW; |
| } |
| |
| static void |
| registerModuleHooks(const char *szMatchModule, HMODULE hReplaceModule) |
| { |
| Module & module = modulesMap[szMatchModule]; |
| module.bInternal = false; |
| FunctionMap & functionMap = module.functionMap; |
| |
| PIMAGE_EXPORT_DIRECTORY pExportDescriptor = getExportDescriptor(hReplaceModule); |
| assert(pExportDescriptor); |
| |
| DWORD *pAddressOfNames = (DWORD *)((BYTE *)hReplaceModule + pExportDescriptor->AddressOfNames); |
| for (DWORD i = 0; i < pExportDescriptor->NumberOfNames; ++i) { |
| const char *szFunctionName = (const char *)((BYTE *)hReplaceModule + pAddressOfNames[i]); |
| LPVOID lpNewAddress = (LPVOID)GetProcAddress(hReplaceModule, szFunctionName); |
| assert(lpNewAddress); |
| |
| functionMap[szFunctionName] = lpNewAddress; |
| } |
| } |
| |
| static void |
| dumpRegisteredHooks(void) |
| { |
| if (VERBOSITY > 1) { |
| ModulesMap::const_iterator modIt; |
| for (modIt = modulesMap.begin(); modIt != modulesMap.end(); ++modIt) { |
| const char *szMatchModule = modIt->first; |
| const Module & module = modIt->second; |
| const FunctionMap & functionMap = module.functionMap; |
| FunctionMap::const_iterator fnIt; |
| for (fnIt = functionMap.begin(); fnIt != functionMap.end(); ++fnIt) { |
| const char *szFunctionName = fnIt->first; |
| debugPrintf("inject: registered hook for %s!%s%s\n", |
| szMatchModule, szFunctionName, |
| module.bInternal ? " (internal)" : ""); |
| } |
| } |
| } |
| } |
| |
| |
| EXTERN_C BOOL WINAPI |
| DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) |
| { |
| const char *szNewDllName = NULL; |
| const char *szNewDllBaseName; |
| |
| switch (fdwReason) { |
| case DLL_PROCESS_ATTACH: |
| InitializeCriticalSection(&g_Mutex); |
| |
| g_hThisModule = hinstDLL; |
| |
| /* |
| * Calling LoadLibrary inside DllMain is strongly discouraged. But it |
| * works quite well, provided that the loaded DLL does not require or do |
| * anything special in its DllMain, which seems to be the general case. |
| * |
| * See also: |
| * - http://stackoverflow.com/questions/4370812/calling-loadlibrary-from-dllmain |
| * - http://msdn.microsoft.com/en-us/library/ms682583 |
| */ |
| |
| if (!USE_SHARED_MEM) { |
| szNewDllName = getenv("INJECT_DLL"); |
| if (!szNewDllName) { |
| debugPrintf("inject: warning: INJECT_DLL not set\n"); |
| return FALSE; |
| } |
| } else { |
| SharedMem *pSharedMem = OpenSharedMemory(NULL); |
| if (!pSharedMem) { |
| debugPrintf("inject: error: failed to open shared memory\n"); |
| return FALSE; |
| } |
| |
| VERBOSITY = pSharedMem->cVerbosity; |
| |
| static char szSharedMemCopy[MAX_PATH]; |
| strncpy(szSharedMemCopy, pSharedMem->szDllName, _countof(szSharedMemCopy) - 1); |
| szSharedMemCopy[_countof(szSharedMemCopy) - 1] = '\0'; |
| |
| szNewDllName = szSharedMemCopy; |
| } |
| |
| if (VERBOSITY > 0) { |
| debugPrintf("inject: DLL_PROCESS_ATTACH\n"); |
| } |
| |
| if (VERBOSITY > 0) { |
| char szProcess[MAX_PATH]; |
| GetModuleFileNameA(NULL, szProcess, sizeof szProcess); |
| debugPrintf("inject: attached to process %s\n", szProcess); |
| } |
| |
| if (VERBOSITY > 0) { |
| debugPrintf("inject: loading %s\n", szNewDllName); |
| } |
| |
| g_hHookModule = LoadLibraryA(szNewDllName); |
| if (!g_hHookModule) { |
| debugPrintf("inject: warning: failed to load %s\n", szNewDllName); |
| return FALSE; |
| } |
| |
| // Ensure we use kernel32.dll's CreateProcessAsUserW, and not advapi32.dll's. |
| { |
| HMODULE hKernel32 = GetModuleHandleA("kernel32.dll"); |
| assert(hKernel32); |
| pfnCreateProcessAsUserW = (PFNCREATEPROCESSASUSERW)GetProcAddress(hKernel32, "CreateProcessAsUserW"); |
| } |
| |
| /* |
| * Hook kernel32.dll functions, and its respective Windows API Set. |
| * |
| * https://msdn.microsoft.com/en-us/library/dn505783.aspx (Windows 8.1) |
| * https://msdn.microsoft.com/en-us/library/hh802935.aspx (Windows 8) |
| * https://docs.microsoft.com/en-us/uwp/win32-and-com/win32-apis |
| */ |
| |
| registerLibraryLoaderHooks("kernel32.dll"); |
| registerLibraryLoaderHooks("api-ms-win-core-libraryloader-l1-1-0.dll"); |
| registerLibraryLoaderHooks("api-ms-win-core-libraryloader-l1-1-1.dll"); |
| registerLibraryLoaderHooks("api-ms-win-core-libraryloader-l1-2-0.dll"); |
| registerLibraryLoaderHooks("api-ms-win-core-libraryloader-l1-2-1.dll"); |
| registerLibraryLoaderHooks("api-ms-win-core-libraryloader-l1-2-2.dll"); |
| registerLibraryLoaderHooks("api-ms-win-core-libraryloader-l2-1-0.dll"); |
| registerLibraryLoaderHooks("api-ms-win-core-kernel32-legacy-l1-1-0.dll"); |
| registerLibraryLoaderHooks("api-ms-win-core-kernel32-legacy-l1-1-1.dll"); |
| registerLibraryLoaderHooks("api-ms-win-core-kernel32-legacy-l1-1-2.dll"); |
| |
| registerProcessThreadsHooks("kernel32.dll"); |
| registerProcessThreadsHooks("api-ms-win-core-processthreads-l1-1-0.dll"); |
| registerProcessThreadsHooks("api-ms-win-core-processthreads-l1-1-1.dll"); |
| registerProcessThreadsHooks("api-ms-win-core-processthreads-l1-1-2.dll"); |
| registerProcessThreadsHooks("api-ms-win-core-processthreads-l1-1-2.dll"); |
| registerProcessThreadsHooks("api-ms-win-core-processthreads-l1-1-3.dll"); |
| |
| szNewDllBaseName = getBaseName(szNewDllName); |
| if (stricmp(szNewDllBaseName, "dxgitrace.dll") == 0) { |
| registerModuleHooks("dxgi.dll", g_hHookModule); |
| registerModuleHooks("d3d10.dll", g_hHookModule); |
| registerModuleHooks("d3d10_1.dll", g_hHookModule); |
| registerModuleHooks("d3d11.dll", g_hHookModule); |
| registerModuleHooks("d3d9.dll", g_hHookModule); // for D3DPERF_* |
| registerModuleHooks("dcomp.dll", g_hHookModule); |
| } else if (stricmp(szNewDllBaseName, "d3d9.dll") == 0) { |
| registerModuleHooks("d3d9.dll", g_hHookModule); |
| registerModuleHooks("dxva2.dll", g_hHookModule); |
| } else if (stricmp(szNewDllBaseName, "d2d1trace.dll") == 0) { |
| registerModuleHooks("d2d1.dll", g_hHookModule); |
| registerModuleHooks("dwrite.dll", g_hHookModule); |
| } else { |
| registerModuleHooks(szNewDllBaseName, g_hHookModule); |
| } |
| |
| dumpRegisteredHooks(); |
| |
| patchAllModules(ACTION_HOOK); |
| break; |
| |
| case DLL_THREAD_ATTACH: |
| break; |
| |
| case DLL_THREAD_DETACH: |
| break; |
| |
| case DLL_PROCESS_DETACH: |
| if (VERBOSITY > 0) { |
| debugPrintf("inject: DLL_PROCESS_DETACH\n"); |
| } |
| |
| assert(!lpvReserved); |
| |
| patchAllModules(ACTION_UNHOOK); |
| |
| if (g_hHookModule) { |
| FreeLibrary(g_hHookModule); |
| } |
| break; |
| } |
| return TRUE; |
| } |
| |
| |
| /* |
| * Prevent the C/C++ runtime from destroying things when the program |
| * terminates. |
| * |
| * There is no effective way to control the order DLLs receive |
| * DLL_PROCESS_DETACH -- patched DLLs might get detacched after we are --, and |
| * unpatching our hooks doesn't always work. So instead just do nothing (and |
| * prevent C/C++ runtime from doing anything too), so our hooks can still work |
| * after we are dettached. |
| */ |
| |
| #ifdef _MSC_VER |
| # define DLLMAIN_CRT_STARTUP _DllMainCRTStartup |
| #else |
| # define DLLMAIN_CRT_STARTUP DllMainCRTStartup |
| # pragma GCC optimize ("no-stack-protector") |
| #endif |
| |
| EXTERN_C BOOL WINAPI |
| DLLMAIN_CRT_STARTUP(HANDLE hDllHandle, DWORD dwReason, LPVOID lpvReserved); |
| |
| EXTERN_C BOOL WINAPI |
| DllMainStartup(HANDLE hDllHandle, DWORD dwReason, LPVOID lpvReserved) |
| { |
| if (dwReason == DLL_PROCESS_DETACH && lpvReserved) { |
| if (VERBOSITY > 0) { |
| debugPrintf("inject: DLL_PROCESS_DETACH\n"); |
| } |
| return TRUE; |
| } |
| |
| return DLLMAIN_CRT_STARTUP(hDllHandle, dwReason, lpvReserved); |
| } |