//===-- interception_win_test.cc ------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // 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 #if !SANITIZER_DEBUG #if SANITIZER_WINDOWS #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 }; #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 }; #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 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, }; // A buffer holding the dynamically generated code under test. u8* ActiveCode; size_t ActiveCodeLength = 4096; template<class T> static void LoadActiveCode( const T &code, uptr *entry_point, FunctionPrefixKind prefix_kind = FunctionPrefixNone) { if (ActiveCode == nullptr) { ActiveCode = (u8*)::VirtualAlloc(nullptr, ActiveCodeLength, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); ASSERT_NE(ActiveCode, nullptr); } 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) { uptr identity_address; LoadActiveCode(code, &identity_address, prefix_kind); 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); } 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_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)); 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)); #else EXPECT_TRUE(TestFunctionPatching(kPatchableCode3, override, prefix)); #endif EXPECT_FALSE(TestFunctionPatching(kPatchableCode4, 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, 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)); } } // namespace __interception #endif // SANITIZER_WINDOWS #endif // #if !SANITIZER_DEBUG