| //===-- interception_win_test.cpp -----------------------------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // This file is a part of ThreadSanitizer/AddressSanitizer runtime. | |
| // Tests for interception_win.h. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "interception/interception.h" | |
| #include "gtest/gtest.h" | |
| // Too slow for debug build | |
| // Disabling for ARM64 since testcases are x86/x64 assembly. | |
| #if !SANITIZER_DEBUG | |
| #if SANITIZER_WINDOWS | |
| # if !SANITIZER_WINDOWS_ARM64 | |
| # include <stdarg.h> | |
| # define WIN32_LEAN_AND_MEAN | |
| # include <windows.h> | |
| namespace __interception { | |
| namespace { | |
| enum FunctionPrefixKind { | |
| FunctionPrefixNone, | |
| FunctionPrefixPadding, | |
| FunctionPrefixHotPatch, | |
| FunctionPrefixDetour, | |
| }; | |
| typedef bool (*TestOverrideFunction)(uptr, uptr, uptr*); | |
| typedef int (*IdentityFunction)(int); | |
| #if SANITIZER_WINDOWS64 | |
| const u8 kIdentityCodeWithPrologue[] = { | |
| 0x55, // push rbp | |
| 0x48, 0x89, 0xE5, // mov rbp,rsp | |
| 0x8B, 0xC1, // mov eax,ecx | |
| 0x5D, // pop rbp | |
| 0xC3, // ret | |
| }; | |
| const u8 kIdentityCodeWithPushPop[] = { | |
| 0x55, // push rbp | |
| 0x48, 0x89, 0xE5, // mov rbp,rsp | |
| 0x53, // push rbx | |
| 0x50, // push rax | |
| 0x58, // pop rax | |
| 0x8B, 0xC1, // mov rax,rcx | |
| 0x5B, // pop rbx | |
| 0x5D, // pop rbp | |
| 0xC3, // ret | |
| }; | |
| const u8 kIdentityTwiceOffset = 16; | |
| const u8 kIdentityTwice[] = { | |
| 0x55, // push rbp | |
| 0x48, 0x89, 0xE5, // mov rbp,rsp | |
| 0x8B, 0xC1, // mov eax,ecx | |
| 0x5D, // pop rbp | |
| 0xC3, // ret | |
| 0x90, 0x90, 0x90, 0x90, | |
| 0x90, 0x90, 0x90, 0x90, | |
| 0x55, // push rbp | |
| 0x48, 0x89, 0xE5, // mov rbp,rsp | |
| 0x8B, 0xC1, // mov eax,ecx | |
| 0x5D, // pop rbp | |
| 0xC3, // ret | |
| }; | |
| const u8 kIdentityCodeWithMov[] = { | |
| 0x89, 0xC8, // mov eax, ecx | |
| 0xC3, // ret | |
| }; | |
| const u8 kIdentityCodeWithJump[] = { | |
| 0xE9, 0x04, 0x00, 0x00, | |
| 0x00, // jmp + 4 | |
| 0xCC, 0xCC, 0xCC, 0xCC, | |
| 0x89, 0xC8, // mov eax, ecx | |
| 0xC3, // ret | |
| }; | |
| const u8 kIdentityCodeWithJumpBackwards[] = { | |
| 0x89, 0xC8, // mov eax, ecx | |
| 0xC3, // ret | |
| 0xE9, 0xF8, 0xFF, 0xFF, | |
| 0xFF, // jmp - 8 | |
| 0xCC, 0xCC, 0xCC, 0xCC, | |
| }; | |
| const u8 kIdentityCodeWithJumpBackwardsOffset = 3; | |
| # else | |
| const u8 kIdentityCodeWithPrologue[] = { | |
| 0x55, // push ebp | |
| 0x8B, 0xEC, // mov ebp,esp | |
| 0x8B, 0x45, 0x08, // mov eax,dword ptr [ebp + 8] | |
| 0x5D, // pop ebp | |
| 0xC3, // ret | |
| }; | |
| const u8 kIdentityCodeWithPushPop[] = { | |
| 0x55, // push ebp | |
| 0x8B, 0xEC, // mov ebp,esp | |
| 0x53, // push ebx | |
| 0x50, // push eax | |
| 0x58, // pop eax | |
| 0x8B, 0x45, 0x08, // mov eax,dword ptr [ebp + 8] | |
| 0x5B, // pop ebx | |
| 0x5D, // pop ebp | |
| 0xC3, // ret | |
| }; | |
| const u8 kIdentityTwiceOffset = 8; | |
| const u8 kIdentityTwice[] = { | |
| 0x55, // push ebp | |
| 0x8B, 0xEC, // mov ebp,esp | |
| 0x8B, 0x45, 0x08, // mov eax,dword ptr [ebp + 8] | |
| 0x5D, // pop ebp | |
| 0xC3, // ret | |
| 0x55, // push ebp | |
| 0x8B, 0xEC, // mov ebp,esp | |
| 0x8B, 0x45, 0x08, // mov eax,dword ptr [ebp + 8] | |
| 0x5D, // pop ebp | |
| 0xC3, // ret | |
| }; | |
| const u8 kIdentityCodeWithMov[] = { | |
| 0x8B, 0x44, 0x24, 0x04, // mov eax,dword ptr [esp + 4] | |
| 0xC3, // ret | |
| }; | |
| const u8 kIdentityCodeWithJump[] = { | |
| 0xE9, 0x04, 0x00, 0x00, | |
| 0x00, // jmp + 4 | |
| 0xCC, 0xCC, 0xCC, 0xCC, | |
| 0x8B, 0x44, 0x24, 0x04, // mov eax,dword ptr [esp + 4] | |
| 0xC3, // ret | |
| }; | |
| const u8 kIdentityCodeWithJumpBackwards[] = { | |
| 0x8B, 0x44, 0x24, 0x04, // mov eax,dword ptr [esp + 4] | |
| 0xC3, // ret | |
| 0xE9, 0xF6, 0xFF, 0xFF, | |
| 0xFF, // jmp - 10 | |
| 0xCC, 0xCC, 0xCC, 0xCC, | |
| }; | |
| const u8 kIdentityCodeWithJumpBackwardsOffset = 5; | |
| # endif | |
| const u8 kPatchableCode1[] = { | |
| 0xB8, 0x4B, 0x00, 0x00, 0x00, // mov eax,4B | |
| 0x33, 0xC9, // xor ecx,ecx | |
| 0xC3, // ret | |
| }; | |
| const u8 kPatchableCode2[] = { | |
| 0x55, // push ebp | |
| 0x8B, 0xEC, // mov ebp,esp | |
| 0x33, 0xC0, // xor eax,eax | |
| 0x5D, // pop ebp | |
| 0xC3, // ret | |
| }; | |
| const u8 kPatchableCode3[] = { | |
| 0x55, // push ebp | |
| 0x8B, 0xEC, // mov ebp,esp | |
| 0x6A, 0x00, // push 0 | |
| 0xE8, 0x3D, 0xFF, 0xFF, 0xFF, // call <func> | |
| }; | |
| const u8 kPatchableCode4[] = { | |
| 0xE9, 0xCC, 0xCC, 0xCC, 0xCC, // jmp <label> | |
| 0x90, 0x90, 0x90, 0x90, | |
| }; | |
| const u8 kPatchableCode5[] = { | |
| 0x55, // push ebp | |
| 0x8b, 0xec, // mov ebp,esp | |
| 0x8d, 0xa4, 0x24, 0x30, 0xfd, 0xff, 0xff, // lea esp,[esp-2D0h] | |
| 0x54, // push esp | |
| }; | |
| #if SANITIZER_WINDOWS64 | |
| u8 kLoadGlobalCode[] = { | |
| 0x8B, 0x05, 0x00, 0x00, 0x00, 0x00, // mov eax [rip + global] | |
| 0xC3, // ret | |
| }; | |
| #endif | |
| const u8 kUnpatchableCode1[] = { | |
| 0xC3, // ret | |
| }; | |
| const u8 kUnpatchableCode2[] = { | |
| 0x33, 0xC9, // xor ecx,ecx | |
| 0xC3, // ret | |
| }; | |
| const u8 kUnpatchableCode3[] = { | |
| 0x75, 0xCC, // jne <label> | |
| 0x33, 0xC9, // xor ecx,ecx | |
| 0xC3, // ret | |
| }; | |
| const u8 kUnpatchableCode4[] = { | |
| 0x74, 0xCC, // jne <label> | |
| 0x33, 0xC9, // xor ecx,ecx | |
| 0xC3, // ret | |
| }; | |
| const u8 kUnpatchableCode5[] = { | |
| 0xEB, 0x02, // jmp <label> | |
| 0x33, 0xC9, // xor ecx,ecx | |
| 0xC3, // ret | |
| }; | |
| const u8 kUnpatchableCode6[] = { | |
| 0xE8, 0xCC, 0xCC, 0xCC, 0xCC, // call <func> | |
| 0x90, 0x90, 0x90, 0x90, | |
| }; | |
| # if SANITIZER_WINDOWS64 | |
| const u8 kUnpatchableCode7[] = { | |
| 0x33, 0xc0, // xor eax,eax | |
| 0x48, 0x85, 0xd2, // test rdx,rdx | |
| 0x74, 0x10, // je +16 (unpatchable) | |
| }; | |
| const u8 kUnpatchableCode8[] = { | |
| 0x48, 0x8b, 0xc1, // mov rax,rcx | |
| 0x0f, 0xb7, 0x10, // movzx edx,word ptr [rax] | |
| 0x48, 0x83, 0xc0, 0x02, // add rax,2 | |
| 0x66, 0x85, 0xd2, // test dx,dx | |
| 0x75, 0xf4, // jne -12 (unpatchable) | |
| }; | |
| const u8 kUnpatchableCode9[] = { | |
| 0x4c, 0x8b, 0xc1, // mov r8,rcx | |
| 0x8a, 0x01, // mov al,byte ptr [rcx] | |
| 0x48, 0xff, 0xc1, // inc rcx | |
| 0x84, 0xc0, // test al,al | |
| 0x75, 0xf7, // jne -9 (unpatchable) | |
| }; | |
| const u8 kPatchableCode6[] = { | |
| 0x48, 0x89, 0x54, 0x24, 0xBB, // mov QWORD PTR [rsp + 0xBB], rdx | |
| 0x33, 0xC9, // xor ecx,ecx | |
| 0xC3, // ret | |
| }; | |
| const u8 kPatchableCode7[] = { | |
| 0x4c, 0x89, 0x4c, 0x24, 0xBB, // mov QWORD PTR [rsp + 0xBB], r9 | |
| 0x33, 0xC9, // xor ecx,ecx | |
| 0xC3, // ret | |
| }; | |
| const u8 kPatchableCode8[] = { | |
| 0x4c, 0x89, 0x44, 0x24, 0xBB, // mov QWORD PTR [rsp + 0xBB], r8 | |
| 0x33, 0xC9, // xor ecx,ecx | |
| 0xC3, // ret | |
| }; | |
| const u8 kPatchableCode9[] = { | |
| 0x8a, 0x01, // al,byte ptr [rcx] | |
| 0x45, 0x33, 0xc0, // xor r8d,r8d | |
| 0x84, 0xc0, // test al,al | |
| }; | |
| const u8 kPatchableCode10[] = { | |
| 0x45, 0x33, 0xc0, // xor r8d,r8d | |
| 0x41, 0x8b, 0xc0, // mov eax,r8d | |
| 0x48, 0x85, 0xd2, // test rdx,rdx | |
| }; | |
| const u8 kPatchableCode11[] = { | |
| 0x48, 0x83, 0xec, 0x38, // sub rsp,38h | |
| 0x83, 0x64, 0x24, 0x28, 0x00, // and dword ptr [rsp+28h],0 | |
| }; | |
| # endif | |
| # if !SANITIZER_WINDOWS64 | |
| const u8 kPatchableCode12[] = { | |
| 0x55, // push ebp | |
| 0x53, // push ebx | |
| 0x57, // push edi | |
| 0x56, // push esi | |
| 0x8b, 0x6c, 0x24, 0x18, // mov ebp,dword ptr[esp+18h] | |
| }; | |
| const u8 kPatchableCode13[] = { | |
| 0x55, // push ebp | |
| 0x53, // push ebx | |
| 0x57, // push edi | |
| 0x56, // push esi | |
| 0x8b, 0x5c, 0x24, 0x14, // mov ebx,dword ptr[esp+14h] | |
| }; | |
| # endif | |
| const u8 kPatchableCode14[] = { | |
| 0x55, // push ebp | |
| 0x89, 0xe5, // mov ebp,esp | |
| 0x53, // push ebx | |
| 0x57, // push edi | |
| 0x56, // push esi | |
| }; | |
| const u8 kUnsupportedCode1[] = { | |
| 0x0f, 0x0b, // ud2 | |
| 0x0f, 0x0b, // ud2 | |
| 0x0f, 0x0b, // ud2 | |
| 0x0f, 0x0b, // ud2 | |
| }; | |
| // A buffer holding the dynamically generated code under test. | |
| u8* ActiveCode; | |
| const size_t ActiveCodeLength = 4096; | |
| int InterceptorFunction(int x); | |
| /// Allocate code memory more than 2GB away from Base. | |
| u8 *AllocateCode2GBAway(u8 *Base) { | |
| // Find a 64K aligned location after Base plus 2GB. | |
| size_t TwoGB = 0x80000000; | |
| size_t AllocGranularity = 0x10000; | |
| Base = (u8 *)((((uptr)Base + TwoGB + AllocGranularity)) & ~(AllocGranularity - 1)); | |
| // Check if that location is free, and if not, loop over regions until we find | |
| // one that is. | |
| MEMORY_BASIC_INFORMATION mbi = {}; | |
| while (sizeof(mbi) == VirtualQuery(Base, &mbi, sizeof(mbi))) { | |
| if (mbi.State & MEM_FREE) break; | |
| Base += mbi.RegionSize; | |
| } | |
| // Allocate one RWX page at the free location. | |
| return (u8 *)::VirtualAlloc(Base, ActiveCodeLength, MEM_COMMIT | MEM_RESERVE, | |
| PAGE_EXECUTE_READWRITE); | |
| } | |
| template<class T> | |
| static void LoadActiveCode( | |
| const T &code, | |
| uptr *entry_point, | |
| FunctionPrefixKind prefix_kind = FunctionPrefixNone) { | |
| if (ActiveCode == nullptr) { | |
| ActiveCode = AllocateCode2GBAway((u8*)&InterceptorFunction); | |
| ASSERT_NE(ActiveCode, nullptr) << "failed to allocate RWX memory 2GB away"; | |
| } | |
| size_t position = 0; | |
| // Add padding to avoid memory violation when scanning the prefix. | |
| for (int i = 0; i < 16; ++i) | |
| ActiveCode[position++] = 0xC3; // Instruction 'ret'. | |
| // Add function padding. | |
| size_t padding = 0; | |
| if (prefix_kind == FunctionPrefixPadding) | |
| padding = 16; | |
| else if (prefix_kind == FunctionPrefixDetour || | |
| prefix_kind == FunctionPrefixHotPatch) | |
| padding = FIRST_32_SECOND_64(5, 6); | |
| // Insert |padding| instructions 'nop'. | |
| for (size_t i = 0; i < padding; ++i) | |
| ActiveCode[position++] = 0x90; | |
| // Keep track of the entry point. | |
| *entry_point = (uptr)&ActiveCode[position]; | |
| // Add the detour instruction (i.e. mov edi, edi) | |
| if (prefix_kind == FunctionPrefixDetour) { | |
| #if SANITIZER_WINDOWS64 | |
| // Note that "mov edi,edi" is NOP in 32-bit only, in 64-bit it clears | |
| // higher bits of RDI. | |
| // Use 66,90H as NOP for Windows64. | |
| ActiveCode[position++] = 0x66; | |
| ActiveCode[position++] = 0x90; | |
| #else | |
| // mov edi,edi. | |
| ActiveCode[position++] = 0x8B; | |
| ActiveCode[position++] = 0xFF; | |
| #endif | |
| } | |
| // Copy the function body. | |
| for (size_t i = 0; i < sizeof(T); ++i) | |
| ActiveCode[position++] = code[i]; | |
| } | |
| int InterceptorFunctionCalled; | |
| IdentityFunction InterceptedRealFunction; | |
| int InterceptorFunction(int x) { | |
| ++InterceptorFunctionCalled; | |
| return InterceptedRealFunction(x); | |
| } | |
| } // namespace | |
| // Tests for interception_win.h | |
| TEST(Interception, InternalGetProcAddress) { | |
| HMODULE ntdll_handle = ::GetModuleHandle("ntdll"); | |
| ASSERT_NE(nullptr, ntdll_handle); | |
| uptr DbgPrint_expected = (uptr)::GetProcAddress(ntdll_handle, "DbgPrint"); | |
| uptr isdigit_expected = (uptr)::GetProcAddress(ntdll_handle, "isdigit"); | |
| uptr DbgPrint_adddress = InternalGetProcAddress(ntdll_handle, "DbgPrint"); | |
| uptr isdigit_address = InternalGetProcAddress(ntdll_handle, "isdigit"); | |
| EXPECT_EQ(DbgPrint_expected, DbgPrint_adddress); | |
| EXPECT_EQ(isdigit_expected, isdigit_address); | |
| EXPECT_NE(DbgPrint_adddress, isdigit_address); | |
| } | |
| template <class T> | |
| static void TestIdentityFunctionPatching( | |
| const T &code, TestOverrideFunction override, | |
| FunctionPrefixKind prefix_kind = FunctionPrefixNone, | |
| int function_start_offset = 0) { | |
| uptr identity_address; | |
| LoadActiveCode(code, &identity_address, prefix_kind); | |
| identity_address += function_start_offset; | |
| IdentityFunction identity = (IdentityFunction)identity_address; | |
| // Validate behavior before dynamic patching. | |
| InterceptorFunctionCalled = 0; | |
| EXPECT_EQ(0, identity(0)); | |
| EXPECT_EQ(42, identity(42)); | |
| EXPECT_EQ(0, InterceptorFunctionCalled); | |
| // Patch the function. | |
| uptr real_identity_address = 0; | |
| bool success = override(identity_address, | |
| (uptr)&InterceptorFunction, | |
| &real_identity_address); | |
| EXPECT_TRUE(success); | |
| EXPECT_NE(0U, real_identity_address); | |
| IdentityFunction real_identity = (IdentityFunction)real_identity_address; | |
| InterceptedRealFunction = real_identity; | |
| // Don't run tests if hooking failed or the real function is not valid. | |
| if (!success || !real_identity_address) | |
| return; | |
| // Calling the redirected function. | |
| InterceptorFunctionCalled = 0; | |
| EXPECT_EQ(0, identity(0)); | |
| EXPECT_EQ(42, identity(42)); | |
| EXPECT_EQ(2, InterceptorFunctionCalled); | |
| // Calling the real function. | |
| InterceptorFunctionCalled = 0; | |
| EXPECT_EQ(0, real_identity(0)); | |
| EXPECT_EQ(42, real_identity(42)); | |
| EXPECT_EQ(0, InterceptorFunctionCalled); | |
| TestOnlyReleaseTrampolineRegions(); | |
| } | |
| # if !SANITIZER_WINDOWS64 | |
| TEST(Interception, OverrideFunctionWithDetour) { | |
| TestOverrideFunction override = OverrideFunctionWithDetour; | |
| FunctionPrefixKind prefix = FunctionPrefixDetour; | |
| TestIdentityFunctionPatching(kIdentityCodeWithPrologue, override, prefix); | |
| TestIdentityFunctionPatching(kIdentityCodeWithPushPop, override, prefix); | |
| TestIdentityFunctionPatching(kIdentityCodeWithMov, override, prefix); | |
| TestIdentityFunctionPatching(kIdentityCodeWithJump, override, prefix); | |
| } | |
| #endif // !SANITIZER_WINDOWS64 | |
| TEST(Interception, OverrideFunctionWithRedirectJump) { | |
| TestOverrideFunction override = OverrideFunctionWithRedirectJump; | |
| TestIdentityFunctionPatching(kIdentityCodeWithJump, override); | |
| TestIdentityFunctionPatching(kIdentityCodeWithJumpBackwards, override, | |
| FunctionPrefixNone, | |
| kIdentityCodeWithJumpBackwardsOffset); | |
| } | |
| TEST(Interception, OverrideFunctionWithHotPatch) { | |
| TestOverrideFunction override = OverrideFunctionWithHotPatch; | |
| FunctionPrefixKind prefix = FunctionPrefixHotPatch; | |
| TestIdentityFunctionPatching(kIdentityCodeWithMov, override, prefix); | |
| } | |
| TEST(Interception, OverrideFunctionWithTrampoline) { | |
| TestOverrideFunction override = OverrideFunctionWithTrampoline; | |
| FunctionPrefixKind prefix = FunctionPrefixNone; | |
| TestIdentityFunctionPatching(kIdentityCodeWithPrologue, override, prefix); | |
| TestIdentityFunctionPatching(kIdentityCodeWithPushPop, override, prefix); | |
| prefix = FunctionPrefixPadding; | |
| TestIdentityFunctionPatching(kIdentityCodeWithPrologue, override, prefix); | |
| TestIdentityFunctionPatching(kIdentityCodeWithPushPop, override, prefix); | |
| } | |
| TEST(Interception, OverrideFunction) { | |
| TestOverrideFunction override = OverrideFunction; | |
| FunctionPrefixKind prefix = FunctionPrefixNone; | |
| TestIdentityFunctionPatching(kIdentityCodeWithPrologue, override, prefix); | |
| TestIdentityFunctionPatching(kIdentityCodeWithPushPop, override, prefix); | |
| TestIdentityFunctionPatching(kIdentityCodeWithJump, override, prefix); | |
| prefix = FunctionPrefixPadding; | |
| TestIdentityFunctionPatching(kIdentityCodeWithPrologue, override, prefix); | |
| TestIdentityFunctionPatching(kIdentityCodeWithPushPop, override, prefix); | |
| TestIdentityFunctionPatching(kIdentityCodeWithMov, override, prefix); | |
| TestIdentityFunctionPatching(kIdentityCodeWithJump, override, prefix); | |
| prefix = FunctionPrefixHotPatch; | |
| TestIdentityFunctionPatching(kIdentityCodeWithPrologue, override, prefix); | |
| TestIdentityFunctionPatching(kIdentityCodeWithPushPop, override, prefix); | |
| TestIdentityFunctionPatching(kIdentityCodeWithMov, override, prefix); | |
| TestIdentityFunctionPatching(kIdentityCodeWithJump, override, prefix); | |
| prefix = FunctionPrefixDetour; | |
| TestIdentityFunctionPatching(kIdentityCodeWithPrologue, override, prefix); | |
| TestIdentityFunctionPatching(kIdentityCodeWithPushPop, override, prefix); | |
| TestIdentityFunctionPatching(kIdentityCodeWithMov, override, prefix); | |
| TestIdentityFunctionPatching(kIdentityCodeWithJump, override, prefix); | |
| } | |
| template<class T> | |
| static void TestIdentityFunctionMultiplePatching( | |
| const T &code, | |
| TestOverrideFunction override, | |
| FunctionPrefixKind prefix_kind = FunctionPrefixNone) { | |
| uptr identity_address; | |
| LoadActiveCode(code, &identity_address, prefix_kind); | |
| // Patch the function. | |
| uptr real_identity_address = 0; | |
| bool success = override(identity_address, | |
| (uptr)&InterceptorFunction, | |
| &real_identity_address); | |
| EXPECT_TRUE(success); | |
| EXPECT_NE(0U, real_identity_address); | |
| // Re-patching the function should not work. | |
| success = override(identity_address, | |
| (uptr)&InterceptorFunction, | |
| &real_identity_address); | |
| EXPECT_FALSE(success); | |
| TestOnlyReleaseTrampolineRegions(); | |
| } | |
| TEST(Interception, OverrideFunctionMultiplePatchingIsFailing) { | |
| #if !SANITIZER_WINDOWS64 | |
| TestIdentityFunctionMultiplePatching(kIdentityCodeWithPrologue, | |
| OverrideFunctionWithDetour, | |
| FunctionPrefixDetour); | |
| #endif | |
| TestIdentityFunctionMultiplePatching(kIdentityCodeWithMov, | |
| OverrideFunctionWithHotPatch, | |
| FunctionPrefixHotPatch); | |
| TestIdentityFunctionMultiplePatching(kIdentityCodeWithPushPop, | |
| OverrideFunctionWithTrampoline, | |
| FunctionPrefixPadding); | |
| } | |
| TEST(Interception, OverrideFunctionTwice) { | |
| uptr identity_address1; | |
| LoadActiveCode(kIdentityTwice, &identity_address1); | |
| uptr identity_address2 = identity_address1 + kIdentityTwiceOffset; | |
| IdentityFunction identity1 = (IdentityFunction)identity_address1; | |
| IdentityFunction identity2 = (IdentityFunction)identity_address2; | |
| // Patch the two functions. | |
| uptr real_identity_address = 0; | |
| EXPECT_TRUE(OverrideFunction(identity_address1, | |
| (uptr)&InterceptorFunction, | |
| &real_identity_address)); | |
| EXPECT_TRUE(OverrideFunction(identity_address2, | |
| (uptr)&InterceptorFunction, | |
| &real_identity_address)); | |
| IdentityFunction real_identity = (IdentityFunction)real_identity_address; | |
| InterceptedRealFunction = real_identity; | |
| // Calling the redirected function. | |
| InterceptorFunctionCalled = 0; | |
| EXPECT_EQ(42, identity1(42)); | |
| EXPECT_EQ(42, identity2(42)); | |
| EXPECT_EQ(2, InterceptorFunctionCalled); | |
| TestOnlyReleaseTrampolineRegions(); | |
| } | |
| template<class T> | |
| static bool TestFunctionPatching( | |
| const T &code, | |
| TestOverrideFunction override, | |
| FunctionPrefixKind prefix_kind = FunctionPrefixNone) { | |
| uptr address; | |
| LoadActiveCode(code, &address, prefix_kind); | |
| uptr unused_real_address = 0; | |
| bool result = override( | |
| address, (uptr)&InterceptorFunction, &unused_real_address); | |
| TestOnlyReleaseTrampolineRegions(); | |
| return result; | |
| } | |
| TEST(Interception, PatchableFunction) { | |
| TestOverrideFunction override = OverrideFunction; | |
| // Test without function padding. | |
| EXPECT_TRUE(TestFunctionPatching(kPatchableCode1, override)); | |
| EXPECT_TRUE(TestFunctionPatching(kPatchableCode2, override)); | |
| #if SANITIZER_WINDOWS64 | |
| EXPECT_FALSE(TestFunctionPatching(kPatchableCode3, override)); | |
| #else | |
| EXPECT_TRUE(TestFunctionPatching(kPatchableCode3, override)); | |
| #endif | |
| EXPECT_TRUE(TestFunctionPatching(kPatchableCode4, override)); | |
| EXPECT_TRUE(TestFunctionPatching(kPatchableCode5, override)); | |
| #if SANITIZER_WINDOWS64 | |
| EXPECT_TRUE(TestFunctionPatching(kLoadGlobalCode, override)); | |
| #endif | |
| EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode1, override)); | |
| EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode2, override)); | |
| EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode3, override)); | |
| EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode4, override)); | |
| EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode5, override)); | |
| EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode6, override)); | |
| } | |
| #if !SANITIZER_WINDOWS64 | |
| TEST(Interception, PatchableFunctionWithDetour) { | |
| TestOverrideFunction override = OverrideFunctionWithDetour; | |
| // Without the prefix, no function can be detoured. | |
| EXPECT_FALSE(TestFunctionPatching(kPatchableCode1, override)); | |
| EXPECT_FALSE(TestFunctionPatching(kPatchableCode2, override)); | |
| EXPECT_FALSE(TestFunctionPatching(kPatchableCode3, override)); | |
| EXPECT_FALSE(TestFunctionPatching(kPatchableCode4, override)); | |
| EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode1, override)); | |
| EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode2, override)); | |
| EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode3, override)); | |
| EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode4, override)); | |
| EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode5, override)); | |
| EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode6, override)); | |
| // With the prefix, all functions can be detoured. | |
| FunctionPrefixKind prefix = FunctionPrefixDetour; | |
| EXPECT_TRUE(TestFunctionPatching(kPatchableCode1, override, prefix)); | |
| EXPECT_TRUE(TestFunctionPatching(kPatchableCode2, override, prefix)); | |
| EXPECT_TRUE(TestFunctionPatching(kPatchableCode3, override, prefix)); | |
| EXPECT_TRUE(TestFunctionPatching(kPatchableCode4, override, prefix)); | |
| EXPECT_TRUE(TestFunctionPatching(kUnpatchableCode1, override, prefix)); | |
| EXPECT_TRUE(TestFunctionPatching(kUnpatchableCode2, override, prefix)); | |
| EXPECT_TRUE(TestFunctionPatching(kUnpatchableCode3, override, prefix)); | |
| EXPECT_TRUE(TestFunctionPatching(kUnpatchableCode4, override, prefix)); | |
| EXPECT_TRUE(TestFunctionPatching(kUnpatchableCode5, override, prefix)); | |
| EXPECT_TRUE(TestFunctionPatching(kUnpatchableCode6, override, prefix)); | |
| } | |
| #endif // !SANITIZER_WINDOWS64 | |
| TEST(Interception, PatchableFunctionWithRedirectJump) { | |
| TestOverrideFunction override = OverrideFunctionWithRedirectJump; | |
| EXPECT_FALSE(TestFunctionPatching(kPatchableCode1, override)); | |
| EXPECT_FALSE(TestFunctionPatching(kPatchableCode2, override)); | |
| EXPECT_FALSE(TestFunctionPatching(kPatchableCode3, override)); | |
| EXPECT_TRUE(TestFunctionPatching(kPatchableCode4, override)); | |
| EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode1, override)); | |
| EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode2, override)); | |
| EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode3, override)); | |
| EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode4, override)); | |
| EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode5, override)); | |
| EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode6, override)); | |
| } | |
| TEST(Interception, PatchableFunctionWithHotPatch) { | |
| TestOverrideFunction override = OverrideFunctionWithHotPatch; | |
| FunctionPrefixKind prefix = FunctionPrefixHotPatch; | |
| EXPECT_TRUE(TestFunctionPatching(kPatchableCode1, override, prefix)); | |
| EXPECT_FALSE(TestFunctionPatching(kPatchableCode2, override, prefix)); | |
| EXPECT_FALSE(TestFunctionPatching(kPatchableCode3, override, prefix)); | |
| EXPECT_FALSE(TestFunctionPatching(kPatchableCode4, override, prefix)); | |
| #if SANITIZER_WINDOWS64 | |
| EXPECT_TRUE(TestFunctionPatching(kPatchableCode6, override, prefix)); | |
| EXPECT_TRUE(TestFunctionPatching(kPatchableCode7, override, prefix)); | |
| EXPECT_TRUE(TestFunctionPatching(kPatchableCode8, override, prefix)); | |
| #endif | |
| EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode1, override, prefix)); | |
| EXPECT_TRUE(TestFunctionPatching(kUnpatchableCode2, override, prefix)); | |
| EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode3, override, prefix)); | |
| EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode4, override, prefix)); | |
| EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode5, override, prefix)); | |
| EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode6, override, prefix)); | |
| } | |
| TEST(Interception, PatchableFunctionWithTrampoline) { | |
| TestOverrideFunction override = OverrideFunctionWithTrampoline; | |
| FunctionPrefixKind prefix = FunctionPrefixPadding; | |
| EXPECT_TRUE(TestFunctionPatching(kPatchableCode1, override, prefix)); | |
| EXPECT_TRUE(TestFunctionPatching(kPatchableCode2, override, prefix)); | |
| #if SANITIZER_WINDOWS64 | |
| EXPECT_FALSE(TestFunctionPatching(kPatchableCode3, override, prefix)); | |
| EXPECT_TRUE(TestFunctionPatching(kPatchableCode9, override, prefix)); | |
| EXPECT_TRUE(TestFunctionPatching(kPatchableCode10, override, prefix)); | |
| EXPECT_TRUE(TestFunctionPatching(kPatchableCode11, override, prefix)); | |
| EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode7, override, prefix)); | |
| EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode8, override, prefix)); | |
| EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode9, override, prefix)); | |
| #else | |
| EXPECT_TRUE(TestFunctionPatching(kPatchableCode3, override, prefix)); | |
| EXPECT_TRUE(TestFunctionPatching(kPatchableCode12, override, prefix)); | |
| EXPECT_TRUE(TestFunctionPatching(kPatchableCode13, override, prefix)); | |
| #endif | |
| EXPECT_FALSE(TestFunctionPatching(kPatchableCode4, override, prefix)); | |
| EXPECT_TRUE(TestFunctionPatching(kPatchableCode14, override, prefix)); | |
| EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode1, override, prefix)); | |
| EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode2, override, prefix)); | |
| EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode3, override, prefix)); | |
| EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode4, override, prefix)); | |
| EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode5, override, prefix)); | |
| EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode6, override, prefix)); | |
| } | |
| TEST(Interception, UnsupportedInstructionWithTrampoline) { | |
| TestOverrideFunction override = OverrideFunctionWithTrampoline; | |
| FunctionPrefixKind prefix = FunctionPrefixPadding; | |
| static bool reportCalled; | |
| reportCalled = false; | |
| struct Local { | |
| static void Report(const char *format, ...) { | |
| if (reportCalled) | |
| FAIL() << "Report called more times than expected"; | |
| reportCalled = true; | |
| ASSERT_STREQ( | |
| "interception_win: unhandled instruction at %p: %02x %02x %02x %02x " | |
| "%02x %02x %02x %02x\n", | |
| format); | |
| va_list args; | |
| va_start(args, format); | |
| u8 *ptr = va_arg(args, u8 *); | |
| for (int i = 0; i < 8; i++) EXPECT_EQ(kUnsupportedCode1[i], ptr[i]); | |
| int bytes[8]; | |
| for (int i = 0; i < 8; i++) { | |
| bytes[i] = va_arg(args, int); | |
| EXPECT_EQ(kUnsupportedCode1[i], bytes[i]); | |
| } | |
| va_end(args); | |
| } | |
| }; | |
| SetErrorReportCallback(Local::Report); | |
| EXPECT_FALSE(TestFunctionPatching(kUnsupportedCode1, override, prefix)); | |
| SetErrorReportCallback(nullptr); | |
| if (!reportCalled) | |
| ADD_FAILURE() << "Report not called"; | |
| } | |
| TEST(Interception, PatchableFunctionPadding) { | |
| TestOverrideFunction override = OverrideFunction; | |
| FunctionPrefixKind prefix = FunctionPrefixPadding; | |
| EXPECT_TRUE(TestFunctionPatching(kPatchableCode1, override, prefix)); | |
| EXPECT_TRUE(TestFunctionPatching(kPatchableCode2, override, prefix)); | |
| #if SANITIZER_WINDOWS64 | |
| EXPECT_FALSE(TestFunctionPatching(kPatchableCode3, override, prefix)); | |
| #else | |
| EXPECT_TRUE(TestFunctionPatching(kPatchableCode3, override, prefix)); | |
| #endif | |
| EXPECT_TRUE(TestFunctionPatching(kPatchableCode4, override, prefix)); | |
| EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode1, override, prefix)); | |
| EXPECT_TRUE(TestFunctionPatching(kUnpatchableCode2, override, prefix)); | |
| EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode3, override, prefix)); | |
| EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode4, override, prefix)); | |
| EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode5, override, prefix)); | |
| EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode6, override, prefix)); | |
| } | |
| TEST(Interception, EmptyExportTable) { | |
| // We try to get a pointer to a function from an executable that doesn't | |
| // export any symbol (empty export table). | |
| uptr FunPtr = InternalGetProcAddress((void *)GetModuleHandleA(0), "example"); | |
| EXPECT_EQ(0U, FunPtr); | |
| } | |
| const struct InstructionSizeData { | |
| size_t size; // hold instruction size or 0 for failure, | |
| // e.g. on control instructions | |
| u8 instr[16]; | |
| size_t rel_offset; // adjustment for RIP-relative addresses when copying | |
| // instructions during hooking via hotpatch or trampoline. | |
| const char *comment; | |
| } data[] = { | |
| // clang-format off | |
| // sorted list | |
| { 0, {0x70, 0x71}, 0, "70 XX : jo XX (short conditional jump)"}, | |
| { 0, {0x71, 0x71}, 0, "71 XX : jno XX (short conditional jump)"}, | |
| { 0, {0x72, 0x71}, 0, "72 XX : jb XX (short conditional jump)"}, | |
| { 0, {0x73, 0x71}, 0, "73 XX : jae XX (short conditional jump)"}, | |
| { 0, {0x74, 0x71}, 0, "74 XX : je XX (short conditional jump)"}, | |
| { 0, {0x75, 0x71}, 0, "75 XX : jne XX (short conditional jump)"}, | |
| { 0, {0x76, 0x71}, 0, "76 XX : jbe XX (short conditional jump)"}, | |
| { 0, {0x77, 0x71}, 0, "77 XX : ja XX (short conditional jump)"}, | |
| { 0, {0x78, 0x71}, 0, "78 XX : js XX (short conditional jump)"}, | |
| { 0, {0x79, 0x71}, 0, "79 XX : jns XX (short conditional jump)"}, | |
| { 0, {0x7A, 0x71}, 0, "7A XX : jp XX (short conditional jump)"}, | |
| { 0, {0x7B, 0x71}, 0, "7B XX : jnp XX (short conditional jump)"}, | |
| { 0, {0x7C, 0x71}, 0, "7C XX : jl XX (short conditional jump)"}, | |
| { 0, {0x7D, 0x71}, 0, "7D XX : jge XX (short conditional jump)"}, | |
| { 0, {0x7E, 0x71}, 0, "7E XX : jle XX (short conditional jump)"}, | |
| { 0, {0x7F, 0x71}, 0, "7F XX : jg XX (short conditional jump)"}, | |
| { 0, {0xE8, 0x71, 0x72, 0x73, 0x74}, 0, "E8 XX XX XX XX : call <func>"}, | |
| { 0, {0xE9, 0x71, 0x72, 0x73, 0x74}, 0, "E9 XX XX XX XX : jmp <label>"}, | |
| { 0, {0xEB, 0x71}, 0, "EB XX : jmp XX (short jump)"}, | |
| { 0, {0xFF, 0x25, 0x72, 0x73, 0x74, 0x75}, 0, "FF 25 XX YY ZZ WW : jmp dword ptr ds:[WWZZYYXX]"}, | |
| { 1, {0x50}, 0, "50 : push eax / rax"}, | |
| { 1, {0x51}, 0, "51 : push ecx / rcx"}, | |
| { 1, {0x52}, 0, "52 : push edx / rdx"}, | |
| { 1, {0x53}, 0, "53 : push ebx / rbx"}, | |
| { 1, {0x54}, 0, "54 : push esp / rsp"}, | |
| { 1, {0x55}, 0, "55 : push ebp / rbp"}, | |
| { 1, {0x56}, 0, "56 : push esi / rsi"}, | |
| { 1, {0x57}, 0, "57 : push edi / rdi"}, | |
| { 1, {0x5D}, 0, "5D : pop ebp / rbp"}, | |
| { 1, {0x90}, 0, "90 : nop"}, | |
| { 1, {0xC3}, 0, "C3 : ret (for small/empty function interception"}, | |
| { 1, {0xCC}, 0, "CC : int 3 i.e. registering weak functions)"}, | |
| { 2, {0x31, 0xC0}, 0, "31 C0 : xor eax, eax"}, | |
| { 2, {0x31, 0xC9}, 0, "31 C9 : xor ecx, ecx"}, | |
| { 2, {0x31, 0xD2}, 0, "31 D2 : xor edx, edx"}, | |
| { 2, {0x33, 0xC0}, 0, "33 C0 : xor eax, eax"}, | |
| { 2, {0x33, 0xC9}, 0, "33 C9 : xor ecx, ecx"}, | |
| { 2, {0x33, 0xD2}, 0, "33 D2 : xor edx, edx"}, | |
| { 2, {0x33, 0xFF}, 0, "33 FF : xor edi, edi"}, | |
| { 2, {0x66, 0x90}, 0, "66 90 : xchg %ax,%ax (Two-byte NOP)"}, | |
| { 2, {0x6A, 0x71}, 0, "6A XX : push XX"}, | |
| { 2, {0x84, 0xC0}, 0, "84 C0 : test al,al"}, | |
| { 2, {0x84, 0xC9}, 0, "84 C9 : test cl,cl"}, | |
| { 2, {0x84, 0xD2}, 0, "84 D2 : test dl,dl"}, | |
| { 2, {0x84, 0xDB}, 0, "84 DB : test bl,bl"}, | |
| { 2, {0x85, 0xD2}, 0, "85 D2 : test edx, edx"}, | |
| { 2, {0x89, 0xc8}, 0, "89 C8 : mov eax, ecx"}, | |
| { 2, {0x89, 0xD1}, 0, "89 D1 : mov ecx, edx"}, | |
| { 2, {0x89, 0xE5}, 0, "89 E5 : mov ebp, esp"}, | |
| { 2, {0x8A, 0x01}, 0, "8A 01 : mov al, byte ptr [ecx]"}, | |
| { 2, {0x8B, 0xC1}, 0, "8B C1 : mov eax, ecx"}, | |
| { 2, {0x8B, 0xEC}, 0, "8B EC : mov ebp, esp"}, | |
| { 2, {0x8B, 0xFF}, 0, "8B FF : mov edi, edi"}, | |
| { 3, {0x80, 0x39, 0x72}, 0, "80 39 XX : cmp BYTE PTR [rcx], XX"}, | |
| { 3, {0x80, 0x3A, 0x72}, 0, "80 3A XX : cmp BYTE PTR [rdx], XX"}, | |
| { 3, {0x83, 0xE4, 0x72}, 0, "83 E4 XX : and esp, XX"}, | |
| { 3, {0x83, 0xEC, 0x72}, 0, "83 EC XX : sub esp, XX"}, | |
| { 3, {0x8B, 0x4D, 0x72}, 0, "8B 4D XX : mov XX(%ebp), ecx"}, | |
| { 3, {0x8B, 0x55, 0x72}, 0, "8B 55 XX : mov XX(%ebp), edx"}, | |
| { 3, {0x8B, 0x75, 0x72}, 0, "8B 75 XX : mov XX(%ebp), esp"}, | |
| { 3, {0x8D, 0x45, 0x72}, 0, "8D 45 XX : lea eax, [ebp + XX]"}, | |
| { 3, {0xc2, 0x71, 0x72}, 0, "C2 XX XX : ret XX (needed for registering weak functions)"}, | |
| { 4, {0x66, 0x0f, 0xef, 0xc0}, 0, "66 0f ef c0 : pxor xmm0, xmm0"}, | |
| { 4, {0x66, 0x83, 0x3A, 0x73}, 0, "66 83 3A XX : cmp word ptr [rdx], XX"}, | |
| { 4, {0x8D, 0x44, 0x24, 0x73}, 0, "8D 44 24 XX : lea eax, [esp + XX]"}, | |
| { 4, {0x8D, 0x4C, 0x24, 0x73}, 0, "8D 4C 24 XX : lea ecx, [esp + XX]"}, | |
| { 4, {0xF3, 0x0F, 0x1E, 0xFA}, 0, "F3 0F 1E FA : endbr64"}, | |
| { 4, {0xF3, 0x0F, 0x1E, 0xFB}, 0, "F3 0F 1E FB : endbr32"}, | |
| { 4, {0xFF, 0x74, 0x24, 0x73}, 0, "FF 74 24 XX : push qword ptr [rsp + XX]"}, | |
| { 5, {0x0F, 0xB6, 0x44, 0x24, 0x74}, 0, "0F B6 44 24 XX : movzx eax, byte ptr [esp + XX]"}, | |
| { 5, {0x0F, 0xB6, 0x4C, 0x24, 0x74}, 0, "0F B6 4C 24 XX : movzx ecx, byte ptr [esp + XX]"}, | |
| { 5, {0x68, 0x71, 0x72, 0x73, 0x74}, 0, "68 XX XX XX XX : push imm32"}, | |
| { 5, {0xb8, 0x71, 0x72, 0x73, 0x74}, 0, "b8 XX XX XX XX : mov eax, XX XX XX XX"}, | |
| { 5, {0xB9, 0x71, 0x72, 0x73, 0x74}, 0, "b9 XX XX XX XX : mov ecx, XX XX XX XX"}, | |
| { 5, {0xBA, 0x71, 0x72, 0x73, 0x74}, 0, "ba XX XX XX XX : mov edx, XX XX XX XX"}, | |
| { 6, {0x81, 0xEC, 0x72, 0x73, 0x74, 0x75}, 0, "81 EC XX XX XX XX : sub esp, XX XX XX XX"}, | |
| { 6, {0xFF, 0x89, 0x72, 0x73, 0x74, 0x75}, 0, "FF 89 XX XX XX XX : dec dword ptr [ecx + XX XX XX XX]"}, | |
| { 7, {0x8D, 0xA4, 0x24, 0x73, 0x74, 0x75, 0x76}, 0, "8D A4 24 XX XX XX XX : lea esp, [esp + XX XX XX XX]"}, | |
| #if SANITIZER_WINDOWS_x64 | |
| // sorted list | |
| { 2, {0x40, 0x50}, 0, "40 50 : push rax"}, | |
| { 2, {0x40, 0x51}, 0, "40 51 : push rcx"}, | |
| { 2, {0x40, 0x52}, 0, "40 52 : push rdx"}, | |
| { 2, {0x40, 0x53}, 0, "40 53 : push rbx"}, | |
| { 2, {0x40, 0x54}, 0, "40 54 : push rsp"}, | |
| { 2, {0x40, 0x55}, 0, "40 55 : push rbp"}, | |
| { 2, {0x40, 0x56}, 0, "40 56 : push rsi"}, | |
| { 2, {0x40, 0x57}, 0, "40 57 : push rdi"}, | |
| { 2, {0x41, 0x54}, 0, "41 54 : push r12"}, | |
| { 2, {0x41, 0x55}, 0, "41 55 : push r13"}, | |
| { 2, {0x41, 0x56}, 0, "41 56 : push r14"}, | |
| { 2, {0x41, 0x57}, 0, "41 57 : push r15"}, | |
| { 2, {0x84, 0xc0}, 0, "84 c0 : test al, al"}, | |
| { 2, {0x8a, 0x01}, 0, "8a 01 : mov al, byte ptr [rcx]"}, | |
| { 3, {0x0f, 0xb6, 0x01}, 0, "0f b6 01 : movzx eax, BYTE PTR [rcx]"}, | |
| { 3, {0x0f, 0xb6, 0x09}, 0, "0f b6 09 : movzx ecx, BYTE PTR [rcx]"}, | |
| { 3, {0x0f, 0xb6, 0x11}, 0, "0f b6 11 : movzx edx, BYTE PTR [rcx]"}, | |
| { 3, {0x0f, 0xb6, 0xc2}, 0, "0f b6 c2 : movzx eax, dl"}, | |
| { 3, {0x0f, 0xb6, 0xd2}, 0, "0f b6 d2 : movzx edx, dl"}, | |
| { 3, {0x0f, 0xb7, 0x02}, 0, "0f b7 02 : movzx eax, WORD PTR [rdx]"}, | |
| { 3, {0x0f, 0xb7, 0x10}, 0, "0f b7 10 : movzx edx, WORD PTR [rax]"}, | |
| { 3, {0x0f, 0xbe, 0xd2}, 0, "0f be d2 : movsx edx, dl"}, | |
| { 3, {0x41, 0x8b, 0xc0}, 0, "41 8b c0 : mov eax, r8d"}, | |
| { 3, {0x41, 0x8b, 0xc1}, 0, "41 8b c1 : mov eax, r9d"}, | |
| { 3, {0x41, 0x8b, 0xc2}, 0, "41 8b c2 : mov eax, r10d"}, | |
| { 3, {0x41, 0x8b, 0xc3}, 0, "41 8b c3 : mov eax, r11d"}, | |
| { 3, {0x41, 0x8b, 0xc4}, 0, "41 8b c4 : mov eax, r12d"}, | |
| { 3, {0x45, 0x31, 0xc0}, 0, "45 31 c0 : xor r8d,r8d"}, | |
| { 3, {0x45, 0x31, 0xc9}, 0, "45 31 c9 : xor r9d,r9d"}, | |
| { 3, {0x45, 0x33, 0xc0}, 0, "45 33 c0 : xor r8d, r8d"}, | |
| { 3, {0x45, 0x33, 0xc9}, 0, "45 33 c9 : xor r9d, r9d"}, | |
| { 3, {0x45, 0x33, 0xd2}, 0, "45 33 d2 : xor r10d, r10d"}, | |
| { 3, {0x45, 0x33, 0xdb}, 0, "45 33 db : xor r11d, r11d"}, | |
| { 3, {0x45, 0x84, 0xc0}, 0, "45 84 c0 : test r8b,r8b"}, | |
| { 3, {0x45, 0x84, 0xc9}, 0, "45 84 c9 : test r9b,r9b"}, | |
| { 3, {0x45, 0x84, 0xd2}, 0, "45 84 d2 : test r10b,r10b"}, | |
| { 3, {0x48, 0x29, 0xd1}, 0, "48 29 d1 : sub rcx, rdx"}, | |
| { 3, {0x48, 0x2b, 0xca}, 0, "48 2b ca : sub rcx, rdx"}, | |
| { 3, {0x48, 0x2b, 0xd1}, 0, "48 2b d1 : sub rdx, rcx"}, | |
| { 3, {0x48, 0x3b, 0xca}, 0, "48 3b ca : cmp rcx, rdx"}, | |
| { 3, {0x48, 0x85, 0xc0}, 0, "48 85 c0 : test rax, rax"}, | |
| { 3, {0x48, 0x85, 0xc9}, 0, "48 85 c9 : test rcx, rcx"}, | |
| { 3, {0x48, 0x85, 0xd2}, 0, "48 85 d2 : test rdx, rdx"}, | |
| { 3, {0x48, 0x85, 0xdb}, 0, "48 85 db : test rbx, rbx"}, | |
| { 3, {0x48, 0x85, 0xe4}, 0, "48 85 e4 : test rsp, rsp"}, | |
| { 3, {0x48, 0x85, 0xed}, 0, "48 85 ed : test rbp, rbp"}, | |
| { 3, {0x48, 0x89, 0xc8}, 0, "48 89 c8 : mov rax,rcx"}, | |
| { 3, {0x48, 0x89, 0xcb}, 0, "48 89 cb : mov rbx,rcx"}, | |
| { 3, {0x48, 0x89, 0xd0}, 0, "48 89 d0 : mov rax,rdx"}, | |
| { 3, {0x48, 0x89, 0xd1}, 0, "48 89 d1 : mov rcx,rdx"}, | |
| { 3, {0x48, 0x89, 0xd3}, 0, "48 89 d3 : mov rbx,rdx"}, | |
| { 3, {0x48, 0x89, 0xe5}, 0, "48 89 e5 : mov rbp, rsp"}, | |
| { 3, {0x48, 0x8b, 0xc1}, 0, "48 8b c1 : mov rax, rcx"}, | |
| { 3, {0x48, 0x8b, 0xc4}, 0, "48 8b c4 : mov rax, rsp"}, | |
| { 3, {0x48, 0x8b, 0xd1}, 0, "48 8b d1 : mov rdx, rcx"}, | |
| { 3, {0x48, 0xf7, 0xd9}, 0, "48 f7 d9 : neg rcx"}, | |
| { 3, {0x48, 0xff, 0xc0}, 0, "48 ff c0 : inc rax"}, | |
| { 3, {0x48, 0xff, 0xc1}, 0, "48 ff c1 : inc rcx"}, | |
| { 3, {0x48, 0xff, 0xc2}, 0, "48 ff c2 : inc rdx"}, | |
| { 3, {0x48, 0xff, 0xc3}, 0, "48 ff c3 : inc rbx"}, | |
| { 3, {0x48, 0xff, 0xc6}, 0, "48 ff c6 : inc rsi"}, | |
| { 3, {0x48, 0xff, 0xc7}, 0, "48 ff c7 : inc rdi"}, | |
| { 3, {0x49, 0x89, 0xc8}, 0, "49 89 c8 : mov r8, rcx"}, | |
| { 3, {0x49, 0x89, 0xc9}, 0, "49 89 c9 : mov r9, rcx"}, | |
| { 3, {0x49, 0x89, 0xca}, 0, "49 89 ca : mov r10,rcx"}, | |
| { 3, {0x49, 0x89, 0xd0}, 0, "49 89 d0 : mov r8, rdx"}, | |
| { 3, {0x49, 0x89, 0xd1}, 0, "49 89 d1 : mov r9, rdx"}, | |
| { 3, {0x49, 0x89, 0xd2}, 0, "49 89 d2 : mov r10, rdx"}, | |
| { 3, {0x49, 0x89, 0xd3}, 0, "49 89 d3 : mov r11, rdx"}, | |
| { 3, {0x49, 0xff, 0xc0}, 0, "49 ff c0 : inc r8"}, | |
| { 3, {0x49, 0xff, 0xc1}, 0, "49 ff c1 : inc r9"}, | |
| { 3, {0x49, 0xff, 0xc2}, 0, "49 ff c2 : inc r10"}, | |
| { 3, {0x49, 0xff, 0xc3}, 0, "49 ff c3 : inc r11"}, | |
| { 3, {0x49, 0xff, 0xc4}, 0, "49 ff c4 : inc r12"}, | |
| { 3, {0x49, 0xff, 0xc5}, 0, "49 ff c5 : inc r13"}, | |
| { 3, {0x49, 0xff, 0xc6}, 0, "49 ff c6 : inc r14"}, | |
| { 3, {0x49, 0xff, 0xc7}, 0, "49 ff c7 : inc r15"}, | |
| { 3, {0x4c, 0x2b, 0xc2}, 0, "4c 2b c2 : sub r8, rdx"}, | |
| { 3, {0x4c, 0x8b, 0xc1}, 0, "4c 8b c1 : mov r8, rcx"}, | |
| { 3, {0x4c, 0x8b, 0xc9}, 0, "4c 8b c9 : mov r9, rcx"}, | |
| { 3, {0x4c, 0x8b, 0xd1}, 0, "4c 8b d1 : mov r10, rcx"}, | |
| { 3, {0x4c, 0x8b, 0xd2}, 0, "4c 8b d2 : mov r10, rdx"}, | |
| { 3, {0x4c, 0x8b, 0xd9}, 0, "4c 8b d9 : mov r11, rcx"}, | |
| { 3, {0x4c, 0x8b, 0xdc}, 0, "4c 8b dc : mov r11, rsp"}, | |
| { 3, {0x4d, 0x0b, 0xc0}, 0, "4d 0b c0 : or r8, r8"}, | |
| { 3, {0x4d, 0x85, 0xc0}, 0, "4d 85 c0 : test r8, r8"}, | |
| { 3, {0x4d, 0x85, 0xc9}, 0, "4d 85 c9 : test r9, r9"}, | |
| { 3, {0x4d, 0x85, 0xd2}, 0, "4d 85 d2 : test r10, r10"}, | |
| { 3, {0x4d, 0x85, 0xdb}, 0, "4d 85 db : test r11, r11"}, | |
| { 3, {0x4d, 0x85, 0xe4}, 0, "4d 85 e4 : test r12, r12"}, | |
| { 3, {0x4d, 0x85, 0xed}, 0, "4d 85 ed : test r13, r13"}, | |
| { 3, {0x4d, 0x85, 0xf6}, 0, "4d 85 f6 : test r14, r14"}, | |
| { 3, {0x4d, 0x85, 0xff}, 0, "4d 85 ff : test r15, r15"}, | |
| { 3, {0xf6, 0xc1, 0x72}, 0, "f6 c1 XX : test cl, XX"}, | |
| { 4, {0x44, 0x0f, 0xb6, 0x01}, 0, "44 0f b6 01 : movzx r8d, BYTE PTR [rcx]"}, | |
| { 4, {0x44, 0x0f, 0xb6, 0x09}, 0, "44 0f b6 09 : movzx r9d, BYTE PTR [rcx]"}, | |
| { 4, {0x44, 0x0f, 0xb6, 0x0a}, 0, "44 0f b6 0a : movzx r8d, BYTE PTR [rdx]"}, | |
| { 4, {0x44, 0x0f, 0xb6, 0x11}, 0, "44 0f b6 11 : movzx r10d, BYTE PTR [rcx]"}, | |
| { 4, {0x44, 0x0f, 0xb6, 0x1a}, 0, "44 0f b6 1a : movzx r11d, BYTE PTR [rdx]"}, | |
| { 4, {0x44, 0x8d, 0x42, 0x73}, 0, "44 8d 42 XX : lea r8d , [rdx + XX]"}, | |
| { 4, {0x48, 0x83, 0xec, 0x73}, 0, "48 83 ec XX : sub rsp, XX"}, | |
| { 4, {0x48, 0x89, 0x58, 0x73}, 0, "48 89 58 XX : mov QWORD PTR[rax + XX], rbx"}, | |
| { 4, {0x49, 0x83, 0xf8, 0x73}, 0, "49 83 f8 XX : cmp r8, XX"}, | |
| { 4, {0x49, 0x8d, 0x48, 0x73}, 0, "49 8d 48 XX : lea rcx, [...]"}, | |
| { 4, {0x4c, 0x8d, 0x04, 0x73}, 0, "4c 8d 04 XX : lea r8, [...]"}, | |
| { 4, {0x4e, 0x8d, 0x14, 0x73}, 0, "4e 8d 14 XX : lea r10, [...]"}, | |
| { 4, {0x4F, 0x8D, 0x0C, 0x73}, 0, "4F 8D 0C XX : lea r9, [...]"}, | |
| { 4, {0x66, 0x83, 0x39, 0x73}, 0, "66 83 39 XX : cmp WORD PTR [rcx], XX"}, | |
| { 4, {0x80, 0x78, 0x72, 0x73}, 0, "80 78 YY XX : cmp BYTE PTR [rax+YY], XX"}, | |
| { 4, {0x80, 0x79, 0x72, 0x73}, 0, "80 79 YY XX : cmp BYTE ptr [rcx+YY], XX"}, | |
| { 4, {0x80, 0x7A, 0x72, 0x73}, 0, "80 7A YY XX : cmp BYTE PTR [rdx+YY], XX"}, | |
| { 4, {0x80, 0x7B, 0x72, 0x73}, 0, "80 7B YY XX : cmp BYTE PTR [rbx+YY], XX"}, | |
| { 4, {0x80, 0x7D, 0x72, 0x73}, 0, "80 7D YY XX : cmp BYTE PTR [rbp+YY], XX"}, | |
| { 4, {0x80, 0x7E, 0x72, 0x73}, 0, "80 7E YY XX : cmp BYTE PTR [rsi+YY], XX"}, | |
| { 4, {0x89, 0x54, 0x24, 0x73}, 0, "89 54 24 XX : mov DWORD PTR[rsp + XX], edx"}, | |
| { 5, {0x0F, 0x1F, 0x44, 0x73, 0x74}, 0, "0F 1F 44 XX XX : nop DWORD PTR [...]"}, | |
| { 5, {0x44, 0x89, 0x44, 0x24, 0x74}, 0, "44 89 44 24 XX : mov DWORD PTR [rsp + XX], r8d"}, | |
| { 5, {0x44, 0x89, 0x4c, 0x24, 0x74}, 0, "44 89 4c 24 XX : mov DWORD PTR [rsp + XX], r9d"}, | |
| { 5, {0x48, 0x89, 0x4C, 0x24, 0x74}, 0, "48 89 4C 24 XX : mov QWORD PTR [rsp + XX], rcx"}, | |
| { 5, {0x48, 0x89, 0x54, 0x24, 0x74}, 0, "48 89 54 24 XX : mov QWORD PTR [rsp + XX], rdx"}, | |
| { 5, {0x48, 0x89, 0x5c, 0x24, 0x74}, 0, "48 89 5c 24 XX : mov QWORD PTR [rsp + XX], rbx"}, | |
| { 5, {0x48, 0x89, 0x6c, 0x24, 0x74}, 0, "48 89 6C 24 XX : mov QWORD ptr [rsp + XX], rbp"}, | |
| { 5, {0x48, 0x89, 0x74, 0x24, 0x74}, 0, "48 89 74 24 XX : mov QWORD PTR [rsp + XX], rsi"}, | |
| { 5, {0x48, 0x89, 0x7c, 0x24, 0x74}, 0, "48 89 7c 24 XX : mov QWORD PTR [rsp + XX], rdi"}, | |
| { 5, {0x48, 0x8b, 0x44, 0x24, 0x74}, 0, "48 8b 44 24 XX : mov rax, QWORD ptr [rsp + XX]"}, | |
| { 5, {0x48, 0x8d, 0x6c, 0x24, 0x74}, 0, "48 8d 6c 24 XX : lea rbp, [rsp + XX]"}, | |
| { 5, {0x4c, 0x89, 0x44, 0x24, 0x74}, 0, "4c 89 44 24 XX : mov QWORD PTR [rsp + XX], r8"}, | |
| { 5, {0x4c, 0x89, 0x4c, 0x24, 0x74}, 0, "4c 89 4c 24 XX : mov QWORD PTR [rsp + XX], r9"}, | |
| { 5, {0x66, 0x48, 0x0F, 0x7E, 0xC0}, 0, "66 48 0F 7E C0 : movq rax, xmm0"}, | |
| { 5, {0x83, 0x44, 0x72, 0x73, 0x74}, 0, "83 44 72 XX YY : add DWORD PTR [rdx+rsi*2+XX],YY"}, | |
| { 5, {0x83, 0x64, 0x24, 0x73, 0x74}, 0, "83 64 24 XX YY : and DWORD PTR [rsp+XX], YY"}, | |
| { 6, {0x41, 0xB8, 0x72, 0x73, 0x74, 0x75}, 0, "41 B8 XX XX XX XX : mov r8d, XX XX XX XX"}, | |
| { 6, {0x41, 0xB9, 0x72, 0x73, 0x74, 0x75}, 0, "41 B9 XX XX XX XX : mov r9d, XX XX XX XX"}, | |
| { 6, {0x48, 0x83, 0x64, 0x24, 0x74, 0x75}, 0, "48 83 64 24 XX YY : and QWORD PTR [rsp + XX], YY"}, | |
| { 6, {0x66, 0x81, 0x78, 0x73, 0x74, 0x75}, 0, "66 81 78 XX YY YY : cmp WORD PTR [rax+XX], YY YY"}, | |
| { 6, {0x66, 0x81, 0x79, 0x73, 0x74, 0x75}, 0, "66 81 79 XX YY YY : cmp WORD PTR [rcx+XX], YY YY"}, | |
| { 6, {0x66, 0x81, 0x7a, 0x73, 0x74, 0x75}, 0, "66 81 7a XX YY YY : cmp WORD PTR [rdx+XX], YY YY"}, | |
| { 6, {0x66, 0x81, 0x7b, 0x73, 0x74, 0x75}, 0, "66 81 7b XX YY YY : cmp WORD PTR [rbx+XX], YY YY"}, | |
| { 6, {0x66, 0x81, 0x7e, 0x73, 0x74, 0x75}, 0, "66 81 7e XX YY YY : cmp WORD PTR [rsi+XX], YY YY"}, | |
| { 6, {0x66, 0x81, 0x7f, 0x73, 0x74, 0x75}, 0, "66 81 7f XX YY YY : cmp WORD PTR [rdi+XX], YY YY"}, | |
| { 6, {0x8A, 0x05, 0x72, 0x73, 0x74, 0x75}, 2, "8A 05 XX XX XX XX : mov al, byte ptr [XX XX XX XX]"}, | |
| { 6, {0x8B, 0x05, 0x72, 0x73, 0x74, 0x75}, 2, "8B 05 XX XX XX XX : mov eax, dword ptr [XX XX XX XX]"}, | |
| { 6, {0xF2, 0x0f, 0x11, 0x44, 0x24, 0x75}, 0, "f2 0f 11 44 24 XX : movsd QWORD PTR [rsp + XX], xmm0"}, | |
| { 6, {0xF2, 0x0f, 0x11, 0x4c, 0x24, 0x75}, 0, "f2 0f 11 4c 24 XX : movsd QWORD PTR [rsp + XX], xmm1"}, | |
| { 6, {0xF2, 0x0f, 0x11, 0x54, 0x24, 0x75}, 0, "f2 0f 11 54 24 XX : movsd QWORD PTR [rsp + XX], xmm2"}, | |
| { 6, {0xF2, 0x0f, 0x11, 0x5c, 0x24, 0x75}, 0, "f2 0f 11 5c 24 XX : movsd QWORD PTR [rsp + XX], xmm3"}, | |
| { 6, {0xF2, 0x0f, 0x11, 0x64, 0x24, 0x75}, 0, "f2 0f 11 64 24 XX : movsd QWORD PTR [rsp + XX], xmm4"}, | |
| { 7, {0x48, 0x81, 0xec, 0x73, 0x74, 0x75, 0x76}, 0, "48 81 EC XX XX XX XX : sub rsp, XXXXXXXX"}, | |
| { 7, {0x48, 0x89, 0x0d, 0x73, 0x74, 0x75, 0x76}, 3, "48 89 0d XX XX XX XX : mov QWORD PTR [rip + XXXXXXXX], rcx"}, | |
| { 7, {0x48, 0x89, 0x15, 0x73, 0x74, 0x75, 0x76}, 3, "48 89 15 XX XX XX XX : mov QWORD PTR [rip + XXXXXXXX], rdx"}, | |
| { 7, {0x48, 0x8b, 0x05, 0x73, 0x74, 0x75, 0x76}, 3, "48 8b 05 XX XX XX XX : mov rax, QWORD PTR [rip + XXXXXXXX]"}, | |
| { 7, {0x48, 0x8d, 0x05, 0x73, 0x74, 0x75, 0x76}, 3, "48 8d 05 XX XX XX XX : lea rax, QWORD PTR [rip + XXXXXXXX]"}, | |
| { 7, {0x48, 0xc7, 0xc0, 0x73, 0x74, 0x75, 0x76}, 0, "48 C7 C0 XX XX XX XX : mov rax, XX XX XX XX"}, | |
| { 7, {0x48, 0xff, 0x25, 0x73, 0x74, 0x75, 0x76}, 3, "48 ff 25 XX XX XX XX : rex.W jmp QWORD PTR [rip + XXXXXXXX]"}, | |
| { 7, {0x4C, 0x8D, 0x15, 0x73, 0x74, 0x75, 0x76}, 3, "4c 8d 15 XX XX XX XX : lea r10, [rip + XX]"}, | |
| { 7, {0x81, 0x78, 0x72, 0x73, 0x74, 0x75, 0x76}, 0, "81 78 YY XX XX XX XX : cmp DWORD PTR [rax+YY], XX XX XX XX"}, | |
| { 7, {0x81, 0x79, 0x72, 0x73, 0x74, 0x75, 0x76}, 0, "81 79 YY XX XX XX XX : cmp dword ptr [rcx+YY], XX XX XX XX"}, | |
| { 7, {0x81, 0x7A, 0x72, 0x73, 0x74, 0x75, 0x76}, 0, "81 7A YY XX XX XX XX : cmp DWORD PTR [rdx+YY], XX XX XX XX"}, | |
| { 7, {0x81, 0x7B, 0x72, 0x73, 0x74, 0x75, 0x76}, 0, "81 7B YY XX XX XX XX : cmp DWORD PTR [rbx+YY], XX XX XX XX"}, | |
| { 7, {0x81, 0x7D, 0x72, 0x73, 0x74, 0x75, 0x76}, 0, "81 7D YY XX XX XX XX : cmp DWORD PTR [rbp+YY], XX XX XX XX"}, | |
| { 7, {0x81, 0x7E, 0x72, 0x73, 0x74, 0x75, 0x76}, 0, "81 7E YY XX XX XX XX : cmp DWORD PTR [rsi+YY], XX XX XX XX"}, | |
| { 8, {0x41, 0x81, 0x78, 0x73, 0x74, 0x75, 0x76, 0x77}, 0, "41 81 78 XX YY YY YY YY : cmp DWORD PTR [r8+YY], XX XX XX XX"}, | |
| { 8, {0x41, 0x81, 0x79, 0x73, 0x74, 0x75, 0x76, 0x77}, 0, "41 81 79 XX YY YY YY YY : cmp DWORD PTR [r9+YY], XX XX XX XX"}, | |
| { 8, {0x41, 0x81, 0x7a, 0x73, 0x74, 0x75, 0x76, 0x77}, 0, "41 81 7a XX YY YY YY YY : cmp DWORD PTR [r10+YY], XX XX XX XX"}, | |
| { 8, {0x41, 0x81, 0x7b, 0x73, 0x74, 0x75, 0x76, 0x77}, 0, "41 81 7b XX YY YY YY YY : cmp DWORD PTR [r11+YY], XX XX XX XX"}, | |
| { 8, {0x41, 0x81, 0x7d, 0x73, 0x74, 0x75, 0x76, 0x77}, 0, "41 81 7d XX YY YY YY YY : cmp DWORD PTR [r13+YY], XX XX XX XX"}, | |
| { 8, {0x41, 0x81, 0x7e, 0x73, 0x74, 0x75, 0x76, 0x77}, 0, "41 81 7e XX YY YY YY YY : cmp DWORD PTR [r14+YY], XX XX XX XX"}, | |
| { 8, {0x41, 0x81, 0x7f, 0x73, 0x74, 0x75, 0x76, 0x77}, 0, "41 81 7f YY XX XX XX XX : cmp DWORD PTR [r15+YY], XX XX XX XX"}, | |
| { 8, {0x48, 0x8D, 0xA4, 0x24, 0x74, 0x75, 0x76, 0x77}, 0, "48 8D A4 24 XX XX XX XX : lea rsp, [rsp + XX XX XX XX]"}, | |
| { 8, {0x81, 0x7c, 0x24, 0x73, 0x74, 0x75, 0x76, 0x77}, 0, "81 7c 24 YY XX XX XX XX : cmp DWORD PTR [rsp+YY], XX XX XX XX"}, | |
| { 8, {0xc7, 0x44, 0x24, 0x73, 0x74, 0x75, 0x76, 0x77}, 0, "C7 44 24 XX YY YY YY YY : mov dword ptr [rsp + XX], YYYYYYYY"}, | |
| { 9, {0x41, 0x81, 0x7c, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78}, 0, "41 81 7c ZZ YY XX XX XX XX : cmp DWORD PTR [reg+reg*n+YY], XX XX XX XX"}, | |
| { 9, {0xA1, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78}, 0, "A1 XX XX XX XX XX XX XX XX : movabs eax, dword ptr ds:[XXXXXXXX]"}, | |
| {10, {0x48, 0xb8, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79}, 0, "48 b8 XX XX XX XX XX XX XX XX : movabsq XX XX XX XX XX XX XX XX, rax"}, | |
| {10, {0x48, 0xba, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79}, 0, "48 ba XX XX XX XX XX XX XX XX : movabsq XX XX XX XX XX XX XX XX, rdx"}, | |
| #else | |
| // sorted list | |
| { 3, {0x8B, 0x45, 0x72}, 0, "8B 45 XX : mov eax, dword ptr [ebp + XX]"}, | |
| { 3, {0x8B, 0x5D, 0x72}, 0, "8B 5D XX : mov ebx, dword ptr [ebp + XX]"}, | |
| { 3, {0x8B, 0x75, 0x72}, 0, "8B 75 XX : mov esi, dword ptr [ebp + XX]"}, | |
| { 3, {0x8B, 0x7D, 0x72}, 0, "8B 7D XX : mov edi, dword ptr [ebp + XX]"}, | |
| { 3, {0xFF, 0x75, 0x72}, 0, "FF 75 XX : push dword ptr [ebp + XX]"}, | |
| { 4, {0x83, 0x7D, 0x72, 0x73}, 0, "83 7D XX YY : cmp dword ptr [ebp + XX], YY"}, | |
| { 4, {0x8A, 0x44, 0x24, 0x73}, 0, "8A 44 24 XX : mov eal, dword ptr [esp + XX]"}, | |
| { 4, {0x8B, 0x44, 0x24, 0x73}, 0, "8B 44 24 XX : mov eax, dword ptr [esp + XX]"}, | |
| { 4, {0x8B, 0x4C, 0x24, 0x73}, 0, "8B 4C 24 XX : mov ecx, dword ptr [esp + XX]"}, | |
| { 4, {0x8B, 0x54, 0x24, 0x73}, 0, "8B 54 24 XX : mov edx, dword ptr [esp + XX]"}, | |
| { 4, {0x8B, 0x5C, 0x24, 0x73}, 0, "8B 5C 24 XX : mov ebx, dword ptr [esp + XX]"}, | |
| { 4, {0x8B, 0x6C, 0x24, 0x73}, 0, "8B 6C 24 XX : mov ebp, dword ptr [esp + XX]"}, | |
| { 4, {0x8B, 0x74, 0x24, 0x73}, 0, "8B 74 24 XX : mov esi, dword ptr [esp + XX]"}, | |
| { 4, {0x8B, 0x7C, 0x24, 0x73}, 0, "8B 7C 24 XX : mov edi, dword ptr [esp + XX]"}, | |
| { 5, {0xA1, 0x71, 0x72, 0x73, 0x74}, 0, "A1 XX XX XX XX : mov eax, dword ptr ds:[XXXXXXXX]"}, | |
| { 6, {0xF7, 0xC1, 0x72, 0x73, 0x74, 0x75}, 0, "F7 C1 XX YY ZZ WW : test ecx, WWZZYYXX"}, | |
| { 7, {0x83, 0x3D, 0x72, 0x73, 0x74, 0x75, 0x76}, 0, "83 3D XX YY ZZ WW TT : cmp TT, WWZZYYXX"}, | |
| #endif | |
| // clang-format on | |
| }; | |
| std::string dumpInstruction(unsigned arrayIndex, | |
| const InstructionSizeData &data) { | |
| std::stringstream ret; | |
| ret << " with arrayIndex=" << arrayIndex << " {"; | |
| for (size_t i = 0; i < data.size; i++) { | |
| if (i > 0) | |
| ret << ", "; | |
| ret << "0x" << std::setfill('0') << std::setw(2) << std::right << std::hex | |
| << (int)data.instr[i]; | |
| } | |
| ret << "} " << data.comment; | |
| return ret.str(); | |
| } | |
| TEST(Interception, GetInstructionSize) { | |
| for (unsigned i = 0; i < sizeof(data) / sizeof(*data); i++) { | |
| size_t rel_offset = ~0L; | |
| size_t size = __interception::TestOnlyGetInstructionSize( | |
| (uptr)data[i].instr, &rel_offset); | |
| EXPECT_EQ(data[i].size, size) << dumpInstruction(i, data[i]); | |
| EXPECT_EQ(data[i].rel_offset, rel_offset) << dumpInstruction(i, data[i]); | |
| } | |
| } | |
| } // namespace __interception | |
| # endif // !SANITIZER_WINDOWS_ARM64 | |
| #endif // SANITIZER_WINDOWS | |
| #endif // #if !SANITIZER_DEBUG |