#include "interception.h"
#if SANITIZER_WINDOWS
#include "sanitizer_common/sanitizer_platform.h"
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <psapi.h>
namespace __interception {
static const int kAddressLength = FIRST_32_SECOND_64(4, 8);
static const int kJumpInstructionLength = 5;
static const int kShortJumpInstructionLength = 2;
UNUSED static const int kIndirectJumpInstructionLength = 6;
static const int kBranchLength =
FIRST_32_SECOND_64(kJumpInstructionLength, kIndirectJumpInstructionLength);
static const int kDirectBranchLength = kBranchLength + kAddressLength;
# if defined(_MSC_VER)
#define INTERCEPTION_FORMAT …
# else
#define INTERCEPTION_FORMAT …
# endif
static void (*ErrorReportCallback)(const char *format, ...)
INTERCEPTION_FORMAT(1, 2);
void SetErrorReportCallback(void (*callback)(const char *format, ...)) {
ErrorReportCallback = callback;
}
#define ReportError …
static void InterceptionFailed() {
ReportError("interception_win: failed due to an unrecoverable error.\n");
__debugbreak();
}
static bool DistanceIsWithin2Gig(uptr from, uptr target) {
#if SANITIZER_WINDOWS64
if (from < target)
return target - from <= (uptr)0x7FFFFFFFU;
else
return from - target <= (uptr)0x80000000U;
#else
return true;
#endif
}
static uptr GetMmapGranularity() {
SYSTEM_INFO si;
GetSystemInfo(&si);
return si.dwAllocationGranularity;
}
UNUSED static uptr RoundUpTo(uptr size, uptr boundary) {
return (size + boundary - 1) & ~(boundary - 1);
}
static size_t _strlen(const char *str) {
const char* p = str;
while (*p != '\0') ++p;
return p - str;
}
static char* _strchr(char* str, char c) {
while (*str) {
if (*str == c)
return str;
++str;
}
return nullptr;
}
static void _memset(void *p, int value, size_t sz) {
for (size_t i = 0; i < sz; ++i)
((char*)p)[i] = (char)value;
}
static void _memcpy(void *dst, void *src, size_t sz) {
char *dst_c = (char*)dst,
*src_c = (char*)src;
for (size_t i = 0; i < sz; ++i)
dst_c[i] = src_c[i];
}
static bool ChangeMemoryProtection(
uptr address, uptr size, DWORD *old_protection) {
return ::VirtualProtect((void*)address, size,
PAGE_EXECUTE_READWRITE,
old_protection) != FALSE;
}
static bool RestoreMemoryProtection(
uptr address, uptr size, DWORD old_protection) {
DWORD unused;
return ::VirtualProtect((void*)address, size,
old_protection,
&unused) != FALSE;
}
static bool IsMemoryPadding(uptr address, uptr size) {
u8* function = (u8*)address;
for (size_t i = 0; i < size; ++i)
if (function[i] != 0x90 && function[i] != 0xCC)
return false;
return true;
}
static const u8 kHintNop8Bytes[] = {
0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00
};
template<class T>
static bool FunctionHasPrefix(uptr address, const T &pattern) {
u8* function = (u8*)address - sizeof(pattern);
for (size_t i = 0; i < sizeof(pattern); ++i)
if (function[i] != pattern[i])
return false;
return true;
}
static bool FunctionHasPadding(uptr address, uptr size) {
if (IsMemoryPadding(address - size, size))
return true;
if (size <= sizeof(kHintNop8Bytes) &&
FunctionHasPrefix(address, kHintNop8Bytes))
return true;
return false;
}
static void WritePadding(uptr from, uptr size) {
_memset((void*)from, 0xCC, (size_t)size);
}
static void WriteJumpInstruction(uptr from, uptr target) {
if (!DistanceIsWithin2Gig(from + kJumpInstructionLength, target)) {
ReportError(
"interception_win: cannot write jmp further than 2GB away, from %p to "
"%p.\n",
(void *)from, (void *)target);
InterceptionFailed();
}
ptrdiff_t offset = target - from - kJumpInstructionLength;
*(u8*)from = 0xE9;
*(u32*)(from + 1) = offset;
}
static void WriteShortJumpInstruction(uptr from, uptr target) {
sptr offset = target - from - kShortJumpInstructionLength;
if (offset < -128 || offset > 127)
InterceptionFailed();
*(u8*)from = 0xEB;
*(u8*)(from + 1) = (u8)offset;
}
#if SANITIZER_WINDOWS64
static void WriteIndirectJumpInstruction(uptr from, uptr indirect_target) {
int offset = indirect_target - from - kIndirectJumpInstructionLength;
if (!DistanceIsWithin2Gig(from + kIndirectJumpInstructionLength,
indirect_target)) {
ReportError(
"interception_win: cannot write indirect jmp with target further than "
"2GB away, from %p to %p.\n",
(void *)from, (void *)indirect_target);
InterceptionFailed();
}
*(u16*)from = 0x25FF;
*(u32*)(from + 2) = offset;
}
#endif
static void WriteBranch(
uptr from, uptr indirect_target, uptr target) {
#if SANITIZER_WINDOWS64
WriteIndirectJumpInstruction(from, indirect_target);
*(u64*)indirect_target = target;
#else
(void)indirect_target;
WriteJumpInstruction(from, target);
#endif
}
static void WriteDirectBranch(uptr from, uptr target) {
#if SANITIZER_WINDOWS64
WriteBranch(from, from + kBranchLength, target);
#else
WriteJumpInstruction(from, target);
#endif
}
struct TrampolineMemoryRegion {
uptr content;
uptr allocated_size;
uptr max_size;
};
UNUSED static const uptr kTrampolineScanLimitRange = 1ull << 31;
static const int kMaxTrampolineRegion = 1024;
static TrampolineMemoryRegion TrampolineRegions[kMaxTrampolineRegion];
static void *AllocateTrampolineRegion(uptr image_address, size_t granularity) {
#if SANITIZER_WINDOWS64
uptr address = image_address;
uptr scanned = 0;
while (scanned < kTrampolineScanLimitRange) {
MEMORY_BASIC_INFORMATION info;
if (!::VirtualQuery((void*)address, &info, sizeof(info)))
return nullptr;
if (info.State == MEM_FREE && info.RegionSize >= granularity) {
void *page = ::VirtualAlloc((void*)RoundUpTo(address, granularity),
granularity,
MEM_RESERVE | MEM_COMMIT,
PAGE_EXECUTE_READWRITE);
return page;
}
address = (uptr)info.BaseAddress + info.RegionSize;
scanned += info.RegionSize;
}
return nullptr;
#else
return ::VirtualAlloc(nullptr,
granularity,
MEM_RESERVE | MEM_COMMIT,
PAGE_EXECUTE_READWRITE);
#endif
}
void TestOnlyReleaseTrampolineRegions() {
for (size_t bucket = 0; bucket < kMaxTrampolineRegion; ++bucket) {
TrampolineMemoryRegion *current = &TrampolineRegions[bucket];
if (current->content == 0)
return;
::VirtualFree((void*)current->content, 0, MEM_RELEASE);
current->content = 0;
}
}
static uptr AllocateMemoryForTrampoline(uptr func_address, size_t size) {
uptr image_address = func_address;
#if SANITIZER_WINDOWS64
HMODULE module;
if (::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
(LPCWSTR)func_address, &module)) {
MODULEINFO module_info;
if (::GetModuleInformation(::GetCurrentProcess(), module,
&module_info, sizeof(module_info))) {
image_address = (uptr)module_info.lpBaseOfDll;
}
}
#endif
TrampolineMemoryRegion *region = nullptr;
for (size_t bucket = 0; bucket < kMaxTrampolineRegion; ++bucket) {
TrampolineMemoryRegion* current = &TrampolineRegions[bucket];
if (current->content == 0) {
size_t bucket_size = GetMmapGranularity();
void *content = AllocateTrampolineRegion(image_address, bucket_size);
if (content == nullptr)
return 0U;
current->content = (uptr)content;
current->allocated_size = 0;
current->max_size = bucket_size;
region = current;
break;
} else if (current->max_size - current->allocated_size > size) {
#if SANITIZER_WINDOWS64
uptr next_address = current->content + current->allocated_size;
if (next_address < image_address ||
next_address - image_address >= 0x7FFF0000)
continue;
#endif
region = current;
break;
}
}
if (region == nullptr)
return 0U;
uptr allocated_space = region->content + region->allocated_size;
region->allocated_size += size;
WritePadding(allocated_space, size);
return allocated_space;
}
# if SANITIZER_WINDOWS_x64
static const u8 kPrologueWithShortJump1[] = {
0x48, 0x8b, 0xc1, 0x0f, 0xb7, 0x10, 0x48, 0x83,
0xc0, 0x02, 0x66, 0x85, 0xd2, 0x75, 0xf4,
};
static const u8 kPrologueWithShortJump2[] = {
0x4c, 0x8b, 0xc1, 0x8a, 0x01, 0x48, 0xff, 0xc1,
0x84, 0xc0, 0x75, 0xf7,
};
#endif
static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) {
#if SANITIZER_ARM64
return 4;
#endif
# if SANITIZER_WINDOWS_x64
if (memcmp((u8*)address, kPrologueWithShortJump1,
sizeof(kPrologueWithShortJump1)) == 0 ||
memcmp((u8*)address, kPrologueWithShortJump2,
sizeof(kPrologueWithShortJump2)) == 0) {
return 0;
}
#endif
switch (*(u64*)address) {
case 0x90909090909006EB:
return 8;
}
switch (*(u8*)address) {
case 0x90:
case 0xC3:
case 0xCC:
return 1;
case 0x50:
case 0x51:
case 0x52:
case 0x53:
case 0x54:
case 0x55:
case 0x56:
case 0x57:
case 0x5D:
return 1;
case 0x6A:
return 2;
case 0x68:
return 5;
case 0xb8:
case 0xB9:
return 5;
case 0xE9:
case 0xE8:
case 0xEB:
case 0x70:
case 0x71:
case 0x72:
case 0x73:
case 0x74:
case 0x75:
case 0x76:
case 0x77:
case 0x78:
case 0x79:
case 0x7A:
case 0x7B:
case 0x7C:
case 0x7D:
case 0x7E:
case 0x7F:
return 0;
}
switch (*(u16*)(address)) {
case 0x018A:
case 0xFF8B:
case 0xEC8B:
case 0xc889:
case 0xE589:
case 0xC18B:
case 0xC033:
case 0xC933:
case 0xD233:
case 0xDB84:
case 0xC984:
case 0xD284:
return 2;
case 0x25FF:
return 0;
}
switch (0x00FFFFFF & *(u32*)address) {
case 0xF8E483:
case 0x64EC83:
return 3;
case 0x24A48D:
return 7;
}
switch (0x000000FF & *(u32 *)address) {
case 0xc2:
return 3;
}
# if SANITIZER_WINDOWS_x64
switch (*(u8*)address) {
case 0xA1:
return 9;
case 0xF2:
switch (*(u32 *)(address + 1)) {
case 0x2444110f:
case 0x244c110f:
case 0x2454110f:
case 0x245c110f:
case 0x2464110f:
return 6;
}
break;
case 0x83:
const u8 next_byte = *(u8*)(address + 1);
const u8 mod = next_byte >> 6;
const u8 rm = next_byte & 7;
if (mod == 1 && rm == 4)
return 5;
}
switch (*(u16*)address) {
case 0x5040:
case 0x5140:
case 0x5240:
case 0x5340:
case 0x5440:
case 0x5540:
case 0x5640:
case 0x5740:
case 0x5441:
case 0x5541:
case 0x5641:
case 0x5741:
case 0x9066:
case 0xc084:
case 0x018a:
return 2;
case 0x058A:
case 0x7E80:
case 0x7D80:
case 0x7A80:
case 0x7880:
case 0x7B80:
case 0x7980:
return 4;
case 0x058B:
if (rel_offset)
*rel_offset = 2;
return 6;
case 0x7E81:
case 0x7D81:
case 0x7A81:
case 0x7881:
case 0x7B81:
case 0x7981:
return 7;
}
switch (0x00FFFFFF & *(u32*)address) {
case 0x07c1f6:
case 0x10b70f:
case 0xc00b4d:
case 0xc03345:
case 0xc08548:
case 0xc0854d:
case 0xc08b41:
case 0xc0ff48:
case 0xc0ff49:
case 0xc18b41:
case 0xc18b48:
case 0xc18b4c:
case 0xc1ff48:
case 0xc1ff49:
case 0xc28b41:
case 0xc2b60f:
case 0xc2ff48:
case 0xc2ff49:
case 0xc38b41:
case 0xc3ff48:
case 0xc3ff49:
case 0xc48b41:
case 0xc48b48:
case 0xc4ff49:
case 0xc5ff49:
case 0xc6ff48:
case 0xc6ff49:
case 0xc7ff48:
case 0xc7ff49:
case 0xc93345:
case 0xc98548:
case 0xc9854d:
case 0xc98b4c:
case 0xca2b48:
case 0xca3b48:
case 0xd12b48:
case 0xd18b48:
case 0xd18b4c:
case 0xd28548:
case 0xd2854d:
case 0xd28b4c:
case 0xd2b60f:
case 0xd98b4c:
case 0xd9f748:
case 0xdb3345:
case 0xdb8548:
case 0xdb854d:
case 0xdc8b4c:
case 0xe0e483:
case 0xe48548:
case 0xe4854d:
case 0xe58948:
case 0xed8548:
case 0xed854d:
case 0xf6854d:
case 0xff854d:
return 3;
case 0x245489:
case 0x428d44:
case 0x588948:
case 0xec8348:
case 0xf88349:
return 4;
case 0x246483:
return 5;
case 0x788166:
case 0x798166:
case 0x7a8166:
case 0x7b8166:
case 0x7e8166:
case 0x7f8166:
return 6;
case 0xec8148:
return 7;
case 0x788141:
case 0x798141:
case 0x7a8141:
case 0x7b8141:
case 0x7c8141:
case 0x7d8141:
case 0x7e8141:
case 0x7f8141:
case 0x247c81:
return 8;
case 0x058b48:
case 0x058d48:
case 0x25ff48:
case 0x158D4C:
if (rel_offset)
*rel_offset = 3;
return 7;
case 0x2444c7:
return 8;
}
switch (*(u32*)(address)) {
case 0x24448b48:
case 0x246c8948:
case 0x245c8948:
case 0x24748948:
case 0x247c8948:
case 0x244C8948:
case 0x24548948:
case 0x244c894c:
case 0x2444894c:
case 0x244c8944:
case 0x24448944:
case 0x246c8d48:
return 5;
case 0x24648348:
return 6;
}
#else
switch (*(u8*)address) {
case 0xA1:
return 5;
}
switch (*(u16*)address) {
case 0x458B:
case 0x5D8B:
case 0x7D8B:
case 0x758B:
case 0xEC83:
case 0x75FF:
return 3;
case 0xC1F7:
case 0x25FF:
return 6;
case 0x3D83:
return 7;
case 0x7D83:
return 4;
}
switch (0x00FFFFFF & *(u32*)address) {
case 0x24448A:
case 0x24448B:
case 0x244C8B:
case 0x24548B:
case 0x245C8B:
case 0x246C8B:
case 0x24748B:
case 0x247C8B:
return 4;
}
switch (*(u32*)address) {
case 0x2444B60F:
return 5;
}
#endif
u8 *bytes = (u8 *)address;
ReportError(
"interception_win: unhandled instruction at %p: %02x %02x %02x %02x %02x "
"%02x %02x %02x\n",
(void *)address, bytes[0], bytes[1], bytes[2], bytes[3], bytes[4],
bytes[5], bytes[6], bytes[7]);
if (::IsDebuggerPresent())
__debugbreak();
return 0;
}
static size_t RoundUpToInstrBoundary(size_t size, uptr address) {
size_t cursor = 0;
while (cursor < size) {
size_t instruction_size = GetInstructionSize(address + cursor);
if (!instruction_size)
return 0;
cursor += instruction_size;
}
return cursor;
}
static bool CopyInstructions(uptr to, uptr from, size_t size) {
size_t cursor = 0;
while (cursor != size) {
size_t rel_offset = 0;
size_t instruction_size = GetInstructionSize(from + cursor, &rel_offset);
if (!instruction_size)
return false;
_memcpy((void *)(to + cursor), (void *)(from + cursor),
(size_t)instruction_size);
if (rel_offset) {
# if SANITIZER_WINDOWS64
s64 delta = to - from;
s64 relocated_offset = *(s32 *)(to + cursor + rel_offset) - delta;
if (-0x8000'0000ll > relocated_offset || relocated_offset > 0x7FFF'FFFFll)
return false;
# else
s32 delta = to - from;
s32 relocated_offset = *(s32 *)(to + cursor + rel_offset) - delta;
# endif
*(s32 *)(to + cursor + rel_offset) = relocated_offset;
}
cursor += instruction_size;
}
return true;
}
#if !SANITIZER_WINDOWS64
bool OverrideFunctionWithDetour(
uptr old_func, uptr new_func, uptr *orig_old_func) {
const int kDetourHeaderLen = 5;
const u16 kDetourInstruction = 0xFF8B;
uptr header = (uptr)old_func - kDetourHeaderLen;
uptr patch_length = kDetourHeaderLen + kShortJumpInstructionLength;
if (*(u16*)old_func != kDetourInstruction ||
!IsMemoryPadding(header, kDetourHeaderLen))
return false;
DWORD protection = 0;
if (!ChangeMemoryProtection(header, patch_length, &protection))
return false;
WriteJumpInstruction(header, new_func);
WriteShortJumpInstruction(old_func, header);
if (!RestoreMemoryProtection(header, patch_length, protection))
return false;
if (orig_old_func)
*orig_old_func = old_func + kShortJumpInstructionLength;
return true;
}
#endif
bool OverrideFunctionWithRedirectJump(
uptr old_func, uptr new_func, uptr *orig_old_func) {
if (*(u8*)old_func != 0xE9)
return false;
if (orig_old_func) {
sptr relative_offset = *(s32 *)(old_func + 1);
uptr absolute_target = old_func + relative_offset + kJumpInstructionLength;
*orig_old_func = absolute_target;
}
#if SANITIZER_WINDOWS64
uptr trampoline = AllocateMemoryForTrampoline(old_func, kDirectBranchLength);
if (!trampoline)
return false;
WriteDirectBranch(trampoline, new_func);
#endif
DWORD protection = 0;
if (!ChangeMemoryProtection(old_func, kJumpInstructionLength, &protection))
return false;
WriteJumpInstruction(old_func, FIRST_32_SECOND_64(new_func, trampoline));
if (!RestoreMemoryProtection(old_func, kJumpInstructionLength, protection))
return false;
return true;
}
bool OverrideFunctionWithHotPatch(
uptr old_func, uptr new_func, uptr *orig_old_func) {
const int kHotPatchHeaderLen = kBranchLength;
uptr header = (uptr)old_func - kHotPatchHeaderLen;
uptr patch_length = kHotPatchHeaderLen + kShortJumpInstructionLength;
size_t instruction_size = GetInstructionSize(old_func);
if (instruction_size < kShortJumpInstructionLength ||
!FunctionHasPadding(old_func, kHotPatchHeaderLen))
return false;
if (orig_old_func) {
uptr trampoline_length = instruction_size + kDirectBranchLength;
uptr trampoline = AllocateMemoryForTrampoline(old_func, trampoline_length);
if (!trampoline)
return false;
if (!CopyInstructions(trampoline, old_func, instruction_size))
return false;
WriteDirectBranch(trampoline + instruction_size,
old_func + instruction_size);
*orig_old_func = trampoline;
}
uptr indirect_address = 0;
#if SANITIZER_WINDOWS64
indirect_address = AllocateMemoryForTrampoline(old_func, kAddressLength);
if (!indirect_address)
return false;
#endif
DWORD protection = 0;
if (!ChangeMemoryProtection(header, patch_length, &protection))
return false;
WriteBranch(header, indirect_address, new_func);
WriteShortJumpInstruction(old_func, header);
if (!RestoreMemoryProtection(header, patch_length, protection))
return false;
return true;
}
bool OverrideFunctionWithTrampoline(
uptr old_func, uptr new_func, uptr *orig_old_func) {
size_t instructions_length = kBranchLength;
size_t padding_length = 0;
uptr indirect_address = 0;
if (orig_old_func) {
instructions_length = RoundUpToInstrBoundary(kBranchLength, old_func);
if (!instructions_length)
return false;
uptr trampoline_length = instructions_length + kDirectBranchLength;
uptr trampoline = AllocateMemoryForTrampoline(old_func, trampoline_length);
if (!trampoline)
return false;
if (!CopyInstructions(trampoline, old_func, instructions_length))
return false;
WriteDirectBranch(trampoline + instructions_length,
old_func + instructions_length);
*orig_old_func = trampoline;
}
#if SANITIZER_WINDOWS64
if (IsMemoryPadding(old_func - kAddressLength, kAddressLength)) {
indirect_address = old_func - kAddressLength;
padding_length = kAddressLength;
} else {
indirect_address = AllocateMemoryForTrampoline(old_func, kAddressLength);
if (!indirect_address)
return false;
}
#endif
uptr patch_address = old_func - padding_length;
uptr patch_length = instructions_length + padding_length;
DWORD protection = 0;
if (!ChangeMemoryProtection(patch_address, patch_length, &protection))
return false;
WriteBranch(old_func, indirect_address, new_func);
if (!RestoreMemoryProtection(patch_address, patch_length, protection))
return false;
return true;
}
bool OverrideFunction(
uptr old_func, uptr new_func, uptr *orig_old_func) {
#if !SANITIZER_WINDOWS64
if (OverrideFunctionWithDetour(old_func, new_func, orig_old_func))
return true;
#endif
if (OverrideFunctionWithRedirectJump(old_func, new_func, orig_old_func))
return true;
if (OverrideFunctionWithHotPatch(old_func, new_func, orig_old_func))
return true;
if (OverrideFunctionWithTrampoline(old_func, new_func, orig_old_func))
return true;
return false;
}
static void **InterestingDLLsAvailable() {
static const char *InterestingDLLs[] = {
"kernel32.dll",
"msvcr100d.dll",
"msvcr110d.dll",
"msvcr120d.dll",
"vcruntime140d.dll",
"ucrtbased.dll",
"msvcr100.dll",
"msvcr110.dll",
"msvcr120.dll",
"vcruntime140.dll",
"ucrtbase.dll",
# if (defined(__MINGW32__) && defined(__i386__))
"libc++.dll",
"libunwind.dll",
# endif
"ntdll.dll",
NULL
};
static void *result[ARRAY_SIZE(InterestingDLLs)] = { 0 };
if (!result[0]) {
for (size_t i = 0, j = 0; InterestingDLLs[i]; ++i) {
if (HMODULE h = GetModuleHandleA(InterestingDLLs[i]))
result[j++] = (void *)h;
}
}
return &result[0];
}
namespace {
template <typename T> class RVAPtr {
public:
RVAPtr(void *module, uptr rva)
: ptr_(reinterpret_cast<T *>(reinterpret_cast<char *>(module) + rva)) {}
operator T *() { return ptr_; }
T *operator->() { return ptr_; }
T *operator++() { return ++ptr_; }
private:
T *ptr_;
};
}
uptr InternalGetProcAddress(void *module, const char *func_name) {
RVAPtr<IMAGE_DOS_HEADER> dos_stub(module, 0);
RVAPtr<IMAGE_NT_HEADERS> headers(module, dos_stub->e_lfanew);
if (!module || dos_stub->e_magic != IMAGE_DOS_SIGNATURE ||
headers->Signature != IMAGE_NT_SIGNATURE ||
headers->FileHeader.SizeOfOptionalHeader <
sizeof(IMAGE_OPTIONAL_HEADER)) {
return 0;
}
IMAGE_DATA_DIRECTORY *export_directory =
&headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
if (export_directory->Size == 0)
return 0;
RVAPtr<IMAGE_EXPORT_DIRECTORY> exports(module,
export_directory->VirtualAddress);
RVAPtr<DWORD> functions(module, exports->AddressOfFunctions);
RVAPtr<DWORD> names(module, exports->AddressOfNames);
RVAPtr<WORD> ordinals(module, exports->AddressOfNameOrdinals);
for (DWORD i = 0; i < exports->NumberOfNames; i++) {
RVAPtr<char> name(module, names[i]);
if (!strcmp(func_name, name)) {
DWORD index = ordinals[i];
RVAPtr<char> func(module, functions[index]);
DWORD offset = functions[index];
if (offset >= export_directory->VirtualAddress &&
offset < export_directory->VirtualAddress + export_directory->Size) {
char function_name[256];
size_t funtion_name_length = _strlen(func);
if (funtion_name_length >= sizeof(function_name) - 1)
InterceptionFailed();
_memcpy(function_name, func, funtion_name_length);
function_name[funtion_name_length] = '\0';
char* separator = _strchr(function_name, '.');
if (!separator)
InterceptionFailed();
*separator = '\0';
void* redirected_module = GetModuleHandleA(function_name);
if (!redirected_module)
InterceptionFailed();
return InternalGetProcAddress(redirected_module, separator + 1);
}
return (uptr)(char *)func;
}
}
return 0;
}
bool OverrideFunction(
const char *func_name, uptr new_func, uptr *orig_old_func) {
bool hooked = false;
void **DLLs = InterestingDLLsAvailable();
for (size_t i = 0; DLLs[i]; ++i) {
uptr func_addr = InternalGetProcAddress(DLLs[i], func_name);
if (func_addr &&
OverrideFunction(func_addr, new_func, orig_old_func)) {
hooked = true;
}
}
return hooked;
}
bool OverrideImportedFunction(const char *module_to_patch,
const char *imported_module,
const char *function_name, uptr new_function,
uptr *orig_old_func) {
HMODULE module = GetModuleHandleA(module_to_patch);
if (!module)
return false;
RVAPtr<IMAGE_DOS_HEADER> dos_stub(module, 0);
RVAPtr<IMAGE_NT_HEADERS> headers(module, dos_stub->e_lfanew);
if (!module || dos_stub->e_magic != IMAGE_DOS_SIGNATURE ||
headers->Signature != IMAGE_NT_SIGNATURE ||
headers->FileHeader.SizeOfOptionalHeader <
sizeof(IMAGE_OPTIONAL_HEADER)) {
return false;
}
IMAGE_DATA_DIRECTORY *import_directory =
&headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
RVAPtr<IMAGE_IMPORT_DESCRIPTOR> imports(module,
import_directory->VirtualAddress);
for (; imports->FirstThunk != 0; ++imports) {
RVAPtr<const char> modname(module, imports->Name);
if (_stricmp(&*modname, imported_module) == 0)
break;
}
if (imports->FirstThunk == 0)
return false;
RVAPtr<IMAGE_THUNK_DATA> name_table(module, imports->OriginalFirstThunk);
RVAPtr<IMAGE_THUNK_DATA> iat(module, imports->FirstThunk);
for (; name_table->u1.Ordinal != 0; ++name_table, ++iat) {
if (!IMAGE_SNAP_BY_ORDINAL(name_table->u1.Ordinal)) {
RVAPtr<IMAGE_IMPORT_BY_NAME> import_by_name(
module, name_table->u1.ForwarderString);
const char *funcname = &import_by_name->Name[0];
if (strcmp(funcname, function_name) == 0)
break;
}
}
if (name_table->u1.Ordinal == 0)
return false;
if (orig_old_func)
*orig_old_func = iat->u1.AddressOfData;
DWORD old_prot, unused_prot;
if (!VirtualProtect(&iat->u1.AddressOfData, 4, PAGE_EXECUTE_READWRITE,
&old_prot))
return false;
iat->u1.AddressOfData = new_function;
if (!VirtualProtect(&iat->u1.AddressOfData, 4, old_prot, &unused_prot))
return false;
return true;
}
}
#endif