//===-- 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