13cab2bb3Spatrick //===-- interception_linux.cpp ----------------------------------*- C++ -*-===//
23cab2bb3Spatrick //
33cab2bb3Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
43cab2bb3Spatrick // See https://llvm.org/LICENSE.txt for license information.
53cab2bb3Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
63cab2bb3Spatrick //
73cab2bb3Spatrick //===----------------------------------------------------------------------===//
83cab2bb3Spatrick //
93cab2bb3Spatrick // This file is a part of AddressSanitizer, an address sanity checker.
103cab2bb3Spatrick //
113cab2bb3Spatrick // Windows-specific interception methods.
123cab2bb3Spatrick //
133cab2bb3Spatrick // This file is implementing several hooking techniques to intercept calls
143cab2bb3Spatrick // to functions. The hooks are dynamically installed by modifying the assembly
153cab2bb3Spatrick // code.
163cab2bb3Spatrick //
173cab2bb3Spatrick // The hooking techniques are making assumptions on the way the code is
183cab2bb3Spatrick // generated and are safe under these assumptions.
193cab2bb3Spatrick //
203cab2bb3Spatrick // On 64-bit architecture, there is no direct 64-bit jump instruction. To allow
213cab2bb3Spatrick // arbitrary branching on the whole memory space, the notion of trampoline
223cab2bb3Spatrick // region is used. A trampoline region is a memory space withing 2G boundary
233cab2bb3Spatrick // where it is safe to add custom assembly code to build 64-bit jumps.
243cab2bb3Spatrick //
253cab2bb3Spatrick // Hooking techniques
263cab2bb3Spatrick // ==================
273cab2bb3Spatrick //
283cab2bb3Spatrick // 1) Detour
293cab2bb3Spatrick //
303cab2bb3Spatrick // The Detour hooking technique is assuming the presence of an header with
313cab2bb3Spatrick // padding and an overridable 2-bytes nop instruction (mov edi, edi). The
323cab2bb3Spatrick // nop instruction can safely be replaced by a 2-bytes jump without any need
333cab2bb3Spatrick // to save the instruction. A jump to the target is encoded in the function
343cab2bb3Spatrick // header and the nop instruction is replaced by a short jump to the header.
353cab2bb3Spatrick //
363cab2bb3Spatrick // head: 5 x nop head: jmp <hook>
373cab2bb3Spatrick // func: mov edi, edi --> func: jmp short <head>
383cab2bb3Spatrick // [...] real: [...]
393cab2bb3Spatrick //
403cab2bb3Spatrick // This technique is only implemented on 32-bit architecture.
413cab2bb3Spatrick // Most of the time, Windows API are hookable with the detour technique.
423cab2bb3Spatrick //
433cab2bb3Spatrick // 2) Redirect Jump
443cab2bb3Spatrick //
453cab2bb3Spatrick // The redirect jump is applicable when the first instruction is a direct
463cab2bb3Spatrick // jump. The instruction is replaced by jump to the hook.
473cab2bb3Spatrick //
483cab2bb3Spatrick // func: jmp <label> --> func: jmp <hook>
493cab2bb3Spatrick //
503cab2bb3Spatrick // On an 64-bit architecture, a trampoline is inserted.
513cab2bb3Spatrick //
523cab2bb3Spatrick // func: jmp <label> --> func: jmp <tramp>
533cab2bb3Spatrick // [...]
543cab2bb3Spatrick //
553cab2bb3Spatrick // [trampoline]
563cab2bb3Spatrick // tramp: jmp QWORD [addr]
573cab2bb3Spatrick // addr: .bytes <hook>
583cab2bb3Spatrick //
59*810390e3Srobert // Note: <real> is equivalent to <label>.
603cab2bb3Spatrick //
613cab2bb3Spatrick // 3) HotPatch
623cab2bb3Spatrick //
633cab2bb3Spatrick // The HotPatch hooking is assuming the presence of an header with padding
643cab2bb3Spatrick // and a first instruction with at least 2-bytes.
653cab2bb3Spatrick //
663cab2bb3Spatrick // The reason to enforce the 2-bytes limitation is to provide the minimal
673cab2bb3Spatrick // space to encode a short jump. HotPatch technique is only rewriting one
683cab2bb3Spatrick // instruction to avoid breaking a sequence of instructions containing a
693cab2bb3Spatrick // branching target.
703cab2bb3Spatrick //
713cab2bb3Spatrick // Assumptions are enforced by MSVC compiler by using the /HOTPATCH flag.
723cab2bb3Spatrick // see: https://msdn.microsoft.com/en-us/library/ms173507.aspx
733cab2bb3Spatrick // Default padding length is 5 bytes in 32-bits and 6 bytes in 64-bits.
743cab2bb3Spatrick //
753cab2bb3Spatrick // head: 5 x nop head: jmp <hook>
763cab2bb3Spatrick // func: <instr> --> func: jmp short <head>
773cab2bb3Spatrick // [...] body: [...]
783cab2bb3Spatrick //
793cab2bb3Spatrick // [trampoline]
803cab2bb3Spatrick // real: <instr>
813cab2bb3Spatrick // jmp <body>
823cab2bb3Spatrick //
833cab2bb3Spatrick // On an 64-bit architecture:
843cab2bb3Spatrick //
853cab2bb3Spatrick // head: 6 x nop head: jmp QWORD [addr1]
863cab2bb3Spatrick // func: <instr> --> func: jmp short <head>
873cab2bb3Spatrick // [...] body: [...]
883cab2bb3Spatrick //
893cab2bb3Spatrick // [trampoline]
903cab2bb3Spatrick // addr1: .bytes <hook>
913cab2bb3Spatrick // real: <instr>
923cab2bb3Spatrick // jmp QWORD [addr2]
933cab2bb3Spatrick // addr2: .bytes <body>
943cab2bb3Spatrick //
953cab2bb3Spatrick // 4) Trampoline
963cab2bb3Spatrick //
973cab2bb3Spatrick // The Trampoline hooking technique is the most aggressive one. It is
983cab2bb3Spatrick // assuming that there is a sequence of instructions that can be safely
993cab2bb3Spatrick // replaced by a jump (enough room and no incoming branches).
1003cab2bb3Spatrick //
1013cab2bb3Spatrick // Unfortunately, these assumptions can't be safely presumed and code may
1023cab2bb3Spatrick // be broken after hooking.
1033cab2bb3Spatrick //
1043cab2bb3Spatrick // func: <instr> --> func: jmp <hook>
1053cab2bb3Spatrick // <instr>
1063cab2bb3Spatrick // [...] body: [...]
1073cab2bb3Spatrick //
1083cab2bb3Spatrick // [trampoline]
1093cab2bb3Spatrick // real: <instr>
1103cab2bb3Spatrick // <instr>
1113cab2bb3Spatrick // jmp <body>
1123cab2bb3Spatrick //
1133cab2bb3Spatrick // On an 64-bit architecture:
1143cab2bb3Spatrick //
1153cab2bb3Spatrick // func: <instr> --> func: jmp QWORD [addr1]
1163cab2bb3Spatrick // <instr>
1173cab2bb3Spatrick // [...] body: [...]
1183cab2bb3Spatrick //
1193cab2bb3Spatrick // [trampoline]
1203cab2bb3Spatrick // addr1: .bytes <hook>
1213cab2bb3Spatrick // real: <instr>
1223cab2bb3Spatrick // <instr>
1233cab2bb3Spatrick // jmp QWORD [addr2]
1243cab2bb3Spatrick // addr2: .bytes <body>
1253cab2bb3Spatrick //===----------------------------------------------------------------------===//
1263cab2bb3Spatrick
1273cab2bb3Spatrick #include "interception.h"
1283cab2bb3Spatrick
1293cab2bb3Spatrick #if SANITIZER_WINDOWS
1303cab2bb3Spatrick #include "sanitizer_common/sanitizer_platform.h"
1313cab2bb3Spatrick #define WIN32_LEAN_AND_MEAN
1323cab2bb3Spatrick #include <windows.h>
1333cab2bb3Spatrick
1343cab2bb3Spatrick namespace __interception {
1353cab2bb3Spatrick
1363cab2bb3Spatrick static const int kAddressLength = FIRST_32_SECOND_64(4, 8);
1373cab2bb3Spatrick static const int kJumpInstructionLength = 5;
1383cab2bb3Spatrick static const int kShortJumpInstructionLength = 2;
139d89ec533Spatrick UNUSED static const int kIndirectJumpInstructionLength = 6;
1403cab2bb3Spatrick static const int kBranchLength =
1413cab2bb3Spatrick FIRST_32_SECOND_64(kJumpInstructionLength, kIndirectJumpInstructionLength);
1423cab2bb3Spatrick static const int kDirectBranchLength = kBranchLength + kAddressLength;
1433cab2bb3Spatrick
InterceptionFailed()1443cab2bb3Spatrick static void InterceptionFailed() {
1453cab2bb3Spatrick // Do we have a good way to abort with an error message here?
1463cab2bb3Spatrick __debugbreak();
1473cab2bb3Spatrick }
1483cab2bb3Spatrick
DistanceIsWithin2Gig(uptr from,uptr target)1493cab2bb3Spatrick static bool DistanceIsWithin2Gig(uptr from, uptr target) {
1503cab2bb3Spatrick #if SANITIZER_WINDOWS64
1513cab2bb3Spatrick if (from < target)
1523cab2bb3Spatrick return target - from <= (uptr)0x7FFFFFFFU;
1533cab2bb3Spatrick else
1543cab2bb3Spatrick return from - target <= (uptr)0x80000000U;
1553cab2bb3Spatrick #else
1563cab2bb3Spatrick // In a 32-bit address space, the address calculation will wrap, so this check
1573cab2bb3Spatrick // is unnecessary.
1583cab2bb3Spatrick return true;
1593cab2bb3Spatrick #endif
1603cab2bb3Spatrick }
1613cab2bb3Spatrick
GetMmapGranularity()1623cab2bb3Spatrick static uptr GetMmapGranularity() {
1633cab2bb3Spatrick SYSTEM_INFO si;
1643cab2bb3Spatrick GetSystemInfo(&si);
1653cab2bb3Spatrick return si.dwAllocationGranularity;
1663cab2bb3Spatrick }
1673cab2bb3Spatrick
RoundUpTo(uptr size,uptr boundary)168d89ec533Spatrick UNUSED static uptr RoundUpTo(uptr size, uptr boundary) {
1693cab2bb3Spatrick return (size + boundary - 1) & ~(boundary - 1);
1703cab2bb3Spatrick }
1713cab2bb3Spatrick
1723cab2bb3Spatrick // FIXME: internal_str* and internal_mem* functions should be moved from the
1733cab2bb3Spatrick // ASan sources into interception/.
1743cab2bb3Spatrick
_strlen(const char * str)1753cab2bb3Spatrick static size_t _strlen(const char *str) {
1763cab2bb3Spatrick const char* p = str;
1773cab2bb3Spatrick while (*p != '\0') ++p;
1783cab2bb3Spatrick return p - str;
1793cab2bb3Spatrick }
1803cab2bb3Spatrick
_strchr(char * str,char c)1813cab2bb3Spatrick static char* _strchr(char* str, char c) {
1823cab2bb3Spatrick while (*str) {
1833cab2bb3Spatrick if (*str == c)
1843cab2bb3Spatrick return str;
1853cab2bb3Spatrick ++str;
1863cab2bb3Spatrick }
1873cab2bb3Spatrick return nullptr;
1883cab2bb3Spatrick }
1893cab2bb3Spatrick
_memset(void * p,int value,size_t sz)1903cab2bb3Spatrick static void _memset(void *p, int value, size_t sz) {
1913cab2bb3Spatrick for (size_t i = 0; i < sz; ++i)
1923cab2bb3Spatrick ((char*)p)[i] = (char)value;
1933cab2bb3Spatrick }
1943cab2bb3Spatrick
_memcpy(void * dst,void * src,size_t sz)1953cab2bb3Spatrick static void _memcpy(void *dst, void *src, size_t sz) {
1963cab2bb3Spatrick char *dst_c = (char*)dst,
1973cab2bb3Spatrick *src_c = (char*)src;
1983cab2bb3Spatrick for (size_t i = 0; i < sz; ++i)
1993cab2bb3Spatrick dst_c[i] = src_c[i];
2003cab2bb3Spatrick }
2013cab2bb3Spatrick
ChangeMemoryProtection(uptr address,uptr size,DWORD * old_protection)2023cab2bb3Spatrick static bool ChangeMemoryProtection(
2033cab2bb3Spatrick uptr address, uptr size, DWORD *old_protection) {
2043cab2bb3Spatrick return ::VirtualProtect((void*)address, size,
2053cab2bb3Spatrick PAGE_EXECUTE_READWRITE,
2063cab2bb3Spatrick old_protection) != FALSE;
2073cab2bb3Spatrick }
2083cab2bb3Spatrick
RestoreMemoryProtection(uptr address,uptr size,DWORD old_protection)2093cab2bb3Spatrick static bool RestoreMemoryProtection(
2103cab2bb3Spatrick uptr address, uptr size, DWORD old_protection) {
2113cab2bb3Spatrick DWORD unused;
2123cab2bb3Spatrick return ::VirtualProtect((void*)address, size,
2133cab2bb3Spatrick old_protection,
2143cab2bb3Spatrick &unused) != FALSE;
2153cab2bb3Spatrick }
2163cab2bb3Spatrick
IsMemoryPadding(uptr address,uptr size)2173cab2bb3Spatrick static bool IsMemoryPadding(uptr address, uptr size) {
2183cab2bb3Spatrick u8* function = (u8*)address;
2193cab2bb3Spatrick for (size_t i = 0; i < size; ++i)
2203cab2bb3Spatrick if (function[i] != 0x90 && function[i] != 0xCC)
2213cab2bb3Spatrick return false;
2223cab2bb3Spatrick return true;
2233cab2bb3Spatrick }
2243cab2bb3Spatrick
2253cab2bb3Spatrick static const u8 kHintNop8Bytes[] = {
2263cab2bb3Spatrick 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00
2273cab2bb3Spatrick };
2283cab2bb3Spatrick
2293cab2bb3Spatrick template<class T>
FunctionHasPrefix(uptr address,const T & pattern)2303cab2bb3Spatrick static bool FunctionHasPrefix(uptr address, const T &pattern) {
2313cab2bb3Spatrick u8* function = (u8*)address - sizeof(pattern);
2323cab2bb3Spatrick for (size_t i = 0; i < sizeof(pattern); ++i)
2333cab2bb3Spatrick if (function[i] != pattern[i])
2343cab2bb3Spatrick return false;
2353cab2bb3Spatrick return true;
2363cab2bb3Spatrick }
2373cab2bb3Spatrick
FunctionHasPadding(uptr address,uptr size)2383cab2bb3Spatrick static bool FunctionHasPadding(uptr address, uptr size) {
2393cab2bb3Spatrick if (IsMemoryPadding(address - size, size))
2403cab2bb3Spatrick return true;
2413cab2bb3Spatrick if (size <= sizeof(kHintNop8Bytes) &&
2423cab2bb3Spatrick FunctionHasPrefix(address, kHintNop8Bytes))
2433cab2bb3Spatrick return true;
2443cab2bb3Spatrick return false;
2453cab2bb3Spatrick }
2463cab2bb3Spatrick
WritePadding(uptr from,uptr size)2473cab2bb3Spatrick static void WritePadding(uptr from, uptr size) {
2483cab2bb3Spatrick _memset((void*)from, 0xCC, (size_t)size);
2493cab2bb3Spatrick }
2503cab2bb3Spatrick
WriteJumpInstruction(uptr from,uptr target)2513cab2bb3Spatrick static void WriteJumpInstruction(uptr from, uptr target) {
2523cab2bb3Spatrick if (!DistanceIsWithin2Gig(from + kJumpInstructionLength, target))
2533cab2bb3Spatrick InterceptionFailed();
2543cab2bb3Spatrick ptrdiff_t offset = target - from - kJumpInstructionLength;
2553cab2bb3Spatrick *(u8*)from = 0xE9;
2563cab2bb3Spatrick *(u32*)(from + 1) = offset;
2573cab2bb3Spatrick }
2583cab2bb3Spatrick
WriteShortJumpInstruction(uptr from,uptr target)2593cab2bb3Spatrick static void WriteShortJumpInstruction(uptr from, uptr target) {
2603cab2bb3Spatrick sptr offset = target - from - kShortJumpInstructionLength;
2613cab2bb3Spatrick if (offset < -128 || offset > 127)
2623cab2bb3Spatrick InterceptionFailed();
2633cab2bb3Spatrick *(u8*)from = 0xEB;
2643cab2bb3Spatrick *(u8*)(from + 1) = (u8)offset;
2653cab2bb3Spatrick }
2663cab2bb3Spatrick
2673cab2bb3Spatrick #if SANITIZER_WINDOWS64
WriteIndirectJumpInstruction(uptr from,uptr indirect_target)2683cab2bb3Spatrick static void WriteIndirectJumpInstruction(uptr from, uptr indirect_target) {
2693cab2bb3Spatrick // jmp [rip + <offset>] = FF 25 <offset> where <offset> is a relative
2703cab2bb3Spatrick // offset.
2713cab2bb3Spatrick // The offset is the distance from then end of the jump instruction to the
2723cab2bb3Spatrick // memory location containing the targeted address. The displacement is still
2733cab2bb3Spatrick // 32-bit in x64, so indirect_target must be located within +/- 2GB range.
2743cab2bb3Spatrick int offset = indirect_target - from - kIndirectJumpInstructionLength;
2753cab2bb3Spatrick if (!DistanceIsWithin2Gig(from + kIndirectJumpInstructionLength,
2763cab2bb3Spatrick indirect_target)) {
2773cab2bb3Spatrick InterceptionFailed();
2783cab2bb3Spatrick }
2793cab2bb3Spatrick *(u16*)from = 0x25FF;
2803cab2bb3Spatrick *(u32*)(from + 2) = offset;
2813cab2bb3Spatrick }
2823cab2bb3Spatrick #endif
2833cab2bb3Spatrick
WriteBranch(uptr from,uptr indirect_target,uptr target)2843cab2bb3Spatrick static void WriteBranch(
2853cab2bb3Spatrick uptr from, uptr indirect_target, uptr target) {
2863cab2bb3Spatrick #if SANITIZER_WINDOWS64
2873cab2bb3Spatrick WriteIndirectJumpInstruction(from, indirect_target);
2883cab2bb3Spatrick *(u64*)indirect_target = target;
2893cab2bb3Spatrick #else
2903cab2bb3Spatrick (void)indirect_target;
2913cab2bb3Spatrick WriteJumpInstruction(from, target);
2923cab2bb3Spatrick #endif
2933cab2bb3Spatrick }
2943cab2bb3Spatrick
WriteDirectBranch(uptr from,uptr target)2953cab2bb3Spatrick static void WriteDirectBranch(uptr from, uptr target) {
2963cab2bb3Spatrick #if SANITIZER_WINDOWS64
2973cab2bb3Spatrick // Emit an indirect jump through immediately following bytes:
2983cab2bb3Spatrick // jmp [rip + kBranchLength]
2993cab2bb3Spatrick // .quad <target>
3003cab2bb3Spatrick WriteBranch(from, from + kBranchLength, target);
3013cab2bb3Spatrick #else
3023cab2bb3Spatrick WriteJumpInstruction(from, target);
3033cab2bb3Spatrick #endif
3043cab2bb3Spatrick }
3053cab2bb3Spatrick
3063cab2bb3Spatrick struct TrampolineMemoryRegion {
3073cab2bb3Spatrick uptr content;
3083cab2bb3Spatrick uptr allocated_size;
3093cab2bb3Spatrick uptr max_size;
3103cab2bb3Spatrick };
3113cab2bb3Spatrick
312d89ec533Spatrick UNUSED static const uptr kTrampolineScanLimitRange = 1 << 31; // 2 gig
3133cab2bb3Spatrick static const int kMaxTrampolineRegion = 1024;
3143cab2bb3Spatrick static TrampolineMemoryRegion TrampolineRegions[kMaxTrampolineRegion];
3153cab2bb3Spatrick
AllocateTrampolineRegion(uptr image_address,size_t granularity)3163cab2bb3Spatrick static void *AllocateTrampolineRegion(uptr image_address, size_t granularity) {
3173cab2bb3Spatrick #if SANITIZER_WINDOWS64
3183cab2bb3Spatrick uptr address = image_address;
3193cab2bb3Spatrick uptr scanned = 0;
3203cab2bb3Spatrick while (scanned < kTrampolineScanLimitRange) {
3213cab2bb3Spatrick MEMORY_BASIC_INFORMATION info;
3223cab2bb3Spatrick if (!::VirtualQuery((void*)address, &info, sizeof(info)))
3233cab2bb3Spatrick return nullptr;
3243cab2bb3Spatrick
3253cab2bb3Spatrick // Check whether a region can be allocated at |address|.
3263cab2bb3Spatrick if (info.State == MEM_FREE && info.RegionSize >= granularity) {
3273cab2bb3Spatrick void *page = ::VirtualAlloc((void*)RoundUpTo(address, granularity),
3283cab2bb3Spatrick granularity,
3293cab2bb3Spatrick MEM_RESERVE | MEM_COMMIT,
3303cab2bb3Spatrick PAGE_EXECUTE_READWRITE);
3313cab2bb3Spatrick return page;
3323cab2bb3Spatrick }
3333cab2bb3Spatrick
3343cab2bb3Spatrick // Move to the next region.
3353cab2bb3Spatrick address = (uptr)info.BaseAddress + info.RegionSize;
3363cab2bb3Spatrick scanned += info.RegionSize;
3373cab2bb3Spatrick }
3383cab2bb3Spatrick return nullptr;
3393cab2bb3Spatrick #else
3403cab2bb3Spatrick return ::VirtualAlloc(nullptr,
3413cab2bb3Spatrick granularity,
3423cab2bb3Spatrick MEM_RESERVE | MEM_COMMIT,
3433cab2bb3Spatrick PAGE_EXECUTE_READWRITE);
3443cab2bb3Spatrick #endif
3453cab2bb3Spatrick }
3463cab2bb3Spatrick
3473cab2bb3Spatrick // Used by unittests to release mapped memory space.
TestOnlyReleaseTrampolineRegions()3483cab2bb3Spatrick void TestOnlyReleaseTrampolineRegions() {
3493cab2bb3Spatrick for (size_t bucket = 0; bucket < kMaxTrampolineRegion; ++bucket) {
3503cab2bb3Spatrick TrampolineMemoryRegion *current = &TrampolineRegions[bucket];
3513cab2bb3Spatrick if (current->content == 0)
3523cab2bb3Spatrick return;
3533cab2bb3Spatrick ::VirtualFree((void*)current->content, 0, MEM_RELEASE);
3543cab2bb3Spatrick current->content = 0;
3553cab2bb3Spatrick }
3563cab2bb3Spatrick }
3573cab2bb3Spatrick
AllocateMemoryForTrampoline(uptr image_address,size_t size)3583cab2bb3Spatrick static uptr AllocateMemoryForTrampoline(uptr image_address, size_t size) {
3593cab2bb3Spatrick // Find a region within 2G with enough space to allocate |size| bytes.
3603cab2bb3Spatrick TrampolineMemoryRegion *region = nullptr;
3613cab2bb3Spatrick for (size_t bucket = 0; bucket < kMaxTrampolineRegion; ++bucket) {
3623cab2bb3Spatrick TrampolineMemoryRegion* current = &TrampolineRegions[bucket];
3633cab2bb3Spatrick if (current->content == 0) {
3643cab2bb3Spatrick // No valid region found, allocate a new region.
3653cab2bb3Spatrick size_t bucket_size = GetMmapGranularity();
3663cab2bb3Spatrick void *content = AllocateTrampolineRegion(image_address, bucket_size);
3673cab2bb3Spatrick if (content == nullptr)
3683cab2bb3Spatrick return 0U;
3693cab2bb3Spatrick
3703cab2bb3Spatrick current->content = (uptr)content;
3713cab2bb3Spatrick current->allocated_size = 0;
3723cab2bb3Spatrick current->max_size = bucket_size;
3733cab2bb3Spatrick region = current;
3743cab2bb3Spatrick break;
3753cab2bb3Spatrick } else if (current->max_size - current->allocated_size > size) {
3763cab2bb3Spatrick #if SANITIZER_WINDOWS64
3773cab2bb3Spatrick // In 64-bits, the memory space must be allocated within 2G boundary.
3783cab2bb3Spatrick uptr next_address = current->content + current->allocated_size;
3793cab2bb3Spatrick if (next_address < image_address ||
3803cab2bb3Spatrick next_address - image_address >= 0x7FFF0000)
3813cab2bb3Spatrick continue;
3823cab2bb3Spatrick #endif
3833cab2bb3Spatrick // The space can be allocated in the current region.
3843cab2bb3Spatrick region = current;
3853cab2bb3Spatrick break;
3863cab2bb3Spatrick }
3873cab2bb3Spatrick }
3883cab2bb3Spatrick
3893cab2bb3Spatrick // Failed to find a region.
3903cab2bb3Spatrick if (region == nullptr)
3913cab2bb3Spatrick return 0U;
3923cab2bb3Spatrick
3933cab2bb3Spatrick // Allocate the space in the current region.
3943cab2bb3Spatrick uptr allocated_space = region->content + region->allocated_size;
3953cab2bb3Spatrick region->allocated_size += size;
3963cab2bb3Spatrick WritePadding(allocated_space, size);
3973cab2bb3Spatrick
3983cab2bb3Spatrick return allocated_space;
3993cab2bb3Spatrick }
4003cab2bb3Spatrick
401*810390e3Srobert // The following prologues cannot be patched because of the short jump
402*810390e3Srobert // jumping to the patching region.
403*810390e3Srobert
404*810390e3Srobert #if SANITIZER_WINDOWS64
405*810390e3Srobert // ntdll!wcslen in Win11
406*810390e3Srobert // 488bc1 mov rax,rcx
407*810390e3Srobert // 0fb710 movzx edx,word ptr [rax]
408*810390e3Srobert // 4883c002 add rax,2
409*810390e3Srobert // 6685d2 test dx,dx
410*810390e3Srobert // 75f4 jne -12
411*810390e3Srobert static const u8 kPrologueWithShortJump1[] = {
412*810390e3Srobert 0x48, 0x8b, 0xc1, 0x0f, 0xb7, 0x10, 0x48, 0x83,
413*810390e3Srobert 0xc0, 0x02, 0x66, 0x85, 0xd2, 0x75, 0xf4,
414*810390e3Srobert };
415*810390e3Srobert
416*810390e3Srobert // ntdll!strrchr in Win11
417*810390e3Srobert // 4c8bc1 mov r8,rcx
418*810390e3Srobert // 8a01 mov al,byte ptr [rcx]
419*810390e3Srobert // 48ffc1 inc rcx
420*810390e3Srobert // 84c0 test al,al
421*810390e3Srobert // 75f7 jne -9
422*810390e3Srobert static const u8 kPrologueWithShortJump2[] = {
423*810390e3Srobert 0x4c, 0x8b, 0xc1, 0x8a, 0x01, 0x48, 0xff, 0xc1,
424*810390e3Srobert 0x84, 0xc0, 0x75, 0xf7,
425*810390e3Srobert };
426*810390e3Srobert #endif
427*810390e3Srobert
4283cab2bb3Spatrick // Returns 0 on error.
GetInstructionSize(uptr address,size_t * rel_offset=nullptr)4293cab2bb3Spatrick static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) {
430*810390e3Srobert #if SANITIZER_WINDOWS64
431*810390e3Srobert if (memcmp((u8*)address, kPrologueWithShortJump1,
432*810390e3Srobert sizeof(kPrologueWithShortJump1)) == 0 ||
433*810390e3Srobert memcmp((u8*)address, kPrologueWithShortJump2,
434*810390e3Srobert sizeof(kPrologueWithShortJump2)) == 0) {
435*810390e3Srobert return 0;
436*810390e3Srobert }
437*810390e3Srobert #endif
438*810390e3Srobert
4393cab2bb3Spatrick switch (*(u64*)address) {
4403cab2bb3Spatrick case 0x90909090909006EB: // stub: jmp over 6 x nop.
4413cab2bb3Spatrick return 8;
4423cab2bb3Spatrick }
4433cab2bb3Spatrick
4443cab2bb3Spatrick switch (*(u8*)address) {
4453cab2bb3Spatrick case 0x90: // 90 : nop
4463cab2bb3Spatrick return 1;
4473cab2bb3Spatrick
4483cab2bb3Spatrick case 0x50: // push eax / rax
4493cab2bb3Spatrick case 0x51: // push ecx / rcx
4503cab2bb3Spatrick case 0x52: // push edx / rdx
4513cab2bb3Spatrick case 0x53: // push ebx / rbx
4523cab2bb3Spatrick case 0x54: // push esp / rsp
4533cab2bb3Spatrick case 0x55: // push ebp / rbp
4543cab2bb3Spatrick case 0x56: // push esi / rsi
4553cab2bb3Spatrick case 0x57: // push edi / rdi
4563cab2bb3Spatrick case 0x5D: // pop ebp / rbp
4573cab2bb3Spatrick return 1;
4583cab2bb3Spatrick
4593cab2bb3Spatrick case 0x6A: // 6A XX = push XX
4603cab2bb3Spatrick return 2;
4613cab2bb3Spatrick
4623cab2bb3Spatrick case 0xb8: // b8 XX XX XX XX : mov eax, XX XX XX XX
4633cab2bb3Spatrick case 0xB9: // b9 XX XX XX XX : mov ecx, XX XX XX XX
4643cab2bb3Spatrick return 5;
4653cab2bb3Spatrick
4663cab2bb3Spatrick // Cannot overwrite control-instruction. Return 0 to indicate failure.
4673cab2bb3Spatrick case 0xE9: // E9 XX XX XX XX : jmp <label>
4683cab2bb3Spatrick case 0xE8: // E8 XX XX XX XX : call <func>
4693cab2bb3Spatrick case 0xC3: // C3 : ret
4703cab2bb3Spatrick case 0xEB: // EB XX : jmp XX (short jump)
4713cab2bb3Spatrick case 0x70: // 7Y YY : jy XX (short conditional jump)
4723cab2bb3Spatrick case 0x71:
4733cab2bb3Spatrick case 0x72:
4743cab2bb3Spatrick case 0x73:
4753cab2bb3Spatrick case 0x74:
4763cab2bb3Spatrick case 0x75:
4773cab2bb3Spatrick case 0x76:
4783cab2bb3Spatrick case 0x77:
4793cab2bb3Spatrick case 0x78:
4803cab2bb3Spatrick case 0x79:
4813cab2bb3Spatrick case 0x7A:
4823cab2bb3Spatrick case 0x7B:
4833cab2bb3Spatrick case 0x7C:
4843cab2bb3Spatrick case 0x7D:
4853cab2bb3Spatrick case 0x7E:
4863cab2bb3Spatrick case 0x7F:
4873cab2bb3Spatrick return 0;
4883cab2bb3Spatrick }
4893cab2bb3Spatrick
4903cab2bb3Spatrick switch (*(u16*)(address)) {
4913cab2bb3Spatrick case 0x018A: // 8A 01 : mov al, byte ptr [ecx]
4923cab2bb3Spatrick case 0xFF8B: // 8B FF : mov edi, edi
4933cab2bb3Spatrick case 0xEC8B: // 8B EC : mov ebp, esp
4943cab2bb3Spatrick case 0xc889: // 89 C8 : mov eax, ecx
4953cab2bb3Spatrick case 0xC18B: // 8B C1 : mov eax, ecx
4963cab2bb3Spatrick case 0xC033: // 33 C0 : xor eax, eax
4973cab2bb3Spatrick case 0xC933: // 33 C9 : xor ecx, ecx
4983cab2bb3Spatrick case 0xD233: // 33 D2 : xor edx, edx
4993cab2bb3Spatrick return 2;
5003cab2bb3Spatrick
5013cab2bb3Spatrick // Cannot overwrite control-instruction. Return 0 to indicate failure.
5023cab2bb3Spatrick case 0x25FF: // FF 25 XX XX XX XX : jmp [XXXXXXXX]
5033cab2bb3Spatrick return 0;
5043cab2bb3Spatrick }
5053cab2bb3Spatrick
5063cab2bb3Spatrick switch (0x00FFFFFF & *(u32*)address) {
5073cab2bb3Spatrick case 0x24A48D: // 8D A4 24 XX XX XX XX : lea esp, [esp + XX XX XX XX]
5083cab2bb3Spatrick return 7;
5093cab2bb3Spatrick }
5103cab2bb3Spatrick
5113cab2bb3Spatrick #if SANITIZER_WINDOWS64
5123cab2bb3Spatrick switch (*(u8*)address) {
5133cab2bb3Spatrick case 0xA1: // A1 XX XX XX XX XX XX XX XX :
5143cab2bb3Spatrick // movabs eax, dword ptr ds:[XXXXXXXX]
5153cab2bb3Spatrick return 9;
516*810390e3Srobert
517*810390e3Srobert case 0x83:
518*810390e3Srobert const u8 next_byte = *(u8*)(address + 1);
519*810390e3Srobert const u8 mod = next_byte >> 6;
520*810390e3Srobert const u8 rm = next_byte & 7;
521*810390e3Srobert if (mod == 1 && rm == 4)
522*810390e3Srobert return 5; // 83 ModR/M SIB Disp8 Imm8
523*810390e3Srobert // add|or|adc|sbb|and|sub|xor|cmp [r+disp8], imm8
5243cab2bb3Spatrick }
5253cab2bb3Spatrick
5263cab2bb3Spatrick switch (*(u16*)address) {
5273cab2bb3Spatrick case 0x5040: // push rax
5283cab2bb3Spatrick case 0x5140: // push rcx
5293cab2bb3Spatrick case 0x5240: // push rdx
5303cab2bb3Spatrick case 0x5340: // push rbx
5313cab2bb3Spatrick case 0x5440: // push rsp
5323cab2bb3Spatrick case 0x5540: // push rbp
5333cab2bb3Spatrick case 0x5640: // push rsi
5343cab2bb3Spatrick case 0x5740: // push rdi
5353cab2bb3Spatrick case 0x5441: // push r12
5363cab2bb3Spatrick case 0x5541: // push r13
5373cab2bb3Spatrick case 0x5641: // push r14
5383cab2bb3Spatrick case 0x5741: // push r15
5393cab2bb3Spatrick case 0x9066: // Two-byte NOP
540*810390e3Srobert case 0xc084: // test al, al
541*810390e3Srobert case 0x018a: // mov al, byte ptr [rcx]
5423cab2bb3Spatrick return 2;
5433cab2bb3Spatrick
5443cab2bb3Spatrick case 0x058B: // 8B 05 XX XX XX XX : mov eax, dword ptr [XX XX XX XX]
5453cab2bb3Spatrick if (rel_offset)
5463cab2bb3Spatrick *rel_offset = 2;
5473cab2bb3Spatrick return 6;
5483cab2bb3Spatrick }
5493cab2bb3Spatrick
5503cab2bb3Spatrick switch (0x00FFFFFF & *(u32*)address) {
5513cab2bb3Spatrick case 0xe58948: // 48 8b c4 : mov rbp, rsp
5523cab2bb3Spatrick case 0xc18b48: // 48 8b c1 : mov rax, rcx
5533cab2bb3Spatrick case 0xc48b48: // 48 8b c4 : mov rax, rsp
5543cab2bb3Spatrick case 0xd9f748: // 48 f7 d9 : neg rcx
5553cab2bb3Spatrick case 0xd12b48: // 48 2b d1 : sub rdx, rcx
5563cab2bb3Spatrick case 0x07c1f6: // f6 c1 07 : test cl, 0x7
5573cab2bb3Spatrick case 0xc98548: // 48 85 C9 : test rcx, rcx
558*810390e3Srobert case 0xd28548: // 48 85 d2 : test rdx, rdx
5593cab2bb3Spatrick case 0xc0854d: // 4d 85 c0 : test r8, r8
5603cab2bb3Spatrick case 0xc2b60f: // 0f b6 c2 : movzx eax, dl
5613cab2bb3Spatrick case 0xc03345: // 45 33 c0 : xor r8d, r8d
5623cab2bb3Spatrick case 0xc93345: // 45 33 c9 : xor r9d, r9d
5633cab2bb3Spatrick case 0xdb3345: // 45 33 DB : xor r11d, r11d
5643cab2bb3Spatrick case 0xd98b4c: // 4c 8b d9 : mov r11, rcx
5653cab2bb3Spatrick case 0xd28b4c: // 4c 8b d2 : mov r10, rdx
5663cab2bb3Spatrick case 0xc98b4c: // 4C 8B C9 : mov r9, rcx
5673cab2bb3Spatrick case 0xc18b4c: // 4C 8B C1 : mov r8, rcx
5683cab2bb3Spatrick case 0xd2b60f: // 0f b6 d2 : movzx edx, dl
5693cab2bb3Spatrick case 0xca2b48: // 48 2b ca : sub rcx, rdx
5703cab2bb3Spatrick case 0x10b70f: // 0f b7 10 : movzx edx, WORD PTR [rax]
5713cab2bb3Spatrick case 0xc00b4d: // 3d 0b c0 : or r8, r8
572*810390e3Srobert case 0xc08b41: // 41 8b c0 : mov eax, r8d
5733cab2bb3Spatrick case 0xd18b48: // 48 8b d1 : mov rdx, rcx
5743cab2bb3Spatrick case 0xdc8b4c: // 4c 8b dc : mov r11, rsp
5753cab2bb3Spatrick case 0xd18b4c: // 4c 8b d1 : mov r10, rcx
5763cab2bb3Spatrick case 0xE0E483: // 83 E4 E0 : and esp, 0xFFFFFFE0
5773cab2bb3Spatrick return 3;
5783cab2bb3Spatrick
5793cab2bb3Spatrick case 0xec8348: // 48 83 ec XX : sub rsp, XX
5803cab2bb3Spatrick case 0xf88349: // 49 83 f8 XX : cmp r8, XX
5813cab2bb3Spatrick case 0x588948: // 48 89 58 XX : mov QWORD PTR[rax + XX], rbx
5823cab2bb3Spatrick return 4;
5833cab2bb3Spatrick
5843cab2bb3Spatrick case 0xec8148: // 48 81 EC XX XX XX XX : sub rsp, XXXXXXXX
5853cab2bb3Spatrick return 7;
5863cab2bb3Spatrick
5873cab2bb3Spatrick case 0x058b48: // 48 8b 05 XX XX XX XX :
5883cab2bb3Spatrick // mov rax, QWORD PTR [rip + XXXXXXXX]
5893cab2bb3Spatrick case 0x25ff48: // 48 ff 25 XX XX XX XX :
5903cab2bb3Spatrick // rex.W jmp QWORD PTR [rip + XXXXXXXX]
5913cab2bb3Spatrick
5923cab2bb3Spatrick // Instructions having offset relative to 'rip' need offset adjustment.
5933cab2bb3Spatrick if (rel_offset)
5943cab2bb3Spatrick *rel_offset = 3;
5953cab2bb3Spatrick return 7;
5963cab2bb3Spatrick
5973cab2bb3Spatrick case 0x2444c7: // C7 44 24 XX YY YY YY YY
5983cab2bb3Spatrick // mov dword ptr [rsp + XX], YYYYYYYY
5993cab2bb3Spatrick return 8;
6003cab2bb3Spatrick }
6013cab2bb3Spatrick
6023cab2bb3Spatrick switch (*(u32*)(address)) {
6033cab2bb3Spatrick case 0x24448b48: // 48 8b 44 24 XX : mov rax, QWORD ptr [rsp + XX]
6043cab2bb3Spatrick case 0x246c8948: // 48 89 6C 24 XX : mov QWORD ptr [rsp + XX], rbp
6053cab2bb3Spatrick case 0x245c8948: // 48 89 5c 24 XX : mov QWORD PTR [rsp + XX], rbx
6063cab2bb3Spatrick case 0x24748948: // 48 89 74 24 XX : mov QWORD PTR [rsp + XX], rsi
607*810390e3Srobert case 0x247c8948: // 48 89 7c 24 XX : mov QWORD PTR [rsp + XX], rdi
6083cab2bb3Spatrick case 0x244C8948: // 48 89 4C 24 XX : mov QWORD PTR [rsp + XX], rcx
6093cab2bb3Spatrick case 0x24548948: // 48 89 54 24 XX : mov QWORD PTR [rsp + XX], rdx
6103cab2bb3Spatrick case 0x244c894c: // 4c 89 4c 24 XX : mov QWORD PTR [rsp + XX], r9
6113cab2bb3Spatrick case 0x2444894c: // 4c 89 44 24 XX : mov QWORD PTR [rsp + XX], r8
6123cab2bb3Spatrick return 5;
6133cab2bb3Spatrick case 0x24648348: // 48 83 64 24 XX : and QWORD PTR [rsp + XX], YY
6143cab2bb3Spatrick return 6;
6153cab2bb3Spatrick }
6163cab2bb3Spatrick
6173cab2bb3Spatrick #else
6183cab2bb3Spatrick
6193cab2bb3Spatrick switch (*(u8*)address) {
6203cab2bb3Spatrick case 0xA1: // A1 XX XX XX XX : mov eax, dword ptr ds:[XXXXXXXX]
6213cab2bb3Spatrick return 5;
6223cab2bb3Spatrick }
6233cab2bb3Spatrick switch (*(u16*)address) {
6243cab2bb3Spatrick case 0x458B: // 8B 45 XX : mov eax, dword ptr [ebp + XX]
6253cab2bb3Spatrick case 0x5D8B: // 8B 5D XX : mov ebx, dword ptr [ebp + XX]
6263cab2bb3Spatrick case 0x7D8B: // 8B 7D XX : mov edi, dword ptr [ebp + XX]
6273cab2bb3Spatrick case 0xEC83: // 83 EC XX : sub esp, XX
6283cab2bb3Spatrick case 0x75FF: // FF 75 XX : push dword ptr [ebp + XX]
6293cab2bb3Spatrick return 3;
6303cab2bb3Spatrick case 0xC1F7: // F7 C1 XX YY ZZ WW : test ecx, WWZZYYXX
6313cab2bb3Spatrick case 0x25FF: // FF 25 XX YY ZZ WW : jmp dword ptr ds:[WWZZYYXX]
6323cab2bb3Spatrick return 6;
6333cab2bb3Spatrick case 0x3D83: // 83 3D XX YY ZZ WW TT : cmp TT, WWZZYYXX
6343cab2bb3Spatrick return 7;
6353cab2bb3Spatrick case 0x7D83: // 83 7D XX YY : cmp dword ptr [ebp + XX], YY
6363cab2bb3Spatrick return 4;
6373cab2bb3Spatrick }
6383cab2bb3Spatrick
6393cab2bb3Spatrick switch (0x00FFFFFF & *(u32*)address) {
6403cab2bb3Spatrick case 0x24448A: // 8A 44 24 XX : mov eal, dword ptr [esp + XX]
6413cab2bb3Spatrick case 0x24448B: // 8B 44 24 XX : mov eax, dword ptr [esp + XX]
6423cab2bb3Spatrick case 0x244C8B: // 8B 4C 24 XX : mov ecx, dword ptr [esp + XX]
6433cab2bb3Spatrick case 0x24548B: // 8B 54 24 XX : mov edx, dword ptr [esp + XX]
6443cab2bb3Spatrick case 0x24748B: // 8B 74 24 XX : mov esi, dword ptr [esp + XX]
6453cab2bb3Spatrick case 0x247C8B: // 8B 7C 24 XX : mov edi, dword ptr [esp + XX]
6463cab2bb3Spatrick return 4;
6473cab2bb3Spatrick }
6483cab2bb3Spatrick
6493cab2bb3Spatrick switch (*(u32*)address) {
6503cab2bb3Spatrick case 0x2444B60F: // 0F B6 44 24 XX : movzx eax, byte ptr [esp + XX]
6513cab2bb3Spatrick return 5;
6523cab2bb3Spatrick }
6533cab2bb3Spatrick #endif
6543cab2bb3Spatrick
6553cab2bb3Spatrick // Unknown instruction!
6563cab2bb3Spatrick // FIXME: Unknown instruction failures might happen when we add a new
6573cab2bb3Spatrick // interceptor or a new compiler version. In either case, they should result
6583cab2bb3Spatrick // in visible and readable error messages. However, merely calling abort()
6593cab2bb3Spatrick // leads to an infinite recursion in CheckFailed.
6603cab2bb3Spatrick InterceptionFailed();
6613cab2bb3Spatrick return 0;
6623cab2bb3Spatrick }
6633cab2bb3Spatrick
6643cab2bb3Spatrick // Returns 0 on error.
RoundUpToInstrBoundary(size_t size,uptr address)6653cab2bb3Spatrick static size_t RoundUpToInstrBoundary(size_t size, uptr address) {
6663cab2bb3Spatrick size_t cursor = 0;
6673cab2bb3Spatrick while (cursor < size) {
6683cab2bb3Spatrick size_t instruction_size = GetInstructionSize(address + cursor);
6693cab2bb3Spatrick if (!instruction_size)
6703cab2bb3Spatrick return 0;
6713cab2bb3Spatrick cursor += instruction_size;
6723cab2bb3Spatrick }
6733cab2bb3Spatrick return cursor;
6743cab2bb3Spatrick }
6753cab2bb3Spatrick
CopyInstructions(uptr to,uptr from,size_t size)6763cab2bb3Spatrick static bool CopyInstructions(uptr to, uptr from, size_t size) {
6773cab2bb3Spatrick size_t cursor = 0;
6783cab2bb3Spatrick while (cursor != size) {
6793cab2bb3Spatrick size_t rel_offset = 0;
6803cab2bb3Spatrick size_t instruction_size = GetInstructionSize(from + cursor, &rel_offset);
6813cab2bb3Spatrick _memcpy((void*)(to + cursor), (void*)(from + cursor),
6823cab2bb3Spatrick (size_t)instruction_size);
6833cab2bb3Spatrick if (rel_offset) {
6843cab2bb3Spatrick uptr delta = to - from;
6853cab2bb3Spatrick uptr relocated_offset = *(u32*)(to + cursor + rel_offset) - delta;
6863cab2bb3Spatrick #if SANITIZER_WINDOWS64
6873cab2bb3Spatrick if (relocated_offset + 0x80000000U >= 0xFFFFFFFFU)
6883cab2bb3Spatrick return false;
6893cab2bb3Spatrick #endif
6903cab2bb3Spatrick *(u32*)(to + cursor + rel_offset) = relocated_offset;
6913cab2bb3Spatrick }
6923cab2bb3Spatrick cursor += instruction_size;
6933cab2bb3Spatrick }
6943cab2bb3Spatrick return true;
6953cab2bb3Spatrick }
6963cab2bb3Spatrick
6973cab2bb3Spatrick
6983cab2bb3Spatrick #if !SANITIZER_WINDOWS64
OverrideFunctionWithDetour(uptr old_func,uptr new_func,uptr * orig_old_func)6993cab2bb3Spatrick bool OverrideFunctionWithDetour(
7003cab2bb3Spatrick uptr old_func, uptr new_func, uptr *orig_old_func) {
7013cab2bb3Spatrick const int kDetourHeaderLen = 5;
7023cab2bb3Spatrick const u16 kDetourInstruction = 0xFF8B;
7033cab2bb3Spatrick
7043cab2bb3Spatrick uptr header = (uptr)old_func - kDetourHeaderLen;
7053cab2bb3Spatrick uptr patch_length = kDetourHeaderLen + kShortJumpInstructionLength;
7063cab2bb3Spatrick
7073cab2bb3Spatrick // Validate that the function is hookable.
7083cab2bb3Spatrick if (*(u16*)old_func != kDetourInstruction ||
7093cab2bb3Spatrick !IsMemoryPadding(header, kDetourHeaderLen))
7103cab2bb3Spatrick return false;
7113cab2bb3Spatrick
7123cab2bb3Spatrick // Change memory protection to writable.
7133cab2bb3Spatrick DWORD protection = 0;
7143cab2bb3Spatrick if (!ChangeMemoryProtection(header, patch_length, &protection))
7153cab2bb3Spatrick return false;
7163cab2bb3Spatrick
7173cab2bb3Spatrick // Write a relative jump to the redirected function.
7183cab2bb3Spatrick WriteJumpInstruction(header, new_func);
7193cab2bb3Spatrick
7203cab2bb3Spatrick // Write the short jump to the function prefix.
7213cab2bb3Spatrick WriteShortJumpInstruction(old_func, header);
7223cab2bb3Spatrick
7233cab2bb3Spatrick // Restore previous memory protection.
7243cab2bb3Spatrick if (!RestoreMemoryProtection(header, patch_length, protection))
7253cab2bb3Spatrick return false;
7263cab2bb3Spatrick
7273cab2bb3Spatrick if (orig_old_func)
7283cab2bb3Spatrick *orig_old_func = old_func + kShortJumpInstructionLength;
7293cab2bb3Spatrick
7303cab2bb3Spatrick return true;
7313cab2bb3Spatrick }
7323cab2bb3Spatrick #endif
7333cab2bb3Spatrick
OverrideFunctionWithRedirectJump(uptr old_func,uptr new_func,uptr * orig_old_func)7343cab2bb3Spatrick bool OverrideFunctionWithRedirectJump(
7353cab2bb3Spatrick uptr old_func, uptr new_func, uptr *orig_old_func) {
7363cab2bb3Spatrick // Check whether the first instruction is a relative jump.
7373cab2bb3Spatrick if (*(u8*)old_func != 0xE9)
7383cab2bb3Spatrick return false;
7393cab2bb3Spatrick
7403cab2bb3Spatrick if (orig_old_func) {
741*810390e3Srobert sptr relative_offset = *(s32 *)(old_func + 1);
7423cab2bb3Spatrick uptr absolute_target = old_func + relative_offset + kJumpInstructionLength;
7433cab2bb3Spatrick *orig_old_func = absolute_target;
7443cab2bb3Spatrick }
7453cab2bb3Spatrick
7463cab2bb3Spatrick #if SANITIZER_WINDOWS64
7473cab2bb3Spatrick // If needed, get memory space for a trampoline jump.
7483cab2bb3Spatrick uptr trampoline = AllocateMemoryForTrampoline(old_func, kDirectBranchLength);
7493cab2bb3Spatrick if (!trampoline)
7503cab2bb3Spatrick return false;
7513cab2bb3Spatrick WriteDirectBranch(trampoline, new_func);
7523cab2bb3Spatrick #endif
7533cab2bb3Spatrick
7543cab2bb3Spatrick // Change memory protection to writable.
7553cab2bb3Spatrick DWORD protection = 0;
7563cab2bb3Spatrick if (!ChangeMemoryProtection(old_func, kJumpInstructionLength, &protection))
7573cab2bb3Spatrick return false;
7583cab2bb3Spatrick
7593cab2bb3Spatrick // Write a relative jump to the redirected function.
7603cab2bb3Spatrick WriteJumpInstruction(old_func, FIRST_32_SECOND_64(new_func, trampoline));
7613cab2bb3Spatrick
7623cab2bb3Spatrick // Restore previous memory protection.
7633cab2bb3Spatrick if (!RestoreMemoryProtection(old_func, kJumpInstructionLength, protection))
7643cab2bb3Spatrick return false;
7653cab2bb3Spatrick
7663cab2bb3Spatrick return true;
7673cab2bb3Spatrick }
7683cab2bb3Spatrick
OverrideFunctionWithHotPatch(uptr old_func,uptr new_func,uptr * orig_old_func)7693cab2bb3Spatrick bool OverrideFunctionWithHotPatch(
7703cab2bb3Spatrick uptr old_func, uptr new_func, uptr *orig_old_func) {
7713cab2bb3Spatrick const int kHotPatchHeaderLen = kBranchLength;
7723cab2bb3Spatrick
7733cab2bb3Spatrick uptr header = (uptr)old_func - kHotPatchHeaderLen;
7743cab2bb3Spatrick uptr patch_length = kHotPatchHeaderLen + kShortJumpInstructionLength;
7753cab2bb3Spatrick
7763cab2bb3Spatrick // Validate that the function is hot patchable.
7773cab2bb3Spatrick size_t instruction_size = GetInstructionSize(old_func);
7783cab2bb3Spatrick if (instruction_size < kShortJumpInstructionLength ||
7793cab2bb3Spatrick !FunctionHasPadding(old_func, kHotPatchHeaderLen))
7803cab2bb3Spatrick return false;
7813cab2bb3Spatrick
7823cab2bb3Spatrick if (orig_old_func) {
7833cab2bb3Spatrick // Put the needed instructions into the trampoline bytes.
7843cab2bb3Spatrick uptr trampoline_length = instruction_size + kDirectBranchLength;
7853cab2bb3Spatrick uptr trampoline = AllocateMemoryForTrampoline(old_func, trampoline_length);
7863cab2bb3Spatrick if (!trampoline)
7873cab2bb3Spatrick return false;
7883cab2bb3Spatrick if (!CopyInstructions(trampoline, old_func, instruction_size))
7893cab2bb3Spatrick return false;
7903cab2bb3Spatrick WriteDirectBranch(trampoline + instruction_size,
7913cab2bb3Spatrick old_func + instruction_size);
7923cab2bb3Spatrick *orig_old_func = trampoline;
7933cab2bb3Spatrick }
7943cab2bb3Spatrick
7953cab2bb3Spatrick // If needed, get memory space for indirect address.
7963cab2bb3Spatrick uptr indirect_address = 0;
7973cab2bb3Spatrick #if SANITIZER_WINDOWS64
7983cab2bb3Spatrick indirect_address = AllocateMemoryForTrampoline(old_func, kAddressLength);
7993cab2bb3Spatrick if (!indirect_address)
8003cab2bb3Spatrick return false;
8013cab2bb3Spatrick #endif
8023cab2bb3Spatrick
8033cab2bb3Spatrick // Change memory protection to writable.
8043cab2bb3Spatrick DWORD protection = 0;
8053cab2bb3Spatrick if (!ChangeMemoryProtection(header, patch_length, &protection))
8063cab2bb3Spatrick return false;
8073cab2bb3Spatrick
8083cab2bb3Spatrick // Write jumps to the redirected function.
8093cab2bb3Spatrick WriteBranch(header, indirect_address, new_func);
8103cab2bb3Spatrick WriteShortJumpInstruction(old_func, header);
8113cab2bb3Spatrick
8123cab2bb3Spatrick // Restore previous memory protection.
8133cab2bb3Spatrick if (!RestoreMemoryProtection(header, patch_length, protection))
8143cab2bb3Spatrick return false;
8153cab2bb3Spatrick
8163cab2bb3Spatrick return true;
8173cab2bb3Spatrick }
8183cab2bb3Spatrick
OverrideFunctionWithTrampoline(uptr old_func,uptr new_func,uptr * orig_old_func)8193cab2bb3Spatrick bool OverrideFunctionWithTrampoline(
8203cab2bb3Spatrick uptr old_func, uptr new_func, uptr *orig_old_func) {
8213cab2bb3Spatrick
8223cab2bb3Spatrick size_t instructions_length = kBranchLength;
8233cab2bb3Spatrick size_t padding_length = 0;
8243cab2bb3Spatrick uptr indirect_address = 0;
8253cab2bb3Spatrick
8263cab2bb3Spatrick if (orig_old_func) {
8273cab2bb3Spatrick // Find out the number of bytes of the instructions we need to copy
8283cab2bb3Spatrick // to the trampoline.
8293cab2bb3Spatrick instructions_length = RoundUpToInstrBoundary(kBranchLength, old_func);
8303cab2bb3Spatrick if (!instructions_length)
8313cab2bb3Spatrick return false;
8323cab2bb3Spatrick
8333cab2bb3Spatrick // Put the needed instructions into the trampoline bytes.
8343cab2bb3Spatrick uptr trampoline_length = instructions_length + kDirectBranchLength;
8353cab2bb3Spatrick uptr trampoline = AllocateMemoryForTrampoline(old_func, trampoline_length);
8363cab2bb3Spatrick if (!trampoline)
8373cab2bb3Spatrick return false;
8383cab2bb3Spatrick if (!CopyInstructions(trampoline, old_func, instructions_length))
8393cab2bb3Spatrick return false;
8403cab2bb3Spatrick WriteDirectBranch(trampoline + instructions_length,
8413cab2bb3Spatrick old_func + instructions_length);
8423cab2bb3Spatrick *orig_old_func = trampoline;
8433cab2bb3Spatrick }
8443cab2bb3Spatrick
8453cab2bb3Spatrick #if SANITIZER_WINDOWS64
8463cab2bb3Spatrick // Check if the targeted address can be encoded in the function padding.
8473cab2bb3Spatrick // Otherwise, allocate it in the trampoline region.
8483cab2bb3Spatrick if (IsMemoryPadding(old_func - kAddressLength, kAddressLength)) {
8493cab2bb3Spatrick indirect_address = old_func - kAddressLength;
8503cab2bb3Spatrick padding_length = kAddressLength;
8513cab2bb3Spatrick } else {
8523cab2bb3Spatrick indirect_address = AllocateMemoryForTrampoline(old_func, kAddressLength);
8533cab2bb3Spatrick if (!indirect_address)
8543cab2bb3Spatrick return false;
8553cab2bb3Spatrick }
8563cab2bb3Spatrick #endif
8573cab2bb3Spatrick
8583cab2bb3Spatrick // Change memory protection to writable.
8593cab2bb3Spatrick uptr patch_address = old_func - padding_length;
8603cab2bb3Spatrick uptr patch_length = instructions_length + padding_length;
8613cab2bb3Spatrick DWORD protection = 0;
8623cab2bb3Spatrick if (!ChangeMemoryProtection(patch_address, patch_length, &protection))
8633cab2bb3Spatrick return false;
8643cab2bb3Spatrick
8653cab2bb3Spatrick // Patch the original function.
8663cab2bb3Spatrick WriteBranch(old_func, indirect_address, new_func);
8673cab2bb3Spatrick
8683cab2bb3Spatrick // Restore previous memory protection.
8693cab2bb3Spatrick if (!RestoreMemoryProtection(patch_address, patch_length, protection))
8703cab2bb3Spatrick return false;
8713cab2bb3Spatrick
8723cab2bb3Spatrick return true;
8733cab2bb3Spatrick }
8743cab2bb3Spatrick
OverrideFunction(uptr old_func,uptr new_func,uptr * orig_old_func)8753cab2bb3Spatrick bool OverrideFunction(
8763cab2bb3Spatrick uptr old_func, uptr new_func, uptr *orig_old_func) {
8773cab2bb3Spatrick #if !SANITIZER_WINDOWS64
8783cab2bb3Spatrick if (OverrideFunctionWithDetour(old_func, new_func, orig_old_func))
8793cab2bb3Spatrick return true;
8803cab2bb3Spatrick #endif
8813cab2bb3Spatrick if (OverrideFunctionWithRedirectJump(old_func, new_func, orig_old_func))
8823cab2bb3Spatrick return true;
8833cab2bb3Spatrick if (OverrideFunctionWithHotPatch(old_func, new_func, orig_old_func))
8843cab2bb3Spatrick return true;
8853cab2bb3Spatrick if (OverrideFunctionWithTrampoline(old_func, new_func, orig_old_func))
8863cab2bb3Spatrick return true;
8873cab2bb3Spatrick return false;
8883cab2bb3Spatrick }
8893cab2bb3Spatrick
InterestingDLLsAvailable()8903cab2bb3Spatrick static void **InterestingDLLsAvailable() {
8913cab2bb3Spatrick static const char *InterestingDLLs[] = {
8923cab2bb3Spatrick "kernel32.dll",
8933cab2bb3Spatrick "msvcr100.dll", // VS2010
8943cab2bb3Spatrick "msvcr110.dll", // VS2012
8953cab2bb3Spatrick "msvcr120.dll", // VS2013
8963cab2bb3Spatrick "vcruntime140.dll", // VS2015
8973cab2bb3Spatrick "ucrtbase.dll", // Universal CRT
8983cab2bb3Spatrick // NTDLL should go last as it exports some functions that we should
8993cab2bb3Spatrick // override in the CRT [presumably only used internally].
9003cab2bb3Spatrick "ntdll.dll", NULL};
9013cab2bb3Spatrick static void *result[ARRAY_SIZE(InterestingDLLs)] = { 0 };
9023cab2bb3Spatrick if (!result[0]) {
9033cab2bb3Spatrick for (size_t i = 0, j = 0; InterestingDLLs[i]; ++i) {
9043cab2bb3Spatrick if (HMODULE h = GetModuleHandleA(InterestingDLLs[i]))
9053cab2bb3Spatrick result[j++] = (void *)h;
9063cab2bb3Spatrick }
9073cab2bb3Spatrick }
9083cab2bb3Spatrick return &result[0];
9093cab2bb3Spatrick }
9103cab2bb3Spatrick
9113cab2bb3Spatrick namespace {
9123cab2bb3Spatrick // Utility for reading loaded PE images.
9133cab2bb3Spatrick template <typename T> class RVAPtr {
9143cab2bb3Spatrick public:
RVAPtr(void * module,uptr rva)9153cab2bb3Spatrick RVAPtr(void *module, uptr rva)
9163cab2bb3Spatrick : ptr_(reinterpret_cast<T *>(reinterpret_cast<char *>(module) + rva)) {}
operator T*()9173cab2bb3Spatrick operator T *() { return ptr_; }
operator ->()9183cab2bb3Spatrick T *operator->() { return ptr_; }
operator ++()9193cab2bb3Spatrick T *operator++() { return ++ptr_; }
9203cab2bb3Spatrick
9213cab2bb3Spatrick private:
9223cab2bb3Spatrick T *ptr_;
9233cab2bb3Spatrick };
9243cab2bb3Spatrick } // namespace
9253cab2bb3Spatrick
9263cab2bb3Spatrick // Internal implementation of GetProcAddress. At least since Windows 8,
9273cab2bb3Spatrick // GetProcAddress appears to initialize DLLs before returning function pointers
9283cab2bb3Spatrick // into them. This is problematic for the sanitizers, because they typically
9293cab2bb3Spatrick // want to intercept malloc *before* MSVCRT initializes. Our internal
9303cab2bb3Spatrick // implementation walks the export list manually without doing initialization.
InternalGetProcAddress(void * module,const char * func_name)9313cab2bb3Spatrick uptr InternalGetProcAddress(void *module, const char *func_name) {
9323cab2bb3Spatrick // Check that the module header is full and present.
9333cab2bb3Spatrick RVAPtr<IMAGE_DOS_HEADER> dos_stub(module, 0);
9343cab2bb3Spatrick RVAPtr<IMAGE_NT_HEADERS> headers(module, dos_stub->e_lfanew);
9353cab2bb3Spatrick if (!module || dos_stub->e_magic != IMAGE_DOS_SIGNATURE || // "MZ"
9363cab2bb3Spatrick headers->Signature != IMAGE_NT_SIGNATURE || // "PE\0\0"
9373cab2bb3Spatrick headers->FileHeader.SizeOfOptionalHeader <
9383cab2bb3Spatrick sizeof(IMAGE_OPTIONAL_HEADER)) {
9393cab2bb3Spatrick return 0;
9403cab2bb3Spatrick }
9413cab2bb3Spatrick
9423cab2bb3Spatrick IMAGE_DATA_DIRECTORY *export_directory =
9433cab2bb3Spatrick &headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
9443cab2bb3Spatrick if (export_directory->Size == 0)
9453cab2bb3Spatrick return 0;
9463cab2bb3Spatrick RVAPtr<IMAGE_EXPORT_DIRECTORY> exports(module,
9473cab2bb3Spatrick export_directory->VirtualAddress);
9483cab2bb3Spatrick RVAPtr<DWORD> functions(module, exports->AddressOfFunctions);
9493cab2bb3Spatrick RVAPtr<DWORD> names(module, exports->AddressOfNames);
9503cab2bb3Spatrick RVAPtr<WORD> ordinals(module, exports->AddressOfNameOrdinals);
9513cab2bb3Spatrick
9523cab2bb3Spatrick for (DWORD i = 0; i < exports->NumberOfNames; i++) {
9533cab2bb3Spatrick RVAPtr<char> name(module, names[i]);
9543cab2bb3Spatrick if (!strcmp(func_name, name)) {
9553cab2bb3Spatrick DWORD index = ordinals[i];
9563cab2bb3Spatrick RVAPtr<char> func(module, functions[index]);
9573cab2bb3Spatrick
9583cab2bb3Spatrick // Handle forwarded functions.
9593cab2bb3Spatrick DWORD offset = functions[index];
9603cab2bb3Spatrick if (offset >= export_directory->VirtualAddress &&
9613cab2bb3Spatrick offset < export_directory->VirtualAddress + export_directory->Size) {
9623cab2bb3Spatrick // An entry for a forwarded function is a string with the following
9633cab2bb3Spatrick // format: "<module> . <function_name>" that is stored into the
9643cab2bb3Spatrick // exported directory.
9653cab2bb3Spatrick char function_name[256];
9663cab2bb3Spatrick size_t funtion_name_length = _strlen(func);
9673cab2bb3Spatrick if (funtion_name_length >= sizeof(function_name) - 1)
9683cab2bb3Spatrick InterceptionFailed();
9693cab2bb3Spatrick
9703cab2bb3Spatrick _memcpy(function_name, func, funtion_name_length);
9713cab2bb3Spatrick function_name[funtion_name_length] = '\0';
9723cab2bb3Spatrick char* separator = _strchr(function_name, '.');
9733cab2bb3Spatrick if (!separator)
9743cab2bb3Spatrick InterceptionFailed();
9753cab2bb3Spatrick *separator = '\0';
9763cab2bb3Spatrick
9773cab2bb3Spatrick void* redirected_module = GetModuleHandleA(function_name);
9783cab2bb3Spatrick if (!redirected_module)
9793cab2bb3Spatrick InterceptionFailed();
9803cab2bb3Spatrick return InternalGetProcAddress(redirected_module, separator + 1);
9813cab2bb3Spatrick }
9823cab2bb3Spatrick
9833cab2bb3Spatrick return (uptr)(char *)func;
9843cab2bb3Spatrick }
9853cab2bb3Spatrick }
9863cab2bb3Spatrick
9873cab2bb3Spatrick return 0;
9883cab2bb3Spatrick }
9893cab2bb3Spatrick
OverrideFunction(const char * func_name,uptr new_func,uptr * orig_old_func)9903cab2bb3Spatrick bool OverrideFunction(
9913cab2bb3Spatrick const char *func_name, uptr new_func, uptr *orig_old_func) {
9923cab2bb3Spatrick bool hooked = false;
9933cab2bb3Spatrick void **DLLs = InterestingDLLsAvailable();
9943cab2bb3Spatrick for (size_t i = 0; DLLs[i]; ++i) {
9953cab2bb3Spatrick uptr func_addr = InternalGetProcAddress(DLLs[i], func_name);
9963cab2bb3Spatrick if (func_addr &&
9973cab2bb3Spatrick OverrideFunction(func_addr, new_func, orig_old_func)) {
9983cab2bb3Spatrick hooked = true;
9993cab2bb3Spatrick }
10003cab2bb3Spatrick }
10013cab2bb3Spatrick return hooked;
10023cab2bb3Spatrick }
10033cab2bb3Spatrick
OverrideImportedFunction(const char * module_to_patch,const char * imported_module,const char * function_name,uptr new_function,uptr * orig_old_func)10043cab2bb3Spatrick bool OverrideImportedFunction(const char *module_to_patch,
10053cab2bb3Spatrick const char *imported_module,
10063cab2bb3Spatrick const char *function_name, uptr new_function,
10073cab2bb3Spatrick uptr *orig_old_func) {
10083cab2bb3Spatrick HMODULE module = GetModuleHandleA(module_to_patch);
10093cab2bb3Spatrick if (!module)
10103cab2bb3Spatrick return false;
10113cab2bb3Spatrick
10123cab2bb3Spatrick // Check that the module header is full and present.
10133cab2bb3Spatrick RVAPtr<IMAGE_DOS_HEADER> dos_stub(module, 0);
10143cab2bb3Spatrick RVAPtr<IMAGE_NT_HEADERS> headers(module, dos_stub->e_lfanew);
10153cab2bb3Spatrick if (!module || dos_stub->e_magic != IMAGE_DOS_SIGNATURE || // "MZ"
10163cab2bb3Spatrick headers->Signature != IMAGE_NT_SIGNATURE || // "PE\0\0"
10173cab2bb3Spatrick headers->FileHeader.SizeOfOptionalHeader <
10183cab2bb3Spatrick sizeof(IMAGE_OPTIONAL_HEADER)) {
10193cab2bb3Spatrick return false;
10203cab2bb3Spatrick }
10213cab2bb3Spatrick
10223cab2bb3Spatrick IMAGE_DATA_DIRECTORY *import_directory =
10233cab2bb3Spatrick &headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
10243cab2bb3Spatrick
10253cab2bb3Spatrick // Iterate the list of imported DLLs. FirstThunk will be null for the last
10263cab2bb3Spatrick // entry.
10273cab2bb3Spatrick RVAPtr<IMAGE_IMPORT_DESCRIPTOR> imports(module,
10283cab2bb3Spatrick import_directory->VirtualAddress);
10293cab2bb3Spatrick for (; imports->FirstThunk != 0; ++imports) {
10303cab2bb3Spatrick RVAPtr<const char> modname(module, imports->Name);
10313cab2bb3Spatrick if (_stricmp(&*modname, imported_module) == 0)
10323cab2bb3Spatrick break;
10333cab2bb3Spatrick }
10343cab2bb3Spatrick if (imports->FirstThunk == 0)
10353cab2bb3Spatrick return false;
10363cab2bb3Spatrick
10373cab2bb3Spatrick // We have two parallel arrays: the import address table (IAT) and the table
10383cab2bb3Spatrick // of names. They start out containing the same data, but the loader rewrites
10393cab2bb3Spatrick // the IAT to hold imported addresses and leaves the name table in
10403cab2bb3Spatrick // OriginalFirstThunk alone.
10413cab2bb3Spatrick RVAPtr<IMAGE_THUNK_DATA> name_table(module, imports->OriginalFirstThunk);
10423cab2bb3Spatrick RVAPtr<IMAGE_THUNK_DATA> iat(module, imports->FirstThunk);
10433cab2bb3Spatrick for (; name_table->u1.Ordinal != 0; ++name_table, ++iat) {
10443cab2bb3Spatrick if (!IMAGE_SNAP_BY_ORDINAL(name_table->u1.Ordinal)) {
10453cab2bb3Spatrick RVAPtr<IMAGE_IMPORT_BY_NAME> import_by_name(
10463cab2bb3Spatrick module, name_table->u1.ForwarderString);
10473cab2bb3Spatrick const char *funcname = &import_by_name->Name[0];
10483cab2bb3Spatrick if (strcmp(funcname, function_name) == 0)
10493cab2bb3Spatrick break;
10503cab2bb3Spatrick }
10513cab2bb3Spatrick }
10523cab2bb3Spatrick if (name_table->u1.Ordinal == 0)
10533cab2bb3Spatrick return false;
10543cab2bb3Spatrick
10553cab2bb3Spatrick // Now we have the correct IAT entry. Do the swap. We have to make the page
10563cab2bb3Spatrick // read/write first.
10573cab2bb3Spatrick if (orig_old_func)
10583cab2bb3Spatrick *orig_old_func = iat->u1.AddressOfData;
10593cab2bb3Spatrick DWORD old_prot, unused_prot;
10603cab2bb3Spatrick if (!VirtualProtect(&iat->u1.AddressOfData, 4, PAGE_EXECUTE_READWRITE,
10613cab2bb3Spatrick &old_prot))
10623cab2bb3Spatrick return false;
10633cab2bb3Spatrick iat->u1.AddressOfData = new_function;
10643cab2bb3Spatrick if (!VirtualProtect(&iat->u1.AddressOfData, 4, old_prot, &unused_prot))
10653cab2bb3Spatrick return false; // Not clear if this failure bothers us.
10663cab2bb3Spatrick return true;
10673cab2bb3Spatrick }
10683cab2bb3Spatrick
10693cab2bb3Spatrick } // namespace __interception
10703cab2bb3Spatrick
1071*810390e3Srobert #endif // SANITIZER_APPLE
1072