michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: michael@0: #if defined(_WIN32) && (defined(_M_IX86) || defined(_M_X64)) michael@0: // This is the .cpp file where the globals live michael@0: #define DHW_IMPLEMENT_GLOBALS michael@0: #include michael@0: #include "prprf.h" michael@0: #include "prlog.h" michael@0: #include "plstr.h" michael@0: #include "prlock.h" michael@0: #include "nscore.h" michael@0: #include "nsDebugHelpWin32.h" michael@0: #else michael@0: #error "nsDebugHelpWin32.cpp should only be built in Win32 x86/x64 builds" michael@0: #endif michael@0: michael@0: michael@0: michael@0: /***************************************************************************/ michael@0: michael@0: michael@0: PRLock* DHWImportHooker::gLock = nullptr; michael@0: DHWImportHooker* DHWImportHooker::gHooks = nullptr; michael@0: decltype(GetProcAddress)* DHWImportHooker::gRealGetProcAddress = nullptr; michael@0: michael@0: michael@0: static bool michael@0: dhwEnsureImageHlpInitialized() michael@0: { michael@0: static bool gInitialized = false; michael@0: static bool gTried = false; michael@0: michael@0: if (!gInitialized && !gTried) { michael@0: gTried = true; michael@0: HMODULE module = ::LoadLibrary("DBGHELP.DLL"); michael@0: if (!module) { michael@0: DWORD dw = GetLastError(); michael@0: printf("DumpStack Error: DBGHELP.DLL wasn't found. GetLastError() returned 0x%8.8X\n" michael@0: " This DLL is needed for succeessfully implementing trace-malloc.\n" michael@0: " This dll ships by default on Win2k. Disabling trace-malloc functionality.\n" michael@0: , dw); michael@0: return false; michael@0: } michael@0: michael@0: #define INIT_PROC(typename_, name_) \ michael@0: dhw##name_ = (decltype(name_)*) ::GetProcAddress(module, #name_); \ michael@0: if(!dhw##name_) return false; michael@0: michael@0: #ifdef _WIN64 michael@0: INIT_PROC(ENUMERATELOADEDMODULES64, EnumerateLoadedModules64); michael@0: #else michael@0: INIT_PROC(ENUMERATELOADEDMODULES, EnumerateLoadedModules); michael@0: #endif michael@0: INIT_PROC(IMAGEDIRECTORYENTRYTODATA, ImageDirectoryEntryToData); michael@0: michael@0: #undef INIT_PROC michael@0: michael@0: gInitialized = true; michael@0: } michael@0: michael@0: return gInitialized; michael@0: } michael@0: michael@0: michael@0: DHWImportHooker& michael@0: DHWImportHooker::getGetProcAddressHooker() michael@0: { michael@0: static DHWImportHooker gGetProcAddress("Kernel32.dll", "GetProcAddress", michael@0: (PROC)DHWImportHooker::GetProcAddress); michael@0: return gGetProcAddress; michael@0: } michael@0: michael@0: michael@0: DHWImportHooker& michael@0: DHWImportHooker::getLoadLibraryWHooker() michael@0: { michael@0: static DHWImportHooker gLoadLibraryW("Kernel32.dll", "LoadLibraryW", michael@0: (PROC)DHWImportHooker::LoadLibraryW); michael@0: return gLoadLibraryW; michael@0: } michael@0: michael@0: DHWImportHooker& michael@0: DHWImportHooker::getLoadLibraryExWHooker() michael@0: { michael@0: static DHWImportHooker gLoadLibraryExW("Kernel32.dll", "LoadLibraryExW", michael@0: (PROC)DHWImportHooker::LoadLibraryExW); michael@0: return gLoadLibraryExW; michael@0: } michael@0: michael@0: DHWImportHooker& michael@0: DHWImportHooker::getLoadLibraryAHooker() michael@0: { michael@0: static DHWImportHooker gLoadLibraryA("Kernel32.dll", "LoadLibraryA", michael@0: (PROC)DHWImportHooker::LoadLibraryA); michael@0: return gLoadLibraryA; michael@0: } michael@0: michael@0: DHWImportHooker& michael@0: DHWImportHooker::getLoadLibraryExAHooker() michael@0: { michael@0: static DHWImportHooker gLoadLibraryExA("Kernel32.dll", "LoadLibraryExA", michael@0: (PROC)DHWImportHooker::LoadLibraryExA); michael@0: return gLoadLibraryExA; michael@0: } michael@0: michael@0: michael@0: static HMODULE ThisModule() michael@0: { michael@0: MEMORY_BASIC_INFORMATION info; michael@0: return VirtualQuery(ThisModule, &info, sizeof(info)) ? michael@0: (HMODULE) info.AllocationBase : nullptr; michael@0: } michael@0: michael@0: DHWImportHooker::DHWImportHooker(const char* aModuleName, michael@0: const char* aFunctionName, michael@0: PROC aHook, michael@0: bool aExcludeOurModule /* = false */) michael@0: : mNext(nullptr), michael@0: mModuleName(aModuleName), michael@0: mFunctionName(aFunctionName), michael@0: mOriginal(nullptr), michael@0: mHook(aHook), michael@0: mIgnoreModule(aExcludeOurModule ? ThisModule() : nullptr), michael@0: mHooking(true) michael@0: { michael@0: //printf("DHWImportHooker hooking %s, function %s\n",aModuleName, aFunctionName); michael@0: michael@0: if(!gLock) michael@0: gLock = PR_NewLock(); michael@0: PR_Lock(gLock); michael@0: michael@0: dhwEnsureImageHlpInitialized(); // for the extra ones we care about. michael@0: michael@0: if(!gRealGetProcAddress) michael@0: gRealGetProcAddress = ::GetProcAddress; michael@0: michael@0: mOriginal = gRealGetProcAddress(::GetModuleHandleA(aModuleName), michael@0: aFunctionName), michael@0: michael@0: mNext = gHooks; michael@0: gHooks = this; michael@0: michael@0: PatchAllModules(); michael@0: michael@0: PR_Unlock(gLock); michael@0: } michael@0: michael@0: DHWImportHooker::~DHWImportHooker() michael@0: { michael@0: PR_Lock(gLock); michael@0: michael@0: mHooking = false; michael@0: PatchAllModules(); michael@0: michael@0: for (DHWImportHooker **cur = &gHooks; michael@0: (PR_ASSERT(*cur), *cur); /* assert that we find this */ michael@0: cur = &(*cur)->mNext) michael@0: { michael@0: if (*cur == this) michael@0: { michael@0: *cur = mNext; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: if(!gHooks) michael@0: { michael@0: PRLock* theLock = gLock; michael@0: gLock = nullptr; michael@0: PR_Unlock(theLock); michael@0: PR_DestroyLock(theLock); michael@0: } michael@0: if (gLock) michael@0: PR_Unlock(gLock); michael@0: } michael@0: michael@0: #ifdef _WIN64 michael@0: static BOOL CALLBACK ModuleEnumCallback(PCSTR ModuleName, michael@0: DWORD64 ModuleBase, michael@0: ULONG ModuleSize, michael@0: PVOID UserContext) michael@0: #else michael@0: static BOOL CALLBACK ModuleEnumCallback(PCSTR ModuleName, michael@0: ULONG ModuleBase, michael@0: ULONG ModuleSize, michael@0: PVOID UserContext) michael@0: #endif michael@0: { michael@0: //printf("Module Name %s\n",ModuleName); michael@0: DHWImportHooker* self = (DHWImportHooker*) UserContext; michael@0: HMODULE aModule = (HMODULE) ModuleBase; michael@0: return self->PatchOneModule(aModule, ModuleName); michael@0: } michael@0: michael@0: bool michael@0: DHWImportHooker::PatchAllModules() michael@0: { michael@0: // Need to cast to PENUMLOADED_MODULES_CALLBACK because the michael@0: // constness of the first parameter of PENUMLOADED_MODULES_CALLBACK michael@0: // varies over SDK versions (from non-const to const over time). michael@0: // See bug 391848 and bug 415426. michael@0: #ifdef _WIN64 michael@0: return dhwEnumerateLoadedModules64(::GetCurrentProcess(), michael@0: (PENUMLOADED_MODULES_CALLBACK64)ModuleEnumCallback, this); michael@0: #else michael@0: return dhwEnumerateLoadedModules(::GetCurrentProcess(), michael@0: (PENUMLOADED_MODULES_CALLBACK)ModuleEnumCallback, this); michael@0: #endif michael@0: } michael@0: michael@0: bool michael@0: DHWImportHooker::PatchOneModule(HMODULE aModule, const char* name) michael@0: { michael@0: if(aModule == mIgnoreModule) michael@0: { michael@0: return true; michael@0: } michael@0: michael@0: // do the fun stuff... michael@0: michael@0: PIMAGE_IMPORT_DESCRIPTOR desc; michael@0: ULONG size; michael@0: michael@0: desc = (PIMAGE_IMPORT_DESCRIPTOR) michael@0: dhwImageDirectoryEntryToData(aModule, true, michael@0: IMAGE_DIRECTORY_ENTRY_IMPORT, &size); michael@0: michael@0: if(!desc) michael@0: { michael@0: return true; michael@0: } michael@0: michael@0: for(; desc->Name; desc++) michael@0: { michael@0: const char* entryModuleName = (const char*) michael@0: ((char*)aModule + desc->Name); michael@0: if(!lstrcmpi(entryModuleName, mModuleName)) michael@0: break; michael@0: } michael@0: michael@0: if(!desc->Name) michael@0: { michael@0: return true; michael@0: } michael@0: michael@0: PIMAGE_THUNK_DATA thunk = (PIMAGE_THUNK_DATA) michael@0: ((char*) aModule + desc->FirstThunk); michael@0: michael@0: for(; thunk->u1.Function; thunk++) michael@0: { michael@0: PROC original; michael@0: PROC replacement; michael@0: michael@0: if(mHooking) michael@0: { michael@0: original = mOriginal; michael@0: replacement = mHook; michael@0: } michael@0: else michael@0: { michael@0: original = mHook; michael@0: replacement = mOriginal; michael@0: } michael@0: michael@0: PROC* ppfn = (PROC*) &thunk->u1.Function; michael@0: if(*ppfn == original) michael@0: { michael@0: DWORD dwDummy; michael@0: VirtualProtect(ppfn, sizeof(ppfn), PAGE_EXECUTE_READWRITE, &dwDummy); michael@0: BOOL result = WriteProcessMemory(GetCurrentProcess(), michael@0: ppfn, &replacement, sizeof(replacement), nullptr); michael@0: if (!result) //failure michael@0: { michael@0: printf("failure name %s func %x\n",name,*ppfn); michael@0: DWORD error = GetLastError(); michael@0: return true; michael@0: } michael@0: else michael@0: { michael@0: // printf("success name %s func %x\n",name,*ppfn); michael@0: DWORD filler = result+1; michael@0: return result; michael@0: } michael@0: } michael@0: michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: DHWImportHooker::ModuleLoaded(HMODULE aModule, DWORD flags) michael@0: { michael@0: //printf("ModuleLoaded\n"); michael@0: if(aModule && !(flags & LOAD_LIBRARY_AS_DATAFILE)) michael@0: { michael@0: PR_Lock(gLock); michael@0: // We don't know that the newly loaded module didn't drag in implicitly michael@0: // linked modules, so we patch everything in sight. michael@0: for(DHWImportHooker* cur = gHooks; cur; cur = cur->mNext) michael@0: cur->PatchAllModules(); michael@0: PR_Unlock(gLock); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: // static michael@0: HMODULE WINAPI michael@0: DHWImportHooker::LoadLibraryW(PCWSTR path) michael@0: { michael@0: //wprintf(L"LoadLibraryW %s\n",path); michael@0: HMODULE hmod = DHW_ORIGINAL(::LoadLibraryW, getLoadLibraryWHooker())(path); michael@0: ModuleLoaded(hmod, 0); michael@0: return hmod; michael@0: } michael@0: michael@0: michael@0: // static michael@0: HMODULE WINAPI michael@0: DHWImportHooker::LoadLibraryExW(PCWSTR path, HANDLE file, DWORD flags) michael@0: { michael@0: //wprintf(L"LoadLibraryExW %s\n",path); michael@0: HMODULE hmod = DHW_ORIGINAL(::LoadLibraryExW, getLoadLibraryExWHooker())(path, file, flags); michael@0: ModuleLoaded(hmod, flags); michael@0: return hmod; michael@0: } michael@0: michael@0: // static michael@0: HMODULE WINAPI michael@0: DHWImportHooker::LoadLibraryA(PCSTR path) michael@0: { michael@0: //printf("LoadLibraryA %s\n",path); michael@0: HMODULE hmod = DHW_ORIGINAL(::LoadLibraryA, getLoadLibraryAHooker())(path); michael@0: ModuleLoaded(hmod, 0); michael@0: return hmod; michael@0: } michael@0: michael@0: // static michael@0: HMODULE WINAPI michael@0: DHWImportHooker::LoadLibraryExA(PCSTR path, HANDLE file, DWORD flags) michael@0: { michael@0: //printf("LoadLibraryExA %s\n",path); michael@0: HMODULE hmod = DHW_ORIGINAL(::LoadLibraryExA, getLoadLibraryExAHooker())(path, file, flags); michael@0: ModuleLoaded(hmod, flags); michael@0: return hmod; michael@0: } michael@0: // static michael@0: FARPROC WINAPI michael@0: DHWImportHooker::GetProcAddress(HMODULE aModule, PCSTR aFunctionName) michael@0: { michael@0: FARPROC pfn = gRealGetProcAddress(aModule, aFunctionName); michael@0: michael@0: if(pfn) michael@0: { michael@0: PR_Lock(gLock); michael@0: for(DHWImportHooker* cur = gHooks; cur; cur = cur->mNext) michael@0: { michael@0: if(pfn == cur->mOriginal) michael@0: { michael@0: pfn = cur->mHook; michael@0: break; michael@0: } michael@0: } michael@0: PR_Unlock(gLock); michael@0: } michael@0: return pfn; michael@0: } michael@0: michael@0: