15f757f3fSDimitry Andric //===-- interception_win.cpp ------------------------------------*- C++ -*-===// 268d75effSDimitry Andric // 368d75effSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 468d75effSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 568d75effSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 668d75effSDimitry Andric // 768d75effSDimitry Andric //===----------------------------------------------------------------------===// 868d75effSDimitry Andric // 968d75effSDimitry Andric // This file is a part of AddressSanitizer, an address sanity checker. 1068d75effSDimitry Andric // 1168d75effSDimitry Andric // Windows-specific interception methods. 1268d75effSDimitry Andric // 1368d75effSDimitry Andric // This file is implementing several hooking techniques to intercept calls 1468d75effSDimitry Andric // to functions. The hooks are dynamically installed by modifying the assembly 1568d75effSDimitry Andric // code. 1668d75effSDimitry Andric // 1768d75effSDimitry Andric // The hooking techniques are making assumptions on the way the code is 1868d75effSDimitry Andric // generated and are safe under these assumptions. 1968d75effSDimitry Andric // 2068d75effSDimitry Andric // On 64-bit architecture, there is no direct 64-bit jump instruction. To allow 2168d75effSDimitry Andric // arbitrary branching on the whole memory space, the notion of trampoline 2268d75effSDimitry Andric // region is used. A trampoline region is a memory space withing 2G boundary 2368d75effSDimitry Andric // where it is safe to add custom assembly code to build 64-bit jumps. 2468d75effSDimitry Andric // 2568d75effSDimitry Andric // Hooking techniques 2668d75effSDimitry Andric // ================== 2768d75effSDimitry Andric // 2868d75effSDimitry Andric // 1) Detour 2968d75effSDimitry Andric // 3068d75effSDimitry Andric // The Detour hooking technique is assuming the presence of an header with 3168d75effSDimitry Andric // padding and an overridable 2-bytes nop instruction (mov edi, edi). The 3268d75effSDimitry Andric // nop instruction can safely be replaced by a 2-bytes jump without any need 3368d75effSDimitry Andric // to save the instruction. A jump to the target is encoded in the function 3468d75effSDimitry Andric // header and the nop instruction is replaced by a short jump to the header. 3568d75effSDimitry Andric // 3668d75effSDimitry Andric // head: 5 x nop head: jmp <hook> 3768d75effSDimitry Andric // func: mov edi, edi --> func: jmp short <head> 3868d75effSDimitry Andric // [...] real: [...] 3968d75effSDimitry Andric // 4068d75effSDimitry Andric // This technique is only implemented on 32-bit architecture. 4168d75effSDimitry Andric // Most of the time, Windows API are hookable with the detour technique. 4268d75effSDimitry Andric // 4368d75effSDimitry Andric // 2) Redirect Jump 4468d75effSDimitry Andric // 4568d75effSDimitry Andric // The redirect jump is applicable when the first instruction is a direct 4668d75effSDimitry Andric // jump. The instruction is replaced by jump to the hook. 4768d75effSDimitry Andric // 4868d75effSDimitry Andric // func: jmp <label> --> func: jmp <hook> 4968d75effSDimitry Andric // 5068d75effSDimitry Andric // On an 64-bit architecture, a trampoline is inserted. 5168d75effSDimitry Andric // 5268d75effSDimitry Andric // func: jmp <label> --> func: jmp <tramp> 5368d75effSDimitry Andric // [...] 5468d75effSDimitry Andric // 5568d75effSDimitry Andric // [trampoline] 5668d75effSDimitry Andric // tramp: jmp QWORD [addr] 5768d75effSDimitry Andric // addr: .bytes <hook> 5868d75effSDimitry Andric // 59349cc55cSDimitry Andric // Note: <real> is equivalent to <label>. 6068d75effSDimitry Andric // 6168d75effSDimitry Andric // 3) HotPatch 6268d75effSDimitry Andric // 6368d75effSDimitry Andric // The HotPatch hooking is assuming the presence of an header with padding 6468d75effSDimitry Andric // and a first instruction with at least 2-bytes. 6568d75effSDimitry Andric // 6668d75effSDimitry Andric // The reason to enforce the 2-bytes limitation is to provide the minimal 6768d75effSDimitry Andric // space to encode a short jump. HotPatch technique is only rewriting one 6868d75effSDimitry Andric // instruction to avoid breaking a sequence of instructions containing a 6968d75effSDimitry Andric // branching target. 7068d75effSDimitry Andric // 7168d75effSDimitry Andric // Assumptions are enforced by MSVC compiler by using the /HOTPATCH flag. 7268d75effSDimitry Andric // see: https://msdn.microsoft.com/en-us/library/ms173507.aspx 7368d75effSDimitry Andric // Default padding length is 5 bytes in 32-bits and 6 bytes in 64-bits. 7468d75effSDimitry Andric // 7568d75effSDimitry Andric // head: 5 x nop head: jmp <hook> 7668d75effSDimitry Andric // func: <instr> --> func: jmp short <head> 7768d75effSDimitry Andric // [...] body: [...] 7868d75effSDimitry Andric // 7968d75effSDimitry Andric // [trampoline] 8068d75effSDimitry Andric // real: <instr> 8168d75effSDimitry Andric // jmp <body> 8268d75effSDimitry Andric // 8368d75effSDimitry Andric // On an 64-bit architecture: 8468d75effSDimitry Andric // 8568d75effSDimitry Andric // head: 6 x nop head: jmp QWORD [addr1] 8668d75effSDimitry Andric // func: <instr> --> func: jmp short <head> 8768d75effSDimitry Andric // [...] body: [...] 8868d75effSDimitry Andric // 8968d75effSDimitry Andric // [trampoline] 9068d75effSDimitry Andric // addr1: .bytes <hook> 9168d75effSDimitry Andric // real: <instr> 9268d75effSDimitry Andric // jmp QWORD [addr2] 9368d75effSDimitry Andric // addr2: .bytes <body> 9468d75effSDimitry Andric // 9568d75effSDimitry Andric // 4) Trampoline 9668d75effSDimitry Andric // 9768d75effSDimitry Andric // The Trampoline hooking technique is the most aggressive one. It is 9868d75effSDimitry Andric // assuming that there is a sequence of instructions that can be safely 9968d75effSDimitry Andric // replaced by a jump (enough room and no incoming branches). 10068d75effSDimitry Andric // 10168d75effSDimitry Andric // Unfortunately, these assumptions can't be safely presumed and code may 10268d75effSDimitry Andric // be broken after hooking. 10368d75effSDimitry Andric // 10468d75effSDimitry Andric // func: <instr> --> func: jmp <hook> 10568d75effSDimitry Andric // <instr> 10668d75effSDimitry Andric // [...] body: [...] 10768d75effSDimitry Andric // 10868d75effSDimitry Andric // [trampoline] 10968d75effSDimitry Andric // real: <instr> 11068d75effSDimitry Andric // <instr> 11168d75effSDimitry Andric // jmp <body> 11268d75effSDimitry Andric // 11368d75effSDimitry Andric // On an 64-bit architecture: 11468d75effSDimitry Andric // 11568d75effSDimitry Andric // func: <instr> --> func: jmp QWORD [addr1] 11668d75effSDimitry Andric // <instr> 11768d75effSDimitry Andric // [...] body: [...] 11868d75effSDimitry Andric // 11968d75effSDimitry Andric // [trampoline] 12068d75effSDimitry Andric // addr1: .bytes <hook> 12168d75effSDimitry Andric // real: <instr> 12268d75effSDimitry Andric // <instr> 12368d75effSDimitry Andric // jmp QWORD [addr2] 12468d75effSDimitry Andric // addr2: .bytes <body> 12568d75effSDimitry Andric //===----------------------------------------------------------------------===// 12668d75effSDimitry Andric 12768d75effSDimitry Andric #include "interception.h" 12868d75effSDimitry Andric 12968d75effSDimitry Andric #if SANITIZER_WINDOWS 13068d75effSDimitry Andric #include "sanitizer_common/sanitizer_platform.h" 13168d75effSDimitry Andric #define WIN32_LEAN_AND_MEAN 13268d75effSDimitry Andric #include <windows.h> 13368d75effSDimitry Andric 13468d75effSDimitry Andric namespace __interception { 13568d75effSDimitry Andric 13668d75effSDimitry Andric static const int kAddressLength = FIRST_32_SECOND_64(4, 8); 13768d75effSDimitry Andric static const int kJumpInstructionLength = 5; 13868d75effSDimitry Andric static const int kShortJumpInstructionLength = 2; 139e8d8bef9SDimitry Andric UNUSED static const int kIndirectJumpInstructionLength = 6; 14068d75effSDimitry Andric static const int kBranchLength = 14168d75effSDimitry Andric FIRST_32_SECOND_64(kJumpInstructionLength, kIndirectJumpInstructionLength); 14268d75effSDimitry Andric static const int kDirectBranchLength = kBranchLength + kAddressLength; 14368d75effSDimitry Andric 14406c3fb27SDimitry Andric # if defined(_MSC_VER) 14506c3fb27SDimitry Andric # define INTERCEPTION_FORMAT(f, a) 14606c3fb27SDimitry Andric # else 14706c3fb27SDimitry Andric # define INTERCEPTION_FORMAT(f, a) __attribute__((format(printf, f, a))) 14806c3fb27SDimitry Andric # endif 14906c3fb27SDimitry Andric 15006c3fb27SDimitry Andric static void (*ErrorReportCallback)(const char *format, ...) 15106c3fb27SDimitry Andric INTERCEPTION_FORMAT(1, 2); 15206c3fb27SDimitry Andric 15306c3fb27SDimitry Andric void SetErrorReportCallback(void (*callback)(const char *format, ...)) { 15406c3fb27SDimitry Andric ErrorReportCallback = callback; 15506c3fb27SDimitry Andric } 15606c3fb27SDimitry Andric 15706c3fb27SDimitry Andric # define ReportError(...) \ 15806c3fb27SDimitry Andric do { \ 15906c3fb27SDimitry Andric if (ErrorReportCallback) \ 16006c3fb27SDimitry Andric ErrorReportCallback(__VA_ARGS__); \ 16106c3fb27SDimitry Andric } while (0) 16206c3fb27SDimitry Andric 16368d75effSDimitry Andric static void InterceptionFailed() { 16406c3fb27SDimitry Andric ReportError("interception_win: failed due to an unrecoverable error.\n"); 16506c3fb27SDimitry Andric // This acts like an abort when no debugger is attached. According to an old 16606c3fb27SDimitry Andric // comment, calling abort() leads to an infinite recursion in CheckFailed. 16768d75effSDimitry Andric __debugbreak(); 16868d75effSDimitry Andric } 16968d75effSDimitry Andric 17068d75effSDimitry Andric static bool DistanceIsWithin2Gig(uptr from, uptr target) { 17168d75effSDimitry Andric #if SANITIZER_WINDOWS64 17268d75effSDimitry Andric if (from < target) 17368d75effSDimitry Andric return target - from <= (uptr)0x7FFFFFFFU; 17468d75effSDimitry Andric else 17568d75effSDimitry Andric return from - target <= (uptr)0x80000000U; 17668d75effSDimitry Andric #else 17768d75effSDimitry Andric // In a 32-bit address space, the address calculation will wrap, so this check 17868d75effSDimitry Andric // is unnecessary. 17968d75effSDimitry Andric return true; 18068d75effSDimitry Andric #endif 18168d75effSDimitry Andric } 18268d75effSDimitry Andric 18368d75effSDimitry Andric static uptr GetMmapGranularity() { 18468d75effSDimitry Andric SYSTEM_INFO si; 18568d75effSDimitry Andric GetSystemInfo(&si); 18668d75effSDimitry Andric return si.dwAllocationGranularity; 18768d75effSDimitry Andric } 18868d75effSDimitry Andric 189e8d8bef9SDimitry Andric UNUSED static uptr RoundUpTo(uptr size, uptr boundary) { 19068d75effSDimitry Andric return (size + boundary - 1) & ~(boundary - 1); 19168d75effSDimitry Andric } 19268d75effSDimitry Andric 19368d75effSDimitry Andric // FIXME: internal_str* and internal_mem* functions should be moved from the 19468d75effSDimitry Andric // ASan sources into interception/. 19568d75effSDimitry Andric 19668d75effSDimitry Andric static size_t _strlen(const char *str) { 19768d75effSDimitry Andric const char* p = str; 19868d75effSDimitry Andric while (*p != '\0') ++p; 19968d75effSDimitry Andric return p - str; 20068d75effSDimitry Andric } 20168d75effSDimitry Andric 20268d75effSDimitry Andric static char* _strchr(char* str, char c) { 20368d75effSDimitry Andric while (*str) { 20468d75effSDimitry Andric if (*str == c) 20568d75effSDimitry Andric return str; 20668d75effSDimitry Andric ++str; 20768d75effSDimitry Andric } 20868d75effSDimitry Andric return nullptr; 20968d75effSDimitry Andric } 21068d75effSDimitry Andric 21168d75effSDimitry Andric static void _memset(void *p, int value, size_t sz) { 21268d75effSDimitry Andric for (size_t i = 0; i < sz; ++i) 21368d75effSDimitry Andric ((char*)p)[i] = (char)value; 21468d75effSDimitry Andric } 21568d75effSDimitry Andric 21668d75effSDimitry Andric static void _memcpy(void *dst, void *src, size_t sz) { 21768d75effSDimitry Andric char *dst_c = (char*)dst, 21868d75effSDimitry Andric *src_c = (char*)src; 21968d75effSDimitry Andric for (size_t i = 0; i < sz; ++i) 22068d75effSDimitry Andric dst_c[i] = src_c[i]; 22168d75effSDimitry Andric } 22268d75effSDimitry Andric 22368d75effSDimitry Andric static bool ChangeMemoryProtection( 22468d75effSDimitry Andric uptr address, uptr size, DWORD *old_protection) { 22568d75effSDimitry Andric return ::VirtualProtect((void*)address, size, 22668d75effSDimitry Andric PAGE_EXECUTE_READWRITE, 22768d75effSDimitry Andric old_protection) != FALSE; 22868d75effSDimitry Andric } 22968d75effSDimitry Andric 23068d75effSDimitry Andric static bool RestoreMemoryProtection( 23168d75effSDimitry Andric uptr address, uptr size, DWORD old_protection) { 23268d75effSDimitry Andric DWORD unused; 23368d75effSDimitry Andric return ::VirtualProtect((void*)address, size, 23468d75effSDimitry Andric old_protection, 23568d75effSDimitry Andric &unused) != FALSE; 23668d75effSDimitry Andric } 23768d75effSDimitry Andric 23868d75effSDimitry Andric static bool IsMemoryPadding(uptr address, uptr size) { 23968d75effSDimitry Andric u8* function = (u8*)address; 24068d75effSDimitry Andric for (size_t i = 0; i < size; ++i) 24168d75effSDimitry Andric if (function[i] != 0x90 && function[i] != 0xCC) 24268d75effSDimitry Andric return false; 24368d75effSDimitry Andric return true; 24468d75effSDimitry Andric } 24568d75effSDimitry Andric 24668d75effSDimitry Andric static const u8 kHintNop8Bytes[] = { 24768d75effSDimitry Andric 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00 24868d75effSDimitry Andric }; 24968d75effSDimitry Andric 25068d75effSDimitry Andric template<class T> 25168d75effSDimitry Andric static bool FunctionHasPrefix(uptr address, const T &pattern) { 25268d75effSDimitry Andric u8* function = (u8*)address - sizeof(pattern); 25368d75effSDimitry Andric for (size_t i = 0; i < sizeof(pattern); ++i) 25468d75effSDimitry Andric if (function[i] != pattern[i]) 25568d75effSDimitry Andric return false; 25668d75effSDimitry Andric return true; 25768d75effSDimitry Andric } 25868d75effSDimitry Andric 25968d75effSDimitry Andric static bool FunctionHasPadding(uptr address, uptr size) { 26068d75effSDimitry Andric if (IsMemoryPadding(address - size, size)) 26168d75effSDimitry Andric return true; 26268d75effSDimitry Andric if (size <= sizeof(kHintNop8Bytes) && 26368d75effSDimitry Andric FunctionHasPrefix(address, kHintNop8Bytes)) 26468d75effSDimitry Andric return true; 26568d75effSDimitry Andric return false; 26668d75effSDimitry Andric } 26768d75effSDimitry Andric 26868d75effSDimitry Andric static void WritePadding(uptr from, uptr size) { 26968d75effSDimitry Andric _memset((void*)from, 0xCC, (size_t)size); 27068d75effSDimitry Andric } 27168d75effSDimitry Andric 27268d75effSDimitry Andric static void WriteJumpInstruction(uptr from, uptr target) { 27306c3fb27SDimitry Andric if (!DistanceIsWithin2Gig(from + kJumpInstructionLength, target)) { 27406c3fb27SDimitry Andric ReportError( 27506c3fb27SDimitry Andric "interception_win: cannot write jmp further than 2GB away, from %p to " 27606c3fb27SDimitry Andric "%p.\n", 27706c3fb27SDimitry Andric (void *)from, (void *)target); 27868d75effSDimitry Andric InterceptionFailed(); 27906c3fb27SDimitry Andric } 28068d75effSDimitry Andric ptrdiff_t offset = target - from - kJumpInstructionLength; 28168d75effSDimitry Andric *(u8*)from = 0xE9; 28268d75effSDimitry Andric *(u32*)(from + 1) = offset; 28368d75effSDimitry Andric } 28468d75effSDimitry Andric 28568d75effSDimitry Andric static void WriteShortJumpInstruction(uptr from, uptr target) { 28668d75effSDimitry Andric sptr offset = target - from - kShortJumpInstructionLength; 28768d75effSDimitry Andric if (offset < -128 || offset > 127) 28868d75effSDimitry Andric InterceptionFailed(); 28968d75effSDimitry Andric *(u8*)from = 0xEB; 29068d75effSDimitry Andric *(u8*)(from + 1) = (u8)offset; 29168d75effSDimitry Andric } 29268d75effSDimitry Andric 29368d75effSDimitry Andric #if SANITIZER_WINDOWS64 29468d75effSDimitry Andric static void WriteIndirectJumpInstruction(uptr from, uptr indirect_target) { 29568d75effSDimitry Andric // jmp [rip + <offset>] = FF 25 <offset> where <offset> is a relative 29668d75effSDimitry Andric // offset. 29768d75effSDimitry Andric // The offset is the distance from then end of the jump instruction to the 29868d75effSDimitry Andric // memory location containing the targeted address. The displacement is still 29968d75effSDimitry Andric // 32-bit in x64, so indirect_target must be located within +/- 2GB range. 30068d75effSDimitry Andric int offset = indirect_target - from - kIndirectJumpInstructionLength; 30168d75effSDimitry Andric if (!DistanceIsWithin2Gig(from + kIndirectJumpInstructionLength, 30268d75effSDimitry Andric indirect_target)) { 30306c3fb27SDimitry Andric ReportError( 30406c3fb27SDimitry Andric "interception_win: cannot write indirect jmp with target further than " 30506c3fb27SDimitry Andric "2GB away, from %p to %p.\n", 30606c3fb27SDimitry Andric (void *)from, (void *)indirect_target); 30768d75effSDimitry Andric InterceptionFailed(); 30868d75effSDimitry Andric } 30968d75effSDimitry Andric *(u16*)from = 0x25FF; 31068d75effSDimitry Andric *(u32*)(from + 2) = offset; 31168d75effSDimitry Andric } 31268d75effSDimitry Andric #endif 31368d75effSDimitry Andric 31468d75effSDimitry Andric static void WriteBranch( 31568d75effSDimitry Andric uptr from, uptr indirect_target, uptr target) { 31668d75effSDimitry Andric #if SANITIZER_WINDOWS64 31768d75effSDimitry Andric WriteIndirectJumpInstruction(from, indirect_target); 31868d75effSDimitry Andric *(u64*)indirect_target = target; 31968d75effSDimitry Andric #else 32068d75effSDimitry Andric (void)indirect_target; 32168d75effSDimitry Andric WriteJumpInstruction(from, target); 32268d75effSDimitry Andric #endif 32368d75effSDimitry Andric } 32468d75effSDimitry Andric 32568d75effSDimitry Andric static void WriteDirectBranch(uptr from, uptr target) { 32668d75effSDimitry Andric #if SANITIZER_WINDOWS64 32768d75effSDimitry Andric // Emit an indirect jump through immediately following bytes: 32868d75effSDimitry Andric // jmp [rip + kBranchLength] 32968d75effSDimitry Andric // .quad <target> 33068d75effSDimitry Andric WriteBranch(from, from + kBranchLength, target); 33168d75effSDimitry Andric #else 33268d75effSDimitry Andric WriteJumpInstruction(from, target); 33368d75effSDimitry Andric #endif 33468d75effSDimitry Andric } 33568d75effSDimitry Andric 33668d75effSDimitry Andric struct TrampolineMemoryRegion { 33768d75effSDimitry Andric uptr content; 33868d75effSDimitry Andric uptr allocated_size; 33968d75effSDimitry Andric uptr max_size; 34068d75effSDimitry Andric }; 34168d75effSDimitry Andric 342*0fca6ea1SDimitry Andric UNUSED static const uptr kTrampolineScanLimitRange = 1ull << 31; // 2 gig 34368d75effSDimitry Andric static const int kMaxTrampolineRegion = 1024; 34468d75effSDimitry Andric static TrampolineMemoryRegion TrampolineRegions[kMaxTrampolineRegion]; 34568d75effSDimitry Andric 34668d75effSDimitry Andric static void *AllocateTrampolineRegion(uptr image_address, size_t granularity) { 34768d75effSDimitry Andric #if SANITIZER_WINDOWS64 34868d75effSDimitry Andric uptr address = image_address; 34968d75effSDimitry Andric uptr scanned = 0; 35068d75effSDimitry Andric while (scanned < kTrampolineScanLimitRange) { 35168d75effSDimitry Andric MEMORY_BASIC_INFORMATION info; 35268d75effSDimitry Andric if (!::VirtualQuery((void*)address, &info, sizeof(info))) 35368d75effSDimitry Andric return nullptr; 35468d75effSDimitry Andric 35568d75effSDimitry Andric // Check whether a region can be allocated at |address|. 35668d75effSDimitry Andric if (info.State == MEM_FREE && info.RegionSize >= granularity) { 35768d75effSDimitry Andric void *page = ::VirtualAlloc((void*)RoundUpTo(address, granularity), 35868d75effSDimitry Andric granularity, 35968d75effSDimitry Andric MEM_RESERVE | MEM_COMMIT, 36068d75effSDimitry Andric PAGE_EXECUTE_READWRITE); 36168d75effSDimitry Andric return page; 36268d75effSDimitry Andric } 36368d75effSDimitry Andric 36468d75effSDimitry Andric // Move to the next region. 36568d75effSDimitry Andric address = (uptr)info.BaseAddress + info.RegionSize; 36668d75effSDimitry Andric scanned += info.RegionSize; 36768d75effSDimitry Andric } 36868d75effSDimitry Andric return nullptr; 36968d75effSDimitry Andric #else 37068d75effSDimitry Andric return ::VirtualAlloc(nullptr, 37168d75effSDimitry Andric granularity, 37268d75effSDimitry Andric MEM_RESERVE | MEM_COMMIT, 37368d75effSDimitry Andric PAGE_EXECUTE_READWRITE); 37468d75effSDimitry Andric #endif 37568d75effSDimitry Andric } 37668d75effSDimitry Andric 37768d75effSDimitry Andric // Used by unittests to release mapped memory space. 37868d75effSDimitry Andric void TestOnlyReleaseTrampolineRegions() { 37968d75effSDimitry Andric for (size_t bucket = 0; bucket < kMaxTrampolineRegion; ++bucket) { 38068d75effSDimitry Andric TrampolineMemoryRegion *current = &TrampolineRegions[bucket]; 38168d75effSDimitry Andric if (current->content == 0) 38268d75effSDimitry Andric return; 38368d75effSDimitry Andric ::VirtualFree((void*)current->content, 0, MEM_RELEASE); 38468d75effSDimitry Andric current->content = 0; 38568d75effSDimitry Andric } 38668d75effSDimitry Andric } 38768d75effSDimitry Andric 38868d75effSDimitry Andric static uptr AllocateMemoryForTrampoline(uptr image_address, size_t size) { 38968d75effSDimitry Andric // Find a region within 2G with enough space to allocate |size| bytes. 39068d75effSDimitry Andric TrampolineMemoryRegion *region = nullptr; 39168d75effSDimitry Andric for (size_t bucket = 0; bucket < kMaxTrampolineRegion; ++bucket) { 39268d75effSDimitry Andric TrampolineMemoryRegion* current = &TrampolineRegions[bucket]; 39368d75effSDimitry Andric if (current->content == 0) { 39468d75effSDimitry Andric // No valid region found, allocate a new region. 39568d75effSDimitry Andric size_t bucket_size = GetMmapGranularity(); 39668d75effSDimitry Andric void *content = AllocateTrampolineRegion(image_address, bucket_size); 39768d75effSDimitry Andric if (content == nullptr) 39868d75effSDimitry Andric return 0U; 39968d75effSDimitry Andric 40068d75effSDimitry Andric current->content = (uptr)content; 40168d75effSDimitry Andric current->allocated_size = 0; 40268d75effSDimitry Andric current->max_size = bucket_size; 40368d75effSDimitry Andric region = current; 40468d75effSDimitry Andric break; 40568d75effSDimitry Andric } else if (current->max_size - current->allocated_size > size) { 40668d75effSDimitry Andric #if SANITIZER_WINDOWS64 40768d75effSDimitry Andric // In 64-bits, the memory space must be allocated within 2G boundary. 40868d75effSDimitry Andric uptr next_address = current->content + current->allocated_size; 40968d75effSDimitry Andric if (next_address < image_address || 41068d75effSDimitry Andric next_address - image_address >= 0x7FFF0000) 41168d75effSDimitry Andric continue; 41268d75effSDimitry Andric #endif 41368d75effSDimitry Andric // The space can be allocated in the current region. 41468d75effSDimitry Andric region = current; 41568d75effSDimitry Andric break; 41668d75effSDimitry Andric } 41768d75effSDimitry Andric } 41868d75effSDimitry Andric 41968d75effSDimitry Andric // Failed to find a region. 42068d75effSDimitry Andric if (region == nullptr) 42168d75effSDimitry Andric return 0U; 42268d75effSDimitry Andric 42368d75effSDimitry Andric // Allocate the space in the current region. 42468d75effSDimitry Andric uptr allocated_space = region->content + region->allocated_size; 42568d75effSDimitry Andric region->allocated_size += size; 42668d75effSDimitry Andric WritePadding(allocated_space, size); 42768d75effSDimitry Andric 42868d75effSDimitry Andric return allocated_space; 42968d75effSDimitry Andric } 43068d75effSDimitry Andric 431349cc55cSDimitry Andric // The following prologues cannot be patched because of the short jump 432349cc55cSDimitry Andric // jumping to the patching region. 433349cc55cSDimitry Andric 4345f757f3fSDimitry Andric // Short jump patterns below are only for x86_64. 4355f757f3fSDimitry Andric # if SANITIZER_WINDOWS_x64 436349cc55cSDimitry Andric // ntdll!wcslen in Win11 437349cc55cSDimitry Andric // 488bc1 mov rax,rcx 438349cc55cSDimitry Andric // 0fb710 movzx edx,word ptr [rax] 439349cc55cSDimitry Andric // 4883c002 add rax,2 440349cc55cSDimitry Andric // 6685d2 test dx,dx 441349cc55cSDimitry Andric // 75f4 jne -12 442349cc55cSDimitry Andric static const u8 kPrologueWithShortJump1[] = { 443349cc55cSDimitry Andric 0x48, 0x8b, 0xc1, 0x0f, 0xb7, 0x10, 0x48, 0x83, 444349cc55cSDimitry Andric 0xc0, 0x02, 0x66, 0x85, 0xd2, 0x75, 0xf4, 445349cc55cSDimitry Andric }; 446349cc55cSDimitry Andric 447349cc55cSDimitry Andric // ntdll!strrchr in Win11 448349cc55cSDimitry Andric // 4c8bc1 mov r8,rcx 449349cc55cSDimitry Andric // 8a01 mov al,byte ptr [rcx] 450349cc55cSDimitry Andric // 48ffc1 inc rcx 451349cc55cSDimitry Andric // 84c0 test al,al 452349cc55cSDimitry Andric // 75f7 jne -9 453349cc55cSDimitry Andric static const u8 kPrologueWithShortJump2[] = { 454349cc55cSDimitry Andric 0x4c, 0x8b, 0xc1, 0x8a, 0x01, 0x48, 0xff, 0xc1, 455349cc55cSDimitry Andric 0x84, 0xc0, 0x75, 0xf7, 456349cc55cSDimitry Andric }; 4571fd87a68SDimitry Andric #endif 458349cc55cSDimitry Andric 45968d75effSDimitry Andric // Returns 0 on error. 46068d75effSDimitry Andric static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) { 4615f757f3fSDimitry Andric #if SANITIZER_ARM64 4625f757f3fSDimitry Andric // An ARM64 instruction is 4 bytes long. 4635f757f3fSDimitry Andric return 4; 4645f757f3fSDimitry Andric #endif 4655f757f3fSDimitry Andric 4665f757f3fSDimitry Andric # if SANITIZER_WINDOWS_x64 467349cc55cSDimitry Andric if (memcmp((u8*)address, kPrologueWithShortJump1, 468349cc55cSDimitry Andric sizeof(kPrologueWithShortJump1)) == 0 || 469349cc55cSDimitry Andric memcmp((u8*)address, kPrologueWithShortJump2, 470349cc55cSDimitry Andric sizeof(kPrologueWithShortJump2)) == 0) { 471349cc55cSDimitry Andric return 0; 472349cc55cSDimitry Andric } 473349cc55cSDimitry Andric #endif 474349cc55cSDimitry Andric 47568d75effSDimitry Andric switch (*(u64*)address) { 47668d75effSDimitry Andric case 0x90909090909006EB: // stub: jmp over 6 x nop. 47768d75effSDimitry Andric return 8; 47868d75effSDimitry Andric } 47968d75effSDimitry Andric 48068d75effSDimitry Andric switch (*(u8*)address) { 48168d75effSDimitry Andric case 0x90: // 90 : nop 482*0fca6ea1SDimitry Andric case 0xC3: // C3 : ret (for small/empty function interception 483*0fca6ea1SDimitry Andric case 0xCC: // CC : int 3 i.e. registering weak functions) 48468d75effSDimitry Andric return 1; 48568d75effSDimitry Andric 48668d75effSDimitry Andric case 0x50: // push eax / rax 48768d75effSDimitry Andric case 0x51: // push ecx / rcx 48868d75effSDimitry Andric case 0x52: // push edx / rdx 48968d75effSDimitry Andric case 0x53: // push ebx / rbx 49068d75effSDimitry Andric case 0x54: // push esp / rsp 49168d75effSDimitry Andric case 0x55: // push ebp / rbp 49268d75effSDimitry Andric case 0x56: // push esi / rsi 49368d75effSDimitry Andric case 0x57: // push edi / rdi 49468d75effSDimitry Andric case 0x5D: // pop ebp / rbp 49568d75effSDimitry Andric return 1; 49668d75effSDimitry Andric 49768d75effSDimitry Andric case 0x6A: // 6A XX = push XX 49868d75effSDimitry Andric return 2; 49968d75effSDimitry Andric 50068d75effSDimitry Andric case 0xb8: // b8 XX XX XX XX : mov eax, XX XX XX XX 50168d75effSDimitry Andric case 0xB9: // b9 XX XX XX XX : mov ecx, XX XX XX XX 50268d75effSDimitry Andric return 5; 50368d75effSDimitry Andric 50468d75effSDimitry Andric // Cannot overwrite control-instruction. Return 0 to indicate failure. 50568d75effSDimitry Andric case 0xE9: // E9 XX XX XX XX : jmp <label> 50668d75effSDimitry Andric case 0xE8: // E8 XX XX XX XX : call <func> 50768d75effSDimitry Andric case 0xEB: // EB XX : jmp XX (short jump) 50868d75effSDimitry Andric case 0x70: // 7Y YY : jy XX (short conditional jump) 50968d75effSDimitry Andric case 0x71: 51068d75effSDimitry Andric case 0x72: 51168d75effSDimitry Andric case 0x73: 51268d75effSDimitry Andric case 0x74: 51368d75effSDimitry Andric case 0x75: 51468d75effSDimitry Andric case 0x76: 51568d75effSDimitry Andric case 0x77: 51668d75effSDimitry Andric case 0x78: 51768d75effSDimitry Andric case 0x79: 51868d75effSDimitry Andric case 0x7A: 51968d75effSDimitry Andric case 0x7B: 52068d75effSDimitry Andric case 0x7C: 52168d75effSDimitry Andric case 0x7D: 52268d75effSDimitry Andric case 0x7E: 52368d75effSDimitry Andric case 0x7F: 52468d75effSDimitry Andric return 0; 52568d75effSDimitry Andric } 52668d75effSDimitry Andric 52768d75effSDimitry Andric switch (*(u16*)(address)) { 52868d75effSDimitry Andric case 0x018A: // 8A 01 : mov al, byte ptr [ecx] 52968d75effSDimitry Andric case 0xFF8B: // 8B FF : mov edi, edi 53068d75effSDimitry Andric case 0xEC8B: // 8B EC : mov ebp, esp 53168d75effSDimitry Andric case 0xc889: // 89 C8 : mov eax, ecx 53206c3fb27SDimitry Andric case 0xE589: // 89 E5 : mov ebp, esp 53368d75effSDimitry Andric case 0xC18B: // 8B C1 : mov eax, ecx 53468d75effSDimitry Andric case 0xC033: // 33 C0 : xor eax, eax 53568d75effSDimitry Andric case 0xC933: // 33 C9 : xor ecx, ecx 53668d75effSDimitry Andric case 0xD233: // 33 D2 : xor edx, edx 53768d75effSDimitry Andric return 2; 53868d75effSDimitry Andric 53968d75effSDimitry Andric // Cannot overwrite control-instruction. Return 0 to indicate failure. 54068d75effSDimitry Andric case 0x25FF: // FF 25 XX XX XX XX : jmp [XXXXXXXX] 54168d75effSDimitry Andric return 0; 54268d75effSDimitry Andric } 54368d75effSDimitry Andric 54468d75effSDimitry Andric switch (0x00FFFFFF & *(u32*)address) { 54568d75effSDimitry Andric case 0x24A48D: // 8D A4 24 XX XX XX XX : lea esp, [esp + XX XX XX XX] 54668d75effSDimitry Andric return 7; 54768d75effSDimitry Andric } 54868d75effSDimitry Andric 549*0fca6ea1SDimitry Andric switch (0x000000FF & *(u32 *)address) { 550*0fca6ea1SDimitry Andric case 0xc2: // C2 XX XX : ret XX (needed for registering weak functions) 551*0fca6ea1SDimitry Andric return 3; 552*0fca6ea1SDimitry Andric } 553*0fca6ea1SDimitry Andric 5545f757f3fSDimitry Andric # if SANITIZER_WINDOWS_x64 55568d75effSDimitry Andric switch (*(u8*)address) { 55668d75effSDimitry Andric case 0xA1: // A1 XX XX XX XX XX XX XX XX : 55768d75effSDimitry Andric // movabs eax, dword ptr ds:[XXXXXXXX] 55868d75effSDimitry Andric return 9; 559349cc55cSDimitry Andric 560349cc55cSDimitry Andric case 0x83: 561349cc55cSDimitry Andric const u8 next_byte = *(u8*)(address + 1); 562349cc55cSDimitry Andric const u8 mod = next_byte >> 6; 563349cc55cSDimitry Andric const u8 rm = next_byte & 7; 564349cc55cSDimitry Andric if (mod == 1 && rm == 4) 565349cc55cSDimitry Andric return 5; // 83 ModR/M SIB Disp8 Imm8 566349cc55cSDimitry Andric // add|or|adc|sbb|and|sub|xor|cmp [r+disp8], imm8 56768d75effSDimitry Andric } 56868d75effSDimitry Andric 56968d75effSDimitry Andric switch (*(u16*)address) { 57068d75effSDimitry Andric case 0x5040: // push rax 57168d75effSDimitry Andric case 0x5140: // push rcx 57268d75effSDimitry Andric case 0x5240: // push rdx 57368d75effSDimitry Andric case 0x5340: // push rbx 57468d75effSDimitry Andric case 0x5440: // push rsp 57568d75effSDimitry Andric case 0x5540: // push rbp 57668d75effSDimitry Andric case 0x5640: // push rsi 57768d75effSDimitry Andric case 0x5740: // push rdi 57868d75effSDimitry Andric case 0x5441: // push r12 57968d75effSDimitry Andric case 0x5541: // push r13 58068d75effSDimitry Andric case 0x5641: // push r14 58168d75effSDimitry Andric case 0x5741: // push r15 58268d75effSDimitry Andric case 0x9066: // Two-byte NOP 583349cc55cSDimitry Andric case 0xc084: // test al, al 584349cc55cSDimitry Andric case 0x018a: // mov al, byte ptr [rcx] 58568d75effSDimitry Andric return 2; 58668d75effSDimitry Andric 5875f757f3fSDimitry Andric case 0x058A: // 8A 05 XX XX XX XX : mov al, byte ptr [XX XX XX XX] 58868d75effSDimitry Andric case 0x058B: // 8B 05 XX XX XX XX : mov eax, dword ptr [XX XX XX XX] 58968d75effSDimitry Andric if (rel_offset) 59068d75effSDimitry Andric *rel_offset = 2; 59168d75effSDimitry Andric return 6; 59268d75effSDimitry Andric } 59368d75effSDimitry Andric 59468d75effSDimitry Andric switch (0x00FFFFFF & *(u32*)address) { 59568d75effSDimitry Andric case 0xe58948: // 48 8b c4 : mov rbp, rsp 59668d75effSDimitry Andric case 0xc18b48: // 48 8b c1 : mov rax, rcx 59768d75effSDimitry Andric case 0xc48b48: // 48 8b c4 : mov rax, rsp 59868d75effSDimitry Andric case 0xd9f748: // 48 f7 d9 : neg rcx 59968d75effSDimitry Andric case 0xd12b48: // 48 2b d1 : sub rdx, rcx 60068d75effSDimitry Andric case 0x07c1f6: // f6 c1 07 : test cl, 0x7 60168d75effSDimitry Andric case 0xc98548: // 48 85 C9 : test rcx, rcx 602349cc55cSDimitry Andric case 0xd28548: // 48 85 d2 : test rdx, rdx 60368d75effSDimitry Andric case 0xc0854d: // 4d 85 c0 : test r8, r8 60468d75effSDimitry Andric case 0xc2b60f: // 0f b6 c2 : movzx eax, dl 60568d75effSDimitry Andric case 0xc03345: // 45 33 c0 : xor r8d, r8d 60668d75effSDimitry Andric case 0xc93345: // 45 33 c9 : xor r9d, r9d 60768d75effSDimitry Andric case 0xdb3345: // 45 33 DB : xor r11d, r11d 60868d75effSDimitry Andric case 0xd98b4c: // 4c 8b d9 : mov r11, rcx 60968d75effSDimitry Andric case 0xd28b4c: // 4c 8b d2 : mov r10, rdx 61068d75effSDimitry Andric case 0xc98b4c: // 4C 8B C9 : mov r9, rcx 61168d75effSDimitry Andric case 0xc18b4c: // 4C 8B C1 : mov r8, rcx 61268d75effSDimitry Andric case 0xd2b60f: // 0f b6 d2 : movzx edx, dl 61368d75effSDimitry Andric case 0xca2b48: // 48 2b ca : sub rcx, rdx 614*0fca6ea1SDimitry Andric case 0xca3b48: // 48 3b ca : cmp rcx, rdx 61568d75effSDimitry Andric case 0x10b70f: // 0f b7 10 : movzx edx, WORD PTR [rax] 61668d75effSDimitry Andric case 0xc00b4d: // 3d 0b c0 : or r8, r8 617349cc55cSDimitry Andric case 0xc08b41: // 41 8b c0 : mov eax, r8d 61868d75effSDimitry Andric case 0xd18b48: // 48 8b d1 : mov rdx, rcx 61968d75effSDimitry Andric case 0xdc8b4c: // 4c 8b dc : mov r11, rsp 62068d75effSDimitry Andric case 0xd18b4c: // 4c 8b d1 : mov r10, rcx 62168d75effSDimitry Andric case 0xE0E483: // 83 E4 E0 : and esp, 0xFFFFFFE0 62268d75effSDimitry Andric return 3; 62368d75effSDimitry Andric 62468d75effSDimitry Andric case 0xec8348: // 48 83 ec XX : sub rsp, XX 62568d75effSDimitry Andric case 0xf88349: // 49 83 f8 XX : cmp r8, XX 62668d75effSDimitry Andric case 0x588948: // 48 89 58 XX : mov QWORD PTR[rax + XX], rbx 62768d75effSDimitry Andric return 4; 62868d75effSDimitry Andric 62968d75effSDimitry Andric case 0xec8148: // 48 81 EC XX XX XX XX : sub rsp, XXXXXXXX 63068d75effSDimitry Andric return 7; 63168d75effSDimitry Andric 63268d75effSDimitry Andric case 0x058b48: // 48 8b 05 XX XX XX XX : 63368d75effSDimitry Andric // mov rax, QWORD PTR [rip + XXXXXXXX] 634*0fca6ea1SDimitry Andric case 0x058d48: // 48 8d 05 XX XX XX XX : 635*0fca6ea1SDimitry Andric // lea rax, QWORD PTR [rip + XXXXXXXX] 63668d75effSDimitry Andric case 0x25ff48: // 48 ff 25 XX XX XX XX : 63768d75effSDimitry Andric // rex.W jmp QWORD PTR [rip + XXXXXXXX] 6385f757f3fSDimitry Andric case 0x158D4C: // 4c 8d 15 XX XX XX XX : lea r10, [rip + XX] 63968d75effSDimitry Andric // Instructions having offset relative to 'rip' need offset adjustment. 64068d75effSDimitry Andric if (rel_offset) 64168d75effSDimitry Andric *rel_offset = 3; 64268d75effSDimitry Andric return 7; 64368d75effSDimitry Andric 64468d75effSDimitry Andric case 0x2444c7: // C7 44 24 XX YY YY YY YY 64568d75effSDimitry Andric // mov dword ptr [rsp + XX], YYYYYYYY 64668d75effSDimitry Andric return 8; 64768d75effSDimitry Andric } 64868d75effSDimitry Andric 64968d75effSDimitry Andric switch (*(u32*)(address)) { 65068d75effSDimitry Andric case 0x24448b48: // 48 8b 44 24 XX : mov rax, QWORD ptr [rsp + XX] 65168d75effSDimitry Andric case 0x246c8948: // 48 89 6C 24 XX : mov QWORD ptr [rsp + XX], rbp 65268d75effSDimitry Andric case 0x245c8948: // 48 89 5c 24 XX : mov QWORD PTR [rsp + XX], rbx 65368d75effSDimitry Andric case 0x24748948: // 48 89 74 24 XX : mov QWORD PTR [rsp + XX], rsi 65404eeddc0SDimitry Andric case 0x247c8948: // 48 89 7c 24 XX : mov QWORD PTR [rsp + XX], rdi 65568d75effSDimitry Andric case 0x244C8948: // 48 89 4C 24 XX : mov QWORD PTR [rsp + XX], rcx 65668d75effSDimitry Andric case 0x24548948: // 48 89 54 24 XX : mov QWORD PTR [rsp + XX], rdx 65768d75effSDimitry Andric case 0x244c894c: // 4c 89 4c 24 XX : mov QWORD PTR [rsp + XX], r9 65868d75effSDimitry Andric case 0x2444894c: // 4c 89 44 24 XX : mov QWORD PTR [rsp + XX], r8 65968d75effSDimitry Andric return 5; 66068d75effSDimitry Andric case 0x24648348: // 48 83 64 24 XX : and QWORD PTR [rsp + XX], YY 66168d75effSDimitry Andric return 6; 66268d75effSDimitry Andric } 66368d75effSDimitry Andric 66468d75effSDimitry Andric #else 66568d75effSDimitry Andric 66668d75effSDimitry Andric switch (*(u8*)address) { 66768d75effSDimitry Andric case 0xA1: // A1 XX XX XX XX : mov eax, dword ptr ds:[XXXXXXXX] 66868d75effSDimitry Andric return 5; 66968d75effSDimitry Andric } 67068d75effSDimitry Andric switch (*(u16*)address) { 67168d75effSDimitry Andric case 0x458B: // 8B 45 XX : mov eax, dword ptr [ebp + XX] 67268d75effSDimitry Andric case 0x5D8B: // 8B 5D XX : mov ebx, dword ptr [ebp + XX] 67368d75effSDimitry Andric case 0x7D8B: // 8B 7D XX : mov edi, dword ptr [ebp + XX] 67468d75effSDimitry Andric case 0xEC83: // 83 EC XX : sub esp, XX 67568d75effSDimitry Andric case 0x75FF: // FF 75 XX : push dword ptr [ebp + XX] 67668d75effSDimitry Andric return 3; 67768d75effSDimitry Andric case 0xC1F7: // F7 C1 XX YY ZZ WW : test ecx, WWZZYYXX 67868d75effSDimitry Andric case 0x25FF: // FF 25 XX YY ZZ WW : jmp dword ptr ds:[WWZZYYXX] 67968d75effSDimitry Andric return 6; 68068d75effSDimitry Andric case 0x3D83: // 83 3D XX YY ZZ WW TT : cmp TT, WWZZYYXX 68168d75effSDimitry Andric return 7; 68268d75effSDimitry Andric case 0x7D83: // 83 7D XX YY : cmp dword ptr [ebp + XX], YY 68368d75effSDimitry Andric return 4; 68468d75effSDimitry Andric } 68568d75effSDimitry Andric 68668d75effSDimitry Andric switch (0x00FFFFFF & *(u32*)address) { 68768d75effSDimitry Andric case 0x24448A: // 8A 44 24 XX : mov eal, dword ptr [esp + XX] 68868d75effSDimitry Andric case 0x24448B: // 8B 44 24 XX : mov eax, dword ptr [esp + XX] 68968d75effSDimitry Andric case 0x244C8B: // 8B 4C 24 XX : mov ecx, dword ptr [esp + XX] 69068d75effSDimitry Andric case 0x24548B: // 8B 54 24 XX : mov edx, dword ptr [esp + XX] 69106c3fb27SDimitry Andric case 0x245C8B: // 8B 5C 24 XX : mov ebx, dword ptr [esp + XX] 69206c3fb27SDimitry Andric case 0x246C8B: // 8B 6C 24 XX : mov ebp, dword ptr [esp + XX] 69368d75effSDimitry Andric case 0x24748B: // 8B 74 24 XX : mov esi, dword ptr [esp + XX] 69468d75effSDimitry Andric case 0x247C8B: // 8B 7C 24 XX : mov edi, dword ptr [esp + XX] 69568d75effSDimitry Andric return 4; 69668d75effSDimitry Andric } 69768d75effSDimitry Andric 69868d75effSDimitry Andric switch (*(u32*)address) { 69968d75effSDimitry Andric case 0x2444B60F: // 0F B6 44 24 XX : movzx eax, byte ptr [esp + XX] 70068d75effSDimitry Andric return 5; 70168d75effSDimitry Andric } 70268d75effSDimitry Andric #endif 70368d75effSDimitry Andric 70406c3fb27SDimitry Andric // Unknown instruction! This might happen when we add a new interceptor, use 70506c3fb27SDimitry Andric // a new compiler version, or if Windows changed how some functions are 70606c3fb27SDimitry Andric // compiled. In either case, we print the address and 8 bytes of instructions 70706c3fb27SDimitry Andric // to notify the user about the error and to help identify the unknown 70806c3fb27SDimitry Andric // instruction. Don't treat this as a fatal error, though we can break the 70906c3fb27SDimitry Andric // debugger if one has been attached. 71006c3fb27SDimitry Andric u8 *bytes = (u8 *)address; 71106c3fb27SDimitry Andric ReportError( 71206c3fb27SDimitry Andric "interception_win: unhandled instruction at %p: %02x %02x %02x %02x %02x " 71306c3fb27SDimitry Andric "%02x %02x %02x\n", 71406c3fb27SDimitry Andric (void *)address, bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], 71506c3fb27SDimitry Andric bytes[5], bytes[6], bytes[7]); 71606c3fb27SDimitry Andric if (::IsDebuggerPresent()) 71706c3fb27SDimitry Andric __debugbreak(); 71868d75effSDimitry Andric return 0; 71968d75effSDimitry Andric } 72068d75effSDimitry Andric 72168d75effSDimitry Andric // Returns 0 on error. 72268d75effSDimitry Andric static size_t RoundUpToInstrBoundary(size_t size, uptr address) { 72368d75effSDimitry Andric size_t cursor = 0; 72468d75effSDimitry Andric while (cursor < size) { 72568d75effSDimitry Andric size_t instruction_size = GetInstructionSize(address + cursor); 72668d75effSDimitry Andric if (!instruction_size) 72768d75effSDimitry Andric return 0; 72868d75effSDimitry Andric cursor += instruction_size; 72968d75effSDimitry Andric } 73068d75effSDimitry Andric return cursor; 73168d75effSDimitry Andric } 73268d75effSDimitry Andric 73368d75effSDimitry Andric static bool CopyInstructions(uptr to, uptr from, size_t size) { 73468d75effSDimitry Andric size_t cursor = 0; 73568d75effSDimitry Andric while (cursor != size) { 73668d75effSDimitry Andric size_t rel_offset = 0; 73768d75effSDimitry Andric size_t instruction_size = GetInstructionSize(from + cursor, &rel_offset); 73806c3fb27SDimitry Andric if (!instruction_size) 73906c3fb27SDimitry Andric return false; 74068d75effSDimitry Andric _memcpy((void *)(to + cursor), (void *)(from + cursor), 74168d75effSDimitry Andric (size_t)instruction_size); 74268d75effSDimitry Andric if (rel_offset) { 74368d75effSDimitry Andric # if SANITIZER_WINDOWS64 7445f757f3fSDimitry Andric // we want to make sure that the new relative offset still fits in 32-bits 7455f757f3fSDimitry Andric // this will be untrue if relocated_offset \notin [-2**31, 2**31) 7465f757f3fSDimitry Andric s64 delta = to - from; 7475f757f3fSDimitry Andric s64 relocated_offset = *(s32 *)(to + cursor + rel_offset) - delta; 7485f757f3fSDimitry Andric if (-0x8000'0000ll > relocated_offset || relocated_offset > 0x7FFF'FFFFll) 74968d75effSDimitry Andric return false; 7505f757f3fSDimitry Andric # else 7515f757f3fSDimitry Andric // on 32-bit, the relative offset will always be correct 7525f757f3fSDimitry Andric s32 delta = to - from; 7535f757f3fSDimitry Andric s32 relocated_offset = *(s32 *)(to + cursor + rel_offset) - delta; 75468d75effSDimitry Andric # endif 7555f757f3fSDimitry Andric *(s32 *)(to + cursor + rel_offset) = relocated_offset; 75668d75effSDimitry Andric } 75768d75effSDimitry Andric cursor += instruction_size; 75868d75effSDimitry Andric } 75968d75effSDimitry Andric return true; 76068d75effSDimitry Andric } 76168d75effSDimitry Andric 76268d75effSDimitry Andric 76368d75effSDimitry Andric #if !SANITIZER_WINDOWS64 76468d75effSDimitry Andric bool OverrideFunctionWithDetour( 76568d75effSDimitry Andric uptr old_func, uptr new_func, uptr *orig_old_func) { 76668d75effSDimitry Andric const int kDetourHeaderLen = 5; 76768d75effSDimitry Andric const u16 kDetourInstruction = 0xFF8B; 76868d75effSDimitry Andric 76968d75effSDimitry Andric uptr header = (uptr)old_func - kDetourHeaderLen; 77068d75effSDimitry Andric uptr patch_length = kDetourHeaderLen + kShortJumpInstructionLength; 77168d75effSDimitry Andric 77268d75effSDimitry Andric // Validate that the function is hookable. 77368d75effSDimitry Andric if (*(u16*)old_func != kDetourInstruction || 77468d75effSDimitry Andric !IsMemoryPadding(header, kDetourHeaderLen)) 77568d75effSDimitry Andric return false; 77668d75effSDimitry Andric 77768d75effSDimitry Andric // Change memory protection to writable. 77868d75effSDimitry Andric DWORD protection = 0; 77968d75effSDimitry Andric if (!ChangeMemoryProtection(header, patch_length, &protection)) 78068d75effSDimitry Andric return false; 78168d75effSDimitry Andric 78268d75effSDimitry Andric // Write a relative jump to the redirected function. 78368d75effSDimitry Andric WriteJumpInstruction(header, new_func); 78468d75effSDimitry Andric 78568d75effSDimitry Andric // Write the short jump to the function prefix. 78668d75effSDimitry Andric WriteShortJumpInstruction(old_func, header); 78768d75effSDimitry Andric 78868d75effSDimitry Andric // Restore previous memory protection. 78968d75effSDimitry Andric if (!RestoreMemoryProtection(header, patch_length, protection)) 79068d75effSDimitry Andric return false; 79168d75effSDimitry Andric 79268d75effSDimitry Andric if (orig_old_func) 79368d75effSDimitry Andric *orig_old_func = old_func + kShortJumpInstructionLength; 79468d75effSDimitry Andric 79568d75effSDimitry Andric return true; 79668d75effSDimitry Andric } 79768d75effSDimitry Andric #endif 79868d75effSDimitry Andric 79968d75effSDimitry Andric bool OverrideFunctionWithRedirectJump( 80068d75effSDimitry Andric uptr old_func, uptr new_func, uptr *orig_old_func) { 80168d75effSDimitry Andric // Check whether the first instruction is a relative jump. 80268d75effSDimitry Andric if (*(u8*)old_func != 0xE9) 80368d75effSDimitry Andric return false; 80468d75effSDimitry Andric 80568d75effSDimitry Andric if (orig_old_func) { 806bdd1243dSDimitry Andric sptr relative_offset = *(s32 *)(old_func + 1); 80768d75effSDimitry Andric uptr absolute_target = old_func + relative_offset + kJumpInstructionLength; 80868d75effSDimitry Andric *orig_old_func = absolute_target; 80968d75effSDimitry Andric } 81068d75effSDimitry Andric 81168d75effSDimitry Andric #if SANITIZER_WINDOWS64 81268d75effSDimitry Andric // If needed, get memory space for a trampoline jump. 81368d75effSDimitry Andric uptr trampoline = AllocateMemoryForTrampoline(old_func, kDirectBranchLength); 81468d75effSDimitry Andric if (!trampoline) 81568d75effSDimitry Andric return false; 81668d75effSDimitry Andric WriteDirectBranch(trampoline, new_func); 81768d75effSDimitry Andric #endif 81868d75effSDimitry Andric 81968d75effSDimitry Andric // Change memory protection to writable. 82068d75effSDimitry Andric DWORD protection = 0; 82168d75effSDimitry Andric if (!ChangeMemoryProtection(old_func, kJumpInstructionLength, &protection)) 82268d75effSDimitry Andric return false; 82368d75effSDimitry Andric 82468d75effSDimitry Andric // Write a relative jump to the redirected function. 82568d75effSDimitry Andric WriteJumpInstruction(old_func, FIRST_32_SECOND_64(new_func, trampoline)); 82668d75effSDimitry Andric 82768d75effSDimitry Andric // Restore previous memory protection. 82868d75effSDimitry Andric if (!RestoreMemoryProtection(old_func, kJumpInstructionLength, protection)) 82968d75effSDimitry Andric return false; 83068d75effSDimitry Andric 83168d75effSDimitry Andric return true; 83268d75effSDimitry Andric } 83368d75effSDimitry Andric 83468d75effSDimitry Andric bool OverrideFunctionWithHotPatch( 83568d75effSDimitry Andric uptr old_func, uptr new_func, uptr *orig_old_func) { 83668d75effSDimitry Andric const int kHotPatchHeaderLen = kBranchLength; 83768d75effSDimitry Andric 83868d75effSDimitry Andric uptr header = (uptr)old_func - kHotPatchHeaderLen; 83968d75effSDimitry Andric uptr patch_length = kHotPatchHeaderLen + kShortJumpInstructionLength; 84068d75effSDimitry Andric 84168d75effSDimitry Andric // Validate that the function is hot patchable. 84268d75effSDimitry Andric size_t instruction_size = GetInstructionSize(old_func); 84368d75effSDimitry Andric if (instruction_size < kShortJumpInstructionLength || 84468d75effSDimitry Andric !FunctionHasPadding(old_func, kHotPatchHeaderLen)) 84568d75effSDimitry Andric return false; 84668d75effSDimitry Andric 84768d75effSDimitry Andric if (orig_old_func) { 84868d75effSDimitry Andric // Put the needed instructions into the trampoline bytes. 84968d75effSDimitry Andric uptr trampoline_length = instruction_size + kDirectBranchLength; 85068d75effSDimitry Andric uptr trampoline = AllocateMemoryForTrampoline(old_func, trampoline_length); 85168d75effSDimitry Andric if (!trampoline) 85268d75effSDimitry Andric return false; 85368d75effSDimitry Andric if (!CopyInstructions(trampoline, old_func, instruction_size)) 85468d75effSDimitry Andric return false; 85568d75effSDimitry Andric WriteDirectBranch(trampoline + instruction_size, 85668d75effSDimitry Andric old_func + instruction_size); 85768d75effSDimitry Andric *orig_old_func = trampoline; 85868d75effSDimitry Andric } 85968d75effSDimitry Andric 86068d75effSDimitry Andric // If needed, get memory space for indirect address. 86168d75effSDimitry Andric uptr indirect_address = 0; 86268d75effSDimitry Andric #if SANITIZER_WINDOWS64 86368d75effSDimitry Andric indirect_address = AllocateMemoryForTrampoline(old_func, kAddressLength); 86468d75effSDimitry Andric if (!indirect_address) 86568d75effSDimitry Andric return false; 86668d75effSDimitry Andric #endif 86768d75effSDimitry Andric 86868d75effSDimitry Andric // Change memory protection to writable. 86968d75effSDimitry Andric DWORD protection = 0; 87068d75effSDimitry Andric if (!ChangeMemoryProtection(header, patch_length, &protection)) 87168d75effSDimitry Andric return false; 87268d75effSDimitry Andric 87368d75effSDimitry Andric // Write jumps to the redirected function. 87468d75effSDimitry Andric WriteBranch(header, indirect_address, new_func); 87568d75effSDimitry Andric WriteShortJumpInstruction(old_func, header); 87668d75effSDimitry Andric 87768d75effSDimitry Andric // Restore previous memory protection. 87868d75effSDimitry Andric if (!RestoreMemoryProtection(header, patch_length, protection)) 87968d75effSDimitry Andric return false; 88068d75effSDimitry Andric 88168d75effSDimitry Andric return true; 88268d75effSDimitry Andric } 88368d75effSDimitry Andric 88468d75effSDimitry Andric bool OverrideFunctionWithTrampoline( 88568d75effSDimitry Andric uptr old_func, uptr new_func, uptr *orig_old_func) { 88668d75effSDimitry Andric 88768d75effSDimitry Andric size_t instructions_length = kBranchLength; 88868d75effSDimitry Andric size_t padding_length = 0; 88968d75effSDimitry Andric uptr indirect_address = 0; 89068d75effSDimitry Andric 89168d75effSDimitry Andric if (orig_old_func) { 89268d75effSDimitry Andric // Find out the number of bytes of the instructions we need to copy 89368d75effSDimitry Andric // to the trampoline. 89468d75effSDimitry Andric instructions_length = RoundUpToInstrBoundary(kBranchLength, old_func); 89568d75effSDimitry Andric if (!instructions_length) 89668d75effSDimitry Andric return false; 89768d75effSDimitry Andric 89868d75effSDimitry Andric // Put the needed instructions into the trampoline bytes. 89968d75effSDimitry Andric uptr trampoline_length = instructions_length + kDirectBranchLength; 90068d75effSDimitry Andric uptr trampoline = AllocateMemoryForTrampoline(old_func, trampoline_length); 90168d75effSDimitry Andric if (!trampoline) 90268d75effSDimitry Andric return false; 90368d75effSDimitry Andric if (!CopyInstructions(trampoline, old_func, instructions_length)) 90468d75effSDimitry Andric return false; 90568d75effSDimitry Andric WriteDirectBranch(trampoline + instructions_length, 90668d75effSDimitry Andric old_func + instructions_length); 90768d75effSDimitry Andric *orig_old_func = trampoline; 90868d75effSDimitry Andric } 90968d75effSDimitry Andric 91068d75effSDimitry Andric #if SANITIZER_WINDOWS64 91168d75effSDimitry Andric // Check if the targeted address can be encoded in the function padding. 91268d75effSDimitry Andric // Otherwise, allocate it in the trampoline region. 91368d75effSDimitry Andric if (IsMemoryPadding(old_func - kAddressLength, kAddressLength)) { 91468d75effSDimitry Andric indirect_address = old_func - kAddressLength; 91568d75effSDimitry Andric padding_length = kAddressLength; 91668d75effSDimitry Andric } else { 91768d75effSDimitry Andric indirect_address = AllocateMemoryForTrampoline(old_func, kAddressLength); 91868d75effSDimitry Andric if (!indirect_address) 91968d75effSDimitry Andric return false; 92068d75effSDimitry Andric } 92168d75effSDimitry Andric #endif 92268d75effSDimitry Andric 92368d75effSDimitry Andric // Change memory protection to writable. 92468d75effSDimitry Andric uptr patch_address = old_func - padding_length; 92568d75effSDimitry Andric uptr patch_length = instructions_length + padding_length; 92668d75effSDimitry Andric DWORD protection = 0; 92768d75effSDimitry Andric if (!ChangeMemoryProtection(patch_address, patch_length, &protection)) 92868d75effSDimitry Andric return false; 92968d75effSDimitry Andric 93068d75effSDimitry Andric // Patch the original function. 93168d75effSDimitry Andric WriteBranch(old_func, indirect_address, new_func); 93268d75effSDimitry Andric 93368d75effSDimitry Andric // Restore previous memory protection. 93468d75effSDimitry Andric if (!RestoreMemoryProtection(patch_address, patch_length, protection)) 93568d75effSDimitry Andric return false; 93668d75effSDimitry Andric 93768d75effSDimitry Andric return true; 93868d75effSDimitry Andric } 93968d75effSDimitry Andric 94068d75effSDimitry Andric bool OverrideFunction( 94168d75effSDimitry Andric uptr old_func, uptr new_func, uptr *orig_old_func) { 94268d75effSDimitry Andric #if !SANITIZER_WINDOWS64 94368d75effSDimitry Andric if (OverrideFunctionWithDetour(old_func, new_func, orig_old_func)) 94468d75effSDimitry Andric return true; 94568d75effSDimitry Andric #endif 94668d75effSDimitry Andric if (OverrideFunctionWithRedirectJump(old_func, new_func, orig_old_func)) 94768d75effSDimitry Andric return true; 94868d75effSDimitry Andric if (OverrideFunctionWithHotPatch(old_func, new_func, orig_old_func)) 94968d75effSDimitry Andric return true; 95068d75effSDimitry Andric if (OverrideFunctionWithTrampoline(old_func, new_func, orig_old_func)) 95168d75effSDimitry Andric return true; 95268d75effSDimitry Andric return false; 95368d75effSDimitry Andric } 95468d75effSDimitry Andric 95568d75effSDimitry Andric static void **InterestingDLLsAvailable() { 95668d75effSDimitry Andric static const char *InterestingDLLs[] = { 95768d75effSDimitry Andric "kernel32.dll", 9587a6dacacSDimitry Andric "msvcr100d.dll", // VS2010 9597a6dacacSDimitry Andric "msvcr110d.dll", // VS2012 9607a6dacacSDimitry Andric "msvcr120d.dll", // VS2013 9617a6dacacSDimitry Andric "vcruntime140d.dll", // VS2015 9627a6dacacSDimitry Andric "ucrtbased.dll", // Universal CRT 96368d75effSDimitry Andric "msvcr100.dll", // VS2010 96468d75effSDimitry Andric "msvcr110.dll", // VS2012 96568d75effSDimitry Andric "msvcr120.dll", // VS2013 96668d75effSDimitry Andric "vcruntime140.dll", // VS2015 96768d75effSDimitry Andric "ucrtbase.dll", // Universal CRT 96806c3fb27SDimitry Andric # if (defined(__MINGW32__) && defined(__i386__)) 96906c3fb27SDimitry Andric "libc++.dll", // libc++ 97006c3fb27SDimitry Andric "libunwind.dll", // libunwind 97106c3fb27SDimitry Andric # endif 97268d75effSDimitry Andric // NTDLL should go last as it exports some functions that we should 97368d75effSDimitry Andric // override in the CRT [presumably only used internally]. 9747a6dacacSDimitry Andric "ntdll.dll", 9757a6dacacSDimitry Andric NULL 9767a6dacacSDimitry Andric }; 97768d75effSDimitry Andric static void *result[ARRAY_SIZE(InterestingDLLs)] = { 0 }; 97868d75effSDimitry Andric if (!result[0]) { 97968d75effSDimitry Andric for (size_t i = 0, j = 0; InterestingDLLs[i]; ++i) { 98068d75effSDimitry Andric if (HMODULE h = GetModuleHandleA(InterestingDLLs[i])) 98168d75effSDimitry Andric result[j++] = (void *)h; 98268d75effSDimitry Andric } 98368d75effSDimitry Andric } 98468d75effSDimitry Andric return &result[0]; 98568d75effSDimitry Andric } 98668d75effSDimitry Andric 98768d75effSDimitry Andric namespace { 98868d75effSDimitry Andric // Utility for reading loaded PE images. 98968d75effSDimitry Andric template <typename T> class RVAPtr { 99068d75effSDimitry Andric public: 99168d75effSDimitry Andric RVAPtr(void *module, uptr rva) 99268d75effSDimitry Andric : ptr_(reinterpret_cast<T *>(reinterpret_cast<char *>(module) + rva)) {} 99368d75effSDimitry Andric operator T *() { return ptr_; } 99468d75effSDimitry Andric T *operator->() { return ptr_; } 99568d75effSDimitry Andric T *operator++() { return ++ptr_; } 99668d75effSDimitry Andric 99768d75effSDimitry Andric private: 99868d75effSDimitry Andric T *ptr_; 99968d75effSDimitry Andric }; 100068d75effSDimitry Andric } // namespace 100168d75effSDimitry Andric 100268d75effSDimitry Andric // Internal implementation of GetProcAddress. At least since Windows 8, 100368d75effSDimitry Andric // GetProcAddress appears to initialize DLLs before returning function pointers 100468d75effSDimitry Andric // into them. This is problematic for the sanitizers, because they typically 100568d75effSDimitry Andric // want to intercept malloc *before* MSVCRT initializes. Our internal 100668d75effSDimitry Andric // implementation walks the export list manually without doing initialization. 100768d75effSDimitry Andric uptr InternalGetProcAddress(void *module, const char *func_name) { 100868d75effSDimitry Andric // Check that the module header is full and present. 100968d75effSDimitry Andric RVAPtr<IMAGE_DOS_HEADER> dos_stub(module, 0); 101068d75effSDimitry Andric RVAPtr<IMAGE_NT_HEADERS> headers(module, dos_stub->e_lfanew); 101168d75effSDimitry Andric if (!module || dos_stub->e_magic != IMAGE_DOS_SIGNATURE || // "MZ" 101268d75effSDimitry Andric headers->Signature != IMAGE_NT_SIGNATURE || // "PE\0\0" 101368d75effSDimitry Andric headers->FileHeader.SizeOfOptionalHeader < 101468d75effSDimitry Andric sizeof(IMAGE_OPTIONAL_HEADER)) { 101568d75effSDimitry Andric return 0; 101668d75effSDimitry Andric } 101768d75effSDimitry Andric 101868d75effSDimitry Andric IMAGE_DATA_DIRECTORY *export_directory = 101968d75effSDimitry Andric &headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; 102068d75effSDimitry Andric if (export_directory->Size == 0) 102168d75effSDimitry Andric return 0; 102268d75effSDimitry Andric RVAPtr<IMAGE_EXPORT_DIRECTORY> exports(module, 102368d75effSDimitry Andric export_directory->VirtualAddress); 102468d75effSDimitry Andric RVAPtr<DWORD> functions(module, exports->AddressOfFunctions); 102568d75effSDimitry Andric RVAPtr<DWORD> names(module, exports->AddressOfNames); 102668d75effSDimitry Andric RVAPtr<WORD> ordinals(module, exports->AddressOfNameOrdinals); 102768d75effSDimitry Andric 102868d75effSDimitry Andric for (DWORD i = 0; i < exports->NumberOfNames; i++) { 102968d75effSDimitry Andric RVAPtr<char> name(module, names[i]); 103068d75effSDimitry Andric if (!strcmp(func_name, name)) { 103168d75effSDimitry Andric DWORD index = ordinals[i]; 103268d75effSDimitry Andric RVAPtr<char> func(module, functions[index]); 103368d75effSDimitry Andric 103468d75effSDimitry Andric // Handle forwarded functions. 103568d75effSDimitry Andric DWORD offset = functions[index]; 103668d75effSDimitry Andric if (offset >= export_directory->VirtualAddress && 103768d75effSDimitry Andric offset < export_directory->VirtualAddress + export_directory->Size) { 103868d75effSDimitry Andric // An entry for a forwarded function is a string with the following 103968d75effSDimitry Andric // format: "<module> . <function_name>" that is stored into the 104068d75effSDimitry Andric // exported directory. 104168d75effSDimitry Andric char function_name[256]; 104268d75effSDimitry Andric size_t funtion_name_length = _strlen(func); 104368d75effSDimitry Andric if (funtion_name_length >= sizeof(function_name) - 1) 104468d75effSDimitry Andric InterceptionFailed(); 104568d75effSDimitry Andric 104668d75effSDimitry Andric _memcpy(function_name, func, funtion_name_length); 104768d75effSDimitry Andric function_name[funtion_name_length] = '\0'; 104868d75effSDimitry Andric char* separator = _strchr(function_name, '.'); 104968d75effSDimitry Andric if (!separator) 105068d75effSDimitry Andric InterceptionFailed(); 105168d75effSDimitry Andric *separator = '\0'; 105268d75effSDimitry Andric 105368d75effSDimitry Andric void* redirected_module = GetModuleHandleA(function_name); 105468d75effSDimitry Andric if (!redirected_module) 105568d75effSDimitry Andric InterceptionFailed(); 105668d75effSDimitry Andric return InternalGetProcAddress(redirected_module, separator + 1); 105768d75effSDimitry Andric } 105868d75effSDimitry Andric 105968d75effSDimitry Andric return (uptr)(char *)func; 106068d75effSDimitry Andric } 106168d75effSDimitry Andric } 106268d75effSDimitry Andric 106368d75effSDimitry Andric return 0; 106468d75effSDimitry Andric } 106568d75effSDimitry Andric 106668d75effSDimitry Andric bool OverrideFunction( 106768d75effSDimitry Andric const char *func_name, uptr new_func, uptr *orig_old_func) { 106868d75effSDimitry Andric bool hooked = false; 106968d75effSDimitry Andric void **DLLs = InterestingDLLsAvailable(); 107068d75effSDimitry Andric for (size_t i = 0; DLLs[i]; ++i) { 107168d75effSDimitry Andric uptr func_addr = InternalGetProcAddress(DLLs[i], func_name); 107268d75effSDimitry Andric if (func_addr && 107368d75effSDimitry Andric OverrideFunction(func_addr, new_func, orig_old_func)) { 107468d75effSDimitry Andric hooked = true; 107568d75effSDimitry Andric } 107668d75effSDimitry Andric } 107768d75effSDimitry Andric return hooked; 107868d75effSDimitry Andric } 107968d75effSDimitry Andric 108068d75effSDimitry Andric bool OverrideImportedFunction(const char *module_to_patch, 108168d75effSDimitry Andric const char *imported_module, 108268d75effSDimitry Andric const char *function_name, uptr new_function, 108368d75effSDimitry Andric uptr *orig_old_func) { 108468d75effSDimitry Andric HMODULE module = GetModuleHandleA(module_to_patch); 108568d75effSDimitry Andric if (!module) 108668d75effSDimitry Andric return false; 108768d75effSDimitry Andric 108868d75effSDimitry Andric // Check that the module header is full and present. 108968d75effSDimitry Andric RVAPtr<IMAGE_DOS_HEADER> dos_stub(module, 0); 109068d75effSDimitry Andric RVAPtr<IMAGE_NT_HEADERS> headers(module, dos_stub->e_lfanew); 109168d75effSDimitry Andric if (!module || dos_stub->e_magic != IMAGE_DOS_SIGNATURE || // "MZ" 109268d75effSDimitry Andric headers->Signature != IMAGE_NT_SIGNATURE || // "PE\0\0" 109368d75effSDimitry Andric headers->FileHeader.SizeOfOptionalHeader < 109468d75effSDimitry Andric sizeof(IMAGE_OPTIONAL_HEADER)) { 109568d75effSDimitry Andric return false; 109668d75effSDimitry Andric } 109768d75effSDimitry Andric 109868d75effSDimitry Andric IMAGE_DATA_DIRECTORY *import_directory = 109968d75effSDimitry Andric &headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; 110068d75effSDimitry Andric 110168d75effSDimitry Andric // Iterate the list of imported DLLs. FirstThunk will be null for the last 110268d75effSDimitry Andric // entry. 110368d75effSDimitry Andric RVAPtr<IMAGE_IMPORT_DESCRIPTOR> imports(module, 110468d75effSDimitry Andric import_directory->VirtualAddress); 110568d75effSDimitry Andric for (; imports->FirstThunk != 0; ++imports) { 110668d75effSDimitry Andric RVAPtr<const char> modname(module, imports->Name); 110768d75effSDimitry Andric if (_stricmp(&*modname, imported_module) == 0) 110868d75effSDimitry Andric break; 110968d75effSDimitry Andric } 111068d75effSDimitry Andric if (imports->FirstThunk == 0) 111168d75effSDimitry Andric return false; 111268d75effSDimitry Andric 111368d75effSDimitry Andric // We have two parallel arrays: the import address table (IAT) and the table 111468d75effSDimitry Andric // of names. They start out containing the same data, but the loader rewrites 111568d75effSDimitry Andric // the IAT to hold imported addresses and leaves the name table in 111668d75effSDimitry Andric // OriginalFirstThunk alone. 111768d75effSDimitry Andric RVAPtr<IMAGE_THUNK_DATA> name_table(module, imports->OriginalFirstThunk); 111868d75effSDimitry Andric RVAPtr<IMAGE_THUNK_DATA> iat(module, imports->FirstThunk); 111968d75effSDimitry Andric for (; name_table->u1.Ordinal != 0; ++name_table, ++iat) { 112068d75effSDimitry Andric if (!IMAGE_SNAP_BY_ORDINAL(name_table->u1.Ordinal)) { 112168d75effSDimitry Andric RVAPtr<IMAGE_IMPORT_BY_NAME> import_by_name( 112268d75effSDimitry Andric module, name_table->u1.ForwarderString); 112368d75effSDimitry Andric const char *funcname = &import_by_name->Name[0]; 112468d75effSDimitry Andric if (strcmp(funcname, function_name) == 0) 112568d75effSDimitry Andric break; 112668d75effSDimitry Andric } 112768d75effSDimitry Andric } 112868d75effSDimitry Andric if (name_table->u1.Ordinal == 0) 112968d75effSDimitry Andric return false; 113068d75effSDimitry Andric 113168d75effSDimitry Andric // Now we have the correct IAT entry. Do the swap. We have to make the page 113268d75effSDimitry Andric // read/write first. 113368d75effSDimitry Andric if (orig_old_func) 113468d75effSDimitry Andric *orig_old_func = iat->u1.AddressOfData; 113568d75effSDimitry Andric DWORD old_prot, unused_prot; 113668d75effSDimitry Andric if (!VirtualProtect(&iat->u1.AddressOfData, 4, PAGE_EXECUTE_READWRITE, 113768d75effSDimitry Andric &old_prot)) 113868d75effSDimitry Andric return false; 113968d75effSDimitry Andric iat->u1.AddressOfData = new_function; 114068d75effSDimitry Andric if (!VirtualProtect(&iat->u1.AddressOfData, 4, old_prot, &unused_prot)) 114168d75effSDimitry Andric return false; // Not clear if this failure bothers us. 114268d75effSDimitry Andric return true; 114368d75effSDimitry Andric } 114468d75effSDimitry Andric 114568d75effSDimitry Andric } // namespace __interception 114668d75effSDimitry Andric 114781ad6265SDimitry Andric #endif // SANITIZER_APPLE 1148