Files
renderdoc/renderdoc/os/win32/win32_hook.cpp
T
2017-04-03 15:19:02 +01:00

811 lines
25 KiB
C++

/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2015-2017 Baldur Karlsson
* Copyright (c) 2014 Crytek
*
* 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.
******************************************************************************/
// must be separate so that it's included first and not sorted by clang-format
#include <windows.h>
#include <tlhelp32.h>
#include <algorithm>
#include <map>
#include <vector>
#include "common/threading.h"
#include "os/os_specific.h"
#include "serialise/string_utils.h"
#define VERBOSE_DEBUG_HOOK OPTION_OFF
using std::vector;
using std::map;
// map from address of IAT entry, to original contents
map<void **, void *> s_InstalledHooks;
Threading::CriticalSection installedLock;
struct FunctionHook
{
FunctionHook(const char *f, void **o, void *d)
: function(f), origptr(o), hookptr(d), excludeModule(NULL)
{
}
bool operator<(const FunctionHook &h) { return function < h.function; }
bool ApplyHook(void **IATentry, bool &already)
{
DWORD oldProtection = PAGE_EXECUTE;
if(*IATentry == hookptr)
{
already = true;
return true;
}
#if ENABLED(VERBOSE_DEBUG_HOOK)
RDCDEBUG("Patching IAT for %s: %p to %p", function.c_str(), IATentry, hookptr);
#endif
{
SCOPED_LOCK(installedLock);
if(s_InstalledHooks.find(IATentry) == s_InstalledHooks.end())
s_InstalledHooks[IATentry] = *IATentry;
}
BOOL success = TRUE;
success = VirtualProtect(IATentry, sizeof(void *), PAGE_READWRITE, &oldProtection);
if(!success)
{
RDCERR("Failed to make IAT entry writeable 0x%p", IATentry);
return false;
}
if(origptr && *origptr == NULL)
*origptr = *IATentry;
*IATentry = hookptr;
success = VirtualProtect(IATentry, sizeof(void *), oldProtection, &oldProtection);
if(!success)
{
RDCERR("Failed to restore IAT entry protection 0x%p", IATentry);
return false;
}
return true;
}
string function;
void **origptr;
void *hookptr;
HMODULE excludeModule;
};
struct DllHookset
{
DllHookset() : module(NULL), OrdinalBase(0) {}
HMODULE module;
// if we have multiple copies of the dll loaded (unlikely), the other module handles will be
// stored here
vector<HMODULE> altmodules;
vector<FunctionHook> FunctionHooks;
DWORD OrdinalBase;
vector<string> OrdinalNames;
void FetchOrdinalNames()
{
byte *baseAddress = (byte *)module;
#if ENABLED(VERBOSE_DEBUG_HOOK)
RDCDEBUG("FetchOrdinalNames");
#endif
PIMAGE_DOS_HEADER dosheader = (PIMAGE_DOS_HEADER)baseAddress;
if(dosheader->e_magic != 0x5a4d)
return;
char *PE00 = (char *)(baseAddress + dosheader->e_lfanew);
PIMAGE_FILE_HEADER fileHeader = (PIMAGE_FILE_HEADER)(PE00 + 4);
PIMAGE_OPTIONAL_HEADER optHeader =
(PIMAGE_OPTIONAL_HEADER)((BYTE *)fileHeader + sizeof(IMAGE_FILE_HEADER));
DWORD eatOffset = optHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
IMAGE_EXPORT_DIRECTORY *exportDesc = (IMAGE_EXPORT_DIRECTORY *)(baseAddress + eatOffset);
WORD *ordinals = (WORD *)(baseAddress + exportDesc->AddressOfNameOrdinals);
DWORD *names = (DWORD *)(baseAddress + exportDesc->AddressOfNames);
DWORD count = RDCMIN(exportDesc->NumberOfFunctions, exportDesc->NumberOfNames);
WORD maxOrdinal = 0;
for(DWORD i = 0; i < count; i++)
maxOrdinal = RDCMAX(maxOrdinal, ordinals[i]);
OrdinalBase = exportDesc->Base;
OrdinalNames.resize(maxOrdinal + 1);
for(DWORD i = 0; i < count; i++)
{
OrdinalNames[ordinals[i]] = (char *)(baseAddress + names[i]);
#if ENABLED(VERBOSE_DEBUG_HOOK)
RDCDEBUG("ordinal found: '%s' %u", OrdinalNames[ordinals[i]].c_str(), (uint32_t)ordinals[i]);
#endif
}
}
};
struct CachedHookData
{
CachedHookData()
{
ownmodule = NULL;
missedOrdinals = false;
RDCEraseEl(lowername);
}
map<string, DllHookset> DllHooks;
HMODULE ownmodule;
Threading::CriticalSection lock;
char lowername[512];
bool missedOrdinals;
void ApplyHooks(const char *modName, HMODULE module)
{
{
size_t i = 0;
while(modName[i])
{
lowername[i] = (char)tolower(modName[i]);
i++;
}
lowername[i] = 0;
}
#if ENABLED(VERBOSE_DEBUG_HOOK)
RDCDEBUG("=== ApplyHooks(%s, %p)", modName, module);
#endif
// fraps seems to non-safely modify the assembly around the hook function, if
// we modify its import descriptors it leads to a crash as it hooks OUR functions.
// instead, skip modifying the import descriptors, it will hook the 'real' d3d functions
// and we can call them and have fraps + renderdoc playing nicely together.
// we also exclude some other overlay renderers here, such as steam's
//
// Also we exclude ourselves here - just in case the application has already loaded
// renderdoc.dll, or tries to load it.
if(strstr(lowername, "fraps") || strstr(lowername, "gameoverlayrenderer") ||
strstr(lowername, STRINGIZE(RDOC_DLL_FILE) ".dll") == lowername)
return;
// set module pointer if we are hooking exports from this module
for(auto it = DllHooks.begin(); it != DllHooks.end(); ++it)
{
if(!_stricmp(it->first.c_str(), modName))
{
if(it->second.module == NULL)
{
it->second.module = module;
it->second.FetchOrdinalNames();
}
else if(it->second.module != module)
{
// if it's already in altmodules, bail
bool already = false;
for(size_t i = 0; i < it->second.altmodules.size(); i++)
{
if(it->second.altmodules[i] == module)
{
already = true;
break;
}
}
if(already)
break;
// check if the previous module is still valid
SetLastError(0);
char filename[MAX_PATH] = {};
GetModuleFileNameA(it->second.module, filename, MAX_PATH - 1);
DWORD err = GetLastError();
char *slash = strrchr(filename, L'\\');
string basename = slash ? strlower(string(slash + 1)) : "";
if(err == 0 && basename == it->first)
{
// previous module is still loaded, add this to the alt modules list
it->second.altmodules.push_back(module);
}
else
{
// previous module is no longer loaded or there's a new file there now, add this as the
// new location
it->second.module = module;
}
}
}
}
// for safety (and because we don't need to), ignore these modules
if(!_stricmp(modName, "kernel32.dll") || !_stricmp(modName, "powrprof.dll") ||
!_stricmp(modName, "opengl32.dll") || !_stricmp(modName, "gdi32.dll") ||
!_stricmp(modName, "nvoglv32.dll") || !_stricmp(modName, "nvoglv64.dll") ||
!_stricmp(modName, "nvcuda.dll") || strstr(lowername, "cudart") == lowername ||
strstr(lowername, "msvcr") == lowername || strstr(lowername, "msvcp") == lowername ||
strstr(lowername, "nv-vk") == lowername || strstr(lowername, "amdvlk") == lowername ||
strstr(lowername, "igvk") == lowername || strstr(lowername, "nvopencl") == lowername ||
strstr(lowername, "nvapi") == lowername)
return;
byte *baseAddress = (byte *)module;
// the module could have been unloaded after our toolhelp snapshot, especially if we spent a
// long time
// dealing with a previous module (like adding our hooks).
wchar_t modpath[1024] = {0};
GetModuleFileNameW(module, modpath, 1023);
if(modpath[0] == 0)
return;
// increment the module reference count, so it doesn't disappear while we're processing it
// there's a very small race condition here between if GetModuleFileName returns, the module is
// unloaded then we load it again. The only way around that is inserting very scary locks
// between here
// and FreeLibrary that I want to avoid. Worst case, we load a dll, hook it, then unload it
// again.
HMODULE refcountModHandle = LoadLibraryW(modpath);
PIMAGE_DOS_HEADER dosheader = (PIMAGE_DOS_HEADER)baseAddress;
if(dosheader->e_magic != 0x5a4d)
{
RDCDEBUG("Ignoring module %s, since magic is 0x%04x not 0x%04x", modName,
(uint32_t)dosheader->e_magic, 0x5a4dU);
FreeLibrary(refcountModHandle);
return;
}
char *PE00 = (char *)(baseAddress + dosheader->e_lfanew);
PIMAGE_FILE_HEADER fileHeader = (PIMAGE_FILE_HEADER)(PE00 + 4);
PIMAGE_OPTIONAL_HEADER optHeader =
(PIMAGE_OPTIONAL_HEADER)((BYTE *)fileHeader + sizeof(IMAGE_FILE_HEADER));
DWORD iatOffset = optHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
IMAGE_IMPORT_DESCRIPTOR *importDesc = (IMAGE_IMPORT_DESCRIPTOR *)(baseAddress + iatOffset);
#if ENABLED(VERBOSE_DEBUG_HOOK)
RDCDEBUG("=== import descriptors:");
#endif
while(iatOffset && importDesc->FirstThunk)
{
const char *dllName = (const char *)(baseAddress + importDesc->Name);
#if ENABLED(VERBOSE_DEBUG_HOOK)
RDCDEBUG("found IAT for %s", dllName);
#endif
DllHookset *hookset = NULL;
for(auto it = DllHooks.begin(); it != DllHooks.end(); ++it)
if(!_stricmp(it->first.c_str(), dllName))
hookset = &it->second;
if(hookset && importDesc->OriginalFirstThunk > 0 && importDesc->FirstThunk > 0)
{
IMAGE_THUNK_DATA *origFirst =
(IMAGE_THUNK_DATA *)(baseAddress + importDesc->OriginalFirstThunk);
IMAGE_THUNK_DATA *first = (IMAGE_THUNK_DATA *)(baseAddress + importDesc->FirstThunk);
#if ENABLED(VERBOSE_DEBUG_HOOK)
RDCDEBUG("Hooking imports for %s", dllName);
#endif
while(origFirst->u1.AddressOfData)
{
void **IATentry = (void **)&first->u1.AddressOfData;
struct hook_find
{
bool operator()(const FunctionHook &a, const char *b)
{
return strcmp(a.function.c_str(), b) < 0;
}
};
#if ENABLED(RDOC_X64)
if(IMAGE_SNAP_BY_ORDINAL64(origFirst->u1.AddressOfData))
#else
if(IMAGE_SNAP_BY_ORDINAL32(origFirst->u1.AddressOfData))
#endif
{
// low bits of origFirst->u1.AddressOfData contain an ordinal
WORD ordinal = IMAGE_ORDINAL64(origFirst->u1.AddressOfData);
#if ENABLED(VERBOSE_DEBUG_HOOK)
RDCDEBUG("Found ordinal import %u", (uint32_t)ordinal);
#endif
if(!hookset->OrdinalNames.empty())
{
if(ordinal >= hookset->OrdinalBase)
{
// rebase into OrdinalNames index
DWORD nameIndex = ordinal - hookset->OrdinalBase;
// it's perfectly valid to have more functions than names, we only
// list those with names - so ignore any others
if(nameIndex < hookset->OrdinalNames.size())
{
const char *importName = (const char *)hookset->OrdinalNames[nameIndex].c_str();
#if ENABLED(VERBOSE_DEBUG_HOOK)
RDCDEBUG("Located ordinal %u as %s", (uint32_t)ordinal, importName);
#endif
auto found =
std::lower_bound(hookset->FunctionHooks.begin(), hookset->FunctionHooks.end(),
importName, hook_find());
if(found != hookset->FunctionHooks.end() &&
!strcmp(found->function.c_str(), importName) && found->excludeModule != module)
{
bool already = false;
bool applied;
{
SCOPED_LOCK(lock);
applied = found->ApplyHook(IATentry, already);
}
// if we failed, or if it's already set and we're not doing a missedOrdinals
// second pass, then just bail out immediately as we've already hooked this
// module and there's no point wasting time re-hooking nothing
if(!applied || (already && !missedOrdinals))
{
#if ENABLED(VERBOSE_DEBUG_HOOK)
RDCDEBUG("Stopping hooking module, %d %d %d", (int)applied, (int)already,
(int)missedOrdinals);
#endif
FreeLibrary(refcountModHandle);
return;
}
}
}
}
else
{
RDCERR("Import ordinal is below ordinal base in %s importing module %s", modName,
dllName);
}
}
else
{
#if ENABLED(VERBOSE_DEBUG_HOOK)
RDCDEBUG("missed ordinals, will try again");
#endif
// the very first time we try to apply hooks, we might apply them to a module
// before we've looked up the ordinal names for the one it's linking against.
// Subsequent times we're only loading one new module - and since it can't
// link to itself we will have all ordinal names loaded.
//
// Setting this flag causes us to do a second pass right at the start
missedOrdinals = true;
}
// continue
origFirst++;
first++;
continue;
}
IMAGE_IMPORT_BY_NAME *import =
(IMAGE_IMPORT_BY_NAME *)(baseAddress + origFirst->u1.AddressOfData);
const char *importName = (const char *)import->Name;
#if ENABLED(VERBOSE_DEBUG_HOOK)
RDCDEBUG("Found normal import %s", importName);
#endif
auto found = std::lower_bound(hookset->FunctionHooks.begin(),
hookset->FunctionHooks.end(), importName, hook_find());
if(found != hookset->FunctionHooks.end() &&
!strcmp(found->function.c_str(), importName) && found->excludeModule != module)
{
bool already = false;
bool applied;
{
SCOPED_LOCK(lock);
applied = found->ApplyHook(IATentry, already);
}
// if we failed, or if it's already set and we're not doing a missedOrdinals
// second pass, then just bail out immediately as we've already hooked this
// module and there's no point wasting time re-hooking nothing
if(!applied || (already && !missedOrdinals))
{
#if ENABLED(VERBOSE_DEBUG_HOOK)
RDCDEBUG("Stopping hooking module, %d %d %d", (int)applied, (int)already,
(int)missedOrdinals);
#endif
FreeLibrary(refcountModHandle);
return;
}
}
origFirst++;
first++;
}
}
else
{
if(hookset)
{
#if ENABLED(VERBOSE_DEBUG_HOOK)
RDCDEBUG("!! Invalid IAT found for %s! %u %u", dllName, importDesc->OriginalFirstThunk,
importDesc->FirstThunk);
#endif
}
}
importDesc++;
}
FreeLibrary(refcountModHandle);
}
};
static CachedHookData *s_HookData = NULL;
static void HookAllModules()
{
HANDLE hModuleSnap = INVALID_HANDLE_VALUE;
// up to 10 retries
for(int i = 0; i < 10; i++)
{
hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetCurrentProcessId());
if(hModuleSnap == INVALID_HANDLE_VALUE)
{
DWORD err = GetLastError();
RDCWARN("CreateToolhelp32Snapshot() -> 0x%08x", err);
// retry if error is ERROR_BAD_LENGTH
if(err == ERROR_BAD_LENGTH)
continue;
}
// didn't retry, or succeeded
break;
}
if(hModuleSnap == INVALID_HANDLE_VALUE)
{
RDCERR("Couldn't create toolhelp dump of modules in process");
return;
}
#ifdef UNICODE
#undef MODULEENTRY32
#undef Module32First
#undef Module32Next
#endif
MODULEENTRY32 me32;
RDCEraseEl(me32);
me32.dwSize = sizeof(MODULEENTRY32);
BOOL success = Module32First(hModuleSnap, &me32);
if(success == FALSE)
{
DWORD err = GetLastError();
RDCERR("Couldn't get first module in process: 0x%08x", err);
CloseHandle(hModuleSnap);
return;
}
uintptr_t ret = 0;
do
{
s_HookData->ApplyHooks(me32.szModule, me32.hModule);
} while(ret == 0 && Module32Next(hModuleSnap, &me32));
CloseHandle(hModuleSnap);
}
HMODULE WINAPI Hooked_LoadLibraryExA(LPCSTR lpLibFileName, HANDLE fileHandle, DWORD flags)
{
bool dohook = true;
if(flags == 0 && GetModuleHandleA(lpLibFileName))
dohook = false;
SetLastError(S_OK);
// we can use the function naked, as when setting up the hook for LoadLibraryExA, our own module
// was excluded from IAT patching
HMODULE mod = LoadLibraryExA(lpLibFileName, fileHandle, flags);
#if ENABLED(VERBOSE_DEBUG_HOOK)
RDCDEBUG("LoadLibraryA(%s)", lpLibFileName);
#endif
DWORD err = GetLastError();
if(dohook)
HookAllModules();
SetLastError(err);
return mod;
}
HMODULE WINAPI Hooked_LoadLibraryExW(LPCWSTR lpLibFileName, HANDLE fileHandle, DWORD flags)
{
bool dohook = true;
if(flags == 0 && GetModuleHandleW(lpLibFileName))
dohook = false;
SetLastError(S_OK);
#if ENABLED(VERBOSE_DEBUG_HOOK)
RDCDEBUG("LoadLibraryW(%ls)", lpLibFileName);
#endif
// we can use the function naked, as when setting up the hook for LoadLibraryExA, our own module
// was excluded from IAT patching
HMODULE mod = LoadLibraryExW(lpLibFileName, fileHandle, flags);
DWORD err = GetLastError();
if(dohook)
HookAllModules();
SetLastError(err);
return mod;
}
HMODULE WINAPI Hooked_LoadLibraryA(LPCSTR lpLibFileName)
{
return Hooked_LoadLibraryExA(lpLibFileName, NULL, 0);
}
HMODULE WINAPI Hooked_LoadLibraryW(LPCWSTR lpLibFileName)
{
return Hooked_LoadLibraryExW(lpLibFileName, NULL, 0);
}
static bool OrdinalAsString(void *func)
{
return uint64_t(func) <= 0xffff;
}
FARPROC WINAPI Hooked_GetProcAddress(HMODULE mod, LPCSTR func)
{
if(mod == NULL || func == NULL)
return (FARPROC)NULL;
if(mod == s_HookData->ownmodule)
return GetProcAddress(mod, func);
#if ENABLED(VERBOSE_DEBUG_HOOK)
if(OrdinalAsString((void *)func))
RDCDEBUG("Hooked_GetProcAddress(%p, %p)", mod, func);
else
RDCDEBUG("Hooked_GetProcAddress(%p, %s)", mod, func);
#endif
for(auto it = s_HookData->DllHooks.begin(); it != s_HookData->DllHooks.end(); ++it)
{
if(it->second.module == NULL)
it->second.module = GetModuleHandleA(it->first.c_str());
bool match = (mod == it->second.module);
if(!match && !it->second.altmodules.empty())
{
for(size_t i = 0; !match && i < it->second.altmodules.size(); i++)
match = (mod == it->second.altmodules[i]);
}
if(match)
{
#if ENABLED(VERBOSE_DEBUG_HOOK)
RDCDEBUG("Located module %s", it->first.c_str());
#endif
if(OrdinalAsString((void *)func))
{
#if ENABLED(VERBOSE_DEBUG_HOOK)
RDCDEBUG("Ordinal hook");
#endif
uint32_t ordinal = (uint16_t)(uintptr_t(func) & 0xffff);
if(ordinal < it->second.OrdinalBase)
{
RDCERR("Unexpected ordinal - lower than ordinalbase %u for %s",
(uint32_t)it->second.OrdinalBase, it->first.c_str());
SetLastError(S_OK);
return GetProcAddress(mod, func);
}
ordinal -= it->second.OrdinalBase;
if(ordinal >= it->second.OrdinalNames.size())
{
RDCERR("Unexpected ordinal - higher than fetched ordinal names (%u) for %s",
(uint32_t)it->second.OrdinalNames.size(), it->first.c_str());
SetLastError(S_OK);
return GetProcAddress(mod, func);
}
func = it->second.OrdinalNames[ordinal].c_str();
#if ENABLED(VERBOSE_DEBUG_HOOK)
RDCDEBUG("found ordinal %s", func);
#endif
}
FunctionHook search(func, NULL, NULL);
auto found =
std::lower_bound(it->second.FunctionHooks.begin(), it->second.FunctionHooks.end(), search);
if(found != it->second.FunctionHooks.end() && !(search < *found))
{
if(found->origptr && *found->origptr == NULL)
*found->origptr = (void *)GetProcAddress(mod, func);
#if ENABLED(VERBOSE_DEBUG_HOOK)
RDCDEBUG("Found hooked function, returning hook pointer %p", found->hookptr);
#endif
SetLastError(S_OK);
if(found->origptr && *found->origptr == NULL)
return NULL;
return (FARPROC)found->hookptr;
}
}
}
#if ENABLED(VERBOSE_DEBUG_HOOK)
RDCDEBUG("No matching hook found, returning original");
#endif
SetLastError(S_OK);
return GetProcAddress(mod, func);
}
void Win32_IAT_BeginHooks()
{
s_HookData = new CachedHookData;
RDCASSERT(s_HookData->DllHooks.empty());
s_HookData->DllHooks["kernel32.dll"].FunctionHooks.push_back(
FunctionHook("LoadLibraryA", NULL, &Hooked_LoadLibraryA));
s_HookData->DllHooks["kernel32.dll"].FunctionHooks.push_back(
FunctionHook("LoadLibraryW", NULL, &Hooked_LoadLibraryW));
s_HookData->DllHooks["kernel32.dll"].FunctionHooks.push_back(
FunctionHook("LoadLibraryExA", NULL, &Hooked_LoadLibraryExA));
s_HookData->DllHooks["kernel32.dll"].FunctionHooks.push_back(
FunctionHook("LoadLibraryExW", NULL, &Hooked_LoadLibraryExW));
s_HookData->DllHooks["kernel32.dll"].FunctionHooks.push_back(
FunctionHook("GetProcAddress", NULL, &Hooked_GetProcAddress));
GetModuleHandleEx(
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
(LPCTSTR)&s_HookData, &s_HookData->ownmodule);
for(auto it = s_HookData->DllHooks.begin(); it != s_HookData->DllHooks.end(); ++it)
for(size_t i = 0; i < it->second.FunctionHooks.size(); i++)
it->second.FunctionHooks[i].excludeModule = s_HookData->ownmodule;
}
// hook all functions for currently loaded modules.
// some of these hooks (as above) will hook LoadLibrary/GetProcAddress, to protect
void Win32_IAT_EndHooks()
{
for(auto it = s_HookData->DllHooks.begin(); it != s_HookData->DllHooks.end(); ++it)
std::sort(it->second.FunctionHooks.begin(), it->second.FunctionHooks.end());
#if ENABLED(VERBOSE_DEBUG_HOOK)
RDCDEBUG("Applying hooks");
#endif
HookAllModules();
if(s_HookData->missedOrdinals)
{
#if ENABLED(VERBOSE_DEBUG_HOOK)
RDCDEBUG("Missed ordinals - applying hooks again");
#endif
// we need to do a second pass now that we know ordinal names to finally hook
// some imports by ordinal only.
HookAllModules();
s_HookData->missedOrdinals = false;
}
}
void Win32_IAT_RemoveHooks()
{
for(auto it = s_InstalledHooks.begin(); it != s_InstalledHooks.end(); ++it)
{
DWORD oldProtection = PAGE_EXECUTE;
BOOL success = TRUE;
void **IATentry = it->first;
success = VirtualProtect(IATentry, sizeof(void *), PAGE_READWRITE, &oldProtection);
if(!success)
{
RDCERR("Failed to make IAT entry writeable 0x%p", IATentry);
continue;
}
*IATentry = it->second;
success = VirtualProtect(IATentry, sizeof(void *), oldProtection, &oldProtection);
if(!success)
{
RDCERR("Failed to restore IAT entry protection 0x%p", IATentry);
continue;
}
}
}
bool Win32_IAT_Hook(void **orig_function_ptr, const char *module_name, const char *function,
void *destination_function_ptr)
{
if(!_stricmp(module_name, "kernel32.dll"))
{
if(!strcmp(function, "LoadLibraryA") || !strcmp(function, "LoadLibraryW") ||
!strcmp(function, "LoadLibraryExA") || !strcmp(function, "LoadLibraryExW") ||
!strcmp(function, "GetProcAddress"))
{
RDCERR("Cannot hook LoadLibrary* or GetProcAddress, as these are hooked internally");
return false;
}
}
s_HookData->DllHooks[strlower(string(module_name))].FunctionHooks.push_back(
FunctionHook(function, orig_function_ptr, destination_function_ptr));
return true;
}