libtsan: Update to LLVM 20.

This commit is contained in:
Alex Rønne Petersen
2025-02-05 13:36:56 +01:00
parent d67d52abe5
commit e99c11856d
83 changed files with 3246 additions and 2051 deletions

View File

@@ -27,7 +27,7 @@
//
// 1) Detour
//
// The Detour hooking technique is assuming the presence of an header with
// The Detour hooking technique is assuming the presence of a header with
// padding and an overridable 2-bytes nop instruction (mov edi, edi). The
// nop instruction can safely be replaced by a 2-bytes jump without any need
// to save the instruction. A jump to the target is encoded in the function
@@ -47,7 +47,7 @@
//
// func: jmp <label> --> func: jmp <hook>
//
// On an 64-bit architecture, a trampoline is inserted.
// On a 64-bit architecture, a trampoline is inserted.
//
// func: jmp <label> --> func: jmp <tramp>
// [...]
@@ -60,7 +60,7 @@
//
// 3) HotPatch
//
// The HotPatch hooking is assuming the presence of an header with padding
// The HotPatch hooking is assuming the presence of a header with padding
// and a first instruction with at least 2-bytes.
//
// The reason to enforce the 2-bytes limitation is to provide the minimal
@@ -80,7 +80,7 @@
// real: <instr>
// jmp <body>
//
// On an 64-bit architecture:
// On a 64-bit architecture:
//
// head: 6 x nop head: jmp QWORD [addr1]
// func: <instr> --> func: jmp short <head>
@@ -110,7 +110,7 @@
// <instr>
// jmp <body>
//
// On an 64-bit architecture:
// On a 64-bit architecture:
//
// func: <instr> --> func: jmp QWORD [addr1]
// <instr>
@@ -130,6 +130,7 @@
#include "sanitizer_common/sanitizer_platform.h"
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <psapi.h>
namespace __interception {
@@ -186,8 +187,12 @@ static uptr GetMmapGranularity() {
return si.dwAllocationGranularity;
}
UNUSED static uptr RoundDownTo(uptr size, uptr boundary) {
return size & ~(boundary - 1);
}
UNUSED static uptr RoundUpTo(uptr size, uptr boundary) {
return (size + boundary - 1) & ~(boundary - 1);
return RoundDownTo(size + boundary - 1, boundary);
}
// FIXME: internal_str* and internal_mem* functions should be moved from the
@@ -208,6 +213,18 @@ static char* _strchr(char* str, char c) {
return nullptr;
}
static int _strcmp(const char *s1, const char *s2) {
while (true) {
unsigned c1 = *s1;
unsigned c2 = *s2;
if (c1 != c2) return (c1 < c2) ? -1 : 1;
if (c1 == 0) break;
s1++;
s2++;
}
return 0;
}
static void _memset(void *p, int value, size_t sz) {
for (size_t i = 0; i < sz; ++i)
((char*)p)[i] = (char)value;
@@ -284,8 +301,11 @@ static void WriteJumpInstruction(uptr from, uptr target) {
static void WriteShortJumpInstruction(uptr from, uptr target) {
sptr offset = target - from - kShortJumpInstructionLength;
if (offset < -128 || offset > 127)
if (offset < -128 || offset > 127) {
ReportError("interception_win: cannot write short jmp from %p to %p\n",
(void *)from, (void *)target);
InterceptionFailed();
}
*(u8*)from = 0xEB;
*(u8*)(from + 1) = (u8)offset;
}
@@ -339,32 +359,78 @@ struct TrampolineMemoryRegion {
uptr max_size;
};
UNUSED static const uptr kTrampolineScanLimitRange = 1ull << 31; // 2 gig
UNUSED static const uptr kTrampolineRangeLimit = 1ull << 31; // 2 gig
static const int kMaxTrampolineRegion = 1024;
static TrampolineMemoryRegion TrampolineRegions[kMaxTrampolineRegion];
static void *AllocateTrampolineRegion(uptr image_address, size_t granularity) {
#if SANITIZER_WINDOWS64
uptr address = image_address;
uptr scanned = 0;
while (scanned < kTrampolineScanLimitRange) {
MEMORY_BASIC_INFORMATION info;
if (!::VirtualQuery((void*)address, &info, sizeof(info)))
return nullptr;
static void *AllocateTrampolineRegion(uptr min_addr, uptr max_addr,
uptr func_addr, size_t granularity) {
# if SANITIZER_WINDOWS64
// Clamp {min,max}_addr to the accessible address space.
SYSTEM_INFO system_info;
::GetSystemInfo(&system_info);
uptr min_virtual_addr =
RoundUpTo((uptr)system_info.lpMinimumApplicationAddress, granularity);
uptr max_virtual_addr =
RoundDownTo((uptr)system_info.lpMaximumApplicationAddress, granularity);
if (min_addr < min_virtual_addr)
min_addr = min_virtual_addr;
if (max_addr > max_virtual_addr)
max_addr = max_virtual_addr;
// Check whether a region can be allocated at |address|.
// This loop probes the virtual address space to find free memory in the
// [min_addr, max_addr] interval. The search starts from func_addr and
// proceeds "outwards" towards the interval bounds using two probes, lo_addr
// and hi_addr, for addresses lower/higher than func_addr. At each step, it
// considers the probe closest to func_addr. If that address is not free, the
// probe is advanced (lower or higher depending on the probe) to the next
// memory block and the search continues.
uptr lo_addr = RoundDownTo(func_addr, granularity);
uptr hi_addr = RoundUpTo(func_addr, granularity);
while (lo_addr >= min_addr || hi_addr <= max_addr) {
// Consider the in-range address closest to func_addr.
uptr addr;
if (lo_addr < min_addr)
addr = hi_addr;
else if (hi_addr > max_addr)
addr = lo_addr;
else
addr = (hi_addr - func_addr < func_addr - lo_addr) ? hi_addr : lo_addr;
MEMORY_BASIC_INFORMATION info;
if (!::VirtualQuery((void *)addr, &info, sizeof(info))) {
ReportError(
"interception_win: VirtualQuery in AllocateTrampolineRegion failed "
"for %p\n",
(void *)addr);
return nullptr;
}
// Check whether a region can be allocated at |addr|.
if (info.State == MEM_FREE && info.RegionSize >= granularity) {
void *page = ::VirtualAlloc((void*)RoundUpTo(address, granularity),
granularity,
MEM_RESERVE | MEM_COMMIT,
PAGE_EXECUTE_READWRITE);
void *page =
::VirtualAlloc((void *)addr, granularity, MEM_RESERVE | MEM_COMMIT,
PAGE_EXECUTE_READWRITE);
if (page == nullptr)
ReportError(
"interception_win: VirtualAlloc in AllocateTrampolineRegion failed "
"for %p\n",
(void *)addr);
return page;
}
// Move to the next region.
address = (uptr)info.BaseAddress + info.RegionSize;
scanned += info.RegionSize;
if (addr == lo_addr)
lo_addr =
RoundDownTo((uptr)info.AllocationBase - granularity, granularity);
if (addr == hi_addr)
hi_addr =
RoundUpTo((uptr)info.BaseAddress + info.RegionSize, granularity);
}
ReportError(
"interception_win: AllocateTrampolineRegion failed to find free memory; "
"min_addr: %p, max_addr: %p, func_addr: %p, granularity: %zu\n",
(void *)min_addr, (void *)max_addr, (void *)func_addr, granularity);
return nullptr;
#else
return ::VirtualAlloc(nullptr,
@@ -385,15 +451,51 @@ void TestOnlyReleaseTrampolineRegions() {
}
}
static uptr AllocateMemoryForTrampoline(uptr image_address, size_t size) {
// Find a region within 2G with enough space to allocate |size| bytes.
static uptr AllocateMemoryForTrampoline(uptr func_address, size_t size) {
# if SANITIZER_WINDOWS64
uptr min_addr = func_address - kTrampolineRangeLimit;
uptr max_addr = func_address + kTrampolineRangeLimit - size;
// Allocate memory within 2GB of the module (DLL or EXE file) so that any
// address within the module can be referenced with PC-relative operands.
// This allows us to not just jump to the trampoline with a PC-relative
// offset, but to relocate any instructions that we copy to the trampoline
// which have references to the original module. If we can't find the base
// address of the module (e.g. if func_address is in mmap'ed memory), just
// stay within 2GB of func_address.
HMODULE module;
if (::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
(LPCWSTR)func_address, &module)) {
MODULEINFO module_info;
if (::GetModuleInformation(::GetCurrentProcess(), module,
&module_info, sizeof(module_info))) {
min_addr = (uptr)module_info.lpBaseOfDll + module_info.SizeOfImage -
kTrampolineRangeLimit;
max_addr = (uptr)module_info.lpBaseOfDll + kTrampolineRangeLimit - size;
}
}
// Check for overflow.
if (min_addr > func_address)
min_addr = 0;
if (max_addr < func_address)
max_addr = ~(uptr)0;
# else
uptr min_addr = 0;
uptr max_addr = ~min_addr;
# endif
// Find a region within [min_addr,max_addr] with enough space to allocate
// |size| bytes.
TrampolineMemoryRegion *region = nullptr;
for (size_t bucket = 0; bucket < kMaxTrampolineRegion; ++bucket) {
TrampolineMemoryRegion* current = &TrampolineRegions[bucket];
if (current->content == 0) {
// No valid region found, allocate a new region.
size_t bucket_size = GetMmapGranularity();
void *content = AllocateTrampolineRegion(image_address, bucket_size);
void *content = AllocateTrampolineRegion(min_addr, max_addr, func_address,
bucket_size);
if (content == nullptr)
return 0U;
@@ -403,13 +505,9 @@ static uptr AllocateMemoryForTrampoline(uptr image_address, size_t size) {
region = current;
break;
} else if (current->max_size - current->allocated_size > size) {
#if SANITIZER_WINDOWS64
// In 64-bits, the memory space must be allocated within 2G boundary.
uptr next_address = current->content + current->allocated_size;
if (next_address < image_address ||
next_address - image_address >= 0x7FFF0000)
continue;
#endif
uptr next_address = current->content + current->allocated_size;
if (next_address < min_addr || next_address > max_addr)
continue;
// The space can be allocated in the current region.
region = current;
break;
@@ -458,6 +556,10 @@ static const u8 kPrologueWithShortJump2[] = {
// Returns 0 on error.
static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) {
if (rel_offset) {
*rel_offset = 0;
}
#if SANITIZER_ARM64
// An ARM64 instruction is 4 bytes long.
return 4;
@@ -497,8 +599,14 @@ static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) {
case 0x6A: // 6A XX = push XX
return 2;
// This instruction can be encoded with a 16-bit immediate but that is
// incredibly unlikely.
case 0x68: // 68 XX XX XX XX : push imm32
return 5;
case 0xb8: // b8 XX XX XX XX : mov eax, XX XX XX XX
case 0xB9: // b9 XX XX XX XX : mov ecx, XX XX XX XX
case 0xBA: // ba XX XX XX XX : mov edx, XX XX XX XX
return 5;
// Cannot overwrite control-instruction. Return 0 to indicate failure.
@@ -529,19 +637,43 @@ static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) {
case 0xFF8B: // 8B FF : mov edi, edi
case 0xEC8B: // 8B EC : mov ebp, esp
case 0xc889: // 89 C8 : mov eax, ecx
case 0xD189: // 89 D1 : mov ecx, edx
case 0xE589: // 89 E5 : mov ebp, esp
case 0xC18B: // 8B C1 : mov eax, ecx
case 0xC031: // 31 C0 : xor eax, eax
case 0xC931: // 31 C9 : xor ecx, ecx
case 0xD231: // 31 D2 : xor edx, edx
case 0xC033: // 33 C0 : xor eax, eax
case 0xC933: // 33 C9 : xor ecx, ecx
case 0xD233: // 33 D2 : xor edx, edx
case 0xDB84: // 84 DB : test bl,bl
case 0xC084: // 84 C0 : test al,al
case 0xC984: // 84 C9 : test cl,cl
case 0xD284: // 84 D2 : test dl,dl
return 2;
case 0x3980: // 80 39 XX : cmp BYTE PTR [rcx], XX
case 0x4D8B: // 8B 4D XX : mov XX(%ebp), ecx
case 0x558B: // 8B 55 XX : mov XX(%ebp), edx
case 0x758B: // 8B 75 XX : mov XX(%ebp), esp
case 0xE483: // 83 E4 XX : and esp, XX
case 0xEC83: // 83 EC XX : sub esp, XX
case 0xC1F6: // F6 C1 XX : test cl, XX
return 3;
case 0x89FF: // FF 89 XX XX XX XX : dec dword ptr [ecx + XX XX XX XX]
case 0xEC81: // 81 EC XX XX XX XX : sub esp, XX XX XX XX
return 6;
// Cannot overwrite control-instruction. Return 0 to indicate failure.
case 0x25FF: // FF 25 XX XX XX XX : jmp [XXXXXXXX]
case 0x25FF: // FF 25 XX YY ZZ WW : jmp dword ptr ds:[WWZZYYXX]
return 0;
}
switch (0x00FFFFFF & *(u32*)address) {
switch (0x00FFFFFF & *(u32 *)address) {
case 0x244C8D: // 8D 4C 24 XX : lea ecx, [esp + XX]
case 0x2474FF: // FF 74 24 XX : push qword ptr [rsp + XX]
return 4;
case 0x24A48D: // 8D A4 24 XX XX XX XX : lea esp, [esp + XX XX XX XX]
return 7;
}
@@ -556,6 +688,21 @@ static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) {
case 0xA1: // A1 XX XX XX XX XX XX XX XX :
// movabs eax, dword ptr ds:[XXXXXXXX]
return 9;
case 0xF2:
switch (*(u32 *)(address + 1)) {
case 0x2444110f: // f2 0f 11 44 24 XX movsd QWORD PTR
// [rsp + XX], xmm0
case 0x244c110f: // f2 0f 11 4c 24 XX movsd QWORD PTR
// [rsp + XX], xmm1
case 0x2454110f: // f2 0f 11 54 24 XX movsd QWORD PTR
// [rsp + XX], xmm2
case 0x245c110f: // f2 0f 11 5c 24 XX movsd QWORD PTR
// [rsp + XX], xmm3
case 0x2464110f: // f2 0f 11 64 24 XX movsd QWORD PTR
// [rsp + XX], xmm4
return 6;
}
break;
case 0x83:
const u8 next_byte = *(u8*)(address + 1);
@@ -584,55 +731,155 @@ static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) {
case 0x018a: // mov al, byte ptr [rcx]
return 2;
case 0x7E80: // 80 7E YY XX cmp BYTE PTR [rsi+YY], XX
case 0x7D80: // 80 7D YY XX cmp BYTE PTR [rbp+YY], XX
case 0x7A80: // 80 7A YY XX cmp BYTE PTR [rdx+YY], XX
case 0x7880: // 80 78 YY XX cmp BYTE PTR [rax+YY], XX
case 0x7B80: // 80 7B YY XX cmp BYTE PTR [rbx+YY], XX
case 0x7980: // 80 79 YY XX cmp BYTE ptr [rcx+YY], XX
return 4;
case 0x058A: // 8A 05 XX XX XX XX : mov al, byte ptr [XX XX XX XX]
case 0x058B: // 8B 05 XX XX XX XX : mov eax, dword ptr [XX XX XX XX]
if (rel_offset)
*rel_offset = 2;
case 0xB841: // 41 B8 XX XX XX XX : mov r8d, XX XX XX XX
return 6;
case 0x7E81: // 81 7E YY XX XX XX XX cmp DWORD PTR [rsi+YY], XX XX XX XX
case 0x7D81: // 81 7D YY XX XX XX XX cmp DWORD PTR [rbp+YY], XX XX XX XX
case 0x7A81: // 81 7A YY XX XX XX XX cmp DWORD PTR [rdx+YY], XX XX XX XX
case 0x7881: // 81 78 YY XX XX XX XX cmp DWORD PTR [rax+YY], XX XX XX XX
case 0x7B81: // 81 7B YY XX XX XX XX cmp DWORD PTR [rbx+YY], XX XX XX XX
case 0x7981: // 81 79 YY XX XX XX XX cmp dword ptr [rcx+YY], XX XX XX XX
return 7;
}
switch (0x00FFFFFF & *(u32*)address) {
case 0xe58948: // 48 8b c4 : mov rbp, rsp
case 0xc18b48: // 48 8b c1 : mov rax, rcx
case 0xc48b48: // 48 8b c4 : mov rax, rsp
case 0xd9f748: // 48 f7 d9 : neg rcx
case 0xd12b48: // 48 2b d1 : sub rdx, rcx
case 0x07c1f6: // f6 c1 07 : test cl, 0x7
case 0xc98548: // 48 85 C9 : test rcx, rcx
case 0xd28548: // 48 85 d2 : test rdx, rdx
case 0xc0854d: // 4d 85 c0 : test r8, r8
case 0xc2b60f: // 0f b6 c2 : movzx eax, dl
switch (0x00FFFFFF & *(u32 *)address) {
case 0x10b70f: // 0f b7 10 : movzx edx, WORD PTR [rax]
case 0xc00b4d: // 4d 0b c0 : or r8, r8
case 0xc03345: // 45 33 c0 : xor r8d, r8d
case 0xc08548: // 48 85 c0 : test rax, rax
case 0xc0854d: // 4d 85 c0 : test r8, r8
case 0xc08b41: // 41 8b c0 : mov eax, r8d
case 0xc0ff48: // 48 ff c0 : inc rax
case 0xc0ff49: // 49 ff c0 : inc r8
case 0xc18b41: // 41 8b c1 : mov eax, r9d
case 0xc18b48: // 48 8b c1 : mov rax, rcx
case 0xc18b4c: // 4c 8b c1 : mov r8, rcx
case 0xc1ff48: // 48 ff c1 : inc rcx
case 0xc1ff49: // 49 ff c1 : inc r9
case 0xc28b41: // 41 8b c2 : mov eax, r10d
case 0x01b60f: // 0f b6 01 : movzx eax, BYTE PTR [rcx]
case 0x09b60f: // 0f b6 09 : movzx ecx, BYTE PTR [rcx]
case 0x11b60f: // 0f b6 11 : movzx edx, BYTE PTR [rcx]
case 0xc2b60f: // 0f b6 c2 : movzx eax, dl
case 0xc2ff48: // 48 ff c2 : inc rdx
case 0xc2ff49: // 49 ff c2 : inc r10
case 0xc38b41: // 41 8b c3 : mov eax, r11d
case 0xc3ff48: // 48 ff c3 : inc rbx
case 0xc3ff49: // 49 ff c3 : inc r11
case 0xc48b41: // 41 8b c4 : mov eax, r12d
case 0xc48b48: // 48 8b c4 : mov rax, rsp
case 0xc4ff49: // 49 ff c4 : inc r12
case 0xc5ff49: // 49 ff c5 : inc r13
case 0xc6ff48: // 48 ff c6 : inc rsi
case 0xc6ff49: // 49 ff c6 : inc r14
case 0xc7ff48: // 48 ff c7 : inc rdi
case 0xc7ff49: // 49 ff c7 : inc r15
case 0xc93345: // 45 33 c9 : xor r9d, r9d
case 0xdb3345: // 45 33 DB : xor r11d, r11d
case 0xd98b4c: // 4c 8b d9 : mov r11, rcx
case 0xd28b4c: // 4c 8b d2 : mov r10, rdx
case 0xc98b4c: // 4C 8B C9 : mov r9, rcx
case 0xc18b4c: // 4C 8B C1 : mov r8, rcx
case 0xd2b60f: // 0f b6 d2 : movzx edx, dl
case 0xc98548: // 48 85 c9 : test rcx, rcx
case 0xc9854d: // 4d 85 c9 : test r9, r9
case 0xc98b4c: // 4c 8b c9 : mov r9, rcx
case 0xd12948: // 48 29 d1 : sub rcx, rdx
case 0xca2b48: // 48 2b ca : sub rcx, rdx
case 0xca3b48: // 48 3b ca : cmp rcx, rdx
case 0x10b70f: // 0f b7 10 : movzx edx, WORD PTR [rax]
case 0xc00b4d: // 3d 0b c0 : or r8, r8
case 0xc08b41: // 41 8b c0 : mov eax, r8d
case 0xd12b48: // 48 2b d1 : sub rdx, rcx
case 0xd18b48: // 48 8b d1 : mov rdx, rcx
case 0xdc8b4c: // 4c 8b dc : mov r11, rsp
case 0xd18b4c: // 4c 8b d1 : mov r10, rcx
case 0xE0E483: // 83 E4 E0 : and esp, 0xFFFFFFE0
case 0xd28548: // 48 85 d2 : test rdx, rdx
case 0xd2854d: // 4d 85 d2 : test r10, r10
case 0xd28b4c: // 4c 8b d2 : mov r10, rdx
case 0xd2b60f: // 0f b6 d2 : movzx edx, dl
case 0xd2be0f: // 0f be d2 : movsx edx, dl
case 0xd98b4c: // 4c 8b d9 : mov r11, rcx
case 0xd9f748: // 48 f7 d9 : neg rcx
case 0xc03145: // 45 31 c0 : xor r8d,r8d
case 0xc93145: // 45 31 c9 : xor r9d,r9d
case 0xdb3345: // 45 33 db : xor r11d, r11d
case 0xc08445: // 45 84 c0 : test r8b,r8b
case 0xd28445: // 45 84 d2 : test r10b,r10b
case 0xdb8548: // 48 85 db : test rbx, rbx
case 0xdb854d: // 4d 85 db : test r11, r11
case 0xdc8b4c: // 4c 8b dc : mov r11, rsp
case 0xe48548: // 48 85 e4 : test rsp, rsp
case 0xe4854d: // 4d 85 e4 : test r12, r12
case 0xc88948: // 48 89 c8 : mov rax,rcx
case 0xcb8948: // 48 89 cb : mov rbx,rcx
case 0xd08948: // 48 89 d0 : mov rax,rdx
case 0xd18948: // 48 89 d1 : mov rcx,rdx
case 0xd38948: // 48 89 d3 : mov rbx,rdx
case 0xe58948: // 48 89 e5 : mov rbp, rsp
case 0xed8548: // 48 85 ed : test rbp, rbp
case 0xc88949: // 49 89 c8 : mov r8, rcx
case 0xc98949: // 49 89 c9 : mov r9, rcx
case 0xca8949: // 49 89 ca : mov r10,rcx
case 0xd08949: // 49 89 d0 : mov r8, rdx
case 0xd18949: // 49 89 d1 : mov r9, rdx
case 0xd28949: // 49 89 d2 : mov r10, rdx
case 0xd38949: // 49 89 d3 : mov r11, rdx
case 0xed854d: // 4d 85 ed : test r13, r13
case 0xf6854d: // 4d 85 f6 : test r14, r14
case 0xff854d: // 4d 85 ff : test r15, r15
return 3;
case 0x245489: // 89 54 24 XX : mov DWORD PTR[rsp + XX], edx
case 0x428d44: // 44 8d 42 XX : lea r8d , [rdx + XX]
case 0x588948: // 48 89 58 XX : mov QWORD PTR[rax + XX], rbx
case 0xec8348: // 48 83 ec XX : sub rsp, XX
case 0xf88349: // 49 83 f8 XX : cmp r8, XX
case 0x588948: // 48 89 58 XX : mov QWORD PTR[rax + XX], rbx
case 0x488d49: // 49 8d 48 XX : lea rcx, [...]
case 0x048d4c: // 4c 8d 04 XX : lea r8, [...]
case 0x148d4e: // 4e 8d 14 XX : lea r10, [...]
case 0x398366: // 66 83 39 XX : cmp WORD PTR [rcx], XX
return 4;
case 0x441F0F: // 0F 1F 44 XX XX : nop DWORD PTR [...]
case 0x246483: // 83 64 24 XX YY : and DWORD PTR [rsp+XX], YY
return 5;
case 0x788166: // 66 81 78 XX YY YY cmp WORD PTR [rax+XX], YY YY
case 0x798166: // 66 81 79 XX YY YY cmp WORD PTR [rcx+XX], YY YY
case 0x7a8166: // 66 81 7a XX YY YY cmp WORD PTR [rdx+XX], YY YY
case 0x7b8166: // 66 81 7b XX YY YY cmp WORD PTR [rbx+XX], YY YY
case 0x7e8166: // 66 81 7e XX YY YY cmp WORD PTR [rsi+XX], YY YY
case 0x7f8166: // 66 81 7f XX YY YY cmp WORD PTR [rdi+XX], YY YY
return 6;
case 0xec8148: // 48 81 EC XX XX XX XX : sub rsp, XXXXXXXX
case 0xc0c748: // 48 C7 C0 XX XX XX XX : mov rax, XX XX XX XX
return 7;
// clang-format off
case 0x788141: // 41 81 78 XX YY YY YY YY : cmp DWORD PTR [r8+YY], XX XX XX XX
case 0x798141: // 41 81 79 XX YY YY YY YY : cmp DWORD PTR [r9+YY], XX XX XX XX
case 0x7a8141: // 41 81 7a XX YY YY YY YY : cmp DWORD PTR [r10+YY], XX XX XX XX
case 0x7b8141: // 41 81 7b XX YY YY YY YY : cmp DWORD PTR [r11+YY], XX XX XX XX
case 0x7d8141: // 41 81 7d XX YY YY YY YY : cmp DWORD PTR [r13+YY], XX XX XX XX
case 0x7e8141: // 41 81 7e XX YY YY YY YY : cmp DWORD PTR [r14+YY], XX XX XX XX
case 0x7f8141: // 41 81 7f YY XX XX XX XX : cmp DWORD PTR [r15+YY], XX XX XX XX
case 0x247c81: // 81 7c 24 YY XX XX XX XX : cmp DWORD PTR [rsp+YY], XX XX XX XX
return 8;
// clang-format on
case 0x058b48: // 48 8b 05 XX XX XX XX :
// mov rax, QWORD PTR [rip + XXXXXXXX]
case 0x058d48: // 48 8d 05 XX XX XX XX :
// lea rax, QWORD PTR [rip + XXXXXXXX]
case 0x0d8948: // 48 89 0d XX XX XX XX :
// mov QWORD PTR [rip + XXXXXXXX], rcx
case 0x158948: // 48 89 15 XX XX XX XX :
// mov QWORD PTR [rip + XXXXXXXX], rdx
case 0x25ff48: // 48 ff 25 XX XX XX XX :
// rex.W jmp QWORD PTR [rip + XXXXXXXX]
case 0x158D4C: // 4c 8d 15 XX XX XX XX : lea r10, [rip + XX]
@@ -644,9 +891,19 @@ static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) {
case 0x2444c7: // C7 44 24 XX YY YY YY YY
// mov dword ptr [rsp + XX], YYYYYYYY
return 8;
case 0x7c8141: // 41 81 7c ZZ YY XX XX XX XX
// cmp DWORD PTR [reg+reg*n+YY], XX XX XX XX
return 9;
}
switch (*(u32*)(address)) {
case 0x01b60f44: // 44 0f b6 01 : movzx r8d, BYTE PTR [rcx]
case 0x09b60f44: // 44 0f b6 09 : movzx r9d, BYTE PTR [rcx]
case 0x0ab60f44: // 44 0f b6 0a : movzx r8d, BYTE PTR [rdx]
case 0x11b60f44: // 44 0f b6 11 : movzx r10d, BYTE PTR [rcx]
case 0x1ab60f44: // 44 0f b6 1a : movzx r11d, BYTE PTR [rdx]
return 4;
case 0x24448b48: // 48 8b 44 24 XX : mov rax, QWORD ptr [rsp + XX]
case 0x246c8948: // 48 89 6C 24 XX : mov QWORD ptr [rsp + XX], rbp
case 0x245c8948: // 48 89 5c 24 XX : mov QWORD PTR [rsp + XX], rbx
@@ -656,9 +913,19 @@ static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) {
case 0x24548948: // 48 89 54 24 XX : mov QWORD PTR [rsp + XX], rdx
case 0x244c894c: // 4c 89 4c 24 XX : mov QWORD PTR [rsp + XX], r9
case 0x2444894c: // 4c 89 44 24 XX : mov QWORD PTR [rsp + XX], r8
case 0x244c8944: // 44 89 4c 24 XX mov DWORD PTR [rsp + XX], r9d
case 0x24448944: // 44 89 44 24 XX mov DWORD PTR [rsp + XX], r8d
case 0x246c8d48: // 48 8d 6c 24 XX : lea rbp, [rsp + XX]
return 5;
case 0x24648348: // 48 83 64 24 XX : and QWORD PTR [rsp + XX], YY
case 0x24648348: // 48 83 64 24 XX YY : and QWORD PTR [rsp + XX], YY
return 6;
case 0x24A48D48: // 48 8D A4 24 XX XX XX XX : lea rsp, [rsp + XX XX XX XX]
return 8;
}
switch (0xFFFFFFFFFFULL & *(u64 *)(address)) {
case 0xC07E0F4866: // 66 48 0F 7E C0 : movq rax, xmm0
return 5;
}
#else
@@ -671,11 +938,10 @@ static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) {
case 0x458B: // 8B 45 XX : mov eax, dword ptr [ebp + XX]
case 0x5D8B: // 8B 5D XX : mov ebx, dword ptr [ebp + XX]
case 0x7D8B: // 8B 7D XX : mov edi, dword ptr [ebp + XX]
case 0xEC83: // 83 EC XX : sub esp, XX
case 0x758B: // 8B 75 XX : mov esi, dword ptr [ebp + XX]
case 0x75FF: // FF 75 XX : push dword ptr [ebp + XX]
return 3;
case 0xC1F7: // F7 C1 XX YY ZZ WW : test ecx, WWZZYYXX
case 0x25FF: // FF 25 XX YY ZZ WW : jmp dword ptr ds:[WWZZYYXX]
return 6;
case 0x3D83: // 83 3D XX YY ZZ WW TT : cmp TT, WWZZYYXX
return 7;
@@ -718,6 +984,10 @@ static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) {
return 0;
}
size_t TestOnlyGetInstructionSize(uptr address, size_t *rel_offset) {
return GetInstructionSize(address, rel_offset);
}
// Returns 0 on error.
static size_t RoundUpToInstrBoundary(size_t size, uptr address) {
size_t cursor = 0;
@@ -745,8 +1015,14 @@ static bool CopyInstructions(uptr to, uptr from, size_t size) {
// this will be untrue if relocated_offset \notin [-2**31, 2**31)
s64 delta = to - from;
s64 relocated_offset = *(s32 *)(to + cursor + rel_offset) - delta;
if (-0x8000'0000ll > relocated_offset || relocated_offset > 0x7FFF'FFFFll)
if (-0x8000'0000ll > relocated_offset ||
relocated_offset > 0x7FFF'FFFFll) {
ReportError(
"interception_win: CopyInstructions relocated_offset %lld outside "
"32-bit range\n",
(long long)relocated_offset);
return false;
}
# else
// on 32-bit, the relative offset will always be correct
s32 delta = to - from;
@@ -969,8 +1245,7 @@ static void **InterestingDLLsAvailable() {
"libc++.dll", // libc++
"libunwind.dll", // libunwind
# endif
// NTDLL should go last as it exports some functions that we should
// override in the CRT [presumably only used internally].
// NTDLL must go last as it gets special treatment in OverrideFunction.
"ntdll.dll",
NULL
};
@@ -1027,7 +1302,7 @@ uptr InternalGetProcAddress(void *module, const char *func_name) {
for (DWORD i = 0; i < exports->NumberOfNames; i++) {
RVAPtr<char> name(module, names[i]);
if (!strcmp(func_name, name)) {
if (!_strcmp(func_name, name)) {
DWORD index = ordinals[i];
RVAPtr<char> func(module, functions[index]);
@@ -1040,19 +1315,27 @@ uptr InternalGetProcAddress(void *module, const char *func_name) {
// exported directory.
char function_name[256];
size_t funtion_name_length = _strlen(func);
if (funtion_name_length >= sizeof(function_name) - 1)
if (funtion_name_length >= sizeof(function_name) - 1) {
ReportError("interception_win: func too long: '%s'\n", (char *)func);
InterceptionFailed();
}
_memcpy(function_name, func, funtion_name_length);
function_name[funtion_name_length] = '\0';
char* separator = _strchr(function_name, '.');
if (!separator)
if (!separator) {
ReportError("interception_win: no separator in '%s'\n",
function_name);
InterceptionFailed();
}
*separator = '\0';
void* redirected_module = GetModuleHandleA(function_name);
if (!redirected_module)
if (!redirected_module) {
ReportError("interception_win: GetModuleHandleA failed for '%s'\n",
function_name);
InterceptionFailed();
}
return InternalGetProcAddress(redirected_module, separator + 1);
}
@@ -1065,9 +1348,22 @@ uptr InternalGetProcAddress(void *module, const char *func_name) {
bool OverrideFunction(
const char *func_name, uptr new_func, uptr *orig_old_func) {
static const char *kNtDllIgnore[] = {
"memcmp", "memcpy", "memmove", "memset"
};
bool hooked = false;
void **DLLs = InterestingDLLsAvailable();
for (size_t i = 0; DLLs[i]; ++i) {
if (DLLs[i + 1] == nullptr) {
// This is the last DLL, i.e. NTDLL. It exports some functions that
// we only want to override in the CRT.
for (const char *ignored : kNtDllIgnore) {
if (_strcmp(func_name, ignored) == 0)
return hooked;
}
}
uptr func_addr = InternalGetProcAddress(DLLs[i], func_name);
if (func_addr &&
OverrideFunction(func_addr, new_func, orig_old_func)) {
@@ -1121,7 +1417,7 @@ bool OverrideImportedFunction(const char *module_to_patch,
RVAPtr<IMAGE_IMPORT_BY_NAME> import_by_name(
module, name_table->u1.ForwarderString);
const char *funcname = &import_by_name->Name[0];
if (strcmp(funcname, function_name) == 0)
if (_strcmp(funcname, function_name) == 0)
break;
}
}
@@ -1144,4 +1440,4 @@ bool OverrideImportedFunction(const char *module_to_patch,
} // namespace __interception
#endif // SANITIZER_APPLE
#endif // SANITIZER_WINDOWS