diff --git a/amalgamate.py b/amalgamate.py index 2828bb0..2f2b51c 100644 --- a/amalgamate.py +++ b/amalgamate.py @@ -14,7 +14,7 @@ SAFETYHOOK_ROOT / 'include' / 'safetyhook', ] INTERNAL_INCLUDE_PATHS = [SAFETYHOOK_ROOT / 'src'] -INCLUDE_REGEXP = re.compile(r'^#\s*include\s*<((?:safety).*)>\s*$') +INCLUDE_REGEXP = re.compile(r'^#\s*include\s*"((?:safety).*)"\s*$') OUTPUT_DIR = SAFETYHOOK_ROOT / 'amalgamated-dist' FILE_HEADER = ['// DO NOT EDIT. This file is auto-generated by `amalgamate.py`.', ''] @@ -22,8 +22,8 @@ # Python versions before 3.10 don't have the root_dir argument for glob, so we # crudely emulate it here. def glob_in_dir( - pattern: str, - root_dir: Path, + pattern: str, + root_dir: Path, ): cwd = os.getcwd() root_dir = root_dir.resolve() @@ -36,8 +36,8 @@ def glob_in_dir( def find_include_path( - include: str, - search_paths: List[Path], + include: str, + search_paths: List[Path], ) -> Path: for search_path in search_paths: path = search_path / include @@ -48,11 +48,11 @@ def find_include_path( def merge_headers( - *, - header: str, - search_paths: List[Path], - covered_headers: Set[Path], - stack: List[str], + *, + header: str, + search_paths: List[Path], + covered_headers: Set[Path], + stack: List[str], ) -> List[str]: # Locate and load header contents. path = find_include_path(header, search_paths) @@ -69,8 +69,8 @@ def merge_headers( include_stack = [] if stack: include_stack = [ - '//', - '// Include stack:', + '//', + '// Include stack:', *(f'// - {x}' for x in stack) ] @@ -89,12 +89,12 @@ def merge_headers( if not match: filtered.append(line) continue - + # Recurse into includes. filtered += merge_headers( - header=match.group(1), + header=match.group(1), search_paths=search_paths, - covered_headers=covered_headers, + covered_headers=covered_headers, stack=stack + [header], ) @@ -105,7 +105,7 @@ def merge_sources(*, source_dir: Path, covered_headers: Set[Path]): output = [ '#define NOMINMAX', '', - '#include ', + '#include "safetyhook.hpp"', '', ] @@ -165,9 +165,9 @@ def main(): covered_headers = set() with open(OUTPUT_DIR / 'safetyhook.hpp', 'w') as f: f.write('\n'.join(FILE_HEADER + merge_headers( - header='safetyhook.hpp', + header='safetyhook.hpp', search_paths=PUBLIC_INCLUDE_PATHS, - covered_headers=covered_headers, + covered_headers=covered_headers, stack=[], ))) @@ -182,4 +182,3 @@ def main(): if __name__ == '__main__': main() - diff --git a/example/midhook.cpp b/example/midhook.cpp index 0823f9e..f9b1f78 100644 --- a/example/midhook.cpp +++ b/example/midhook.cpp @@ -1,9 +1,9 @@ #include -#if __has_include() -#include -#elif __has_include() -#include +#if __has_include("Zydis/Zydis.h") +#include "Zydis/Zydis.h" +#elif __has_include("Zydis.h") +#include "Zydis.h" #else #error "Zydis not found" #endif @@ -15,9 +15,9 @@ __declspec(noinline) int add_42(int a) { } void hooked_add_42(SafetyHookContext& ctx) { -#ifdef _M_X64 +#if SAFETYHOOK_ARCH_X86_64 ctx.rax = 1337; -#else +#elif SAFETYHOOK_ARCH_X86_32 ctx.eax = 1337; #endif } @@ -30,12 +30,10 @@ int main() { // Let's disassemble add_42 and hook its RET. ZydisDecoder decoder{}; -#if defined(_M_X64) +#if SAFETYHOOK_ARCH_X86_64 ZydisDecoderInit(&decoder, ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_STACK_WIDTH_64); -#elif defined(_M_IX86) +#elif SAFETYHOOK_ARCH_X86_32 ZydisDecoderInit(&decoder, ZYDIS_MACHINE_MODE_LEGACY_32, ZYDIS_STACK_WIDTH_32); -#else -#error "Unsupported architecture" #endif auto ip = reinterpret_cast(add_42); diff --git a/include/safetyhook.hpp b/include/safetyhook.hpp index 1206908..a85938d 100644 --- a/include/safetyhook.hpp +++ b/include/safetyhook.hpp @@ -1,10 +1,10 @@ #pragma once -#include -#include -#include -#include -#include +#include "safetyhook/easy.hpp" +#include "safetyhook/inline_hook.hpp" +#include "safetyhook/mid_hook.hpp" +#include "safetyhook/thread_freezer.hpp" +#include "safetyhook/vmt_hook.hpp" using SafetyHookContext = safetyhook::Context; using SafetyHookInline = safetyhook::InlineHook; diff --git a/include/safetyhook/common.hpp b/include/safetyhook/common.hpp new file mode 100644 index 0000000..82ebe70 --- /dev/null +++ b/include/safetyhook/common.hpp @@ -0,0 +1,39 @@ +#pragma once + +#if defined(_MSC_VER) +#define SAFETYHOOK_COMPILER_MSVC 1 +#define SAFETYHOOK_COMPILER_GCC 0 +#define SAFETYHOOK_COMPILER_CLANG 0 +#elif defined(__GNUC__) +#define SAFETYHOOK_COMPILER_MSVC 0 +#define SAFETYHOOK_COMPILER_GCC 1 +#define SAFETYHOOK_COMPILER_CLANG 0 +#elif defined(__clang__) +#define SAFETYHOOK_COMPILER_MSVC 0 +#define SAFETYHOOK_COMPILER_GCC 0 +#define SAFETYHOOK_COMPILER_CLANG 1 +#else +#error "Unsupported compiler" +#endif + +#if SAFETYHOOK_COMPILER_MSVC +#if defined(_M_IX86) +#define SAFETYHOOK_ARCH_X86_32 1 +#define SAFETYHOOK_ARCH_X86_64 0 +#elif defined(_M_X64) +#define SAFETYHOOK_ARCH_X86_32 0 +#define SAFETYHOOK_ARCH_X86_64 1 +#else +#error "Unsupported architecture" +#endif +#elif SAFETYHOOK_COMPILER_GCC || SAFETYHOOK_COMPILER_CLANG +#if defined(__i386__) +#define SAFETYHOOK_ARCH_X86_32 1 +#define SAFETYHOOK_ARCH_X86_64 0 +#elif defined(__x86_64__) +#define SAFETYHOOK_ARCH_X86_32 0 +#define SAFETYHOOK_ARCH_X86_64 1 +#else +#error "Unsupported architecture" +#endif +#endif diff --git a/include/safetyhook/context.hpp b/include/safetyhook/context.hpp index 049516b..ef4f41c 100644 --- a/include/safetyhook/context.hpp +++ b/include/safetyhook/context.hpp @@ -5,6 +5,8 @@ #include +#include "safetyhook/common.hpp" + namespace safetyhook { union Xmm { uint8_t u8[16]; @@ -42,9 +44,9 @@ struct Context32 { /// to the registers at the moment the hook is called. /// @note The structure is different depending on architecture. /// @note The structure only provides access to integer registers. -#ifdef _M_X64 +#if SAFETYHOOK_ARCH_X86_64 using Context = Context64; -#else +#elif SAFETYHOOK_ARCH_X86_32 using Context = Context32; #endif diff --git a/include/safetyhook/easy.hpp b/include/safetyhook/easy.hpp index 8e12f9f..bb4bb28 100644 --- a/include/safetyhook/easy.hpp +++ b/include/safetyhook/easy.hpp @@ -3,10 +3,10 @@ #pragma once -#include -#include -#include -#include +#include "safetyhook/inline_hook.hpp" +#include "safetyhook/mid_hook.hpp" +#include "safetyhook/utility.hpp" +#include "safetyhook/vmt_hook.hpp" namespace safetyhook { /// @brief Easy to use API for creating an InlineHook. diff --git a/include/safetyhook/inline_hook.hpp b/include/safetyhook/inline_hook.hpp index 381b5c4..4114615 100644 --- a/include/safetyhook/inline_hook.hpp +++ b/include/safetyhook/inline_hook.hpp @@ -10,8 +10,9 @@ #include #include -#include -#include +#include "safetyhook/allocator.hpp" +#include "safetyhook/common.hpp" +#include "safetyhook/utility.hpp" namespace safetyhook { /// @brief An inline hook. @@ -294,7 +295,7 @@ class InlineHook final { const std::shared_ptr& allocator, uint8_t* target, uint8_t* destination); std::expected e9_hook(const std::shared_ptr& allocator); -#ifdef _M_X64 +#if SAFETYHOOK_ARCH_X86_64 std::expected ff_hook(const std::shared_ptr& allocator); #endif diff --git a/include/safetyhook/mid_hook.hpp b/include/safetyhook/mid_hook.hpp index bd2e90d..2601e84 100644 --- a/include/safetyhook/mid_hook.hpp +++ b/include/safetyhook/mid_hook.hpp @@ -6,10 +6,10 @@ #include #include -#include -#include -#include -#include +#include "safetyhook/allocator.hpp" +#include "safetyhook/context.hpp" +#include "safetyhook/inline_hook.hpp" +#include "safetyhook/utility.hpp" namespace safetyhook { diff --git a/include/safetyhook/vmt_hook.hpp b/include/safetyhook/vmt_hook.hpp index f35d560..bf624c0 100644 --- a/include/safetyhook/vmt_hook.hpp +++ b/include/safetyhook/vmt_hook.hpp @@ -7,8 +7,8 @@ #include #include -#include -#include +#include "safetyhook/allocator.hpp" +#include "safetyhook/utility.hpp" namespace safetyhook { /// @brief A hook class that allows for hooking a single method in a VMT. diff --git a/src/allocator.cpp b/src/allocator.cpp index 64165bc..df2d6ce 100644 --- a/src/allocator.cpp +++ b/src/allocator.cpp @@ -11,7 +11,7 @@ #error "Windows.h not found" #endif -#include +#include "safetyhook/allocator.hpp" namespace safetyhook { template constexpr T align_up(T address, size_t align) { diff --git a/src/easy.cpp b/src/easy.cpp index f79fbbb..65efb06 100644 --- a/src/easy.cpp +++ b/src/easy.cpp @@ -1,4 +1,4 @@ -#include +#include "safetyhook/easy.hpp" namespace safetyhook { InlineHook create_inline(void* target, void* destination) { diff --git a/src/inline_hook.cpp b/src/inline_hook.cpp index e0acc03..35de774 100644 --- a/src/inline_hook.cpp +++ b/src/inline_hook.cpp @@ -8,19 +8,20 @@ #error "Windows.h not found" #endif -#if __has_include() -#include -#elif __has_include() -#include +#if __has_include("Zydis/Zydis.h") +#include "Zydis/Zydis.h" +#elif __has_include("Zydis.h") +#include "Zydis.h" #else #error "Zydis not found" #endif -#include -#include -#include +#include "safetyhook/allocator.hpp" +#include "safetyhook/common.hpp" +#include "safetyhook/thread_freezer.hpp" +#include "safetyhook/utility.hpp" -#include +#include "safetyhook/inline_hook.hpp" namespace safetyhook { @@ -30,7 +31,7 @@ struct JmpE9 { uint32_t offset{0}; }; -#if defined(_M_X64) +#if SAFETYHOOK_ARCH_X86_64 struct JmpFF { uint8_t opcode0{0xFF}; uint8_t opcode1{0x25}; @@ -47,7 +48,7 @@ struct TrampolineEpilogueFF { JmpFF jmp_to_original{}; uint64_t original_address{}; }; -#elif defined(_M_IX86) +#elif SAFETYHOOK_ARCH_X86_32 struct TrampolineEpilogueE9 { JmpE9 jmp_to_original{}; JmpE9 jmp_to_destination{}; @@ -55,7 +56,7 @@ struct TrampolineEpilogueE9 { #endif #pragma pack(pop) -#ifdef _M_X64 +#if SAFETYHOOK_ARCH_X86_64 static auto make_jmp_ff(uint8_t* src, uint8_t* dst, uint8_t* data) { JmpFF jmp{}; @@ -120,12 +121,10 @@ static bool decode(ZydisDecodedInstruction* ix, uint8_t* ip) { ZydisDecoder decoder{}; ZyanStatus status; -#if defined(_M_X64) +#if SAFETYHOOK_ARCH_X86_64 status = ZydisDecoderInit(&decoder, ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_STACK_WIDTH_64); -#elif defined(_M_IX86) +#elif SAFETYHOOK_ARCH_X86_32 status = ZydisDecoderInit(&decoder, ZYDIS_MACHINE_MODE_LEGACY_32, ZYDIS_STACK_WIDTH_32); -#else -#error "Unsupported architecture" #endif if (!ZYAN_SUCCESS(status)) { @@ -190,11 +189,11 @@ std::expected InlineHook::setup( m_destination = destination; if (auto e9_result = e9_hook(allocator); !e9_result) { -#ifdef _M_X64 +#if SAFETYHOOK_ARCH_X86_64 if (auto ff_result = ff_hook(allocator); !ff_result) { return ff_result; } -#else +#elif SAFETYHOOK_ARCH_X86_32 return e9_result; #endif } @@ -314,13 +313,13 @@ std::expected InlineHook::e9_hook(const std::shared_ptr src = reinterpret_cast(&trampoline_epilogue->jmp_to_destination); dst = m_destination; -#ifdef _M_X64 +#if SAFETYHOOK_ARCH_X86_64 auto data = reinterpret_cast(&trampoline_epilogue->destination_address); if (auto result = emit_jmp_ff(src, dst, data); !result) { return std::unexpected{result.error()}; } -#else +#elif SAFETYHOOK_ARCH_X86_32 if (auto result = emit_jmp_e9(src, dst); !result) { return std::unexpected{result.error()}; } @@ -350,7 +349,7 @@ std::expected InlineHook::e9_hook(const std::shared_ptr return {}; } -#ifdef _M_X64 +#if SAFETYHOOK_ARCH_X86_64 std::expected InlineHook::ff_hook(const std::shared_ptr& allocator) { m_original_bytes.clear(); m_trampoline_size = sizeof(TrampolineEpilogueFF); diff --git a/src/mid_hook.cpp b/src/mid_hook.cpp index 7456011..a28391d 100644 --- a/src/mid_hook.cpp +++ b/src/mid_hook.cpp @@ -1,15 +1,15 @@ #include #include -#include -#include -#include +#include "safetyhook/allocator.hpp" +#include "safetyhook/inline_hook.hpp" +#include "safetyhook/utility.hpp" -#include +#include "safetyhook/mid_hook.hpp" namespace safetyhook { -#ifdef _M_X64 +#if SAFETYHOOK_ARCH_X86_64 constexpr std::array asm_data = {0xFF, 0x35, 0x79, 0x01, 0x00, 0x00, 0x54, 0x54, 0x55, 0x50, 0x53, 0x51, 0x52, 0x56, 0x57, 0x41, 0x50, 0x41, 0x51, 0x41, 0x52, 0x41, 0x53, 0x41, 0x54, 0x41, 0x55, 0x41, 0x56, 0x41, 0x57, 0x9C, 0x48, 0x81, 0xEC, 0x00, 0x01, 0x00, 0x00, 0xF3, 0x44, 0x0F, 0x7F, 0xBC, 0x24, 0xF0, 0x00, 0x00, 0x00, 0xF3, @@ -31,7 +31,7 @@ constexpr std::array asm_data = {0xFF, 0x35, 0x79, 0x01, 0x00, 0x0 0x00, 0x00, 0x48, 0x81, 0xC4, 0x00, 0x01, 0x00, 0x00, 0x9D, 0x41, 0x5F, 0x41, 0x5E, 0x41, 0x5D, 0x41, 0x5C, 0x41, 0x5B, 0x41, 0x5A, 0x41, 0x59, 0x41, 0x58, 0x5F, 0x5E, 0x5A, 0x59, 0x5B, 0x58, 0x5D, 0x48, 0x8D, 0x64, 0x24, 0x08, 0x5C, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; -#else +#elif SAFETYHOOK_ARCH_X86_32 constexpr std::array asm_data = {0xFF, 0x35, 0xA7, 0x00, 0x00, 0x00, 0x54, 0x54, 0x55, 0x50, 0x53, 0x51, 0x52, 0x56, 0x57, 0x9C, 0x81, 0xEC, 0x80, 0x00, 0x00, 0x00, 0xF3, 0x0F, 0x7F, 0x7C, 0x24, 0x70, 0xF3, 0x0F, 0x7F, 0x74, 0x24, 0x60, 0xF3, 0x0F, 0x7F, 0x6C, 0x24, 0x50, 0xF3, 0x0F, 0x7F, 0x64, 0x24, 0x40, 0xF3, 0x0F, 0x7F, 0x5C, @@ -97,9 +97,9 @@ std::expected MidHook::setup( std::copy(asm_data.begin(), asm_data.end(), m_stub.data()); -#ifdef _M_X64 +#if SAFETYHOOK_ARCH_X86_64 store(m_stub.data() + sizeof(asm_data) - 16, m_destination); -#else +#elif SAFETYHOOK_ARCH_X86_32 store(m_stub.data() + sizeof(asm_data) - 8, m_destination); // 32-bit has some relocations we need to fix up as well. @@ -116,9 +116,9 @@ std::expected MidHook::setup( m_hook = std::move(*hook_result); -#ifdef _M_X64 +#if SAFETYHOOK_ARCH_X86_64 store(m_stub.data() + sizeof(asm_data) - 8, m_hook.trampoline().data()); -#else +#elif SAFETYHOOK_ARCH_X86_32 store(m_stub.data() + sizeof(asm_data) - 4, m_hook.trampoline().data()); #endif diff --git a/src/thread_freezer.cpp b/src/thread_freezer.cpp index 1ac88d9..b9b3ae0 100644 --- a/src/thread_freezer.cpp +++ b/src/thread_freezer.cpp @@ -7,7 +7,9 @@ #endif #include -#include +#include "safetyhook/common.hpp" + +#include "safetyhook/thread_freezer.hpp" #pragma comment(lib, "ntdll") @@ -120,9 +122,9 @@ void execute_while_frozen( void fix_ip(ThreadContext thread_ctx, uint8_t* old_ip, uint8_t* new_ip) { auto* ctx = reinterpret_cast(thread_ctx); -#ifdef _M_X64 +#if SAFETYHOOK_ARCH_X86_64 auto ip = ctx->Rip; -#else +#elif SAFETYHOOK_ARCH_X86_32 auto ip = ctx->Eip; #endif @@ -130,9 +132,9 @@ void fix_ip(ThreadContext thread_ctx, uint8_t* old_ip, uint8_t* new_ip) { ip = reinterpret_cast(new_ip); } -#ifdef _M_X64 +#if SAFETYHOOK_ARCH_X86_64 ctx->Rip = ip; -#else +#elif SAFETYHOOK_ARCH_X86_32 ctx->Eip = ip; #endif } diff --git a/src/utility.cpp b/src/utility.cpp index 8fc6cc1..1803d24 100644 --- a/src/utility.cpp +++ b/src/utility.cpp @@ -6,7 +6,7 @@ #error "Windows.h not found" #endif -#include +#include "safetyhook/utility.hpp" namespace safetyhook { bool is_page_executable(uint8_t* address) { diff --git a/src/vmt_hook.cpp b/src/vmt_hook.cpp index 587c6ca..d62d74e 100644 --- a/src/vmt_hook.cpp +++ b/src/vmt_hook.cpp @@ -6,9 +6,9 @@ #error "Windows.h not found" #endif -#include +#include "safetyhook/thread_freezer.hpp" -#include +#include "safetyhook/vmt_hook.hpp" namespace safetyhook { VmHook::VmHook(VmHook&& other) noexcept { diff --git a/test/inline_hook.x86_64.cpp b/test/inline_hook.x86_64.cpp index 6dc4760..1ce045e 100644 --- a/test/inline_hook.x86_64.cpp +++ b/test/inline_hook.x86_64.cpp @@ -1,9 +1,9 @@ -#if defined(_M_X64) - #include #include #include +#if SAFETYHOOK_ARCH_X86_64 + using namespace std::literals; using namespace boost::ut; using namespace Xbyak::util; diff --git a/test/mid_hook.cpp b/test/mid_hook.cpp index 316cf29..d6920ab 100644 --- a/test/mid_hook.cpp +++ b/test/mid_hook.cpp @@ -15,12 +15,10 @@ static suite<"mid hook"> mid_hook_tests = [] { struct Hook { static void add_42(SafetyHookContext& ctx) { -#if defined(_M_X64) +#if SAFETYHOOK_ARCH_X86_64 ctx.rcx = 1337 - 42; -#elif defined(_M_IX86) +#elif SAFETYHOOK_ARCH_X86_32 ctx.ecx = 1337 - 42; -#else -#error "Unsupported architecture" #endif } }; @@ -38,7 +36,7 @@ static suite<"mid hook"> mid_hook_tests = [] { expect(Target::add_42(2) == 44_i); }; -#ifdef _M_X64 +#if SAFETYHOOK_ARCH_X86_64 "Mid hook to change an XMM register"_test = [] { struct Target { __declspec(noinline) static float __fastcall add_42(float a) { return a + 0.42f; }