168d75effSDimitry Andric //===-- interception_linux.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 14468d75effSDimitry Andric static void InterceptionFailed() { 14568d75effSDimitry Andric // Do we have a good way to abort with an error message here? 14668d75effSDimitry Andric __debugbreak(); 14768d75effSDimitry Andric } 14868d75effSDimitry Andric 14968d75effSDimitry Andric static bool DistanceIsWithin2Gig(uptr from, uptr target) { 15068d75effSDimitry Andric #if SANITIZER_WINDOWS64 15168d75effSDimitry Andric if (from < target) 15268d75effSDimitry Andric return target - from <= (uptr)0x7FFFFFFFU; 15368d75effSDimitry Andric else 15468d75effSDimitry Andric return from - target <= (uptr)0x80000000U; 15568d75effSDimitry Andric #else 15668d75effSDimitry Andric // In a 32-bit address space, the address calculation will wrap, so this check 15768d75effSDimitry Andric // is unnecessary. 15868d75effSDimitry Andric return true; 15968d75effSDimitry Andric #endif 16068d75effSDimitry Andric } 16168d75effSDimitry Andric 16268d75effSDimitry Andric static uptr GetMmapGranularity() { 16368d75effSDimitry Andric SYSTEM_INFO si; 16468d75effSDimitry Andric GetSystemInfo(&si); 16568d75effSDimitry Andric return si.dwAllocationGranularity; 16668d75effSDimitry Andric } 16768d75effSDimitry Andric 168e8d8bef9SDimitry Andric UNUSED static uptr RoundUpTo(uptr size, uptr boundary) { 16968d75effSDimitry Andric return (size + boundary - 1) & ~(boundary - 1); 17068d75effSDimitry Andric } 17168d75effSDimitry Andric 17268d75effSDimitry Andric // FIXME: internal_str* and internal_mem* functions should be moved from the 17368d75effSDimitry Andric // ASan sources into interception/. 17468d75effSDimitry Andric 17568d75effSDimitry Andric static size_t _strlen(const char *str) { 17668d75effSDimitry Andric const char* p = str; 17768d75effSDimitry Andric while (*p != '\0') ++p; 17868d75effSDimitry Andric return p - str; 17968d75effSDimitry Andric } 18068d75effSDimitry Andric 18168d75effSDimitry Andric static char* _strchr(char* str, char c) { 18268d75effSDimitry Andric while (*str) { 18368d75effSDimitry Andric if (*str == c) 18468d75effSDimitry Andric return str; 18568d75effSDimitry Andric ++str; 18668d75effSDimitry Andric } 18768d75effSDimitry Andric return nullptr; 18868d75effSDimitry Andric } 18968d75effSDimitry Andric 19068d75effSDimitry Andric static void _memset(void *p, int value, size_t sz) { 19168d75effSDimitry Andric for (size_t i = 0; i < sz; ++i) 19268d75effSDimitry Andric ((char*)p)[i] = (char)value; 19368d75effSDimitry Andric } 19468d75effSDimitry Andric 19568d75effSDimitry Andric static void _memcpy(void *dst, void *src, size_t sz) { 19668d75effSDimitry Andric char *dst_c = (char*)dst, 19768d75effSDimitry Andric *src_c = (char*)src; 19868d75effSDimitry Andric for (size_t i = 0; i < sz; ++i) 19968d75effSDimitry Andric dst_c[i] = src_c[i]; 20068d75effSDimitry Andric } 20168d75effSDimitry Andric 20268d75effSDimitry Andric static bool ChangeMemoryProtection( 20368d75effSDimitry Andric uptr address, uptr size, DWORD *old_protection) { 20468d75effSDimitry Andric return ::VirtualProtect((void*)address, size, 20568d75effSDimitry Andric PAGE_EXECUTE_READWRITE, 20668d75effSDimitry Andric old_protection) != FALSE; 20768d75effSDimitry Andric } 20868d75effSDimitry Andric 20968d75effSDimitry Andric static bool RestoreMemoryProtection( 21068d75effSDimitry Andric uptr address, uptr size, DWORD old_protection) { 21168d75effSDimitry Andric DWORD unused; 21268d75effSDimitry Andric return ::VirtualProtect((void*)address, size, 21368d75effSDimitry Andric old_protection, 21468d75effSDimitry Andric &unused) != FALSE; 21568d75effSDimitry Andric } 21668d75effSDimitry Andric 21768d75effSDimitry Andric static bool IsMemoryPadding(uptr address, uptr size) { 21868d75effSDimitry Andric u8* function = (u8*)address; 21968d75effSDimitry Andric for (size_t i = 0; i < size; ++i) 22068d75effSDimitry Andric if (function[i] != 0x90 && function[i] != 0xCC) 22168d75effSDimitry Andric return false; 22268d75effSDimitry Andric return true; 22368d75effSDimitry Andric } 22468d75effSDimitry Andric 22568d75effSDimitry Andric static const u8 kHintNop8Bytes[] = { 22668d75effSDimitry Andric 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00 22768d75effSDimitry Andric }; 22868d75effSDimitry Andric 22968d75effSDimitry Andric template<class T> 23068d75effSDimitry Andric static bool FunctionHasPrefix(uptr address, const T &pattern) { 23168d75effSDimitry Andric u8* function = (u8*)address - sizeof(pattern); 23268d75effSDimitry Andric for (size_t i = 0; i < sizeof(pattern); ++i) 23368d75effSDimitry Andric if (function[i] != pattern[i]) 23468d75effSDimitry Andric return false; 23568d75effSDimitry Andric return true; 23668d75effSDimitry Andric } 23768d75effSDimitry Andric 23868d75effSDimitry Andric static bool FunctionHasPadding(uptr address, uptr size) { 23968d75effSDimitry Andric if (IsMemoryPadding(address - size, size)) 24068d75effSDimitry Andric return true; 24168d75effSDimitry Andric if (size <= sizeof(kHintNop8Bytes) && 24268d75effSDimitry Andric FunctionHasPrefix(address, kHintNop8Bytes)) 24368d75effSDimitry Andric return true; 24468d75effSDimitry Andric return false; 24568d75effSDimitry Andric } 24668d75effSDimitry Andric 24768d75effSDimitry Andric static void WritePadding(uptr from, uptr size) { 24868d75effSDimitry Andric _memset((void*)from, 0xCC, (size_t)size); 24968d75effSDimitry Andric } 25068d75effSDimitry Andric 25168d75effSDimitry Andric static void WriteJumpInstruction(uptr from, uptr target) { 25268d75effSDimitry Andric if (!DistanceIsWithin2Gig(from + kJumpInstructionLength, target)) 25368d75effSDimitry Andric InterceptionFailed(); 25468d75effSDimitry Andric ptrdiff_t offset = target - from - kJumpInstructionLength; 25568d75effSDimitry Andric *(u8*)from = 0xE9; 25668d75effSDimitry Andric *(u32*)(from + 1) = offset; 25768d75effSDimitry Andric } 25868d75effSDimitry Andric 25968d75effSDimitry Andric static void WriteShortJumpInstruction(uptr from, uptr target) { 26068d75effSDimitry Andric sptr offset = target - from - kShortJumpInstructionLength; 26168d75effSDimitry Andric if (offset < -128 || offset > 127) 26268d75effSDimitry Andric InterceptionFailed(); 26368d75effSDimitry Andric *(u8*)from = 0xEB; 26468d75effSDimitry Andric *(u8*)(from + 1) = (u8)offset; 26568d75effSDimitry Andric } 26668d75effSDimitry Andric 26768d75effSDimitry Andric #if SANITIZER_WINDOWS64 26868d75effSDimitry Andric static void WriteIndirectJumpInstruction(uptr from, uptr indirect_target) { 26968d75effSDimitry Andric // jmp [rip + <offset>] = FF 25 <offset> where <offset> is a relative 27068d75effSDimitry Andric // offset. 27168d75effSDimitry Andric // The offset is the distance from then end of the jump instruction to the 27268d75effSDimitry Andric // memory location containing the targeted address. The displacement is still 27368d75effSDimitry Andric // 32-bit in x64, so indirect_target must be located within +/- 2GB range. 27468d75effSDimitry Andric int offset = indirect_target - from - kIndirectJumpInstructionLength; 27568d75effSDimitry Andric if (!DistanceIsWithin2Gig(from + kIndirectJumpInstructionLength, 27668d75effSDimitry Andric indirect_target)) { 27768d75effSDimitry Andric InterceptionFailed(); 27868d75effSDimitry Andric } 27968d75effSDimitry Andric *(u16*)from = 0x25FF; 28068d75effSDimitry Andric *(u32*)(from + 2) = offset; 28168d75effSDimitry Andric } 28268d75effSDimitry Andric #endif 28368d75effSDimitry Andric 28468d75effSDimitry Andric static void WriteBranch( 28568d75effSDimitry Andric uptr from, uptr indirect_target, uptr target) { 28668d75effSDimitry Andric #if SANITIZER_WINDOWS64 28768d75effSDimitry Andric WriteIndirectJumpInstruction(from, indirect_target); 28868d75effSDimitry Andric *(u64*)indirect_target = target; 28968d75effSDimitry Andric #else 29068d75effSDimitry Andric (void)indirect_target; 29168d75effSDimitry Andric WriteJumpInstruction(from, target); 29268d75effSDimitry Andric #endif 29368d75effSDimitry Andric } 29468d75effSDimitry Andric 29568d75effSDimitry Andric static void WriteDirectBranch(uptr from, uptr target) { 29668d75effSDimitry Andric #if SANITIZER_WINDOWS64 29768d75effSDimitry Andric // Emit an indirect jump through immediately following bytes: 29868d75effSDimitry Andric // jmp [rip + kBranchLength] 29968d75effSDimitry Andric // .quad <target> 30068d75effSDimitry Andric WriteBranch(from, from + kBranchLength, target); 30168d75effSDimitry Andric #else 30268d75effSDimitry Andric WriteJumpInstruction(from, target); 30368d75effSDimitry Andric #endif 30468d75effSDimitry Andric } 30568d75effSDimitry Andric 30668d75effSDimitry Andric struct TrampolineMemoryRegion { 30768d75effSDimitry Andric uptr content; 30868d75effSDimitry Andric uptr allocated_size; 30968d75effSDimitry Andric uptr max_size; 31068d75effSDimitry Andric }; 31168d75effSDimitry Andric 312e8d8bef9SDimitry Andric UNUSED static const uptr kTrampolineScanLimitRange = 1 << 31; // 2 gig 31368d75effSDimitry Andric static const int kMaxTrampolineRegion = 1024; 31468d75effSDimitry Andric static TrampolineMemoryRegion TrampolineRegions[kMaxTrampolineRegion]; 31568d75effSDimitry Andric 31668d75effSDimitry Andric static void *AllocateTrampolineRegion(uptr image_address, size_t granularity) { 31768d75effSDimitry Andric #if SANITIZER_WINDOWS64 31868d75effSDimitry Andric uptr address = image_address; 31968d75effSDimitry Andric uptr scanned = 0; 32068d75effSDimitry Andric while (scanned < kTrampolineScanLimitRange) { 32168d75effSDimitry Andric MEMORY_BASIC_INFORMATION info; 32268d75effSDimitry Andric if (!::VirtualQuery((void*)address, &info, sizeof(info))) 32368d75effSDimitry Andric return nullptr; 32468d75effSDimitry Andric 32568d75effSDimitry Andric // Check whether a region can be allocated at |address|. 32668d75effSDimitry Andric if (info.State == MEM_FREE && info.RegionSize >= granularity) { 32768d75effSDimitry Andric void *page = ::VirtualAlloc((void*)RoundUpTo(address, granularity), 32868d75effSDimitry Andric granularity, 32968d75effSDimitry Andric MEM_RESERVE | MEM_COMMIT, 33068d75effSDimitry Andric PAGE_EXECUTE_READWRITE); 33168d75effSDimitry Andric return page; 33268d75effSDimitry Andric } 33368d75effSDimitry Andric 33468d75effSDimitry Andric // Move to the next region. 33568d75effSDimitry Andric address = (uptr)info.BaseAddress + info.RegionSize; 33668d75effSDimitry Andric scanned += info.RegionSize; 33768d75effSDimitry Andric } 33868d75effSDimitry Andric return nullptr; 33968d75effSDimitry Andric #else 34068d75effSDimitry Andric return ::VirtualAlloc(nullptr, 34168d75effSDimitry Andric granularity, 34268d75effSDimitry Andric MEM_RESERVE | MEM_COMMIT, 34368d75effSDimitry Andric PAGE_EXECUTE_READWRITE); 34468d75effSDimitry Andric #endif 34568d75effSDimitry Andric } 34668d75effSDimitry Andric 34768d75effSDimitry Andric // Used by unittests to release mapped memory space. 34868d75effSDimitry Andric void TestOnlyReleaseTrampolineRegions() { 34968d75effSDimitry Andric for (size_t bucket = 0; bucket < kMaxTrampolineRegion; ++bucket) { 35068d75effSDimitry Andric TrampolineMemoryRegion *current = &TrampolineRegions[bucket]; 35168d75effSDimitry Andric if (current->content == 0) 35268d75effSDimitry Andric return; 35368d75effSDimitry Andric ::VirtualFree((void*)current->content, 0, MEM_RELEASE); 35468d75effSDimitry Andric current->content = 0; 35568d75effSDimitry Andric } 35668d75effSDimitry Andric } 35768d75effSDimitry Andric 35868d75effSDimitry Andric static uptr AllocateMemoryForTrampoline(uptr image_address, size_t size) { 35968d75effSDimitry Andric // Find a region within 2G with enough space to allocate |size| bytes. 36068d75effSDimitry Andric TrampolineMemoryRegion *region = nullptr; 36168d75effSDimitry Andric for (size_t bucket = 0; bucket < kMaxTrampolineRegion; ++bucket) { 36268d75effSDimitry Andric TrampolineMemoryRegion* current = &TrampolineRegions[bucket]; 36368d75effSDimitry Andric if (current->content == 0) { 36468d75effSDimitry Andric // No valid region found, allocate a new region. 36568d75effSDimitry Andric size_t bucket_size = GetMmapGranularity(); 36668d75effSDimitry Andric void *content = AllocateTrampolineRegion(image_address, bucket_size); 36768d75effSDimitry Andric if (content == nullptr) 36868d75effSDimitry Andric return 0U; 36968d75effSDimitry Andric 37068d75effSDimitry Andric current->content = (uptr)content; 37168d75effSDimitry Andric current->allocated_size = 0; 37268d75effSDimitry Andric current->max_size = bucket_size; 37368d75effSDimitry Andric region = current; 37468d75effSDimitry Andric break; 37568d75effSDimitry Andric } else if (current->max_size - current->allocated_size > size) { 37668d75effSDimitry Andric #if SANITIZER_WINDOWS64 37768d75effSDimitry Andric // In 64-bits, the memory space must be allocated within 2G boundary. 37868d75effSDimitry Andric uptr next_address = current->content + current->allocated_size; 37968d75effSDimitry Andric if (next_address < image_address || 38068d75effSDimitry Andric next_address - image_address >= 0x7FFF0000) 38168d75effSDimitry Andric continue; 38268d75effSDimitry Andric #endif 38368d75effSDimitry Andric // The space can be allocated in the current region. 38468d75effSDimitry Andric region = current; 38568d75effSDimitry Andric break; 38668d75effSDimitry Andric } 38768d75effSDimitry Andric } 38868d75effSDimitry Andric 38968d75effSDimitry Andric // Failed to find a region. 39068d75effSDimitry Andric if (region == nullptr) 39168d75effSDimitry Andric return 0U; 39268d75effSDimitry Andric 39368d75effSDimitry Andric // Allocate the space in the current region. 39468d75effSDimitry Andric uptr allocated_space = region->content + region->allocated_size; 39568d75effSDimitry Andric region->allocated_size += size; 39668d75effSDimitry Andric WritePadding(allocated_space, size); 39768d75effSDimitry Andric 39868d75effSDimitry Andric return allocated_space; 39968d75effSDimitry Andric } 40068d75effSDimitry Andric 401349cc55cSDimitry Andric // The following prologues cannot be patched because of the short jump 402349cc55cSDimitry Andric // jumping to the patching region. 403349cc55cSDimitry Andric 4041fd87a68SDimitry Andric #if SANITIZER_WINDOWS64 405349cc55cSDimitry Andric // ntdll!wcslen in Win11 406349cc55cSDimitry Andric // 488bc1 mov rax,rcx 407349cc55cSDimitry Andric // 0fb710 movzx edx,word ptr [rax] 408349cc55cSDimitry Andric // 4883c002 add rax,2 409349cc55cSDimitry Andric // 6685d2 test dx,dx 410349cc55cSDimitry Andric // 75f4 jne -12 411349cc55cSDimitry Andric static const u8 kPrologueWithShortJump1[] = { 412349cc55cSDimitry Andric 0x48, 0x8b, 0xc1, 0x0f, 0xb7, 0x10, 0x48, 0x83, 413349cc55cSDimitry Andric 0xc0, 0x02, 0x66, 0x85, 0xd2, 0x75, 0xf4, 414349cc55cSDimitry Andric }; 415349cc55cSDimitry Andric 416349cc55cSDimitry Andric // ntdll!strrchr in Win11 417349cc55cSDimitry Andric // 4c8bc1 mov r8,rcx 418349cc55cSDimitry Andric // 8a01 mov al,byte ptr [rcx] 419349cc55cSDimitry Andric // 48ffc1 inc rcx 420349cc55cSDimitry Andric // 84c0 test al,al 421349cc55cSDimitry Andric // 75f7 jne -9 422349cc55cSDimitry Andric static const u8 kPrologueWithShortJump2[] = { 423349cc55cSDimitry Andric 0x4c, 0x8b, 0xc1, 0x8a, 0x01, 0x48, 0xff, 0xc1, 424349cc55cSDimitry Andric 0x84, 0xc0, 0x75, 0xf7, 425349cc55cSDimitry Andric }; 4261fd87a68SDimitry Andric #endif 427349cc55cSDimitry Andric 42868d75effSDimitry Andric // Returns 0 on error. 42968d75effSDimitry Andric static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) { 430349cc55cSDimitry Andric #if SANITIZER_WINDOWS64 431349cc55cSDimitry Andric if (memcmp((u8*)address, kPrologueWithShortJump1, 432349cc55cSDimitry Andric sizeof(kPrologueWithShortJump1)) == 0 || 433349cc55cSDimitry Andric memcmp((u8*)address, kPrologueWithShortJump2, 434349cc55cSDimitry Andric sizeof(kPrologueWithShortJump2)) == 0) { 435349cc55cSDimitry Andric return 0; 436349cc55cSDimitry Andric } 437349cc55cSDimitry Andric #endif 438349cc55cSDimitry Andric 43968d75effSDimitry Andric switch (*(u64*)address) { 44068d75effSDimitry Andric case 0x90909090909006EB: // stub: jmp over 6 x nop. 44168d75effSDimitry Andric return 8; 44268d75effSDimitry Andric } 44368d75effSDimitry Andric 44468d75effSDimitry Andric switch (*(u8*)address) { 44568d75effSDimitry Andric case 0x90: // 90 : nop 44668d75effSDimitry Andric return 1; 44768d75effSDimitry Andric 44868d75effSDimitry Andric case 0x50: // push eax / rax 44968d75effSDimitry Andric case 0x51: // push ecx / rcx 45068d75effSDimitry Andric case 0x52: // push edx / rdx 45168d75effSDimitry Andric case 0x53: // push ebx / rbx 45268d75effSDimitry Andric case 0x54: // push esp / rsp 45368d75effSDimitry Andric case 0x55: // push ebp / rbp 45468d75effSDimitry Andric case 0x56: // push esi / rsi 45568d75effSDimitry Andric case 0x57: // push edi / rdi 45668d75effSDimitry Andric case 0x5D: // pop ebp / rbp 45768d75effSDimitry Andric return 1; 45868d75effSDimitry Andric 45968d75effSDimitry Andric case 0x6A: // 6A XX = push XX 46068d75effSDimitry Andric return 2; 46168d75effSDimitry Andric 46268d75effSDimitry Andric case 0xb8: // b8 XX XX XX XX : mov eax, XX XX XX XX 46368d75effSDimitry Andric case 0xB9: // b9 XX XX XX XX : mov ecx, XX XX XX XX 46468d75effSDimitry Andric return 5; 46568d75effSDimitry Andric 46668d75effSDimitry Andric // Cannot overwrite control-instruction. Return 0 to indicate failure. 46768d75effSDimitry Andric case 0xE9: // E9 XX XX XX XX : jmp <label> 46868d75effSDimitry Andric case 0xE8: // E8 XX XX XX XX : call <func> 46968d75effSDimitry Andric case 0xC3: // C3 : ret 47068d75effSDimitry Andric case 0xEB: // EB XX : jmp XX (short jump) 47168d75effSDimitry Andric case 0x70: // 7Y YY : jy XX (short conditional jump) 47268d75effSDimitry Andric case 0x71: 47368d75effSDimitry Andric case 0x72: 47468d75effSDimitry Andric case 0x73: 47568d75effSDimitry Andric case 0x74: 47668d75effSDimitry Andric case 0x75: 47768d75effSDimitry Andric case 0x76: 47868d75effSDimitry Andric case 0x77: 47968d75effSDimitry Andric case 0x78: 48068d75effSDimitry Andric case 0x79: 48168d75effSDimitry Andric case 0x7A: 48268d75effSDimitry Andric case 0x7B: 48368d75effSDimitry Andric case 0x7C: 48468d75effSDimitry Andric case 0x7D: 48568d75effSDimitry Andric case 0x7E: 48668d75effSDimitry Andric case 0x7F: 48768d75effSDimitry Andric return 0; 48868d75effSDimitry Andric } 48968d75effSDimitry Andric 49068d75effSDimitry Andric switch (*(u16*)(address)) { 49168d75effSDimitry Andric case 0x018A: // 8A 01 : mov al, byte ptr [ecx] 49268d75effSDimitry Andric case 0xFF8B: // 8B FF : mov edi, edi 49368d75effSDimitry Andric case 0xEC8B: // 8B EC : mov ebp, esp 49468d75effSDimitry Andric case 0xc889: // 89 C8 : mov eax, ecx 49568d75effSDimitry Andric case 0xC18B: // 8B C1 : mov eax, ecx 49668d75effSDimitry Andric case 0xC033: // 33 C0 : xor eax, eax 49768d75effSDimitry Andric case 0xC933: // 33 C9 : xor ecx, ecx 49868d75effSDimitry Andric case 0xD233: // 33 D2 : xor edx, edx 49968d75effSDimitry Andric return 2; 50068d75effSDimitry Andric 50168d75effSDimitry Andric // Cannot overwrite control-instruction. Return 0 to indicate failure. 50268d75effSDimitry Andric case 0x25FF: // FF 25 XX XX XX XX : jmp [XXXXXXXX] 50368d75effSDimitry Andric return 0; 50468d75effSDimitry Andric } 50568d75effSDimitry Andric 50668d75effSDimitry Andric switch (0x00FFFFFF & *(u32*)address) { 50768d75effSDimitry Andric case 0x24A48D: // 8D A4 24 XX XX XX XX : lea esp, [esp + XX XX XX XX] 50868d75effSDimitry Andric return 7; 50968d75effSDimitry Andric } 51068d75effSDimitry Andric 51168d75effSDimitry Andric #if SANITIZER_WINDOWS64 51268d75effSDimitry Andric switch (*(u8*)address) { 51368d75effSDimitry Andric case 0xA1: // A1 XX XX XX XX XX XX XX XX : 51468d75effSDimitry Andric // movabs eax, dword ptr ds:[XXXXXXXX] 51568d75effSDimitry Andric return 9; 516349cc55cSDimitry Andric 517349cc55cSDimitry Andric case 0x83: 518349cc55cSDimitry Andric const u8 next_byte = *(u8*)(address + 1); 519349cc55cSDimitry Andric const u8 mod = next_byte >> 6; 520349cc55cSDimitry Andric const u8 rm = next_byte & 7; 521349cc55cSDimitry Andric if (mod == 1 && rm == 4) 522349cc55cSDimitry Andric return 5; // 83 ModR/M SIB Disp8 Imm8 523349cc55cSDimitry Andric // add|or|adc|sbb|and|sub|xor|cmp [r+disp8], imm8 52468d75effSDimitry Andric } 52568d75effSDimitry Andric 52668d75effSDimitry Andric switch (*(u16*)address) { 52768d75effSDimitry Andric case 0x5040: // push rax 52868d75effSDimitry Andric case 0x5140: // push rcx 52968d75effSDimitry Andric case 0x5240: // push rdx 53068d75effSDimitry Andric case 0x5340: // push rbx 53168d75effSDimitry Andric case 0x5440: // push rsp 53268d75effSDimitry Andric case 0x5540: // push rbp 53368d75effSDimitry Andric case 0x5640: // push rsi 53468d75effSDimitry Andric case 0x5740: // push rdi 53568d75effSDimitry Andric case 0x5441: // push r12 53668d75effSDimitry Andric case 0x5541: // push r13 53768d75effSDimitry Andric case 0x5641: // push r14 53868d75effSDimitry Andric case 0x5741: // push r15 53968d75effSDimitry Andric case 0x9066: // Two-byte NOP 540349cc55cSDimitry Andric case 0xc084: // test al, al 541349cc55cSDimitry Andric case 0x018a: // mov al, byte ptr [rcx] 54268d75effSDimitry Andric return 2; 54368d75effSDimitry Andric 54468d75effSDimitry Andric case 0x058B: // 8B 05 XX XX XX XX : mov eax, dword ptr [XX XX XX XX] 54568d75effSDimitry Andric if (rel_offset) 54668d75effSDimitry Andric *rel_offset = 2; 54768d75effSDimitry Andric return 6; 54868d75effSDimitry Andric } 54968d75effSDimitry Andric 55068d75effSDimitry Andric switch (0x00FFFFFF & *(u32*)address) { 55168d75effSDimitry Andric case 0xe58948: // 48 8b c4 : mov rbp, rsp 55268d75effSDimitry Andric case 0xc18b48: // 48 8b c1 : mov rax, rcx 55368d75effSDimitry Andric case 0xc48b48: // 48 8b c4 : mov rax, rsp 55468d75effSDimitry Andric case 0xd9f748: // 48 f7 d9 : neg rcx 55568d75effSDimitry Andric case 0xd12b48: // 48 2b d1 : sub rdx, rcx 55668d75effSDimitry Andric case 0x07c1f6: // f6 c1 07 : test cl, 0x7 55768d75effSDimitry Andric case 0xc98548: // 48 85 C9 : test rcx, rcx 558349cc55cSDimitry Andric case 0xd28548: // 48 85 d2 : test rdx, rdx 55968d75effSDimitry Andric case 0xc0854d: // 4d 85 c0 : test r8, r8 56068d75effSDimitry Andric case 0xc2b60f: // 0f b6 c2 : movzx eax, dl 56168d75effSDimitry Andric case 0xc03345: // 45 33 c0 : xor r8d, r8d 56268d75effSDimitry Andric case 0xc93345: // 45 33 c9 : xor r9d, r9d 56368d75effSDimitry Andric case 0xdb3345: // 45 33 DB : xor r11d, r11d 56468d75effSDimitry Andric case 0xd98b4c: // 4c 8b d9 : mov r11, rcx 56568d75effSDimitry Andric case 0xd28b4c: // 4c 8b d2 : mov r10, rdx 56668d75effSDimitry Andric case 0xc98b4c: // 4C 8B C9 : mov r9, rcx 56768d75effSDimitry Andric case 0xc18b4c: // 4C 8B C1 : mov r8, rcx 56868d75effSDimitry Andric case 0xd2b60f: // 0f b6 d2 : movzx edx, dl 56968d75effSDimitry Andric case 0xca2b48: // 48 2b ca : sub rcx, rdx 57068d75effSDimitry Andric case 0x10b70f: // 0f b7 10 : movzx edx, WORD PTR [rax] 57168d75effSDimitry Andric case 0xc00b4d: // 3d 0b c0 : or r8, r8 572349cc55cSDimitry Andric case 0xc08b41: // 41 8b c0 : mov eax, r8d 57368d75effSDimitry Andric case 0xd18b48: // 48 8b d1 : mov rdx, rcx 57468d75effSDimitry Andric case 0xdc8b4c: // 4c 8b dc : mov r11, rsp 57568d75effSDimitry Andric case 0xd18b4c: // 4c 8b d1 : mov r10, rcx 57668d75effSDimitry Andric case 0xE0E483: // 83 E4 E0 : and esp, 0xFFFFFFE0 57768d75effSDimitry Andric return 3; 57868d75effSDimitry Andric 57968d75effSDimitry Andric case 0xec8348: // 48 83 ec XX : sub rsp, XX 58068d75effSDimitry Andric case 0xf88349: // 49 83 f8 XX : cmp r8, XX 58168d75effSDimitry Andric case 0x588948: // 48 89 58 XX : mov QWORD PTR[rax + XX], rbx 58268d75effSDimitry Andric return 4; 58368d75effSDimitry Andric 58468d75effSDimitry Andric case 0xec8148: // 48 81 EC XX XX XX XX : sub rsp, XXXXXXXX 58568d75effSDimitry Andric return 7; 58668d75effSDimitry Andric 58768d75effSDimitry Andric case 0x058b48: // 48 8b 05 XX XX XX XX : 58868d75effSDimitry Andric // mov rax, QWORD PTR [rip + XXXXXXXX] 58968d75effSDimitry Andric case 0x25ff48: // 48 ff 25 XX XX XX XX : 59068d75effSDimitry Andric // rex.W jmp QWORD PTR [rip + XXXXXXXX] 59168d75effSDimitry Andric 59268d75effSDimitry Andric // Instructions having offset relative to 'rip' need offset adjustment. 59368d75effSDimitry Andric if (rel_offset) 59468d75effSDimitry Andric *rel_offset = 3; 59568d75effSDimitry Andric return 7; 59668d75effSDimitry Andric 59768d75effSDimitry Andric case 0x2444c7: // C7 44 24 XX YY YY YY YY 59868d75effSDimitry Andric // mov dword ptr [rsp + XX], YYYYYYYY 59968d75effSDimitry Andric return 8; 60068d75effSDimitry Andric } 60168d75effSDimitry Andric 60268d75effSDimitry Andric switch (*(u32*)(address)) { 60368d75effSDimitry Andric case 0x24448b48: // 48 8b 44 24 XX : mov rax, QWORD ptr [rsp + XX] 60468d75effSDimitry Andric case 0x246c8948: // 48 89 6C 24 XX : mov QWORD ptr [rsp + XX], rbp 60568d75effSDimitry Andric case 0x245c8948: // 48 89 5c 24 XX : mov QWORD PTR [rsp + XX], rbx 60668d75effSDimitry Andric case 0x24748948: // 48 89 74 24 XX : mov QWORD PTR [rsp + XX], rsi 60704eeddc0SDimitry Andric case 0x247c8948: // 48 89 7c 24 XX : mov QWORD PTR [rsp + XX], rdi 60868d75effSDimitry Andric case 0x244C8948: // 48 89 4C 24 XX : mov QWORD PTR [rsp + XX], rcx 60968d75effSDimitry Andric case 0x24548948: // 48 89 54 24 XX : mov QWORD PTR [rsp + XX], rdx 61068d75effSDimitry Andric case 0x244c894c: // 4c 89 4c 24 XX : mov QWORD PTR [rsp + XX], r9 61168d75effSDimitry Andric case 0x2444894c: // 4c 89 44 24 XX : mov QWORD PTR [rsp + XX], r8 61268d75effSDimitry Andric return 5; 61368d75effSDimitry Andric case 0x24648348: // 48 83 64 24 XX : and QWORD PTR [rsp + XX], YY 61468d75effSDimitry Andric return 6; 61568d75effSDimitry Andric } 61668d75effSDimitry Andric 61768d75effSDimitry Andric #else 61868d75effSDimitry Andric 61968d75effSDimitry Andric switch (*(u8*)address) { 62068d75effSDimitry Andric case 0xA1: // A1 XX XX XX XX : mov eax, dword ptr ds:[XXXXXXXX] 62168d75effSDimitry Andric return 5; 62268d75effSDimitry Andric } 62368d75effSDimitry Andric switch (*(u16*)address) { 62468d75effSDimitry Andric case 0x458B: // 8B 45 XX : mov eax, dword ptr [ebp + XX] 62568d75effSDimitry Andric case 0x5D8B: // 8B 5D XX : mov ebx, dword ptr [ebp + XX] 62668d75effSDimitry Andric case 0x7D8B: // 8B 7D XX : mov edi, dword ptr [ebp + XX] 62768d75effSDimitry Andric case 0xEC83: // 83 EC XX : sub esp, XX 62868d75effSDimitry Andric case 0x75FF: // FF 75 XX : push dword ptr [ebp + XX] 62968d75effSDimitry Andric return 3; 63068d75effSDimitry Andric case 0xC1F7: // F7 C1 XX YY ZZ WW : test ecx, WWZZYYXX 63168d75effSDimitry Andric case 0x25FF: // FF 25 XX YY ZZ WW : jmp dword ptr ds:[WWZZYYXX] 63268d75effSDimitry Andric return 6; 63368d75effSDimitry Andric case 0x3D83: // 83 3D XX YY ZZ WW TT : cmp TT, WWZZYYXX 63468d75effSDimitry Andric return 7; 63568d75effSDimitry Andric case 0x7D83: // 83 7D XX YY : cmp dword ptr [ebp + XX], YY 63668d75effSDimitry Andric return 4; 63768d75effSDimitry Andric } 63868d75effSDimitry Andric 63968d75effSDimitry Andric switch (0x00FFFFFF & *(u32*)address) { 64068d75effSDimitry Andric case 0x24448A: // 8A 44 24 XX : mov eal, dword ptr [esp + XX] 64168d75effSDimitry Andric case 0x24448B: // 8B 44 24 XX : mov eax, dword ptr [esp + XX] 64268d75effSDimitry Andric case 0x244C8B: // 8B 4C 24 XX : mov ecx, dword ptr [esp + XX] 64368d75effSDimitry Andric case 0x24548B: // 8B 54 24 XX : mov edx, dword ptr [esp + XX] 64468d75effSDimitry Andric case 0x24748B: // 8B 74 24 XX : mov esi, dword ptr [esp + XX] 64568d75effSDimitry Andric case 0x247C8B: // 8B 7C 24 XX : mov edi, dword ptr [esp + XX] 64668d75effSDimitry Andric return 4; 64768d75effSDimitry Andric } 64868d75effSDimitry Andric 64968d75effSDimitry Andric switch (*(u32*)address) { 65068d75effSDimitry Andric case 0x2444B60F: // 0F B6 44 24 XX : movzx eax, byte ptr [esp + XX] 65168d75effSDimitry Andric return 5; 65268d75effSDimitry Andric } 65368d75effSDimitry Andric #endif 65468d75effSDimitry Andric 65568d75effSDimitry Andric // Unknown instruction! 65668d75effSDimitry Andric // FIXME: Unknown instruction failures might happen when we add a new 65768d75effSDimitry Andric // interceptor or a new compiler version. In either case, they should result 65868d75effSDimitry Andric // in visible and readable error messages. However, merely calling abort() 65968d75effSDimitry Andric // leads to an infinite recursion in CheckFailed. 66068d75effSDimitry Andric InterceptionFailed(); 66168d75effSDimitry Andric return 0; 66268d75effSDimitry Andric } 66368d75effSDimitry Andric 66468d75effSDimitry Andric // Returns 0 on error. 66568d75effSDimitry Andric static size_t RoundUpToInstrBoundary(size_t size, uptr address) { 66668d75effSDimitry Andric size_t cursor = 0; 66768d75effSDimitry Andric while (cursor < size) { 66868d75effSDimitry Andric size_t instruction_size = GetInstructionSize(address + cursor); 66968d75effSDimitry Andric if (!instruction_size) 67068d75effSDimitry Andric return 0; 67168d75effSDimitry Andric cursor += instruction_size; 67268d75effSDimitry Andric } 67368d75effSDimitry Andric return cursor; 67468d75effSDimitry Andric } 67568d75effSDimitry Andric 67668d75effSDimitry Andric static bool CopyInstructions(uptr to, uptr from, size_t size) { 67768d75effSDimitry Andric size_t cursor = 0; 67868d75effSDimitry Andric while (cursor != size) { 67968d75effSDimitry Andric size_t rel_offset = 0; 68068d75effSDimitry Andric size_t instruction_size = GetInstructionSize(from + cursor, &rel_offset); 68168d75effSDimitry Andric _memcpy((void*)(to + cursor), (void*)(from + cursor), 68268d75effSDimitry Andric (size_t)instruction_size); 68368d75effSDimitry Andric if (rel_offset) { 68468d75effSDimitry Andric uptr delta = to - from; 68568d75effSDimitry Andric uptr relocated_offset = *(u32*)(to + cursor + rel_offset) - delta; 68668d75effSDimitry Andric #if SANITIZER_WINDOWS64 68768d75effSDimitry Andric if (relocated_offset + 0x80000000U >= 0xFFFFFFFFU) 68868d75effSDimitry Andric return false; 68968d75effSDimitry Andric #endif 69068d75effSDimitry Andric *(u32*)(to + cursor + rel_offset) = relocated_offset; 69168d75effSDimitry Andric } 69268d75effSDimitry Andric cursor += instruction_size; 69368d75effSDimitry Andric } 69468d75effSDimitry Andric return true; 69568d75effSDimitry Andric } 69668d75effSDimitry Andric 69768d75effSDimitry Andric 69868d75effSDimitry Andric #if !SANITIZER_WINDOWS64 69968d75effSDimitry Andric bool OverrideFunctionWithDetour( 70068d75effSDimitry Andric uptr old_func, uptr new_func, uptr *orig_old_func) { 70168d75effSDimitry Andric const int kDetourHeaderLen = 5; 70268d75effSDimitry Andric const u16 kDetourInstruction = 0xFF8B; 70368d75effSDimitry Andric 70468d75effSDimitry Andric uptr header = (uptr)old_func - kDetourHeaderLen; 70568d75effSDimitry Andric uptr patch_length = kDetourHeaderLen + kShortJumpInstructionLength; 70668d75effSDimitry Andric 70768d75effSDimitry Andric // Validate that the function is hookable. 70868d75effSDimitry Andric if (*(u16*)old_func != kDetourInstruction || 70968d75effSDimitry Andric !IsMemoryPadding(header, kDetourHeaderLen)) 71068d75effSDimitry Andric return false; 71168d75effSDimitry Andric 71268d75effSDimitry Andric // Change memory protection to writable. 71368d75effSDimitry Andric DWORD protection = 0; 71468d75effSDimitry Andric if (!ChangeMemoryProtection(header, patch_length, &protection)) 71568d75effSDimitry Andric return false; 71668d75effSDimitry Andric 71768d75effSDimitry Andric // Write a relative jump to the redirected function. 71868d75effSDimitry Andric WriteJumpInstruction(header, new_func); 71968d75effSDimitry Andric 72068d75effSDimitry Andric // Write the short jump to the function prefix. 72168d75effSDimitry Andric WriteShortJumpInstruction(old_func, header); 72268d75effSDimitry Andric 72368d75effSDimitry Andric // Restore previous memory protection. 72468d75effSDimitry Andric if (!RestoreMemoryProtection(header, patch_length, protection)) 72568d75effSDimitry Andric return false; 72668d75effSDimitry Andric 72768d75effSDimitry Andric if (orig_old_func) 72868d75effSDimitry Andric *orig_old_func = old_func + kShortJumpInstructionLength; 72968d75effSDimitry Andric 73068d75effSDimitry Andric return true; 73168d75effSDimitry Andric } 73268d75effSDimitry Andric #endif 73368d75effSDimitry Andric 73468d75effSDimitry Andric bool OverrideFunctionWithRedirectJump( 73568d75effSDimitry Andric uptr old_func, uptr new_func, uptr *orig_old_func) { 73668d75effSDimitry Andric // Check whether the first instruction is a relative jump. 73768d75effSDimitry Andric if (*(u8*)old_func != 0xE9) 73868d75effSDimitry Andric return false; 73968d75effSDimitry Andric 74068d75effSDimitry Andric if (orig_old_func) { 74168d75effSDimitry Andric uptr relative_offset = *(u32*)(old_func + 1); 74268d75effSDimitry Andric uptr absolute_target = old_func + relative_offset + kJumpInstructionLength; 74368d75effSDimitry Andric *orig_old_func = absolute_target; 74468d75effSDimitry Andric } 74568d75effSDimitry Andric 74668d75effSDimitry Andric #if SANITIZER_WINDOWS64 74768d75effSDimitry Andric // If needed, get memory space for a trampoline jump. 74868d75effSDimitry Andric uptr trampoline = AllocateMemoryForTrampoline(old_func, kDirectBranchLength); 74968d75effSDimitry Andric if (!trampoline) 75068d75effSDimitry Andric return false; 75168d75effSDimitry Andric WriteDirectBranch(trampoline, new_func); 75268d75effSDimitry Andric #endif 75368d75effSDimitry Andric 75468d75effSDimitry Andric // Change memory protection to writable. 75568d75effSDimitry Andric DWORD protection = 0; 75668d75effSDimitry Andric if (!ChangeMemoryProtection(old_func, kJumpInstructionLength, &protection)) 75768d75effSDimitry Andric return false; 75868d75effSDimitry Andric 75968d75effSDimitry Andric // Write a relative jump to the redirected function. 76068d75effSDimitry Andric WriteJumpInstruction(old_func, FIRST_32_SECOND_64(new_func, trampoline)); 76168d75effSDimitry Andric 76268d75effSDimitry Andric // Restore previous memory protection. 76368d75effSDimitry Andric if (!RestoreMemoryProtection(old_func, kJumpInstructionLength, protection)) 76468d75effSDimitry Andric return false; 76568d75effSDimitry Andric 76668d75effSDimitry Andric return true; 76768d75effSDimitry Andric } 76868d75effSDimitry Andric 76968d75effSDimitry Andric bool OverrideFunctionWithHotPatch( 77068d75effSDimitry Andric uptr old_func, uptr new_func, uptr *orig_old_func) { 77168d75effSDimitry Andric const int kHotPatchHeaderLen = kBranchLength; 77268d75effSDimitry Andric 77368d75effSDimitry Andric uptr header = (uptr)old_func - kHotPatchHeaderLen; 77468d75effSDimitry Andric uptr patch_length = kHotPatchHeaderLen + kShortJumpInstructionLength; 77568d75effSDimitry Andric 77668d75effSDimitry Andric // Validate that the function is hot patchable. 77768d75effSDimitry Andric size_t instruction_size = GetInstructionSize(old_func); 77868d75effSDimitry Andric if (instruction_size < kShortJumpInstructionLength || 77968d75effSDimitry Andric !FunctionHasPadding(old_func, kHotPatchHeaderLen)) 78068d75effSDimitry Andric return false; 78168d75effSDimitry Andric 78268d75effSDimitry Andric if (orig_old_func) { 78368d75effSDimitry Andric // Put the needed instructions into the trampoline bytes. 78468d75effSDimitry Andric uptr trampoline_length = instruction_size + kDirectBranchLength; 78568d75effSDimitry Andric uptr trampoline = AllocateMemoryForTrampoline(old_func, trampoline_length); 78668d75effSDimitry Andric if (!trampoline) 78768d75effSDimitry Andric return false; 78868d75effSDimitry Andric if (!CopyInstructions(trampoline, old_func, instruction_size)) 78968d75effSDimitry Andric return false; 79068d75effSDimitry Andric WriteDirectBranch(trampoline + instruction_size, 79168d75effSDimitry Andric old_func + instruction_size); 79268d75effSDimitry Andric *orig_old_func = trampoline; 79368d75effSDimitry Andric } 79468d75effSDimitry Andric 79568d75effSDimitry Andric // If needed, get memory space for indirect address. 79668d75effSDimitry Andric uptr indirect_address = 0; 79768d75effSDimitry Andric #if SANITIZER_WINDOWS64 79868d75effSDimitry Andric indirect_address = AllocateMemoryForTrampoline(old_func, kAddressLength); 79968d75effSDimitry Andric if (!indirect_address) 80068d75effSDimitry Andric return false; 80168d75effSDimitry Andric #endif 80268d75effSDimitry Andric 80368d75effSDimitry Andric // Change memory protection to writable. 80468d75effSDimitry Andric DWORD protection = 0; 80568d75effSDimitry Andric if (!ChangeMemoryProtection(header, patch_length, &protection)) 80668d75effSDimitry Andric return false; 80768d75effSDimitry Andric 80868d75effSDimitry Andric // Write jumps to the redirected function. 80968d75effSDimitry Andric WriteBranch(header, indirect_address, new_func); 81068d75effSDimitry Andric WriteShortJumpInstruction(old_func, header); 81168d75effSDimitry Andric 81268d75effSDimitry Andric // Restore previous memory protection. 81368d75effSDimitry Andric if (!RestoreMemoryProtection(header, patch_length, protection)) 81468d75effSDimitry Andric return false; 81568d75effSDimitry Andric 81668d75effSDimitry Andric return true; 81768d75effSDimitry Andric } 81868d75effSDimitry Andric 81968d75effSDimitry Andric bool OverrideFunctionWithTrampoline( 82068d75effSDimitry Andric uptr old_func, uptr new_func, uptr *orig_old_func) { 82168d75effSDimitry Andric 82268d75effSDimitry Andric size_t instructions_length = kBranchLength; 82368d75effSDimitry Andric size_t padding_length = 0; 82468d75effSDimitry Andric uptr indirect_address = 0; 82568d75effSDimitry Andric 82668d75effSDimitry Andric if (orig_old_func) { 82768d75effSDimitry Andric // Find out the number of bytes of the instructions we need to copy 82868d75effSDimitry Andric // to the trampoline. 82968d75effSDimitry Andric instructions_length = RoundUpToInstrBoundary(kBranchLength, old_func); 83068d75effSDimitry Andric if (!instructions_length) 83168d75effSDimitry Andric return false; 83268d75effSDimitry Andric 83368d75effSDimitry Andric // Put the needed instructions into the trampoline bytes. 83468d75effSDimitry Andric uptr trampoline_length = instructions_length + kDirectBranchLength; 83568d75effSDimitry Andric uptr trampoline = AllocateMemoryForTrampoline(old_func, trampoline_length); 83668d75effSDimitry Andric if (!trampoline) 83768d75effSDimitry Andric return false; 83868d75effSDimitry Andric if (!CopyInstructions(trampoline, old_func, instructions_length)) 83968d75effSDimitry Andric return false; 84068d75effSDimitry Andric WriteDirectBranch(trampoline + instructions_length, 84168d75effSDimitry Andric old_func + instructions_length); 84268d75effSDimitry Andric *orig_old_func = trampoline; 84368d75effSDimitry Andric } 84468d75effSDimitry Andric 84568d75effSDimitry Andric #if SANITIZER_WINDOWS64 84668d75effSDimitry Andric // Check if the targeted address can be encoded in the function padding. 84768d75effSDimitry Andric // Otherwise, allocate it in the trampoline region. 84868d75effSDimitry Andric if (IsMemoryPadding(old_func - kAddressLength, kAddressLength)) { 84968d75effSDimitry Andric indirect_address = old_func - kAddressLength; 85068d75effSDimitry Andric padding_length = kAddressLength; 85168d75effSDimitry Andric } else { 85268d75effSDimitry Andric indirect_address = AllocateMemoryForTrampoline(old_func, kAddressLength); 85368d75effSDimitry Andric if (!indirect_address) 85468d75effSDimitry Andric return false; 85568d75effSDimitry Andric } 85668d75effSDimitry Andric #endif 85768d75effSDimitry Andric 85868d75effSDimitry Andric // Change memory protection to writable. 85968d75effSDimitry Andric uptr patch_address = old_func - padding_length; 86068d75effSDimitry Andric uptr patch_length = instructions_length + padding_length; 86168d75effSDimitry Andric DWORD protection = 0; 86268d75effSDimitry Andric if (!ChangeMemoryProtection(patch_address, patch_length, &protection)) 86368d75effSDimitry Andric return false; 86468d75effSDimitry Andric 86568d75effSDimitry Andric // Patch the original function. 86668d75effSDimitry Andric WriteBranch(old_func, indirect_address, new_func); 86768d75effSDimitry Andric 86868d75effSDimitry Andric // Restore previous memory protection. 86968d75effSDimitry Andric if (!RestoreMemoryProtection(patch_address, patch_length, protection)) 87068d75effSDimitry Andric return false; 87168d75effSDimitry Andric 87268d75effSDimitry Andric return true; 87368d75effSDimitry Andric } 87468d75effSDimitry Andric 87568d75effSDimitry Andric bool OverrideFunction( 87668d75effSDimitry Andric uptr old_func, uptr new_func, uptr *orig_old_func) { 87768d75effSDimitry Andric #if !SANITIZER_WINDOWS64 87868d75effSDimitry Andric if (OverrideFunctionWithDetour(old_func, new_func, orig_old_func)) 87968d75effSDimitry Andric return true; 88068d75effSDimitry Andric #endif 88168d75effSDimitry Andric if (OverrideFunctionWithRedirectJump(old_func, new_func, orig_old_func)) 88268d75effSDimitry Andric return true; 88368d75effSDimitry Andric if (OverrideFunctionWithHotPatch(old_func, new_func, orig_old_func)) 88468d75effSDimitry Andric return true; 88568d75effSDimitry Andric if (OverrideFunctionWithTrampoline(old_func, new_func, orig_old_func)) 88668d75effSDimitry Andric return true; 88768d75effSDimitry Andric return false; 88868d75effSDimitry Andric } 88968d75effSDimitry Andric 89068d75effSDimitry Andric static void **InterestingDLLsAvailable() { 89168d75effSDimitry Andric static const char *InterestingDLLs[] = { 89268d75effSDimitry Andric "kernel32.dll", 89368d75effSDimitry Andric "msvcr100.dll", // VS2010 89468d75effSDimitry Andric "msvcr110.dll", // VS2012 89568d75effSDimitry Andric "msvcr120.dll", // VS2013 89668d75effSDimitry Andric "vcruntime140.dll", // VS2015 89768d75effSDimitry Andric "ucrtbase.dll", // Universal CRT 89868d75effSDimitry Andric // NTDLL should go last as it exports some functions that we should 89968d75effSDimitry Andric // override in the CRT [presumably only used internally]. 90068d75effSDimitry Andric "ntdll.dll", NULL}; 90168d75effSDimitry Andric static void *result[ARRAY_SIZE(InterestingDLLs)] = { 0 }; 90268d75effSDimitry Andric if (!result[0]) { 90368d75effSDimitry Andric for (size_t i = 0, j = 0; InterestingDLLs[i]; ++i) { 90468d75effSDimitry Andric if (HMODULE h = GetModuleHandleA(InterestingDLLs[i])) 90568d75effSDimitry Andric result[j++] = (void *)h; 90668d75effSDimitry Andric } 90768d75effSDimitry Andric } 90868d75effSDimitry Andric return &result[0]; 90968d75effSDimitry Andric } 91068d75effSDimitry Andric 91168d75effSDimitry Andric namespace { 91268d75effSDimitry Andric // Utility for reading loaded PE images. 91368d75effSDimitry Andric template <typename T> class RVAPtr { 91468d75effSDimitry Andric public: 91568d75effSDimitry Andric RVAPtr(void *module, uptr rva) 91668d75effSDimitry Andric : ptr_(reinterpret_cast<T *>(reinterpret_cast<char *>(module) + rva)) {} 91768d75effSDimitry Andric operator T *() { return ptr_; } 91868d75effSDimitry Andric T *operator->() { return ptr_; } 91968d75effSDimitry Andric T *operator++() { return ++ptr_; } 92068d75effSDimitry Andric 92168d75effSDimitry Andric private: 92268d75effSDimitry Andric T *ptr_; 92368d75effSDimitry Andric }; 92468d75effSDimitry Andric } // namespace 92568d75effSDimitry Andric 92668d75effSDimitry Andric // Internal implementation of GetProcAddress. At least since Windows 8, 92768d75effSDimitry Andric // GetProcAddress appears to initialize DLLs before returning function pointers 92868d75effSDimitry Andric // into them. This is problematic for the sanitizers, because they typically 92968d75effSDimitry Andric // want to intercept malloc *before* MSVCRT initializes. Our internal 93068d75effSDimitry Andric // implementation walks the export list manually without doing initialization. 93168d75effSDimitry Andric uptr InternalGetProcAddress(void *module, const char *func_name) { 93268d75effSDimitry Andric // Check that the module header is full and present. 93368d75effSDimitry Andric RVAPtr<IMAGE_DOS_HEADER> dos_stub(module, 0); 93468d75effSDimitry Andric RVAPtr<IMAGE_NT_HEADERS> headers(module, dos_stub->e_lfanew); 93568d75effSDimitry Andric if (!module || dos_stub->e_magic != IMAGE_DOS_SIGNATURE || // "MZ" 93668d75effSDimitry Andric headers->Signature != IMAGE_NT_SIGNATURE || // "PE\0\0" 93768d75effSDimitry Andric headers->FileHeader.SizeOfOptionalHeader < 93868d75effSDimitry Andric sizeof(IMAGE_OPTIONAL_HEADER)) { 93968d75effSDimitry Andric return 0; 94068d75effSDimitry Andric } 94168d75effSDimitry Andric 94268d75effSDimitry Andric IMAGE_DATA_DIRECTORY *export_directory = 94368d75effSDimitry Andric &headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; 94468d75effSDimitry Andric if (export_directory->Size == 0) 94568d75effSDimitry Andric return 0; 94668d75effSDimitry Andric RVAPtr<IMAGE_EXPORT_DIRECTORY> exports(module, 94768d75effSDimitry Andric export_directory->VirtualAddress); 94868d75effSDimitry Andric RVAPtr<DWORD> functions(module, exports->AddressOfFunctions); 94968d75effSDimitry Andric RVAPtr<DWORD> names(module, exports->AddressOfNames); 95068d75effSDimitry Andric RVAPtr<WORD> ordinals(module, exports->AddressOfNameOrdinals); 95168d75effSDimitry Andric 95268d75effSDimitry Andric for (DWORD i = 0; i < exports->NumberOfNames; i++) { 95368d75effSDimitry Andric RVAPtr<char> name(module, names[i]); 95468d75effSDimitry Andric if (!strcmp(func_name, name)) { 95568d75effSDimitry Andric DWORD index = ordinals[i]; 95668d75effSDimitry Andric RVAPtr<char> func(module, functions[index]); 95768d75effSDimitry Andric 95868d75effSDimitry Andric // Handle forwarded functions. 95968d75effSDimitry Andric DWORD offset = functions[index]; 96068d75effSDimitry Andric if (offset >= export_directory->VirtualAddress && 96168d75effSDimitry Andric offset < export_directory->VirtualAddress + export_directory->Size) { 96268d75effSDimitry Andric // An entry for a forwarded function is a string with the following 96368d75effSDimitry Andric // format: "<module> . <function_name>" that is stored into the 96468d75effSDimitry Andric // exported directory. 96568d75effSDimitry Andric char function_name[256]; 96668d75effSDimitry Andric size_t funtion_name_length = _strlen(func); 96768d75effSDimitry Andric if (funtion_name_length >= sizeof(function_name) - 1) 96868d75effSDimitry Andric InterceptionFailed(); 96968d75effSDimitry Andric 97068d75effSDimitry Andric _memcpy(function_name, func, funtion_name_length); 97168d75effSDimitry Andric function_name[funtion_name_length] = '\0'; 97268d75effSDimitry Andric char* separator = _strchr(function_name, '.'); 97368d75effSDimitry Andric if (!separator) 97468d75effSDimitry Andric InterceptionFailed(); 97568d75effSDimitry Andric *separator = '\0'; 97668d75effSDimitry Andric 97768d75effSDimitry Andric void* redirected_module = GetModuleHandleA(function_name); 97868d75effSDimitry Andric if (!redirected_module) 97968d75effSDimitry Andric InterceptionFailed(); 98068d75effSDimitry Andric return InternalGetProcAddress(redirected_module, separator + 1); 98168d75effSDimitry Andric } 98268d75effSDimitry Andric 98368d75effSDimitry Andric return (uptr)(char *)func; 98468d75effSDimitry Andric } 98568d75effSDimitry Andric } 98668d75effSDimitry Andric 98768d75effSDimitry Andric return 0; 98868d75effSDimitry Andric } 98968d75effSDimitry Andric 99068d75effSDimitry Andric bool OverrideFunction( 99168d75effSDimitry Andric const char *func_name, uptr new_func, uptr *orig_old_func) { 99268d75effSDimitry Andric bool hooked = false; 99368d75effSDimitry Andric void **DLLs = InterestingDLLsAvailable(); 99468d75effSDimitry Andric for (size_t i = 0; DLLs[i]; ++i) { 99568d75effSDimitry Andric uptr func_addr = InternalGetProcAddress(DLLs[i], func_name); 99668d75effSDimitry Andric if (func_addr && 99768d75effSDimitry Andric OverrideFunction(func_addr, new_func, orig_old_func)) { 99868d75effSDimitry Andric hooked = true; 99968d75effSDimitry Andric } 100068d75effSDimitry Andric } 100168d75effSDimitry Andric return hooked; 100268d75effSDimitry Andric } 100368d75effSDimitry Andric 100468d75effSDimitry Andric bool OverrideImportedFunction(const char *module_to_patch, 100568d75effSDimitry Andric const char *imported_module, 100668d75effSDimitry Andric const char *function_name, uptr new_function, 100768d75effSDimitry Andric uptr *orig_old_func) { 100868d75effSDimitry Andric HMODULE module = GetModuleHandleA(module_to_patch); 100968d75effSDimitry Andric if (!module) 101068d75effSDimitry Andric return false; 101168d75effSDimitry Andric 101268d75effSDimitry Andric // Check that the module header is full and present. 101368d75effSDimitry Andric RVAPtr<IMAGE_DOS_HEADER> dos_stub(module, 0); 101468d75effSDimitry Andric RVAPtr<IMAGE_NT_HEADERS> headers(module, dos_stub->e_lfanew); 101568d75effSDimitry Andric if (!module || dos_stub->e_magic != IMAGE_DOS_SIGNATURE || // "MZ" 101668d75effSDimitry Andric headers->Signature != IMAGE_NT_SIGNATURE || // "PE\0\0" 101768d75effSDimitry Andric headers->FileHeader.SizeOfOptionalHeader < 101868d75effSDimitry Andric sizeof(IMAGE_OPTIONAL_HEADER)) { 101968d75effSDimitry Andric return false; 102068d75effSDimitry Andric } 102168d75effSDimitry Andric 102268d75effSDimitry Andric IMAGE_DATA_DIRECTORY *import_directory = 102368d75effSDimitry Andric &headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; 102468d75effSDimitry Andric 102568d75effSDimitry Andric // Iterate the list of imported DLLs. FirstThunk will be null for the last 102668d75effSDimitry Andric // entry. 102768d75effSDimitry Andric RVAPtr<IMAGE_IMPORT_DESCRIPTOR> imports(module, 102868d75effSDimitry Andric import_directory->VirtualAddress); 102968d75effSDimitry Andric for (; imports->FirstThunk != 0; ++imports) { 103068d75effSDimitry Andric RVAPtr<const char> modname(module, imports->Name); 103168d75effSDimitry Andric if (_stricmp(&*modname, imported_module) == 0) 103268d75effSDimitry Andric break; 103368d75effSDimitry Andric } 103468d75effSDimitry Andric if (imports->FirstThunk == 0) 103568d75effSDimitry Andric return false; 103668d75effSDimitry Andric 103768d75effSDimitry Andric // We have two parallel arrays: the import address table (IAT) and the table 103868d75effSDimitry Andric // of names. They start out containing the same data, but the loader rewrites 103968d75effSDimitry Andric // the IAT to hold imported addresses and leaves the name table in 104068d75effSDimitry Andric // OriginalFirstThunk alone. 104168d75effSDimitry Andric RVAPtr<IMAGE_THUNK_DATA> name_table(module, imports->OriginalFirstThunk); 104268d75effSDimitry Andric RVAPtr<IMAGE_THUNK_DATA> iat(module, imports->FirstThunk); 104368d75effSDimitry Andric for (; name_table->u1.Ordinal != 0; ++name_table, ++iat) { 104468d75effSDimitry Andric if (!IMAGE_SNAP_BY_ORDINAL(name_table->u1.Ordinal)) { 104568d75effSDimitry Andric RVAPtr<IMAGE_IMPORT_BY_NAME> import_by_name( 104668d75effSDimitry Andric module, name_table->u1.ForwarderString); 104768d75effSDimitry Andric const char *funcname = &import_by_name->Name[0]; 104868d75effSDimitry Andric if (strcmp(funcname, function_name) == 0) 104968d75effSDimitry Andric break; 105068d75effSDimitry Andric } 105168d75effSDimitry Andric } 105268d75effSDimitry Andric if (name_table->u1.Ordinal == 0) 105368d75effSDimitry Andric return false; 105468d75effSDimitry Andric 105568d75effSDimitry Andric // Now we have the correct IAT entry. Do the swap. We have to make the page 105668d75effSDimitry Andric // read/write first. 105768d75effSDimitry Andric if (orig_old_func) 105868d75effSDimitry Andric *orig_old_func = iat->u1.AddressOfData; 105968d75effSDimitry Andric DWORD old_prot, unused_prot; 106068d75effSDimitry Andric if (!VirtualProtect(&iat->u1.AddressOfData, 4, PAGE_EXECUTE_READWRITE, 106168d75effSDimitry Andric &old_prot)) 106268d75effSDimitry Andric return false; 106368d75effSDimitry Andric iat->u1.AddressOfData = new_function; 106468d75effSDimitry Andric if (!VirtualProtect(&iat->u1.AddressOfData, 4, old_prot, &unused_prot)) 106568d75effSDimitry Andric return false; // Not clear if this failure bothers us. 106668d75effSDimitry Andric return true; 106768d75effSDimitry Andric } 106868d75effSDimitry Andric 106968d75effSDimitry Andric } // namespace __interception 107068d75effSDimitry Andric 1071*81ad6265SDimitry Andric #endif // SANITIZER_APPLE 1072