1*3cab2bb3Spatrick //===-- interception_linux.cpp ----------------------------------*- C++ -*-===// 2*3cab2bb3Spatrick // 3*3cab2bb3Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*3cab2bb3Spatrick // See https://llvm.org/LICENSE.txt for license information. 5*3cab2bb3Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*3cab2bb3Spatrick // 7*3cab2bb3Spatrick //===----------------------------------------------------------------------===// 8*3cab2bb3Spatrick // 9*3cab2bb3Spatrick // This file is a part of AddressSanitizer, an address sanity checker. 10*3cab2bb3Spatrick // 11*3cab2bb3Spatrick // Windows-specific interception methods. 12*3cab2bb3Spatrick // 13*3cab2bb3Spatrick // This file is implementing several hooking techniques to intercept calls 14*3cab2bb3Spatrick // to functions. The hooks are dynamically installed by modifying the assembly 15*3cab2bb3Spatrick // code. 16*3cab2bb3Spatrick // 17*3cab2bb3Spatrick // The hooking techniques are making assumptions on the way the code is 18*3cab2bb3Spatrick // generated and are safe under these assumptions. 19*3cab2bb3Spatrick // 20*3cab2bb3Spatrick // On 64-bit architecture, there is no direct 64-bit jump instruction. To allow 21*3cab2bb3Spatrick // arbitrary branching on the whole memory space, the notion of trampoline 22*3cab2bb3Spatrick // region is used. A trampoline region is a memory space withing 2G boundary 23*3cab2bb3Spatrick // where it is safe to add custom assembly code to build 64-bit jumps. 24*3cab2bb3Spatrick // 25*3cab2bb3Spatrick // Hooking techniques 26*3cab2bb3Spatrick // ================== 27*3cab2bb3Spatrick // 28*3cab2bb3Spatrick // 1) Detour 29*3cab2bb3Spatrick // 30*3cab2bb3Spatrick // The Detour hooking technique is assuming the presence of an header with 31*3cab2bb3Spatrick // padding and an overridable 2-bytes nop instruction (mov edi, edi). The 32*3cab2bb3Spatrick // nop instruction can safely be replaced by a 2-bytes jump without any need 33*3cab2bb3Spatrick // to save the instruction. A jump to the target is encoded in the function 34*3cab2bb3Spatrick // header and the nop instruction is replaced by a short jump to the header. 35*3cab2bb3Spatrick // 36*3cab2bb3Spatrick // head: 5 x nop head: jmp <hook> 37*3cab2bb3Spatrick // func: mov edi, edi --> func: jmp short <head> 38*3cab2bb3Spatrick // [...] real: [...] 39*3cab2bb3Spatrick // 40*3cab2bb3Spatrick // This technique is only implemented on 32-bit architecture. 41*3cab2bb3Spatrick // Most of the time, Windows API are hookable with the detour technique. 42*3cab2bb3Spatrick // 43*3cab2bb3Spatrick // 2) Redirect Jump 44*3cab2bb3Spatrick // 45*3cab2bb3Spatrick // The redirect jump is applicable when the first instruction is a direct 46*3cab2bb3Spatrick // jump. The instruction is replaced by jump to the hook. 47*3cab2bb3Spatrick // 48*3cab2bb3Spatrick // func: jmp <label> --> func: jmp <hook> 49*3cab2bb3Spatrick // 50*3cab2bb3Spatrick // On an 64-bit architecture, a trampoline is inserted. 51*3cab2bb3Spatrick // 52*3cab2bb3Spatrick // func: jmp <label> --> func: jmp <tramp> 53*3cab2bb3Spatrick // [...] 54*3cab2bb3Spatrick // 55*3cab2bb3Spatrick // [trampoline] 56*3cab2bb3Spatrick // tramp: jmp QWORD [addr] 57*3cab2bb3Spatrick // addr: .bytes <hook> 58*3cab2bb3Spatrick // 59*3cab2bb3Spatrick // Note: <real> is equilavent to <label>. 60*3cab2bb3Spatrick // 61*3cab2bb3Spatrick // 3) HotPatch 62*3cab2bb3Spatrick // 63*3cab2bb3Spatrick // The HotPatch hooking is assuming the presence of an header with padding 64*3cab2bb3Spatrick // and a first instruction with at least 2-bytes. 65*3cab2bb3Spatrick // 66*3cab2bb3Spatrick // The reason to enforce the 2-bytes limitation is to provide the minimal 67*3cab2bb3Spatrick // space to encode a short jump. HotPatch technique is only rewriting one 68*3cab2bb3Spatrick // instruction to avoid breaking a sequence of instructions containing a 69*3cab2bb3Spatrick // branching target. 70*3cab2bb3Spatrick // 71*3cab2bb3Spatrick // Assumptions are enforced by MSVC compiler by using the /HOTPATCH flag. 72*3cab2bb3Spatrick // see: https://msdn.microsoft.com/en-us/library/ms173507.aspx 73*3cab2bb3Spatrick // Default padding length is 5 bytes in 32-bits and 6 bytes in 64-bits. 74*3cab2bb3Spatrick // 75*3cab2bb3Spatrick // head: 5 x nop head: jmp <hook> 76*3cab2bb3Spatrick // func: <instr> --> func: jmp short <head> 77*3cab2bb3Spatrick // [...] body: [...] 78*3cab2bb3Spatrick // 79*3cab2bb3Spatrick // [trampoline] 80*3cab2bb3Spatrick // real: <instr> 81*3cab2bb3Spatrick // jmp <body> 82*3cab2bb3Spatrick // 83*3cab2bb3Spatrick // On an 64-bit architecture: 84*3cab2bb3Spatrick // 85*3cab2bb3Spatrick // head: 6 x nop head: jmp QWORD [addr1] 86*3cab2bb3Spatrick // func: <instr> --> func: jmp short <head> 87*3cab2bb3Spatrick // [...] body: [...] 88*3cab2bb3Spatrick // 89*3cab2bb3Spatrick // [trampoline] 90*3cab2bb3Spatrick // addr1: .bytes <hook> 91*3cab2bb3Spatrick // real: <instr> 92*3cab2bb3Spatrick // jmp QWORD [addr2] 93*3cab2bb3Spatrick // addr2: .bytes <body> 94*3cab2bb3Spatrick // 95*3cab2bb3Spatrick // 4) Trampoline 96*3cab2bb3Spatrick // 97*3cab2bb3Spatrick // The Trampoline hooking technique is the most aggressive one. It is 98*3cab2bb3Spatrick // assuming that there is a sequence of instructions that can be safely 99*3cab2bb3Spatrick // replaced by a jump (enough room and no incoming branches). 100*3cab2bb3Spatrick // 101*3cab2bb3Spatrick // Unfortunately, these assumptions can't be safely presumed and code may 102*3cab2bb3Spatrick // be broken after hooking. 103*3cab2bb3Spatrick // 104*3cab2bb3Spatrick // func: <instr> --> func: jmp <hook> 105*3cab2bb3Spatrick // <instr> 106*3cab2bb3Spatrick // [...] body: [...] 107*3cab2bb3Spatrick // 108*3cab2bb3Spatrick // [trampoline] 109*3cab2bb3Spatrick // real: <instr> 110*3cab2bb3Spatrick // <instr> 111*3cab2bb3Spatrick // jmp <body> 112*3cab2bb3Spatrick // 113*3cab2bb3Spatrick // On an 64-bit architecture: 114*3cab2bb3Spatrick // 115*3cab2bb3Spatrick // func: <instr> --> func: jmp QWORD [addr1] 116*3cab2bb3Spatrick // <instr> 117*3cab2bb3Spatrick // [...] body: [...] 118*3cab2bb3Spatrick // 119*3cab2bb3Spatrick // [trampoline] 120*3cab2bb3Spatrick // addr1: .bytes <hook> 121*3cab2bb3Spatrick // real: <instr> 122*3cab2bb3Spatrick // <instr> 123*3cab2bb3Spatrick // jmp QWORD [addr2] 124*3cab2bb3Spatrick // addr2: .bytes <body> 125*3cab2bb3Spatrick //===----------------------------------------------------------------------===// 126*3cab2bb3Spatrick 127*3cab2bb3Spatrick #include "interception.h" 128*3cab2bb3Spatrick 129*3cab2bb3Spatrick #if SANITIZER_WINDOWS 130*3cab2bb3Spatrick #include "sanitizer_common/sanitizer_platform.h" 131*3cab2bb3Spatrick #define WIN32_LEAN_AND_MEAN 132*3cab2bb3Spatrick #include <windows.h> 133*3cab2bb3Spatrick 134*3cab2bb3Spatrick namespace __interception { 135*3cab2bb3Spatrick 136*3cab2bb3Spatrick static const int kAddressLength = FIRST_32_SECOND_64(4, 8); 137*3cab2bb3Spatrick static const int kJumpInstructionLength = 5; 138*3cab2bb3Spatrick static const int kShortJumpInstructionLength = 2; 139*3cab2bb3Spatrick static const int kIndirectJumpInstructionLength = 6; 140*3cab2bb3Spatrick static const int kBranchLength = 141*3cab2bb3Spatrick FIRST_32_SECOND_64(kJumpInstructionLength, kIndirectJumpInstructionLength); 142*3cab2bb3Spatrick static const int kDirectBranchLength = kBranchLength + kAddressLength; 143*3cab2bb3Spatrick 144*3cab2bb3Spatrick static void InterceptionFailed() { 145*3cab2bb3Spatrick // Do we have a good way to abort with an error message here? 146*3cab2bb3Spatrick __debugbreak(); 147*3cab2bb3Spatrick } 148*3cab2bb3Spatrick 149*3cab2bb3Spatrick static bool DistanceIsWithin2Gig(uptr from, uptr target) { 150*3cab2bb3Spatrick #if SANITIZER_WINDOWS64 151*3cab2bb3Spatrick if (from < target) 152*3cab2bb3Spatrick return target - from <= (uptr)0x7FFFFFFFU; 153*3cab2bb3Spatrick else 154*3cab2bb3Spatrick return from - target <= (uptr)0x80000000U; 155*3cab2bb3Spatrick #else 156*3cab2bb3Spatrick // In a 32-bit address space, the address calculation will wrap, so this check 157*3cab2bb3Spatrick // is unnecessary. 158*3cab2bb3Spatrick return true; 159*3cab2bb3Spatrick #endif 160*3cab2bb3Spatrick } 161*3cab2bb3Spatrick 162*3cab2bb3Spatrick static uptr GetMmapGranularity() { 163*3cab2bb3Spatrick SYSTEM_INFO si; 164*3cab2bb3Spatrick GetSystemInfo(&si); 165*3cab2bb3Spatrick return si.dwAllocationGranularity; 166*3cab2bb3Spatrick } 167*3cab2bb3Spatrick 168*3cab2bb3Spatrick static uptr RoundUpTo(uptr size, uptr boundary) { 169*3cab2bb3Spatrick return (size + boundary - 1) & ~(boundary - 1); 170*3cab2bb3Spatrick } 171*3cab2bb3Spatrick 172*3cab2bb3Spatrick // FIXME: internal_str* and internal_mem* functions should be moved from the 173*3cab2bb3Spatrick // ASan sources into interception/. 174*3cab2bb3Spatrick 175*3cab2bb3Spatrick static size_t _strlen(const char *str) { 176*3cab2bb3Spatrick const char* p = str; 177*3cab2bb3Spatrick while (*p != '\0') ++p; 178*3cab2bb3Spatrick return p - str; 179*3cab2bb3Spatrick } 180*3cab2bb3Spatrick 181*3cab2bb3Spatrick static char* _strchr(char* str, char c) { 182*3cab2bb3Spatrick while (*str) { 183*3cab2bb3Spatrick if (*str == c) 184*3cab2bb3Spatrick return str; 185*3cab2bb3Spatrick ++str; 186*3cab2bb3Spatrick } 187*3cab2bb3Spatrick return nullptr; 188*3cab2bb3Spatrick } 189*3cab2bb3Spatrick 190*3cab2bb3Spatrick static void _memset(void *p, int value, size_t sz) { 191*3cab2bb3Spatrick for (size_t i = 0; i < sz; ++i) 192*3cab2bb3Spatrick ((char*)p)[i] = (char)value; 193*3cab2bb3Spatrick } 194*3cab2bb3Spatrick 195*3cab2bb3Spatrick static void _memcpy(void *dst, void *src, size_t sz) { 196*3cab2bb3Spatrick char *dst_c = (char*)dst, 197*3cab2bb3Spatrick *src_c = (char*)src; 198*3cab2bb3Spatrick for (size_t i = 0; i < sz; ++i) 199*3cab2bb3Spatrick dst_c[i] = src_c[i]; 200*3cab2bb3Spatrick } 201*3cab2bb3Spatrick 202*3cab2bb3Spatrick static bool ChangeMemoryProtection( 203*3cab2bb3Spatrick uptr address, uptr size, DWORD *old_protection) { 204*3cab2bb3Spatrick return ::VirtualProtect((void*)address, size, 205*3cab2bb3Spatrick PAGE_EXECUTE_READWRITE, 206*3cab2bb3Spatrick old_protection) != FALSE; 207*3cab2bb3Spatrick } 208*3cab2bb3Spatrick 209*3cab2bb3Spatrick static bool RestoreMemoryProtection( 210*3cab2bb3Spatrick uptr address, uptr size, DWORD old_protection) { 211*3cab2bb3Spatrick DWORD unused; 212*3cab2bb3Spatrick return ::VirtualProtect((void*)address, size, 213*3cab2bb3Spatrick old_protection, 214*3cab2bb3Spatrick &unused) != FALSE; 215*3cab2bb3Spatrick } 216*3cab2bb3Spatrick 217*3cab2bb3Spatrick static bool IsMemoryPadding(uptr address, uptr size) { 218*3cab2bb3Spatrick u8* function = (u8*)address; 219*3cab2bb3Spatrick for (size_t i = 0; i < size; ++i) 220*3cab2bb3Spatrick if (function[i] != 0x90 && function[i] != 0xCC) 221*3cab2bb3Spatrick return false; 222*3cab2bb3Spatrick return true; 223*3cab2bb3Spatrick } 224*3cab2bb3Spatrick 225*3cab2bb3Spatrick static const u8 kHintNop8Bytes[] = { 226*3cab2bb3Spatrick 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00 227*3cab2bb3Spatrick }; 228*3cab2bb3Spatrick 229*3cab2bb3Spatrick template<class T> 230*3cab2bb3Spatrick static bool FunctionHasPrefix(uptr address, const T &pattern) { 231*3cab2bb3Spatrick u8* function = (u8*)address - sizeof(pattern); 232*3cab2bb3Spatrick for (size_t i = 0; i < sizeof(pattern); ++i) 233*3cab2bb3Spatrick if (function[i] != pattern[i]) 234*3cab2bb3Spatrick return false; 235*3cab2bb3Spatrick return true; 236*3cab2bb3Spatrick } 237*3cab2bb3Spatrick 238*3cab2bb3Spatrick static bool FunctionHasPadding(uptr address, uptr size) { 239*3cab2bb3Spatrick if (IsMemoryPadding(address - size, size)) 240*3cab2bb3Spatrick return true; 241*3cab2bb3Spatrick if (size <= sizeof(kHintNop8Bytes) && 242*3cab2bb3Spatrick FunctionHasPrefix(address, kHintNop8Bytes)) 243*3cab2bb3Spatrick return true; 244*3cab2bb3Spatrick return false; 245*3cab2bb3Spatrick } 246*3cab2bb3Spatrick 247*3cab2bb3Spatrick static void WritePadding(uptr from, uptr size) { 248*3cab2bb3Spatrick _memset((void*)from, 0xCC, (size_t)size); 249*3cab2bb3Spatrick } 250*3cab2bb3Spatrick 251*3cab2bb3Spatrick static void WriteJumpInstruction(uptr from, uptr target) { 252*3cab2bb3Spatrick if (!DistanceIsWithin2Gig(from + kJumpInstructionLength, target)) 253*3cab2bb3Spatrick InterceptionFailed(); 254*3cab2bb3Spatrick ptrdiff_t offset = target - from - kJumpInstructionLength; 255*3cab2bb3Spatrick *(u8*)from = 0xE9; 256*3cab2bb3Spatrick *(u32*)(from + 1) = offset; 257*3cab2bb3Spatrick } 258*3cab2bb3Spatrick 259*3cab2bb3Spatrick static void WriteShortJumpInstruction(uptr from, uptr target) { 260*3cab2bb3Spatrick sptr offset = target - from - kShortJumpInstructionLength; 261*3cab2bb3Spatrick if (offset < -128 || offset > 127) 262*3cab2bb3Spatrick InterceptionFailed(); 263*3cab2bb3Spatrick *(u8*)from = 0xEB; 264*3cab2bb3Spatrick *(u8*)(from + 1) = (u8)offset; 265*3cab2bb3Spatrick } 266*3cab2bb3Spatrick 267*3cab2bb3Spatrick #if SANITIZER_WINDOWS64 268*3cab2bb3Spatrick static void WriteIndirectJumpInstruction(uptr from, uptr indirect_target) { 269*3cab2bb3Spatrick // jmp [rip + <offset>] = FF 25 <offset> where <offset> is a relative 270*3cab2bb3Spatrick // offset. 271*3cab2bb3Spatrick // The offset is the distance from then end of the jump instruction to the 272*3cab2bb3Spatrick // memory location containing the targeted address. The displacement is still 273*3cab2bb3Spatrick // 32-bit in x64, so indirect_target must be located within +/- 2GB range. 274*3cab2bb3Spatrick int offset = indirect_target - from - kIndirectJumpInstructionLength; 275*3cab2bb3Spatrick if (!DistanceIsWithin2Gig(from + kIndirectJumpInstructionLength, 276*3cab2bb3Spatrick indirect_target)) { 277*3cab2bb3Spatrick InterceptionFailed(); 278*3cab2bb3Spatrick } 279*3cab2bb3Spatrick *(u16*)from = 0x25FF; 280*3cab2bb3Spatrick *(u32*)(from + 2) = offset; 281*3cab2bb3Spatrick } 282*3cab2bb3Spatrick #endif 283*3cab2bb3Spatrick 284*3cab2bb3Spatrick static void WriteBranch( 285*3cab2bb3Spatrick uptr from, uptr indirect_target, uptr target) { 286*3cab2bb3Spatrick #if SANITIZER_WINDOWS64 287*3cab2bb3Spatrick WriteIndirectJumpInstruction(from, indirect_target); 288*3cab2bb3Spatrick *(u64*)indirect_target = target; 289*3cab2bb3Spatrick #else 290*3cab2bb3Spatrick (void)indirect_target; 291*3cab2bb3Spatrick WriteJumpInstruction(from, target); 292*3cab2bb3Spatrick #endif 293*3cab2bb3Spatrick } 294*3cab2bb3Spatrick 295*3cab2bb3Spatrick static void WriteDirectBranch(uptr from, uptr target) { 296*3cab2bb3Spatrick #if SANITIZER_WINDOWS64 297*3cab2bb3Spatrick // Emit an indirect jump through immediately following bytes: 298*3cab2bb3Spatrick // jmp [rip + kBranchLength] 299*3cab2bb3Spatrick // .quad <target> 300*3cab2bb3Spatrick WriteBranch(from, from + kBranchLength, target); 301*3cab2bb3Spatrick #else 302*3cab2bb3Spatrick WriteJumpInstruction(from, target); 303*3cab2bb3Spatrick #endif 304*3cab2bb3Spatrick } 305*3cab2bb3Spatrick 306*3cab2bb3Spatrick struct TrampolineMemoryRegion { 307*3cab2bb3Spatrick uptr content; 308*3cab2bb3Spatrick uptr allocated_size; 309*3cab2bb3Spatrick uptr max_size; 310*3cab2bb3Spatrick }; 311*3cab2bb3Spatrick 312*3cab2bb3Spatrick static const uptr kTrampolineScanLimitRange = 1 << 31; // 2 gig 313*3cab2bb3Spatrick static const int kMaxTrampolineRegion = 1024; 314*3cab2bb3Spatrick static TrampolineMemoryRegion TrampolineRegions[kMaxTrampolineRegion]; 315*3cab2bb3Spatrick 316*3cab2bb3Spatrick static void *AllocateTrampolineRegion(uptr image_address, size_t granularity) { 317*3cab2bb3Spatrick #if SANITIZER_WINDOWS64 318*3cab2bb3Spatrick uptr address = image_address; 319*3cab2bb3Spatrick uptr scanned = 0; 320*3cab2bb3Spatrick while (scanned < kTrampolineScanLimitRange) { 321*3cab2bb3Spatrick MEMORY_BASIC_INFORMATION info; 322*3cab2bb3Spatrick if (!::VirtualQuery((void*)address, &info, sizeof(info))) 323*3cab2bb3Spatrick return nullptr; 324*3cab2bb3Spatrick 325*3cab2bb3Spatrick // Check whether a region can be allocated at |address|. 326*3cab2bb3Spatrick if (info.State == MEM_FREE && info.RegionSize >= granularity) { 327*3cab2bb3Spatrick void *page = ::VirtualAlloc((void*)RoundUpTo(address, granularity), 328*3cab2bb3Spatrick granularity, 329*3cab2bb3Spatrick MEM_RESERVE | MEM_COMMIT, 330*3cab2bb3Spatrick PAGE_EXECUTE_READWRITE); 331*3cab2bb3Spatrick return page; 332*3cab2bb3Spatrick } 333*3cab2bb3Spatrick 334*3cab2bb3Spatrick // Move to the next region. 335*3cab2bb3Spatrick address = (uptr)info.BaseAddress + info.RegionSize; 336*3cab2bb3Spatrick scanned += info.RegionSize; 337*3cab2bb3Spatrick } 338*3cab2bb3Spatrick return nullptr; 339*3cab2bb3Spatrick #else 340*3cab2bb3Spatrick return ::VirtualAlloc(nullptr, 341*3cab2bb3Spatrick granularity, 342*3cab2bb3Spatrick MEM_RESERVE | MEM_COMMIT, 343*3cab2bb3Spatrick PAGE_EXECUTE_READWRITE); 344*3cab2bb3Spatrick #endif 345*3cab2bb3Spatrick } 346*3cab2bb3Spatrick 347*3cab2bb3Spatrick // Used by unittests to release mapped memory space. 348*3cab2bb3Spatrick void TestOnlyReleaseTrampolineRegions() { 349*3cab2bb3Spatrick for (size_t bucket = 0; bucket < kMaxTrampolineRegion; ++bucket) { 350*3cab2bb3Spatrick TrampolineMemoryRegion *current = &TrampolineRegions[bucket]; 351*3cab2bb3Spatrick if (current->content == 0) 352*3cab2bb3Spatrick return; 353*3cab2bb3Spatrick ::VirtualFree((void*)current->content, 0, MEM_RELEASE); 354*3cab2bb3Spatrick current->content = 0; 355*3cab2bb3Spatrick } 356*3cab2bb3Spatrick } 357*3cab2bb3Spatrick 358*3cab2bb3Spatrick static uptr AllocateMemoryForTrampoline(uptr image_address, size_t size) { 359*3cab2bb3Spatrick // Find a region within 2G with enough space to allocate |size| bytes. 360*3cab2bb3Spatrick TrampolineMemoryRegion *region = nullptr; 361*3cab2bb3Spatrick for (size_t bucket = 0; bucket < kMaxTrampolineRegion; ++bucket) { 362*3cab2bb3Spatrick TrampolineMemoryRegion* current = &TrampolineRegions[bucket]; 363*3cab2bb3Spatrick if (current->content == 0) { 364*3cab2bb3Spatrick // No valid region found, allocate a new region. 365*3cab2bb3Spatrick size_t bucket_size = GetMmapGranularity(); 366*3cab2bb3Spatrick void *content = AllocateTrampolineRegion(image_address, bucket_size); 367*3cab2bb3Spatrick if (content == nullptr) 368*3cab2bb3Spatrick return 0U; 369*3cab2bb3Spatrick 370*3cab2bb3Spatrick current->content = (uptr)content; 371*3cab2bb3Spatrick current->allocated_size = 0; 372*3cab2bb3Spatrick current->max_size = bucket_size; 373*3cab2bb3Spatrick region = current; 374*3cab2bb3Spatrick break; 375*3cab2bb3Spatrick } else if (current->max_size - current->allocated_size > size) { 376*3cab2bb3Spatrick #if SANITIZER_WINDOWS64 377*3cab2bb3Spatrick // In 64-bits, the memory space must be allocated within 2G boundary. 378*3cab2bb3Spatrick uptr next_address = current->content + current->allocated_size; 379*3cab2bb3Spatrick if (next_address < image_address || 380*3cab2bb3Spatrick next_address - image_address >= 0x7FFF0000) 381*3cab2bb3Spatrick continue; 382*3cab2bb3Spatrick #endif 383*3cab2bb3Spatrick // The space can be allocated in the current region. 384*3cab2bb3Spatrick region = current; 385*3cab2bb3Spatrick break; 386*3cab2bb3Spatrick } 387*3cab2bb3Spatrick } 388*3cab2bb3Spatrick 389*3cab2bb3Spatrick // Failed to find a region. 390*3cab2bb3Spatrick if (region == nullptr) 391*3cab2bb3Spatrick return 0U; 392*3cab2bb3Spatrick 393*3cab2bb3Spatrick // Allocate the space in the current region. 394*3cab2bb3Spatrick uptr allocated_space = region->content + region->allocated_size; 395*3cab2bb3Spatrick region->allocated_size += size; 396*3cab2bb3Spatrick WritePadding(allocated_space, size); 397*3cab2bb3Spatrick 398*3cab2bb3Spatrick return allocated_space; 399*3cab2bb3Spatrick } 400*3cab2bb3Spatrick 401*3cab2bb3Spatrick // Returns 0 on error. 402*3cab2bb3Spatrick static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) { 403*3cab2bb3Spatrick switch (*(u64*)address) { 404*3cab2bb3Spatrick case 0x90909090909006EB: // stub: jmp over 6 x nop. 405*3cab2bb3Spatrick return 8; 406*3cab2bb3Spatrick } 407*3cab2bb3Spatrick 408*3cab2bb3Spatrick switch (*(u8*)address) { 409*3cab2bb3Spatrick case 0x90: // 90 : nop 410*3cab2bb3Spatrick return 1; 411*3cab2bb3Spatrick 412*3cab2bb3Spatrick case 0x50: // push eax / rax 413*3cab2bb3Spatrick case 0x51: // push ecx / rcx 414*3cab2bb3Spatrick case 0x52: // push edx / rdx 415*3cab2bb3Spatrick case 0x53: // push ebx / rbx 416*3cab2bb3Spatrick case 0x54: // push esp / rsp 417*3cab2bb3Spatrick case 0x55: // push ebp / rbp 418*3cab2bb3Spatrick case 0x56: // push esi / rsi 419*3cab2bb3Spatrick case 0x57: // push edi / rdi 420*3cab2bb3Spatrick case 0x5D: // pop ebp / rbp 421*3cab2bb3Spatrick return 1; 422*3cab2bb3Spatrick 423*3cab2bb3Spatrick case 0x6A: // 6A XX = push XX 424*3cab2bb3Spatrick return 2; 425*3cab2bb3Spatrick 426*3cab2bb3Spatrick case 0xb8: // b8 XX XX XX XX : mov eax, XX XX XX XX 427*3cab2bb3Spatrick case 0xB9: // b9 XX XX XX XX : mov ecx, XX XX XX XX 428*3cab2bb3Spatrick return 5; 429*3cab2bb3Spatrick 430*3cab2bb3Spatrick // Cannot overwrite control-instruction. Return 0 to indicate failure. 431*3cab2bb3Spatrick case 0xE9: // E9 XX XX XX XX : jmp <label> 432*3cab2bb3Spatrick case 0xE8: // E8 XX XX XX XX : call <func> 433*3cab2bb3Spatrick case 0xC3: // C3 : ret 434*3cab2bb3Spatrick case 0xEB: // EB XX : jmp XX (short jump) 435*3cab2bb3Spatrick case 0x70: // 7Y YY : jy XX (short conditional jump) 436*3cab2bb3Spatrick case 0x71: 437*3cab2bb3Spatrick case 0x72: 438*3cab2bb3Spatrick case 0x73: 439*3cab2bb3Spatrick case 0x74: 440*3cab2bb3Spatrick case 0x75: 441*3cab2bb3Spatrick case 0x76: 442*3cab2bb3Spatrick case 0x77: 443*3cab2bb3Spatrick case 0x78: 444*3cab2bb3Spatrick case 0x79: 445*3cab2bb3Spatrick case 0x7A: 446*3cab2bb3Spatrick case 0x7B: 447*3cab2bb3Spatrick case 0x7C: 448*3cab2bb3Spatrick case 0x7D: 449*3cab2bb3Spatrick case 0x7E: 450*3cab2bb3Spatrick case 0x7F: 451*3cab2bb3Spatrick return 0; 452*3cab2bb3Spatrick } 453*3cab2bb3Spatrick 454*3cab2bb3Spatrick switch (*(u16*)(address)) { 455*3cab2bb3Spatrick case 0x018A: // 8A 01 : mov al, byte ptr [ecx] 456*3cab2bb3Spatrick case 0xFF8B: // 8B FF : mov edi, edi 457*3cab2bb3Spatrick case 0xEC8B: // 8B EC : mov ebp, esp 458*3cab2bb3Spatrick case 0xc889: // 89 C8 : mov eax, ecx 459*3cab2bb3Spatrick case 0xC18B: // 8B C1 : mov eax, ecx 460*3cab2bb3Spatrick case 0xC033: // 33 C0 : xor eax, eax 461*3cab2bb3Spatrick case 0xC933: // 33 C9 : xor ecx, ecx 462*3cab2bb3Spatrick case 0xD233: // 33 D2 : xor edx, edx 463*3cab2bb3Spatrick return 2; 464*3cab2bb3Spatrick 465*3cab2bb3Spatrick // Cannot overwrite control-instruction. Return 0 to indicate failure. 466*3cab2bb3Spatrick case 0x25FF: // FF 25 XX XX XX XX : jmp [XXXXXXXX] 467*3cab2bb3Spatrick return 0; 468*3cab2bb3Spatrick } 469*3cab2bb3Spatrick 470*3cab2bb3Spatrick switch (0x00FFFFFF & *(u32*)address) { 471*3cab2bb3Spatrick case 0x24A48D: // 8D A4 24 XX XX XX XX : lea esp, [esp + XX XX XX XX] 472*3cab2bb3Spatrick return 7; 473*3cab2bb3Spatrick } 474*3cab2bb3Spatrick 475*3cab2bb3Spatrick #if SANITIZER_WINDOWS64 476*3cab2bb3Spatrick switch (*(u8*)address) { 477*3cab2bb3Spatrick case 0xA1: // A1 XX XX XX XX XX XX XX XX : 478*3cab2bb3Spatrick // movabs eax, dword ptr ds:[XXXXXXXX] 479*3cab2bb3Spatrick return 9; 480*3cab2bb3Spatrick } 481*3cab2bb3Spatrick 482*3cab2bb3Spatrick switch (*(u16*)address) { 483*3cab2bb3Spatrick case 0x5040: // push rax 484*3cab2bb3Spatrick case 0x5140: // push rcx 485*3cab2bb3Spatrick case 0x5240: // push rdx 486*3cab2bb3Spatrick case 0x5340: // push rbx 487*3cab2bb3Spatrick case 0x5440: // push rsp 488*3cab2bb3Spatrick case 0x5540: // push rbp 489*3cab2bb3Spatrick case 0x5640: // push rsi 490*3cab2bb3Spatrick case 0x5740: // push rdi 491*3cab2bb3Spatrick case 0x5441: // push r12 492*3cab2bb3Spatrick case 0x5541: // push r13 493*3cab2bb3Spatrick case 0x5641: // push r14 494*3cab2bb3Spatrick case 0x5741: // push r15 495*3cab2bb3Spatrick case 0x9066: // Two-byte NOP 496*3cab2bb3Spatrick return 2; 497*3cab2bb3Spatrick 498*3cab2bb3Spatrick case 0x058B: // 8B 05 XX XX XX XX : mov eax, dword ptr [XX XX XX XX] 499*3cab2bb3Spatrick if (rel_offset) 500*3cab2bb3Spatrick *rel_offset = 2; 501*3cab2bb3Spatrick return 6; 502*3cab2bb3Spatrick } 503*3cab2bb3Spatrick 504*3cab2bb3Spatrick switch (0x00FFFFFF & *(u32*)address) { 505*3cab2bb3Spatrick case 0xe58948: // 48 8b c4 : mov rbp, rsp 506*3cab2bb3Spatrick case 0xc18b48: // 48 8b c1 : mov rax, rcx 507*3cab2bb3Spatrick case 0xc48b48: // 48 8b c4 : mov rax, rsp 508*3cab2bb3Spatrick case 0xd9f748: // 48 f7 d9 : neg rcx 509*3cab2bb3Spatrick case 0xd12b48: // 48 2b d1 : sub rdx, rcx 510*3cab2bb3Spatrick case 0x07c1f6: // f6 c1 07 : test cl, 0x7 511*3cab2bb3Spatrick case 0xc98548: // 48 85 C9 : test rcx, rcx 512*3cab2bb3Spatrick case 0xc0854d: // 4d 85 c0 : test r8, r8 513*3cab2bb3Spatrick case 0xc2b60f: // 0f b6 c2 : movzx eax, dl 514*3cab2bb3Spatrick case 0xc03345: // 45 33 c0 : xor r8d, r8d 515*3cab2bb3Spatrick case 0xc93345: // 45 33 c9 : xor r9d, r9d 516*3cab2bb3Spatrick case 0xdb3345: // 45 33 DB : xor r11d, r11d 517*3cab2bb3Spatrick case 0xd98b4c: // 4c 8b d9 : mov r11, rcx 518*3cab2bb3Spatrick case 0xd28b4c: // 4c 8b d2 : mov r10, rdx 519*3cab2bb3Spatrick case 0xc98b4c: // 4C 8B C9 : mov r9, rcx 520*3cab2bb3Spatrick case 0xc18b4c: // 4C 8B C1 : mov r8, rcx 521*3cab2bb3Spatrick case 0xd2b60f: // 0f b6 d2 : movzx edx, dl 522*3cab2bb3Spatrick case 0xca2b48: // 48 2b ca : sub rcx, rdx 523*3cab2bb3Spatrick case 0x10b70f: // 0f b7 10 : movzx edx, WORD PTR [rax] 524*3cab2bb3Spatrick case 0xc00b4d: // 3d 0b c0 : or r8, r8 525*3cab2bb3Spatrick case 0xd18b48: // 48 8b d1 : mov rdx, rcx 526*3cab2bb3Spatrick case 0xdc8b4c: // 4c 8b dc : mov r11, rsp 527*3cab2bb3Spatrick case 0xd18b4c: // 4c 8b d1 : mov r10, rcx 528*3cab2bb3Spatrick case 0xE0E483: // 83 E4 E0 : and esp, 0xFFFFFFE0 529*3cab2bb3Spatrick return 3; 530*3cab2bb3Spatrick 531*3cab2bb3Spatrick case 0xec8348: // 48 83 ec XX : sub rsp, XX 532*3cab2bb3Spatrick case 0xf88349: // 49 83 f8 XX : cmp r8, XX 533*3cab2bb3Spatrick case 0x588948: // 48 89 58 XX : mov QWORD PTR[rax + XX], rbx 534*3cab2bb3Spatrick return 4; 535*3cab2bb3Spatrick 536*3cab2bb3Spatrick case 0xec8148: // 48 81 EC XX XX XX XX : sub rsp, XXXXXXXX 537*3cab2bb3Spatrick return 7; 538*3cab2bb3Spatrick 539*3cab2bb3Spatrick case 0x058b48: // 48 8b 05 XX XX XX XX : 540*3cab2bb3Spatrick // mov rax, QWORD PTR [rip + XXXXXXXX] 541*3cab2bb3Spatrick case 0x25ff48: // 48 ff 25 XX XX XX XX : 542*3cab2bb3Spatrick // rex.W jmp QWORD PTR [rip + XXXXXXXX] 543*3cab2bb3Spatrick 544*3cab2bb3Spatrick // Instructions having offset relative to 'rip' need offset adjustment. 545*3cab2bb3Spatrick if (rel_offset) 546*3cab2bb3Spatrick *rel_offset = 3; 547*3cab2bb3Spatrick return 7; 548*3cab2bb3Spatrick 549*3cab2bb3Spatrick case 0x2444c7: // C7 44 24 XX YY YY YY YY 550*3cab2bb3Spatrick // mov dword ptr [rsp + XX], YYYYYYYY 551*3cab2bb3Spatrick return 8; 552*3cab2bb3Spatrick } 553*3cab2bb3Spatrick 554*3cab2bb3Spatrick switch (*(u32*)(address)) { 555*3cab2bb3Spatrick case 0x24448b48: // 48 8b 44 24 XX : mov rax, QWORD ptr [rsp + XX] 556*3cab2bb3Spatrick case 0x246c8948: // 48 89 6C 24 XX : mov QWORD ptr [rsp + XX], rbp 557*3cab2bb3Spatrick case 0x245c8948: // 48 89 5c 24 XX : mov QWORD PTR [rsp + XX], rbx 558*3cab2bb3Spatrick case 0x24748948: // 48 89 74 24 XX : mov QWORD PTR [rsp + XX], rsi 559*3cab2bb3Spatrick case 0x244C8948: // 48 89 4C 24 XX : mov QWORD PTR [rsp + XX], rcx 560*3cab2bb3Spatrick case 0x24548948: // 48 89 54 24 XX : mov QWORD PTR [rsp + XX], rdx 561*3cab2bb3Spatrick case 0x244c894c: // 4c 89 4c 24 XX : mov QWORD PTR [rsp + XX], r9 562*3cab2bb3Spatrick case 0x2444894c: // 4c 89 44 24 XX : mov QWORD PTR [rsp + XX], r8 563*3cab2bb3Spatrick return 5; 564*3cab2bb3Spatrick case 0x24648348: // 48 83 64 24 XX : and QWORD PTR [rsp + XX], YY 565*3cab2bb3Spatrick return 6; 566*3cab2bb3Spatrick } 567*3cab2bb3Spatrick 568*3cab2bb3Spatrick #else 569*3cab2bb3Spatrick 570*3cab2bb3Spatrick switch (*(u8*)address) { 571*3cab2bb3Spatrick case 0xA1: // A1 XX XX XX XX : mov eax, dword ptr ds:[XXXXXXXX] 572*3cab2bb3Spatrick return 5; 573*3cab2bb3Spatrick } 574*3cab2bb3Spatrick switch (*(u16*)address) { 575*3cab2bb3Spatrick case 0x458B: // 8B 45 XX : mov eax, dword ptr [ebp + XX] 576*3cab2bb3Spatrick case 0x5D8B: // 8B 5D XX : mov ebx, dword ptr [ebp + XX] 577*3cab2bb3Spatrick case 0x7D8B: // 8B 7D XX : mov edi, dword ptr [ebp + XX] 578*3cab2bb3Spatrick case 0xEC83: // 83 EC XX : sub esp, XX 579*3cab2bb3Spatrick case 0x75FF: // FF 75 XX : push dword ptr [ebp + XX] 580*3cab2bb3Spatrick return 3; 581*3cab2bb3Spatrick case 0xC1F7: // F7 C1 XX YY ZZ WW : test ecx, WWZZYYXX 582*3cab2bb3Spatrick case 0x25FF: // FF 25 XX YY ZZ WW : jmp dword ptr ds:[WWZZYYXX] 583*3cab2bb3Spatrick return 6; 584*3cab2bb3Spatrick case 0x3D83: // 83 3D XX YY ZZ WW TT : cmp TT, WWZZYYXX 585*3cab2bb3Spatrick return 7; 586*3cab2bb3Spatrick case 0x7D83: // 83 7D XX YY : cmp dword ptr [ebp + XX], YY 587*3cab2bb3Spatrick return 4; 588*3cab2bb3Spatrick } 589*3cab2bb3Spatrick 590*3cab2bb3Spatrick switch (0x00FFFFFF & *(u32*)address) { 591*3cab2bb3Spatrick case 0x24448A: // 8A 44 24 XX : mov eal, dword ptr [esp + XX] 592*3cab2bb3Spatrick case 0x24448B: // 8B 44 24 XX : mov eax, dword ptr [esp + XX] 593*3cab2bb3Spatrick case 0x244C8B: // 8B 4C 24 XX : mov ecx, dword ptr [esp + XX] 594*3cab2bb3Spatrick case 0x24548B: // 8B 54 24 XX : mov edx, dword ptr [esp + XX] 595*3cab2bb3Spatrick case 0x24748B: // 8B 74 24 XX : mov esi, dword ptr [esp + XX] 596*3cab2bb3Spatrick case 0x247C8B: // 8B 7C 24 XX : mov edi, dword ptr [esp + XX] 597*3cab2bb3Spatrick return 4; 598*3cab2bb3Spatrick } 599*3cab2bb3Spatrick 600*3cab2bb3Spatrick switch (*(u32*)address) { 601*3cab2bb3Spatrick case 0x2444B60F: // 0F B6 44 24 XX : movzx eax, byte ptr [esp + XX] 602*3cab2bb3Spatrick return 5; 603*3cab2bb3Spatrick } 604*3cab2bb3Spatrick #endif 605*3cab2bb3Spatrick 606*3cab2bb3Spatrick // Unknown instruction! 607*3cab2bb3Spatrick // FIXME: Unknown instruction failures might happen when we add a new 608*3cab2bb3Spatrick // interceptor or a new compiler version. In either case, they should result 609*3cab2bb3Spatrick // in visible and readable error messages. However, merely calling abort() 610*3cab2bb3Spatrick // leads to an infinite recursion in CheckFailed. 611*3cab2bb3Spatrick InterceptionFailed(); 612*3cab2bb3Spatrick return 0; 613*3cab2bb3Spatrick } 614*3cab2bb3Spatrick 615*3cab2bb3Spatrick // Returns 0 on error. 616*3cab2bb3Spatrick static size_t RoundUpToInstrBoundary(size_t size, uptr address) { 617*3cab2bb3Spatrick size_t cursor = 0; 618*3cab2bb3Spatrick while (cursor < size) { 619*3cab2bb3Spatrick size_t instruction_size = GetInstructionSize(address + cursor); 620*3cab2bb3Spatrick if (!instruction_size) 621*3cab2bb3Spatrick return 0; 622*3cab2bb3Spatrick cursor += instruction_size; 623*3cab2bb3Spatrick } 624*3cab2bb3Spatrick return cursor; 625*3cab2bb3Spatrick } 626*3cab2bb3Spatrick 627*3cab2bb3Spatrick static bool CopyInstructions(uptr to, uptr from, size_t size) { 628*3cab2bb3Spatrick size_t cursor = 0; 629*3cab2bb3Spatrick while (cursor != size) { 630*3cab2bb3Spatrick size_t rel_offset = 0; 631*3cab2bb3Spatrick size_t instruction_size = GetInstructionSize(from + cursor, &rel_offset); 632*3cab2bb3Spatrick _memcpy((void*)(to + cursor), (void*)(from + cursor), 633*3cab2bb3Spatrick (size_t)instruction_size); 634*3cab2bb3Spatrick if (rel_offset) { 635*3cab2bb3Spatrick uptr delta = to - from; 636*3cab2bb3Spatrick uptr relocated_offset = *(u32*)(to + cursor + rel_offset) - delta; 637*3cab2bb3Spatrick #if SANITIZER_WINDOWS64 638*3cab2bb3Spatrick if (relocated_offset + 0x80000000U >= 0xFFFFFFFFU) 639*3cab2bb3Spatrick return false; 640*3cab2bb3Spatrick #endif 641*3cab2bb3Spatrick *(u32*)(to + cursor + rel_offset) = relocated_offset; 642*3cab2bb3Spatrick } 643*3cab2bb3Spatrick cursor += instruction_size; 644*3cab2bb3Spatrick } 645*3cab2bb3Spatrick return true; 646*3cab2bb3Spatrick } 647*3cab2bb3Spatrick 648*3cab2bb3Spatrick 649*3cab2bb3Spatrick #if !SANITIZER_WINDOWS64 650*3cab2bb3Spatrick bool OverrideFunctionWithDetour( 651*3cab2bb3Spatrick uptr old_func, uptr new_func, uptr *orig_old_func) { 652*3cab2bb3Spatrick const int kDetourHeaderLen = 5; 653*3cab2bb3Spatrick const u16 kDetourInstruction = 0xFF8B; 654*3cab2bb3Spatrick 655*3cab2bb3Spatrick uptr header = (uptr)old_func - kDetourHeaderLen; 656*3cab2bb3Spatrick uptr patch_length = kDetourHeaderLen + kShortJumpInstructionLength; 657*3cab2bb3Spatrick 658*3cab2bb3Spatrick // Validate that the function is hookable. 659*3cab2bb3Spatrick if (*(u16*)old_func != kDetourInstruction || 660*3cab2bb3Spatrick !IsMemoryPadding(header, kDetourHeaderLen)) 661*3cab2bb3Spatrick return false; 662*3cab2bb3Spatrick 663*3cab2bb3Spatrick // Change memory protection to writable. 664*3cab2bb3Spatrick DWORD protection = 0; 665*3cab2bb3Spatrick if (!ChangeMemoryProtection(header, patch_length, &protection)) 666*3cab2bb3Spatrick return false; 667*3cab2bb3Spatrick 668*3cab2bb3Spatrick // Write a relative jump to the redirected function. 669*3cab2bb3Spatrick WriteJumpInstruction(header, new_func); 670*3cab2bb3Spatrick 671*3cab2bb3Spatrick // Write the short jump to the function prefix. 672*3cab2bb3Spatrick WriteShortJumpInstruction(old_func, header); 673*3cab2bb3Spatrick 674*3cab2bb3Spatrick // Restore previous memory protection. 675*3cab2bb3Spatrick if (!RestoreMemoryProtection(header, patch_length, protection)) 676*3cab2bb3Spatrick return false; 677*3cab2bb3Spatrick 678*3cab2bb3Spatrick if (orig_old_func) 679*3cab2bb3Spatrick *orig_old_func = old_func + kShortJumpInstructionLength; 680*3cab2bb3Spatrick 681*3cab2bb3Spatrick return true; 682*3cab2bb3Spatrick } 683*3cab2bb3Spatrick #endif 684*3cab2bb3Spatrick 685*3cab2bb3Spatrick bool OverrideFunctionWithRedirectJump( 686*3cab2bb3Spatrick uptr old_func, uptr new_func, uptr *orig_old_func) { 687*3cab2bb3Spatrick // Check whether the first instruction is a relative jump. 688*3cab2bb3Spatrick if (*(u8*)old_func != 0xE9) 689*3cab2bb3Spatrick return false; 690*3cab2bb3Spatrick 691*3cab2bb3Spatrick if (orig_old_func) { 692*3cab2bb3Spatrick uptr relative_offset = *(u32*)(old_func + 1); 693*3cab2bb3Spatrick uptr absolute_target = old_func + relative_offset + kJumpInstructionLength; 694*3cab2bb3Spatrick *orig_old_func = absolute_target; 695*3cab2bb3Spatrick } 696*3cab2bb3Spatrick 697*3cab2bb3Spatrick #if SANITIZER_WINDOWS64 698*3cab2bb3Spatrick // If needed, get memory space for a trampoline jump. 699*3cab2bb3Spatrick uptr trampoline = AllocateMemoryForTrampoline(old_func, kDirectBranchLength); 700*3cab2bb3Spatrick if (!trampoline) 701*3cab2bb3Spatrick return false; 702*3cab2bb3Spatrick WriteDirectBranch(trampoline, new_func); 703*3cab2bb3Spatrick #endif 704*3cab2bb3Spatrick 705*3cab2bb3Spatrick // Change memory protection to writable. 706*3cab2bb3Spatrick DWORD protection = 0; 707*3cab2bb3Spatrick if (!ChangeMemoryProtection(old_func, kJumpInstructionLength, &protection)) 708*3cab2bb3Spatrick return false; 709*3cab2bb3Spatrick 710*3cab2bb3Spatrick // Write a relative jump to the redirected function. 711*3cab2bb3Spatrick WriteJumpInstruction(old_func, FIRST_32_SECOND_64(new_func, trampoline)); 712*3cab2bb3Spatrick 713*3cab2bb3Spatrick // Restore previous memory protection. 714*3cab2bb3Spatrick if (!RestoreMemoryProtection(old_func, kJumpInstructionLength, protection)) 715*3cab2bb3Spatrick return false; 716*3cab2bb3Spatrick 717*3cab2bb3Spatrick return true; 718*3cab2bb3Spatrick } 719*3cab2bb3Spatrick 720*3cab2bb3Spatrick bool OverrideFunctionWithHotPatch( 721*3cab2bb3Spatrick uptr old_func, uptr new_func, uptr *orig_old_func) { 722*3cab2bb3Spatrick const int kHotPatchHeaderLen = kBranchLength; 723*3cab2bb3Spatrick 724*3cab2bb3Spatrick uptr header = (uptr)old_func - kHotPatchHeaderLen; 725*3cab2bb3Spatrick uptr patch_length = kHotPatchHeaderLen + kShortJumpInstructionLength; 726*3cab2bb3Spatrick 727*3cab2bb3Spatrick // Validate that the function is hot patchable. 728*3cab2bb3Spatrick size_t instruction_size = GetInstructionSize(old_func); 729*3cab2bb3Spatrick if (instruction_size < kShortJumpInstructionLength || 730*3cab2bb3Spatrick !FunctionHasPadding(old_func, kHotPatchHeaderLen)) 731*3cab2bb3Spatrick return false; 732*3cab2bb3Spatrick 733*3cab2bb3Spatrick if (orig_old_func) { 734*3cab2bb3Spatrick // Put the needed instructions into the trampoline bytes. 735*3cab2bb3Spatrick uptr trampoline_length = instruction_size + kDirectBranchLength; 736*3cab2bb3Spatrick uptr trampoline = AllocateMemoryForTrampoline(old_func, trampoline_length); 737*3cab2bb3Spatrick if (!trampoline) 738*3cab2bb3Spatrick return false; 739*3cab2bb3Spatrick if (!CopyInstructions(trampoline, old_func, instruction_size)) 740*3cab2bb3Spatrick return false; 741*3cab2bb3Spatrick WriteDirectBranch(trampoline + instruction_size, 742*3cab2bb3Spatrick old_func + instruction_size); 743*3cab2bb3Spatrick *orig_old_func = trampoline; 744*3cab2bb3Spatrick } 745*3cab2bb3Spatrick 746*3cab2bb3Spatrick // If needed, get memory space for indirect address. 747*3cab2bb3Spatrick uptr indirect_address = 0; 748*3cab2bb3Spatrick #if SANITIZER_WINDOWS64 749*3cab2bb3Spatrick indirect_address = AllocateMemoryForTrampoline(old_func, kAddressLength); 750*3cab2bb3Spatrick if (!indirect_address) 751*3cab2bb3Spatrick return false; 752*3cab2bb3Spatrick #endif 753*3cab2bb3Spatrick 754*3cab2bb3Spatrick // Change memory protection to writable. 755*3cab2bb3Spatrick DWORD protection = 0; 756*3cab2bb3Spatrick if (!ChangeMemoryProtection(header, patch_length, &protection)) 757*3cab2bb3Spatrick return false; 758*3cab2bb3Spatrick 759*3cab2bb3Spatrick // Write jumps to the redirected function. 760*3cab2bb3Spatrick WriteBranch(header, indirect_address, new_func); 761*3cab2bb3Spatrick WriteShortJumpInstruction(old_func, header); 762*3cab2bb3Spatrick 763*3cab2bb3Spatrick // Restore previous memory protection. 764*3cab2bb3Spatrick if (!RestoreMemoryProtection(header, patch_length, protection)) 765*3cab2bb3Spatrick return false; 766*3cab2bb3Spatrick 767*3cab2bb3Spatrick return true; 768*3cab2bb3Spatrick } 769*3cab2bb3Spatrick 770*3cab2bb3Spatrick bool OverrideFunctionWithTrampoline( 771*3cab2bb3Spatrick uptr old_func, uptr new_func, uptr *orig_old_func) { 772*3cab2bb3Spatrick 773*3cab2bb3Spatrick size_t instructions_length = kBranchLength; 774*3cab2bb3Spatrick size_t padding_length = 0; 775*3cab2bb3Spatrick uptr indirect_address = 0; 776*3cab2bb3Spatrick 777*3cab2bb3Spatrick if (orig_old_func) { 778*3cab2bb3Spatrick // Find out the number of bytes of the instructions we need to copy 779*3cab2bb3Spatrick // to the trampoline. 780*3cab2bb3Spatrick instructions_length = RoundUpToInstrBoundary(kBranchLength, old_func); 781*3cab2bb3Spatrick if (!instructions_length) 782*3cab2bb3Spatrick return false; 783*3cab2bb3Spatrick 784*3cab2bb3Spatrick // Put the needed instructions into the trampoline bytes. 785*3cab2bb3Spatrick uptr trampoline_length = instructions_length + kDirectBranchLength; 786*3cab2bb3Spatrick uptr trampoline = AllocateMemoryForTrampoline(old_func, trampoline_length); 787*3cab2bb3Spatrick if (!trampoline) 788*3cab2bb3Spatrick return false; 789*3cab2bb3Spatrick if (!CopyInstructions(trampoline, old_func, instructions_length)) 790*3cab2bb3Spatrick return false; 791*3cab2bb3Spatrick WriteDirectBranch(trampoline + instructions_length, 792*3cab2bb3Spatrick old_func + instructions_length); 793*3cab2bb3Spatrick *orig_old_func = trampoline; 794*3cab2bb3Spatrick } 795*3cab2bb3Spatrick 796*3cab2bb3Spatrick #if SANITIZER_WINDOWS64 797*3cab2bb3Spatrick // Check if the targeted address can be encoded in the function padding. 798*3cab2bb3Spatrick // Otherwise, allocate it in the trampoline region. 799*3cab2bb3Spatrick if (IsMemoryPadding(old_func - kAddressLength, kAddressLength)) { 800*3cab2bb3Spatrick indirect_address = old_func - kAddressLength; 801*3cab2bb3Spatrick padding_length = kAddressLength; 802*3cab2bb3Spatrick } else { 803*3cab2bb3Spatrick indirect_address = AllocateMemoryForTrampoline(old_func, kAddressLength); 804*3cab2bb3Spatrick if (!indirect_address) 805*3cab2bb3Spatrick return false; 806*3cab2bb3Spatrick } 807*3cab2bb3Spatrick #endif 808*3cab2bb3Spatrick 809*3cab2bb3Spatrick // Change memory protection to writable. 810*3cab2bb3Spatrick uptr patch_address = old_func - padding_length; 811*3cab2bb3Spatrick uptr patch_length = instructions_length + padding_length; 812*3cab2bb3Spatrick DWORD protection = 0; 813*3cab2bb3Spatrick if (!ChangeMemoryProtection(patch_address, patch_length, &protection)) 814*3cab2bb3Spatrick return false; 815*3cab2bb3Spatrick 816*3cab2bb3Spatrick // Patch the original function. 817*3cab2bb3Spatrick WriteBranch(old_func, indirect_address, new_func); 818*3cab2bb3Spatrick 819*3cab2bb3Spatrick // Restore previous memory protection. 820*3cab2bb3Spatrick if (!RestoreMemoryProtection(patch_address, patch_length, protection)) 821*3cab2bb3Spatrick return false; 822*3cab2bb3Spatrick 823*3cab2bb3Spatrick return true; 824*3cab2bb3Spatrick } 825*3cab2bb3Spatrick 826*3cab2bb3Spatrick bool OverrideFunction( 827*3cab2bb3Spatrick uptr old_func, uptr new_func, uptr *orig_old_func) { 828*3cab2bb3Spatrick #if !SANITIZER_WINDOWS64 829*3cab2bb3Spatrick if (OverrideFunctionWithDetour(old_func, new_func, orig_old_func)) 830*3cab2bb3Spatrick return true; 831*3cab2bb3Spatrick #endif 832*3cab2bb3Spatrick if (OverrideFunctionWithRedirectJump(old_func, new_func, orig_old_func)) 833*3cab2bb3Spatrick return true; 834*3cab2bb3Spatrick if (OverrideFunctionWithHotPatch(old_func, new_func, orig_old_func)) 835*3cab2bb3Spatrick return true; 836*3cab2bb3Spatrick if (OverrideFunctionWithTrampoline(old_func, new_func, orig_old_func)) 837*3cab2bb3Spatrick return true; 838*3cab2bb3Spatrick return false; 839*3cab2bb3Spatrick } 840*3cab2bb3Spatrick 841*3cab2bb3Spatrick static void **InterestingDLLsAvailable() { 842*3cab2bb3Spatrick static const char *InterestingDLLs[] = { 843*3cab2bb3Spatrick "kernel32.dll", 844*3cab2bb3Spatrick "msvcr100.dll", // VS2010 845*3cab2bb3Spatrick "msvcr110.dll", // VS2012 846*3cab2bb3Spatrick "msvcr120.dll", // VS2013 847*3cab2bb3Spatrick "vcruntime140.dll", // VS2015 848*3cab2bb3Spatrick "ucrtbase.dll", // Universal CRT 849*3cab2bb3Spatrick // NTDLL should go last as it exports some functions that we should 850*3cab2bb3Spatrick // override in the CRT [presumably only used internally]. 851*3cab2bb3Spatrick "ntdll.dll", NULL}; 852*3cab2bb3Spatrick static void *result[ARRAY_SIZE(InterestingDLLs)] = { 0 }; 853*3cab2bb3Spatrick if (!result[0]) { 854*3cab2bb3Spatrick for (size_t i = 0, j = 0; InterestingDLLs[i]; ++i) { 855*3cab2bb3Spatrick if (HMODULE h = GetModuleHandleA(InterestingDLLs[i])) 856*3cab2bb3Spatrick result[j++] = (void *)h; 857*3cab2bb3Spatrick } 858*3cab2bb3Spatrick } 859*3cab2bb3Spatrick return &result[0]; 860*3cab2bb3Spatrick } 861*3cab2bb3Spatrick 862*3cab2bb3Spatrick namespace { 863*3cab2bb3Spatrick // Utility for reading loaded PE images. 864*3cab2bb3Spatrick template <typename T> class RVAPtr { 865*3cab2bb3Spatrick public: 866*3cab2bb3Spatrick RVAPtr(void *module, uptr rva) 867*3cab2bb3Spatrick : ptr_(reinterpret_cast<T *>(reinterpret_cast<char *>(module) + rva)) {} 868*3cab2bb3Spatrick operator T *() { return ptr_; } 869*3cab2bb3Spatrick T *operator->() { return ptr_; } 870*3cab2bb3Spatrick T *operator++() { return ++ptr_; } 871*3cab2bb3Spatrick 872*3cab2bb3Spatrick private: 873*3cab2bb3Spatrick T *ptr_; 874*3cab2bb3Spatrick }; 875*3cab2bb3Spatrick } // namespace 876*3cab2bb3Spatrick 877*3cab2bb3Spatrick // Internal implementation of GetProcAddress. At least since Windows 8, 878*3cab2bb3Spatrick // GetProcAddress appears to initialize DLLs before returning function pointers 879*3cab2bb3Spatrick // into them. This is problematic for the sanitizers, because they typically 880*3cab2bb3Spatrick // want to intercept malloc *before* MSVCRT initializes. Our internal 881*3cab2bb3Spatrick // implementation walks the export list manually without doing initialization. 882*3cab2bb3Spatrick uptr InternalGetProcAddress(void *module, const char *func_name) { 883*3cab2bb3Spatrick // Check that the module header is full and present. 884*3cab2bb3Spatrick RVAPtr<IMAGE_DOS_HEADER> dos_stub(module, 0); 885*3cab2bb3Spatrick RVAPtr<IMAGE_NT_HEADERS> headers(module, dos_stub->e_lfanew); 886*3cab2bb3Spatrick if (!module || dos_stub->e_magic != IMAGE_DOS_SIGNATURE || // "MZ" 887*3cab2bb3Spatrick headers->Signature != IMAGE_NT_SIGNATURE || // "PE\0\0" 888*3cab2bb3Spatrick headers->FileHeader.SizeOfOptionalHeader < 889*3cab2bb3Spatrick sizeof(IMAGE_OPTIONAL_HEADER)) { 890*3cab2bb3Spatrick return 0; 891*3cab2bb3Spatrick } 892*3cab2bb3Spatrick 893*3cab2bb3Spatrick IMAGE_DATA_DIRECTORY *export_directory = 894*3cab2bb3Spatrick &headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; 895*3cab2bb3Spatrick if (export_directory->Size == 0) 896*3cab2bb3Spatrick return 0; 897*3cab2bb3Spatrick RVAPtr<IMAGE_EXPORT_DIRECTORY> exports(module, 898*3cab2bb3Spatrick export_directory->VirtualAddress); 899*3cab2bb3Spatrick RVAPtr<DWORD> functions(module, exports->AddressOfFunctions); 900*3cab2bb3Spatrick RVAPtr<DWORD> names(module, exports->AddressOfNames); 901*3cab2bb3Spatrick RVAPtr<WORD> ordinals(module, exports->AddressOfNameOrdinals); 902*3cab2bb3Spatrick 903*3cab2bb3Spatrick for (DWORD i = 0; i < exports->NumberOfNames; i++) { 904*3cab2bb3Spatrick RVAPtr<char> name(module, names[i]); 905*3cab2bb3Spatrick if (!strcmp(func_name, name)) { 906*3cab2bb3Spatrick DWORD index = ordinals[i]; 907*3cab2bb3Spatrick RVAPtr<char> func(module, functions[index]); 908*3cab2bb3Spatrick 909*3cab2bb3Spatrick // Handle forwarded functions. 910*3cab2bb3Spatrick DWORD offset = functions[index]; 911*3cab2bb3Spatrick if (offset >= export_directory->VirtualAddress && 912*3cab2bb3Spatrick offset < export_directory->VirtualAddress + export_directory->Size) { 913*3cab2bb3Spatrick // An entry for a forwarded function is a string with the following 914*3cab2bb3Spatrick // format: "<module> . <function_name>" that is stored into the 915*3cab2bb3Spatrick // exported directory. 916*3cab2bb3Spatrick char function_name[256]; 917*3cab2bb3Spatrick size_t funtion_name_length = _strlen(func); 918*3cab2bb3Spatrick if (funtion_name_length >= sizeof(function_name) - 1) 919*3cab2bb3Spatrick InterceptionFailed(); 920*3cab2bb3Spatrick 921*3cab2bb3Spatrick _memcpy(function_name, func, funtion_name_length); 922*3cab2bb3Spatrick function_name[funtion_name_length] = '\0'; 923*3cab2bb3Spatrick char* separator = _strchr(function_name, '.'); 924*3cab2bb3Spatrick if (!separator) 925*3cab2bb3Spatrick InterceptionFailed(); 926*3cab2bb3Spatrick *separator = '\0'; 927*3cab2bb3Spatrick 928*3cab2bb3Spatrick void* redirected_module = GetModuleHandleA(function_name); 929*3cab2bb3Spatrick if (!redirected_module) 930*3cab2bb3Spatrick InterceptionFailed(); 931*3cab2bb3Spatrick return InternalGetProcAddress(redirected_module, separator + 1); 932*3cab2bb3Spatrick } 933*3cab2bb3Spatrick 934*3cab2bb3Spatrick return (uptr)(char *)func; 935*3cab2bb3Spatrick } 936*3cab2bb3Spatrick } 937*3cab2bb3Spatrick 938*3cab2bb3Spatrick return 0; 939*3cab2bb3Spatrick } 940*3cab2bb3Spatrick 941*3cab2bb3Spatrick bool OverrideFunction( 942*3cab2bb3Spatrick const char *func_name, uptr new_func, uptr *orig_old_func) { 943*3cab2bb3Spatrick bool hooked = false; 944*3cab2bb3Spatrick void **DLLs = InterestingDLLsAvailable(); 945*3cab2bb3Spatrick for (size_t i = 0; DLLs[i]; ++i) { 946*3cab2bb3Spatrick uptr func_addr = InternalGetProcAddress(DLLs[i], func_name); 947*3cab2bb3Spatrick if (func_addr && 948*3cab2bb3Spatrick OverrideFunction(func_addr, new_func, orig_old_func)) { 949*3cab2bb3Spatrick hooked = true; 950*3cab2bb3Spatrick } 951*3cab2bb3Spatrick } 952*3cab2bb3Spatrick return hooked; 953*3cab2bb3Spatrick } 954*3cab2bb3Spatrick 955*3cab2bb3Spatrick bool OverrideImportedFunction(const char *module_to_patch, 956*3cab2bb3Spatrick const char *imported_module, 957*3cab2bb3Spatrick const char *function_name, uptr new_function, 958*3cab2bb3Spatrick uptr *orig_old_func) { 959*3cab2bb3Spatrick HMODULE module = GetModuleHandleA(module_to_patch); 960*3cab2bb3Spatrick if (!module) 961*3cab2bb3Spatrick return false; 962*3cab2bb3Spatrick 963*3cab2bb3Spatrick // Check that the module header is full and present. 964*3cab2bb3Spatrick RVAPtr<IMAGE_DOS_HEADER> dos_stub(module, 0); 965*3cab2bb3Spatrick RVAPtr<IMAGE_NT_HEADERS> headers(module, dos_stub->e_lfanew); 966*3cab2bb3Spatrick if (!module || dos_stub->e_magic != IMAGE_DOS_SIGNATURE || // "MZ" 967*3cab2bb3Spatrick headers->Signature != IMAGE_NT_SIGNATURE || // "PE\0\0" 968*3cab2bb3Spatrick headers->FileHeader.SizeOfOptionalHeader < 969*3cab2bb3Spatrick sizeof(IMAGE_OPTIONAL_HEADER)) { 970*3cab2bb3Spatrick return false; 971*3cab2bb3Spatrick } 972*3cab2bb3Spatrick 973*3cab2bb3Spatrick IMAGE_DATA_DIRECTORY *import_directory = 974*3cab2bb3Spatrick &headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; 975*3cab2bb3Spatrick 976*3cab2bb3Spatrick // Iterate the list of imported DLLs. FirstThunk will be null for the last 977*3cab2bb3Spatrick // entry. 978*3cab2bb3Spatrick RVAPtr<IMAGE_IMPORT_DESCRIPTOR> imports(module, 979*3cab2bb3Spatrick import_directory->VirtualAddress); 980*3cab2bb3Spatrick for (; imports->FirstThunk != 0; ++imports) { 981*3cab2bb3Spatrick RVAPtr<const char> modname(module, imports->Name); 982*3cab2bb3Spatrick if (_stricmp(&*modname, imported_module) == 0) 983*3cab2bb3Spatrick break; 984*3cab2bb3Spatrick } 985*3cab2bb3Spatrick if (imports->FirstThunk == 0) 986*3cab2bb3Spatrick return false; 987*3cab2bb3Spatrick 988*3cab2bb3Spatrick // We have two parallel arrays: the import address table (IAT) and the table 989*3cab2bb3Spatrick // of names. They start out containing the same data, but the loader rewrites 990*3cab2bb3Spatrick // the IAT to hold imported addresses and leaves the name table in 991*3cab2bb3Spatrick // OriginalFirstThunk alone. 992*3cab2bb3Spatrick RVAPtr<IMAGE_THUNK_DATA> name_table(module, imports->OriginalFirstThunk); 993*3cab2bb3Spatrick RVAPtr<IMAGE_THUNK_DATA> iat(module, imports->FirstThunk); 994*3cab2bb3Spatrick for (; name_table->u1.Ordinal != 0; ++name_table, ++iat) { 995*3cab2bb3Spatrick if (!IMAGE_SNAP_BY_ORDINAL(name_table->u1.Ordinal)) { 996*3cab2bb3Spatrick RVAPtr<IMAGE_IMPORT_BY_NAME> import_by_name( 997*3cab2bb3Spatrick module, name_table->u1.ForwarderString); 998*3cab2bb3Spatrick const char *funcname = &import_by_name->Name[0]; 999*3cab2bb3Spatrick if (strcmp(funcname, function_name) == 0) 1000*3cab2bb3Spatrick break; 1001*3cab2bb3Spatrick } 1002*3cab2bb3Spatrick } 1003*3cab2bb3Spatrick if (name_table->u1.Ordinal == 0) 1004*3cab2bb3Spatrick return false; 1005*3cab2bb3Spatrick 1006*3cab2bb3Spatrick // Now we have the correct IAT entry. Do the swap. We have to make the page 1007*3cab2bb3Spatrick // read/write first. 1008*3cab2bb3Spatrick if (orig_old_func) 1009*3cab2bb3Spatrick *orig_old_func = iat->u1.AddressOfData; 1010*3cab2bb3Spatrick DWORD old_prot, unused_prot; 1011*3cab2bb3Spatrick if (!VirtualProtect(&iat->u1.AddressOfData, 4, PAGE_EXECUTE_READWRITE, 1012*3cab2bb3Spatrick &old_prot)) 1013*3cab2bb3Spatrick return false; 1014*3cab2bb3Spatrick iat->u1.AddressOfData = new_function; 1015*3cab2bb3Spatrick if (!VirtualProtect(&iat->u1.AddressOfData, 4, old_prot, &unused_prot)) 1016*3cab2bb3Spatrick return false; // Not clear if this failure bothers us. 1017*3cab2bb3Spatrick return true; 1018*3cab2bb3Spatrick } 1019*3cab2bb3Spatrick 1020*3cab2bb3Spatrick } // namespace __interception 1021*3cab2bb3Spatrick 1022*3cab2bb3Spatrick #endif // SANITIZER_MAC 1023