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 342e8d8bef9SDimitry Andric UNUSED static const uptr kTrampolineScanLimitRange = 1 << 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 48268d75effSDimitry Andric return 1; 48368d75effSDimitry Andric 48468d75effSDimitry Andric case 0x50: // push eax / rax 48568d75effSDimitry Andric case 0x51: // push ecx / rcx 48668d75effSDimitry Andric case 0x52: // push edx / rdx 48768d75effSDimitry Andric case 0x53: // push ebx / rbx 48868d75effSDimitry Andric case 0x54: // push esp / rsp 48968d75effSDimitry Andric case 0x55: // push ebp / rbp 49068d75effSDimitry Andric case 0x56: // push esi / rsi 49168d75effSDimitry Andric case 0x57: // push edi / rdi 49268d75effSDimitry Andric case 0x5D: // pop ebp / rbp 49368d75effSDimitry Andric return 1; 49468d75effSDimitry Andric 49568d75effSDimitry Andric case 0x6A: // 6A XX = push XX 49668d75effSDimitry Andric return 2; 49768d75effSDimitry Andric 49868d75effSDimitry Andric case 0xb8: // b8 XX XX XX XX : mov eax, XX XX XX XX 49968d75effSDimitry Andric case 0xB9: // b9 XX XX XX XX : mov ecx, XX XX XX XX 50068d75effSDimitry Andric return 5; 50168d75effSDimitry Andric 50268d75effSDimitry Andric // Cannot overwrite control-instruction. Return 0 to indicate failure. 50368d75effSDimitry Andric case 0xE9: // E9 XX XX XX XX : jmp <label> 50468d75effSDimitry Andric case 0xE8: // E8 XX XX XX XX : call <func> 50568d75effSDimitry Andric case 0xC3: // C3 : ret 50668d75effSDimitry Andric case 0xEB: // EB XX : jmp XX (short jump) 50768d75effSDimitry Andric case 0x70: // 7Y YY : jy XX (short conditional jump) 50868d75effSDimitry Andric case 0x71: 50968d75effSDimitry Andric case 0x72: 51068d75effSDimitry Andric case 0x73: 51168d75effSDimitry Andric case 0x74: 51268d75effSDimitry Andric case 0x75: 51368d75effSDimitry Andric case 0x76: 51468d75effSDimitry Andric case 0x77: 51568d75effSDimitry Andric case 0x78: 51668d75effSDimitry Andric case 0x79: 51768d75effSDimitry Andric case 0x7A: 51868d75effSDimitry Andric case 0x7B: 51968d75effSDimitry Andric case 0x7C: 52068d75effSDimitry Andric case 0x7D: 52168d75effSDimitry Andric case 0x7E: 52268d75effSDimitry Andric case 0x7F: 52368d75effSDimitry Andric return 0; 52468d75effSDimitry Andric } 52568d75effSDimitry Andric 52668d75effSDimitry Andric switch (*(u16*)(address)) { 52768d75effSDimitry Andric case 0x018A: // 8A 01 : mov al, byte ptr [ecx] 52868d75effSDimitry Andric case 0xFF8B: // 8B FF : mov edi, edi 52968d75effSDimitry Andric case 0xEC8B: // 8B EC : mov ebp, esp 53068d75effSDimitry Andric case 0xc889: // 89 C8 : mov eax, ecx 53106c3fb27SDimitry Andric case 0xE589: // 89 E5 : mov ebp, esp 53268d75effSDimitry Andric case 0xC18B: // 8B C1 : mov eax, ecx 53368d75effSDimitry Andric case 0xC033: // 33 C0 : xor eax, eax 53468d75effSDimitry Andric case 0xC933: // 33 C9 : xor ecx, ecx 53568d75effSDimitry Andric case 0xD233: // 33 D2 : xor edx, edx 53668d75effSDimitry Andric return 2; 53768d75effSDimitry Andric 53868d75effSDimitry Andric // Cannot overwrite control-instruction. Return 0 to indicate failure. 53968d75effSDimitry Andric case 0x25FF: // FF 25 XX XX XX XX : jmp [XXXXXXXX] 54068d75effSDimitry Andric return 0; 54168d75effSDimitry Andric } 54268d75effSDimitry Andric 54368d75effSDimitry Andric switch (0x00FFFFFF & *(u32*)address) { 54468d75effSDimitry Andric case 0x24A48D: // 8D A4 24 XX XX XX XX : lea esp, [esp + XX XX XX XX] 54568d75effSDimitry Andric return 7; 54668d75effSDimitry Andric } 54768d75effSDimitry Andric 5485f757f3fSDimitry Andric # if SANITIZER_WINDOWS_x64 54968d75effSDimitry Andric switch (*(u8*)address) { 55068d75effSDimitry Andric case 0xA1: // A1 XX XX XX XX XX XX XX XX : 55168d75effSDimitry Andric // movabs eax, dword ptr ds:[XXXXXXXX] 55268d75effSDimitry Andric return 9; 553349cc55cSDimitry Andric 554349cc55cSDimitry Andric case 0x83: 555349cc55cSDimitry Andric const u8 next_byte = *(u8*)(address + 1); 556349cc55cSDimitry Andric const u8 mod = next_byte >> 6; 557349cc55cSDimitry Andric const u8 rm = next_byte & 7; 558349cc55cSDimitry Andric if (mod == 1 && rm == 4) 559349cc55cSDimitry Andric return 5; // 83 ModR/M SIB Disp8 Imm8 560349cc55cSDimitry Andric // add|or|adc|sbb|and|sub|xor|cmp [r+disp8], imm8 56168d75effSDimitry Andric } 56268d75effSDimitry Andric 56368d75effSDimitry Andric switch (*(u16*)address) { 56468d75effSDimitry Andric case 0x5040: // push rax 56568d75effSDimitry Andric case 0x5140: // push rcx 56668d75effSDimitry Andric case 0x5240: // push rdx 56768d75effSDimitry Andric case 0x5340: // push rbx 56868d75effSDimitry Andric case 0x5440: // push rsp 56968d75effSDimitry Andric case 0x5540: // push rbp 57068d75effSDimitry Andric case 0x5640: // push rsi 57168d75effSDimitry Andric case 0x5740: // push rdi 57268d75effSDimitry Andric case 0x5441: // push r12 57368d75effSDimitry Andric case 0x5541: // push r13 57468d75effSDimitry Andric case 0x5641: // push r14 57568d75effSDimitry Andric case 0x5741: // push r15 57668d75effSDimitry Andric case 0x9066: // Two-byte NOP 577349cc55cSDimitry Andric case 0xc084: // test al, al 578349cc55cSDimitry Andric case 0x018a: // mov al, byte ptr [rcx] 57968d75effSDimitry Andric return 2; 58068d75effSDimitry Andric 5815f757f3fSDimitry Andric case 0x058A: // 8A 05 XX XX XX XX : mov al, byte ptr [XX XX XX XX] 58268d75effSDimitry Andric case 0x058B: // 8B 05 XX XX XX XX : mov eax, dword ptr [XX XX XX XX] 58368d75effSDimitry Andric if (rel_offset) 58468d75effSDimitry Andric *rel_offset = 2; 58568d75effSDimitry Andric return 6; 58668d75effSDimitry Andric } 58768d75effSDimitry Andric 58868d75effSDimitry Andric switch (0x00FFFFFF & *(u32*)address) { 58968d75effSDimitry Andric case 0xe58948: // 48 8b c4 : mov rbp, rsp 59068d75effSDimitry Andric case 0xc18b48: // 48 8b c1 : mov rax, rcx 59168d75effSDimitry Andric case 0xc48b48: // 48 8b c4 : mov rax, rsp 59268d75effSDimitry Andric case 0xd9f748: // 48 f7 d9 : neg rcx 59368d75effSDimitry Andric case 0xd12b48: // 48 2b d1 : sub rdx, rcx 59468d75effSDimitry Andric case 0x07c1f6: // f6 c1 07 : test cl, 0x7 59568d75effSDimitry Andric case 0xc98548: // 48 85 C9 : test rcx, rcx 596349cc55cSDimitry Andric case 0xd28548: // 48 85 d2 : test rdx, rdx 59768d75effSDimitry Andric case 0xc0854d: // 4d 85 c0 : test r8, r8 59868d75effSDimitry Andric case 0xc2b60f: // 0f b6 c2 : movzx eax, dl 59968d75effSDimitry Andric case 0xc03345: // 45 33 c0 : xor r8d, r8d 60068d75effSDimitry Andric case 0xc93345: // 45 33 c9 : xor r9d, r9d 60168d75effSDimitry Andric case 0xdb3345: // 45 33 DB : xor r11d, r11d 60268d75effSDimitry Andric case 0xd98b4c: // 4c 8b d9 : mov r11, rcx 60368d75effSDimitry Andric case 0xd28b4c: // 4c 8b d2 : mov r10, rdx 60468d75effSDimitry Andric case 0xc98b4c: // 4C 8B C9 : mov r9, rcx 60568d75effSDimitry Andric case 0xc18b4c: // 4C 8B C1 : mov r8, rcx 60668d75effSDimitry Andric case 0xd2b60f: // 0f b6 d2 : movzx edx, dl 60768d75effSDimitry Andric case 0xca2b48: // 48 2b ca : sub rcx, rdx 60868d75effSDimitry Andric case 0x10b70f: // 0f b7 10 : movzx edx, WORD PTR [rax] 60968d75effSDimitry Andric case 0xc00b4d: // 3d 0b c0 : or r8, r8 610349cc55cSDimitry Andric case 0xc08b41: // 41 8b c0 : mov eax, r8d 61168d75effSDimitry Andric case 0xd18b48: // 48 8b d1 : mov rdx, rcx 61268d75effSDimitry Andric case 0xdc8b4c: // 4c 8b dc : mov r11, rsp 61368d75effSDimitry Andric case 0xd18b4c: // 4c 8b d1 : mov r10, rcx 61468d75effSDimitry Andric case 0xE0E483: // 83 E4 E0 : and esp, 0xFFFFFFE0 61568d75effSDimitry Andric return 3; 61668d75effSDimitry Andric 61768d75effSDimitry Andric case 0xec8348: // 48 83 ec XX : sub rsp, XX 61868d75effSDimitry Andric case 0xf88349: // 49 83 f8 XX : cmp r8, XX 61968d75effSDimitry Andric case 0x588948: // 48 89 58 XX : mov QWORD PTR[rax + XX], rbx 62068d75effSDimitry Andric return 4; 62168d75effSDimitry Andric 62268d75effSDimitry Andric case 0xec8148: // 48 81 EC XX XX XX XX : sub rsp, XXXXXXXX 62368d75effSDimitry Andric return 7; 62468d75effSDimitry Andric 62568d75effSDimitry Andric case 0x058b48: // 48 8b 05 XX XX XX XX : 62668d75effSDimitry Andric // mov rax, QWORD PTR [rip + XXXXXXXX] 62768d75effSDimitry Andric case 0x25ff48: // 48 ff 25 XX XX XX XX : 62868d75effSDimitry Andric // rex.W jmp QWORD PTR [rip + XXXXXXXX] 6295f757f3fSDimitry Andric case 0x158D4C: // 4c 8d 15 XX XX XX XX : lea r10, [rip + XX] 63068d75effSDimitry Andric // Instructions having offset relative to 'rip' need offset adjustment. 63168d75effSDimitry Andric if (rel_offset) 63268d75effSDimitry Andric *rel_offset = 3; 63368d75effSDimitry Andric return 7; 63468d75effSDimitry Andric 63568d75effSDimitry Andric case 0x2444c7: // C7 44 24 XX YY YY YY YY 63668d75effSDimitry Andric // mov dword ptr [rsp + XX], YYYYYYYY 63768d75effSDimitry Andric return 8; 63868d75effSDimitry Andric } 63968d75effSDimitry Andric 64068d75effSDimitry Andric switch (*(u32*)(address)) { 64168d75effSDimitry Andric case 0x24448b48: // 48 8b 44 24 XX : mov rax, QWORD ptr [rsp + XX] 64268d75effSDimitry Andric case 0x246c8948: // 48 89 6C 24 XX : mov QWORD ptr [rsp + XX], rbp 64368d75effSDimitry Andric case 0x245c8948: // 48 89 5c 24 XX : mov QWORD PTR [rsp + XX], rbx 64468d75effSDimitry Andric case 0x24748948: // 48 89 74 24 XX : mov QWORD PTR [rsp + XX], rsi 64504eeddc0SDimitry Andric case 0x247c8948: // 48 89 7c 24 XX : mov QWORD PTR [rsp + XX], rdi 64668d75effSDimitry Andric case 0x244C8948: // 48 89 4C 24 XX : mov QWORD PTR [rsp + XX], rcx 64768d75effSDimitry Andric case 0x24548948: // 48 89 54 24 XX : mov QWORD PTR [rsp + XX], rdx 64868d75effSDimitry Andric case 0x244c894c: // 4c 89 4c 24 XX : mov QWORD PTR [rsp + XX], r9 64968d75effSDimitry Andric case 0x2444894c: // 4c 89 44 24 XX : mov QWORD PTR [rsp + XX], r8 65068d75effSDimitry Andric return 5; 65168d75effSDimitry Andric case 0x24648348: // 48 83 64 24 XX : and QWORD PTR [rsp + XX], YY 65268d75effSDimitry Andric return 6; 65368d75effSDimitry Andric } 65468d75effSDimitry Andric 65568d75effSDimitry Andric #else 65668d75effSDimitry Andric 65768d75effSDimitry Andric switch (*(u8*)address) { 65868d75effSDimitry Andric case 0xA1: // A1 XX XX XX XX : mov eax, dword ptr ds:[XXXXXXXX] 65968d75effSDimitry Andric return 5; 66068d75effSDimitry Andric } 66168d75effSDimitry Andric switch (*(u16*)address) { 66268d75effSDimitry Andric case 0x458B: // 8B 45 XX : mov eax, dword ptr [ebp + XX] 66368d75effSDimitry Andric case 0x5D8B: // 8B 5D XX : mov ebx, dword ptr [ebp + XX] 66468d75effSDimitry Andric case 0x7D8B: // 8B 7D XX : mov edi, dword ptr [ebp + XX] 66568d75effSDimitry Andric case 0xEC83: // 83 EC XX : sub esp, XX 66668d75effSDimitry Andric case 0x75FF: // FF 75 XX : push dword ptr [ebp + XX] 66768d75effSDimitry Andric return 3; 66868d75effSDimitry Andric case 0xC1F7: // F7 C1 XX YY ZZ WW : test ecx, WWZZYYXX 66968d75effSDimitry Andric case 0x25FF: // FF 25 XX YY ZZ WW : jmp dword ptr ds:[WWZZYYXX] 67068d75effSDimitry Andric return 6; 67168d75effSDimitry Andric case 0x3D83: // 83 3D XX YY ZZ WW TT : cmp TT, WWZZYYXX 67268d75effSDimitry Andric return 7; 67368d75effSDimitry Andric case 0x7D83: // 83 7D XX YY : cmp dword ptr [ebp + XX], YY 67468d75effSDimitry Andric return 4; 67568d75effSDimitry Andric } 67668d75effSDimitry Andric 67768d75effSDimitry Andric switch (0x00FFFFFF & *(u32*)address) { 67868d75effSDimitry Andric case 0x24448A: // 8A 44 24 XX : mov eal, dword ptr [esp + XX] 67968d75effSDimitry Andric case 0x24448B: // 8B 44 24 XX : mov eax, dword ptr [esp + XX] 68068d75effSDimitry Andric case 0x244C8B: // 8B 4C 24 XX : mov ecx, dword ptr [esp + XX] 68168d75effSDimitry Andric case 0x24548B: // 8B 54 24 XX : mov edx, dword ptr [esp + XX] 68206c3fb27SDimitry Andric case 0x245C8B: // 8B 5C 24 XX : mov ebx, dword ptr [esp + XX] 68306c3fb27SDimitry Andric case 0x246C8B: // 8B 6C 24 XX : mov ebp, dword ptr [esp + XX] 68468d75effSDimitry Andric case 0x24748B: // 8B 74 24 XX : mov esi, dword ptr [esp + XX] 68568d75effSDimitry Andric case 0x247C8B: // 8B 7C 24 XX : mov edi, dword ptr [esp + XX] 68668d75effSDimitry Andric return 4; 68768d75effSDimitry Andric } 68868d75effSDimitry Andric 68968d75effSDimitry Andric switch (*(u32*)address) { 69068d75effSDimitry Andric case 0x2444B60F: // 0F B6 44 24 XX : movzx eax, byte ptr [esp + XX] 69168d75effSDimitry Andric return 5; 69268d75effSDimitry Andric } 69368d75effSDimitry Andric #endif 69468d75effSDimitry Andric 69506c3fb27SDimitry Andric // Unknown instruction! This might happen when we add a new interceptor, use 69606c3fb27SDimitry Andric // a new compiler version, or if Windows changed how some functions are 69706c3fb27SDimitry Andric // compiled. In either case, we print the address and 8 bytes of instructions 69806c3fb27SDimitry Andric // to notify the user about the error and to help identify the unknown 69906c3fb27SDimitry Andric // instruction. Don't treat this as a fatal error, though we can break the 70006c3fb27SDimitry Andric // debugger if one has been attached. 70106c3fb27SDimitry Andric u8 *bytes = (u8 *)address; 70206c3fb27SDimitry Andric ReportError( 70306c3fb27SDimitry Andric "interception_win: unhandled instruction at %p: %02x %02x %02x %02x %02x " 70406c3fb27SDimitry Andric "%02x %02x %02x\n", 70506c3fb27SDimitry Andric (void *)address, bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], 70606c3fb27SDimitry Andric bytes[5], bytes[6], bytes[7]); 70706c3fb27SDimitry Andric if (::IsDebuggerPresent()) 70806c3fb27SDimitry Andric __debugbreak(); 70968d75effSDimitry Andric return 0; 71068d75effSDimitry Andric } 71168d75effSDimitry Andric 71268d75effSDimitry Andric // Returns 0 on error. 71368d75effSDimitry Andric static size_t RoundUpToInstrBoundary(size_t size, uptr address) { 71468d75effSDimitry Andric size_t cursor = 0; 71568d75effSDimitry Andric while (cursor < size) { 71668d75effSDimitry Andric size_t instruction_size = GetInstructionSize(address + cursor); 71768d75effSDimitry Andric if (!instruction_size) 71868d75effSDimitry Andric return 0; 71968d75effSDimitry Andric cursor += instruction_size; 72068d75effSDimitry Andric } 72168d75effSDimitry Andric return cursor; 72268d75effSDimitry Andric } 72368d75effSDimitry Andric 72468d75effSDimitry Andric static bool CopyInstructions(uptr to, uptr from, size_t size) { 72568d75effSDimitry Andric size_t cursor = 0; 72668d75effSDimitry Andric while (cursor != size) { 72768d75effSDimitry Andric size_t rel_offset = 0; 72868d75effSDimitry Andric size_t instruction_size = GetInstructionSize(from + cursor, &rel_offset); 72906c3fb27SDimitry Andric if (!instruction_size) 73006c3fb27SDimitry Andric return false; 73168d75effSDimitry Andric _memcpy((void *)(to + cursor), (void *)(from + cursor), 73268d75effSDimitry Andric (size_t)instruction_size); 73368d75effSDimitry Andric if (rel_offset) { 73468d75effSDimitry Andric # if SANITIZER_WINDOWS64 7355f757f3fSDimitry Andric // we want to make sure that the new relative offset still fits in 32-bits 7365f757f3fSDimitry Andric // this will be untrue if relocated_offset \notin [-2**31, 2**31) 7375f757f3fSDimitry Andric s64 delta = to - from; 7385f757f3fSDimitry Andric s64 relocated_offset = *(s32 *)(to + cursor + rel_offset) - delta; 7395f757f3fSDimitry Andric if (-0x8000'0000ll > relocated_offset || relocated_offset > 0x7FFF'FFFFll) 74068d75effSDimitry Andric return false; 7415f757f3fSDimitry Andric # else 7425f757f3fSDimitry Andric // on 32-bit, the relative offset will always be correct 7435f757f3fSDimitry Andric s32 delta = to - from; 7445f757f3fSDimitry Andric s32 relocated_offset = *(s32 *)(to + cursor + rel_offset) - delta; 74568d75effSDimitry Andric # endif 7465f757f3fSDimitry Andric *(s32 *)(to + cursor + rel_offset) = relocated_offset; 74768d75effSDimitry Andric } 74868d75effSDimitry Andric cursor += instruction_size; 74968d75effSDimitry Andric } 75068d75effSDimitry Andric return true; 75168d75effSDimitry Andric } 75268d75effSDimitry Andric 75368d75effSDimitry Andric 75468d75effSDimitry Andric #if !SANITIZER_WINDOWS64 75568d75effSDimitry Andric bool OverrideFunctionWithDetour( 75668d75effSDimitry Andric uptr old_func, uptr new_func, uptr *orig_old_func) { 75768d75effSDimitry Andric const int kDetourHeaderLen = 5; 75868d75effSDimitry Andric const u16 kDetourInstruction = 0xFF8B; 75968d75effSDimitry Andric 76068d75effSDimitry Andric uptr header = (uptr)old_func - kDetourHeaderLen; 76168d75effSDimitry Andric uptr patch_length = kDetourHeaderLen + kShortJumpInstructionLength; 76268d75effSDimitry Andric 76368d75effSDimitry Andric // Validate that the function is hookable. 76468d75effSDimitry Andric if (*(u16*)old_func != kDetourInstruction || 76568d75effSDimitry Andric !IsMemoryPadding(header, kDetourHeaderLen)) 76668d75effSDimitry Andric return false; 76768d75effSDimitry Andric 76868d75effSDimitry Andric // Change memory protection to writable. 76968d75effSDimitry Andric DWORD protection = 0; 77068d75effSDimitry Andric if (!ChangeMemoryProtection(header, patch_length, &protection)) 77168d75effSDimitry Andric return false; 77268d75effSDimitry Andric 77368d75effSDimitry Andric // Write a relative jump to the redirected function. 77468d75effSDimitry Andric WriteJumpInstruction(header, new_func); 77568d75effSDimitry Andric 77668d75effSDimitry Andric // Write the short jump to the function prefix. 77768d75effSDimitry Andric WriteShortJumpInstruction(old_func, header); 77868d75effSDimitry Andric 77968d75effSDimitry Andric // Restore previous memory protection. 78068d75effSDimitry Andric if (!RestoreMemoryProtection(header, patch_length, protection)) 78168d75effSDimitry Andric return false; 78268d75effSDimitry Andric 78368d75effSDimitry Andric if (orig_old_func) 78468d75effSDimitry Andric *orig_old_func = old_func + kShortJumpInstructionLength; 78568d75effSDimitry Andric 78668d75effSDimitry Andric return true; 78768d75effSDimitry Andric } 78868d75effSDimitry Andric #endif 78968d75effSDimitry Andric 79068d75effSDimitry Andric bool OverrideFunctionWithRedirectJump( 79168d75effSDimitry Andric uptr old_func, uptr new_func, uptr *orig_old_func) { 79268d75effSDimitry Andric // Check whether the first instruction is a relative jump. 79368d75effSDimitry Andric if (*(u8*)old_func != 0xE9) 79468d75effSDimitry Andric return false; 79568d75effSDimitry Andric 79668d75effSDimitry Andric if (orig_old_func) { 797bdd1243dSDimitry Andric sptr relative_offset = *(s32 *)(old_func + 1); 79868d75effSDimitry Andric uptr absolute_target = old_func + relative_offset + kJumpInstructionLength; 79968d75effSDimitry Andric *orig_old_func = absolute_target; 80068d75effSDimitry Andric } 80168d75effSDimitry Andric 80268d75effSDimitry Andric #if SANITIZER_WINDOWS64 80368d75effSDimitry Andric // If needed, get memory space for a trampoline jump. 80468d75effSDimitry Andric uptr trampoline = AllocateMemoryForTrampoline(old_func, kDirectBranchLength); 80568d75effSDimitry Andric if (!trampoline) 80668d75effSDimitry Andric return false; 80768d75effSDimitry Andric WriteDirectBranch(trampoline, new_func); 80868d75effSDimitry Andric #endif 80968d75effSDimitry Andric 81068d75effSDimitry Andric // Change memory protection to writable. 81168d75effSDimitry Andric DWORD protection = 0; 81268d75effSDimitry Andric if (!ChangeMemoryProtection(old_func, kJumpInstructionLength, &protection)) 81368d75effSDimitry Andric return false; 81468d75effSDimitry Andric 81568d75effSDimitry Andric // Write a relative jump to the redirected function. 81668d75effSDimitry Andric WriteJumpInstruction(old_func, FIRST_32_SECOND_64(new_func, trampoline)); 81768d75effSDimitry Andric 81868d75effSDimitry Andric // Restore previous memory protection. 81968d75effSDimitry Andric if (!RestoreMemoryProtection(old_func, kJumpInstructionLength, protection)) 82068d75effSDimitry Andric return false; 82168d75effSDimitry Andric 82268d75effSDimitry Andric return true; 82368d75effSDimitry Andric } 82468d75effSDimitry Andric 82568d75effSDimitry Andric bool OverrideFunctionWithHotPatch( 82668d75effSDimitry Andric uptr old_func, uptr new_func, uptr *orig_old_func) { 82768d75effSDimitry Andric const int kHotPatchHeaderLen = kBranchLength; 82868d75effSDimitry Andric 82968d75effSDimitry Andric uptr header = (uptr)old_func - kHotPatchHeaderLen; 83068d75effSDimitry Andric uptr patch_length = kHotPatchHeaderLen + kShortJumpInstructionLength; 83168d75effSDimitry Andric 83268d75effSDimitry Andric // Validate that the function is hot patchable. 83368d75effSDimitry Andric size_t instruction_size = GetInstructionSize(old_func); 83468d75effSDimitry Andric if (instruction_size < kShortJumpInstructionLength || 83568d75effSDimitry Andric !FunctionHasPadding(old_func, kHotPatchHeaderLen)) 83668d75effSDimitry Andric return false; 83768d75effSDimitry Andric 83868d75effSDimitry Andric if (orig_old_func) { 83968d75effSDimitry Andric // Put the needed instructions into the trampoline bytes. 84068d75effSDimitry Andric uptr trampoline_length = instruction_size + kDirectBranchLength; 84168d75effSDimitry Andric uptr trampoline = AllocateMemoryForTrampoline(old_func, trampoline_length); 84268d75effSDimitry Andric if (!trampoline) 84368d75effSDimitry Andric return false; 84468d75effSDimitry Andric if (!CopyInstructions(trampoline, old_func, instruction_size)) 84568d75effSDimitry Andric return false; 84668d75effSDimitry Andric WriteDirectBranch(trampoline + instruction_size, 84768d75effSDimitry Andric old_func + instruction_size); 84868d75effSDimitry Andric *orig_old_func = trampoline; 84968d75effSDimitry Andric } 85068d75effSDimitry Andric 85168d75effSDimitry Andric // If needed, get memory space for indirect address. 85268d75effSDimitry Andric uptr indirect_address = 0; 85368d75effSDimitry Andric #if SANITIZER_WINDOWS64 85468d75effSDimitry Andric indirect_address = AllocateMemoryForTrampoline(old_func, kAddressLength); 85568d75effSDimitry Andric if (!indirect_address) 85668d75effSDimitry Andric return false; 85768d75effSDimitry Andric #endif 85868d75effSDimitry Andric 85968d75effSDimitry Andric // Change memory protection to writable. 86068d75effSDimitry Andric DWORD protection = 0; 86168d75effSDimitry Andric if (!ChangeMemoryProtection(header, patch_length, &protection)) 86268d75effSDimitry Andric return false; 86368d75effSDimitry Andric 86468d75effSDimitry Andric // Write jumps to the redirected function. 86568d75effSDimitry Andric WriteBranch(header, indirect_address, new_func); 86668d75effSDimitry Andric WriteShortJumpInstruction(old_func, header); 86768d75effSDimitry Andric 86868d75effSDimitry Andric // Restore previous memory protection. 86968d75effSDimitry Andric if (!RestoreMemoryProtection(header, patch_length, protection)) 87068d75effSDimitry Andric return false; 87168d75effSDimitry Andric 87268d75effSDimitry Andric return true; 87368d75effSDimitry Andric } 87468d75effSDimitry Andric 87568d75effSDimitry Andric bool OverrideFunctionWithTrampoline( 87668d75effSDimitry Andric uptr old_func, uptr new_func, uptr *orig_old_func) { 87768d75effSDimitry Andric 87868d75effSDimitry Andric size_t instructions_length = kBranchLength; 87968d75effSDimitry Andric size_t padding_length = 0; 88068d75effSDimitry Andric uptr indirect_address = 0; 88168d75effSDimitry Andric 88268d75effSDimitry Andric if (orig_old_func) { 88368d75effSDimitry Andric // Find out the number of bytes of the instructions we need to copy 88468d75effSDimitry Andric // to the trampoline. 88568d75effSDimitry Andric instructions_length = RoundUpToInstrBoundary(kBranchLength, old_func); 88668d75effSDimitry Andric if (!instructions_length) 88768d75effSDimitry Andric return false; 88868d75effSDimitry Andric 88968d75effSDimitry Andric // Put the needed instructions into the trampoline bytes. 89068d75effSDimitry Andric uptr trampoline_length = instructions_length + kDirectBranchLength; 89168d75effSDimitry Andric uptr trampoline = AllocateMemoryForTrampoline(old_func, trampoline_length); 89268d75effSDimitry Andric if (!trampoline) 89368d75effSDimitry Andric return false; 89468d75effSDimitry Andric if (!CopyInstructions(trampoline, old_func, instructions_length)) 89568d75effSDimitry Andric return false; 89668d75effSDimitry Andric WriteDirectBranch(trampoline + instructions_length, 89768d75effSDimitry Andric old_func + instructions_length); 89868d75effSDimitry Andric *orig_old_func = trampoline; 89968d75effSDimitry Andric } 90068d75effSDimitry Andric 90168d75effSDimitry Andric #if SANITIZER_WINDOWS64 90268d75effSDimitry Andric // Check if the targeted address can be encoded in the function padding. 90368d75effSDimitry Andric // Otherwise, allocate it in the trampoline region. 90468d75effSDimitry Andric if (IsMemoryPadding(old_func - kAddressLength, kAddressLength)) { 90568d75effSDimitry Andric indirect_address = old_func - kAddressLength; 90668d75effSDimitry Andric padding_length = kAddressLength; 90768d75effSDimitry Andric } else { 90868d75effSDimitry Andric indirect_address = AllocateMemoryForTrampoline(old_func, kAddressLength); 90968d75effSDimitry Andric if (!indirect_address) 91068d75effSDimitry Andric return false; 91168d75effSDimitry Andric } 91268d75effSDimitry Andric #endif 91368d75effSDimitry Andric 91468d75effSDimitry Andric // Change memory protection to writable. 91568d75effSDimitry Andric uptr patch_address = old_func - padding_length; 91668d75effSDimitry Andric uptr patch_length = instructions_length + padding_length; 91768d75effSDimitry Andric DWORD protection = 0; 91868d75effSDimitry Andric if (!ChangeMemoryProtection(patch_address, patch_length, &protection)) 91968d75effSDimitry Andric return false; 92068d75effSDimitry Andric 92168d75effSDimitry Andric // Patch the original function. 92268d75effSDimitry Andric WriteBranch(old_func, indirect_address, new_func); 92368d75effSDimitry Andric 92468d75effSDimitry Andric // Restore previous memory protection. 92568d75effSDimitry Andric if (!RestoreMemoryProtection(patch_address, patch_length, protection)) 92668d75effSDimitry Andric return false; 92768d75effSDimitry Andric 92868d75effSDimitry Andric return true; 92968d75effSDimitry Andric } 93068d75effSDimitry Andric 93168d75effSDimitry Andric bool OverrideFunction( 93268d75effSDimitry Andric uptr old_func, uptr new_func, uptr *orig_old_func) { 93368d75effSDimitry Andric #if !SANITIZER_WINDOWS64 93468d75effSDimitry Andric if (OverrideFunctionWithDetour(old_func, new_func, orig_old_func)) 93568d75effSDimitry Andric return true; 93668d75effSDimitry Andric #endif 93768d75effSDimitry Andric if (OverrideFunctionWithRedirectJump(old_func, new_func, orig_old_func)) 93868d75effSDimitry Andric return true; 93968d75effSDimitry Andric if (OverrideFunctionWithHotPatch(old_func, new_func, orig_old_func)) 94068d75effSDimitry Andric return true; 94168d75effSDimitry Andric if (OverrideFunctionWithTrampoline(old_func, new_func, orig_old_func)) 94268d75effSDimitry Andric return true; 94368d75effSDimitry Andric return false; 94468d75effSDimitry Andric } 94568d75effSDimitry Andric 94668d75effSDimitry Andric static void **InterestingDLLsAvailable() { 94768d75effSDimitry Andric static const char *InterestingDLLs[] = { 94868d75effSDimitry Andric "kernel32.dll", 949*7a6dacacSDimitry Andric "msvcr100d.dll", // VS2010 950*7a6dacacSDimitry Andric "msvcr110d.dll", // VS2012 951*7a6dacacSDimitry Andric "msvcr120d.dll", // VS2013 952*7a6dacacSDimitry Andric "vcruntime140d.dll", // VS2015 953*7a6dacacSDimitry Andric "ucrtbased.dll", // Universal CRT 95468d75effSDimitry Andric "msvcr100.dll", // VS2010 95568d75effSDimitry Andric "msvcr110.dll", // VS2012 95668d75effSDimitry Andric "msvcr120.dll", // VS2013 95768d75effSDimitry Andric "vcruntime140.dll", // VS2015 95868d75effSDimitry Andric "ucrtbase.dll", // Universal CRT 95906c3fb27SDimitry Andric # if (defined(__MINGW32__) && defined(__i386__)) 96006c3fb27SDimitry Andric "libc++.dll", // libc++ 96106c3fb27SDimitry Andric "libunwind.dll", // libunwind 96206c3fb27SDimitry Andric # endif 96368d75effSDimitry Andric // NTDLL should go last as it exports some functions that we should 96468d75effSDimitry Andric // override in the CRT [presumably only used internally]. 965*7a6dacacSDimitry Andric "ntdll.dll", 966*7a6dacacSDimitry Andric NULL 967*7a6dacacSDimitry Andric }; 96868d75effSDimitry Andric static void *result[ARRAY_SIZE(InterestingDLLs)] = { 0 }; 96968d75effSDimitry Andric if (!result[0]) { 97068d75effSDimitry Andric for (size_t i = 0, j = 0; InterestingDLLs[i]; ++i) { 97168d75effSDimitry Andric if (HMODULE h = GetModuleHandleA(InterestingDLLs[i])) 97268d75effSDimitry Andric result[j++] = (void *)h; 97368d75effSDimitry Andric } 97468d75effSDimitry Andric } 97568d75effSDimitry Andric return &result[0]; 97668d75effSDimitry Andric } 97768d75effSDimitry Andric 97868d75effSDimitry Andric namespace { 97968d75effSDimitry Andric // Utility for reading loaded PE images. 98068d75effSDimitry Andric template <typename T> class RVAPtr { 98168d75effSDimitry Andric public: 98268d75effSDimitry Andric RVAPtr(void *module, uptr rva) 98368d75effSDimitry Andric : ptr_(reinterpret_cast<T *>(reinterpret_cast<char *>(module) + rva)) {} 98468d75effSDimitry Andric operator T *() { return ptr_; } 98568d75effSDimitry Andric T *operator->() { return ptr_; } 98668d75effSDimitry Andric T *operator++() { return ++ptr_; } 98768d75effSDimitry Andric 98868d75effSDimitry Andric private: 98968d75effSDimitry Andric T *ptr_; 99068d75effSDimitry Andric }; 99168d75effSDimitry Andric } // namespace 99268d75effSDimitry Andric 99368d75effSDimitry Andric // Internal implementation of GetProcAddress. At least since Windows 8, 99468d75effSDimitry Andric // GetProcAddress appears to initialize DLLs before returning function pointers 99568d75effSDimitry Andric // into them. This is problematic for the sanitizers, because they typically 99668d75effSDimitry Andric // want to intercept malloc *before* MSVCRT initializes. Our internal 99768d75effSDimitry Andric // implementation walks the export list manually without doing initialization. 99868d75effSDimitry Andric uptr InternalGetProcAddress(void *module, const char *func_name) { 99968d75effSDimitry Andric // Check that the module header is full and present. 100068d75effSDimitry Andric RVAPtr<IMAGE_DOS_HEADER> dos_stub(module, 0); 100168d75effSDimitry Andric RVAPtr<IMAGE_NT_HEADERS> headers(module, dos_stub->e_lfanew); 100268d75effSDimitry Andric if (!module || dos_stub->e_magic != IMAGE_DOS_SIGNATURE || // "MZ" 100368d75effSDimitry Andric headers->Signature != IMAGE_NT_SIGNATURE || // "PE\0\0" 100468d75effSDimitry Andric headers->FileHeader.SizeOfOptionalHeader < 100568d75effSDimitry Andric sizeof(IMAGE_OPTIONAL_HEADER)) { 100668d75effSDimitry Andric return 0; 100768d75effSDimitry Andric } 100868d75effSDimitry Andric 100968d75effSDimitry Andric IMAGE_DATA_DIRECTORY *export_directory = 101068d75effSDimitry Andric &headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; 101168d75effSDimitry Andric if (export_directory->Size == 0) 101268d75effSDimitry Andric return 0; 101368d75effSDimitry Andric RVAPtr<IMAGE_EXPORT_DIRECTORY> exports(module, 101468d75effSDimitry Andric export_directory->VirtualAddress); 101568d75effSDimitry Andric RVAPtr<DWORD> functions(module, exports->AddressOfFunctions); 101668d75effSDimitry Andric RVAPtr<DWORD> names(module, exports->AddressOfNames); 101768d75effSDimitry Andric RVAPtr<WORD> ordinals(module, exports->AddressOfNameOrdinals); 101868d75effSDimitry Andric 101968d75effSDimitry Andric for (DWORD i = 0; i < exports->NumberOfNames; i++) { 102068d75effSDimitry Andric RVAPtr<char> name(module, names[i]); 102168d75effSDimitry Andric if (!strcmp(func_name, name)) { 102268d75effSDimitry Andric DWORD index = ordinals[i]; 102368d75effSDimitry Andric RVAPtr<char> func(module, functions[index]); 102468d75effSDimitry Andric 102568d75effSDimitry Andric // Handle forwarded functions. 102668d75effSDimitry Andric DWORD offset = functions[index]; 102768d75effSDimitry Andric if (offset >= export_directory->VirtualAddress && 102868d75effSDimitry Andric offset < export_directory->VirtualAddress + export_directory->Size) { 102968d75effSDimitry Andric // An entry for a forwarded function is a string with the following 103068d75effSDimitry Andric // format: "<module> . <function_name>" that is stored into the 103168d75effSDimitry Andric // exported directory. 103268d75effSDimitry Andric char function_name[256]; 103368d75effSDimitry Andric size_t funtion_name_length = _strlen(func); 103468d75effSDimitry Andric if (funtion_name_length >= sizeof(function_name) - 1) 103568d75effSDimitry Andric InterceptionFailed(); 103668d75effSDimitry Andric 103768d75effSDimitry Andric _memcpy(function_name, func, funtion_name_length); 103868d75effSDimitry Andric function_name[funtion_name_length] = '\0'; 103968d75effSDimitry Andric char* separator = _strchr(function_name, '.'); 104068d75effSDimitry Andric if (!separator) 104168d75effSDimitry Andric InterceptionFailed(); 104268d75effSDimitry Andric *separator = '\0'; 104368d75effSDimitry Andric 104468d75effSDimitry Andric void* redirected_module = GetModuleHandleA(function_name); 104568d75effSDimitry Andric if (!redirected_module) 104668d75effSDimitry Andric InterceptionFailed(); 104768d75effSDimitry Andric return InternalGetProcAddress(redirected_module, separator + 1); 104868d75effSDimitry Andric } 104968d75effSDimitry Andric 105068d75effSDimitry Andric return (uptr)(char *)func; 105168d75effSDimitry Andric } 105268d75effSDimitry Andric } 105368d75effSDimitry Andric 105468d75effSDimitry Andric return 0; 105568d75effSDimitry Andric } 105668d75effSDimitry Andric 105768d75effSDimitry Andric bool OverrideFunction( 105868d75effSDimitry Andric const char *func_name, uptr new_func, uptr *orig_old_func) { 105968d75effSDimitry Andric bool hooked = false; 106068d75effSDimitry Andric void **DLLs = InterestingDLLsAvailable(); 106168d75effSDimitry Andric for (size_t i = 0; DLLs[i]; ++i) { 106268d75effSDimitry Andric uptr func_addr = InternalGetProcAddress(DLLs[i], func_name); 106368d75effSDimitry Andric if (func_addr && 106468d75effSDimitry Andric OverrideFunction(func_addr, new_func, orig_old_func)) { 106568d75effSDimitry Andric hooked = true; 106668d75effSDimitry Andric } 106768d75effSDimitry Andric } 106868d75effSDimitry Andric return hooked; 106968d75effSDimitry Andric } 107068d75effSDimitry Andric 107168d75effSDimitry Andric bool OverrideImportedFunction(const char *module_to_patch, 107268d75effSDimitry Andric const char *imported_module, 107368d75effSDimitry Andric const char *function_name, uptr new_function, 107468d75effSDimitry Andric uptr *orig_old_func) { 107568d75effSDimitry Andric HMODULE module = GetModuleHandleA(module_to_patch); 107668d75effSDimitry Andric if (!module) 107768d75effSDimitry Andric return false; 107868d75effSDimitry Andric 107968d75effSDimitry Andric // Check that the module header is full and present. 108068d75effSDimitry Andric RVAPtr<IMAGE_DOS_HEADER> dos_stub(module, 0); 108168d75effSDimitry Andric RVAPtr<IMAGE_NT_HEADERS> headers(module, dos_stub->e_lfanew); 108268d75effSDimitry Andric if (!module || dos_stub->e_magic != IMAGE_DOS_SIGNATURE || // "MZ" 108368d75effSDimitry Andric headers->Signature != IMAGE_NT_SIGNATURE || // "PE\0\0" 108468d75effSDimitry Andric headers->FileHeader.SizeOfOptionalHeader < 108568d75effSDimitry Andric sizeof(IMAGE_OPTIONAL_HEADER)) { 108668d75effSDimitry Andric return false; 108768d75effSDimitry Andric } 108868d75effSDimitry Andric 108968d75effSDimitry Andric IMAGE_DATA_DIRECTORY *import_directory = 109068d75effSDimitry Andric &headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; 109168d75effSDimitry Andric 109268d75effSDimitry Andric // Iterate the list of imported DLLs. FirstThunk will be null for the last 109368d75effSDimitry Andric // entry. 109468d75effSDimitry Andric RVAPtr<IMAGE_IMPORT_DESCRIPTOR> imports(module, 109568d75effSDimitry Andric import_directory->VirtualAddress); 109668d75effSDimitry Andric for (; imports->FirstThunk != 0; ++imports) { 109768d75effSDimitry Andric RVAPtr<const char> modname(module, imports->Name); 109868d75effSDimitry Andric if (_stricmp(&*modname, imported_module) == 0) 109968d75effSDimitry Andric break; 110068d75effSDimitry Andric } 110168d75effSDimitry Andric if (imports->FirstThunk == 0) 110268d75effSDimitry Andric return false; 110368d75effSDimitry Andric 110468d75effSDimitry Andric // We have two parallel arrays: the import address table (IAT) and the table 110568d75effSDimitry Andric // of names. They start out containing the same data, but the loader rewrites 110668d75effSDimitry Andric // the IAT to hold imported addresses and leaves the name table in 110768d75effSDimitry Andric // OriginalFirstThunk alone. 110868d75effSDimitry Andric RVAPtr<IMAGE_THUNK_DATA> name_table(module, imports->OriginalFirstThunk); 110968d75effSDimitry Andric RVAPtr<IMAGE_THUNK_DATA> iat(module, imports->FirstThunk); 111068d75effSDimitry Andric for (; name_table->u1.Ordinal != 0; ++name_table, ++iat) { 111168d75effSDimitry Andric if (!IMAGE_SNAP_BY_ORDINAL(name_table->u1.Ordinal)) { 111268d75effSDimitry Andric RVAPtr<IMAGE_IMPORT_BY_NAME> import_by_name( 111368d75effSDimitry Andric module, name_table->u1.ForwarderString); 111468d75effSDimitry Andric const char *funcname = &import_by_name->Name[0]; 111568d75effSDimitry Andric if (strcmp(funcname, function_name) == 0) 111668d75effSDimitry Andric break; 111768d75effSDimitry Andric } 111868d75effSDimitry Andric } 111968d75effSDimitry Andric if (name_table->u1.Ordinal == 0) 112068d75effSDimitry Andric return false; 112168d75effSDimitry Andric 112268d75effSDimitry Andric // Now we have the correct IAT entry. Do the swap. We have to make the page 112368d75effSDimitry Andric // read/write first. 112468d75effSDimitry Andric if (orig_old_func) 112568d75effSDimitry Andric *orig_old_func = iat->u1.AddressOfData; 112668d75effSDimitry Andric DWORD old_prot, unused_prot; 112768d75effSDimitry Andric if (!VirtualProtect(&iat->u1.AddressOfData, 4, PAGE_EXECUTE_READWRITE, 112868d75effSDimitry Andric &old_prot)) 112968d75effSDimitry Andric return false; 113068d75effSDimitry Andric iat->u1.AddressOfData = new_function; 113168d75effSDimitry Andric if (!VirtualProtect(&iat->u1.AddressOfData, 4, old_prot, &unused_prot)) 113268d75effSDimitry Andric return false; // Not clear if this failure bothers us. 113368d75effSDimitry Andric return true; 113468d75effSDimitry Andric } 113568d75effSDimitry Andric 113668d75effSDimitry Andric } // namespace __interception 113768d75effSDimitry Andric 113881ad6265SDimitry Andric #endif // SANITIZER_APPLE 1139