From 176e2c98f681404a22b5062abea847c6eb529f08 Mon Sep 17 00:00:00 2001 From: Florian Bernd Date: Thu, 6 Feb 2020 13:27:16 +0100 Subject: [PATCH] `Trampoline` and `Transaction` progress and bugfixes related to #4 --- examples/InlineHook.c | 99 +++++++++++-------- include/Zyrex/Internal/InlineHook.h | 9 +- include/Zyrex/Internal/Trampoline.h | 27 +++++- include/Zyrex/Transaction.h | 81 ++++++++++++++-- src/Barrier.c | 8 +- src/InlineHook.c | 5 +- src/Relocation.c | 24 ++--- src/Trampoline.c | 144 +++++++++++++++++++++------- src/Transaction.c | 106 +++++++++++++------- 9 files changed, 360 insertions(+), 143 deletions(-) diff --git a/examples/InlineHook.c b/examples/InlineHook.c index 0bf3760..9352f13 100644 --- a/examples/InlineHook.c +++ b/examples/InlineHook.c @@ -29,21 +29,23 @@ * @brief Demonstrates the inline-hook. */ +#include #include #include #include #include -#include #include "Zyrex/Transaction.h" #include +#include + /* ============================================================================================== */ /* Entry point */ /* ============================================================================================== */ -typedef DWORD (__stdcall *functype)(void* lpThreadParameter); +typedef DWORD (__stdcall functype)(void* param); -const void* original = NULL; +static functype* volatile original = NULL; DWORD __stdcall xxx2(void* param) { @@ -65,56 +67,69 @@ DWORD __stdcall callback(void* param) puts("hello from callback\n"); - return ((functype)original)(NULL) + 1; + return (*original)(NULL) + 1; +} + +typedef BOOL (WINAPI FuncCopyFileW)(_In_ LPCWSTR lpExistingFileName, _In_ LPCWSTR lpNewFileName, + _In_ BOOL bFailIfExists); + +static FuncCopyFileW* volatile CopyFileWOriginal = ZYAN_NULL; + +BOOL WINAPI CopyFileWCallback(_In_ LPCWSTR lpExistingFileName, _In_ LPCWSTR lpNewFileName, + _In_ BOOL bFailIfExists) +{ + ZYAN_UNUSED(lpExistingFileName); + ZYAN_UNUSED(lpNewFileName); + ZYAN_UNUSED(bFailIfExists); + + puts("CopyFileW callback"); + + const BOOL result = CopyFileWOriginal(lpExistingFileName, lpNewFileName, bFailIfExists); + const DWORD error = GetLastError(); + ZYAN_UNUSED(error); + + return result; } int main() { + ZyrexInitialize(); ZyrexTransactionBegin(); - ZyrexInstallInlineHook((void*)&xxx2, (const void*)&callback, &original); - int x = xxx2(0); - printf("%x\n", x); + ZyrexInstallInlineHook((void*)&xxx2, (const void*)&callback, (ZyanConstVoidPointer*)&original); + /*ZyrexInstallInlineHook((void*)&CopyFileW, (const void*)&CopyFileWCallback, + (ZyanConstVoidPointer*)&CopyFileWOriginal);*/ ZyrexTransactionCommit(); - + printf("%x\n", (unsigned int)xxx2(0)); ZyrexTransactionBegin(); - ZyrexRemoveInlineHook((void*)&xxx2, &original); - int z = xxx2(0); - printf("%x\n", z); + ZyrexRemoveInlineHook((ZyanConstVoidPointer*)&original); ZyrexTransactionCommit(); - - - //ZyrexTransactionAbort(); - -// ZyanU8 buffer[] = -// { // E1, E2 -// 0xEB, 0xFE, 0x75, 0x02, 0xeb, 0xfb, 0x67, 0xE3, 0xf8, 0x48, 0x8B, 0x05, 0xF5, 0xFF, 0xFF, 0xFF, 0x90, 0x90, 0x90, 0xC3 -// }; -// void* const buf = VirtualAlloc(NULL, sizeof(buffer), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); -// memcpy(buf, buffer, sizeof(buffer)); -// -// const functype xxx = xxx2;// (functype)buf; -// -// -// ZyrexTrampolineChunk* trampoline; -// const ZyanStatus status = -// ZyrexTrampolineCreate((const void*)xxx, (const void*)&callback, 5, &trampoline); -// if (ZYAN_SUCCESS(status)) -// { -// original = &trampoline->code_buffer; -// -//#ifdef ZYAN_X64 -// ZyrexAttachInlineHook((void*)xxx, &trampoline->callback_jump); -//#else -// ZyrexAttachInlineHook(xxx, &callback); -//#endif -// -// printf("%.8X", ((functype)xxx)(NULL)); -// -// ZyrexTrampolineFree(trampoline); -// } + printf("%x\n", (unsigned int)xxx2(0)); + + + //ZyanU8 buffer[] = + //{ // E1, E2 + // /*0xEB, 0xFE, */0x75, 0x02, 0xeb, 0xfb, 0x67, 0xE3, 0xf8, 0x48, 0x8B, 0x05, 0xF5, 0xFF, 0xFF, 0xFF, 0x90, 0x90, 0x90, 0xC3 + //}; + //void* const buf = VirtualAlloc(NULL, sizeof(buffer), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); + //memcpy(buf, buffer, sizeof(buffer)); + + //functype* const xxx = (functype*)buf; + + + //ZyrexTrampolineChunk* trampoline; + //const ZyanStatus status = + // ZyrexTrampolineCreate((const void*)xxx, (const void*)&callback, 5, &trampoline); + //if (ZYAN_SUCCESS(status)) + //{ + // original = (functype*)&trampoline->code_buffer; + + // printf("%.8X", ((functype*)original)(NULL)); + + // ZyrexTrampolineFree(trampoline); + //} return 0; } diff --git a/include/Zyrex/Internal/InlineHook.h b/include/Zyrex/Internal/InlineHook.h index 19c2bdf..56fb10e 100644 --- a/include/Zyrex/Internal/InlineHook.h +++ b/include/Zyrex/Internal/InlineHook.h @@ -27,6 +27,10 @@ #ifndef ZYREX_INLINE_HOOK_H #define ZYREX_INLINE_HOOK_H +#include +#ifdef ZYAN_WINDOWS +# include +#endif #include #include @@ -42,13 +46,14 @@ extern "C" { /* Attaching and detaching */ /* ---------------------------------------------------------------------------------------------- */ - -#include +#ifdef ZYAN_WINDOWS ZyanStatus ZyrexMigrateThread(DWORD thread_id, const void* source, ZyanUSize source_length, const void* destination, ZyanUSize destination_length, const ZyrexInstructionTranslationMap* translation_map); +#endif + /* ---------------------------------------------------------------------------------------------- */ /* ============================================================================================== */ diff --git a/include/Zyrex/Internal/Trampoline.h b/include/Zyrex/Internal/Trampoline.h index 15e5efa..7f22555 100644 --- a/include/Zyrex/Internal/Trampoline.h +++ b/include/Zyrex/Internal/Trampoline.h @@ -81,7 +81,7 @@ extern "C" { /** * @brief Defines the trampoline region signature. */ -#define ZYREX_TRAMPOLINE_REGION_SIGNATURE 'zrex' +#define ZYREX_TRAMPOLINE_REGION_SIGNATURE 0x7A726578 /* ============================================================================================== */ /* Enums and types */ @@ -127,6 +127,10 @@ typedef struct ZyrexInstructionTranslationItem_ * buffer. */ ZyanU8 offset_destination; + /** + * @brief An absolute target address outside the destination buffer. + */ + ZyanUPointer target_address; } ZyrexInstructionTranslationItem; /** @@ -206,6 +210,10 @@ typedef struct ZyrexTrampolineChunk_ /* Functions */ /* ============================================================================================== */ +/* ---------------------------------------------------------------------------------------------- */ +/* Creation and destruction */ +/* ---------------------------------------------------------------------------------------------- */ + /** * @brief Creates a new trampoline. * @@ -232,6 +240,23 @@ ZyanStatus ZyrexTrampolineCreate(const void* address, const void* callback, */ ZyanStatus ZyrexTrampolineFree(ZyrexTrampolineChunk* trampoline); +/* ---------------------------------------------------------------------------------------------- */ +/* Searching */ +/* ---------------------------------------------------------------------------------------------- */ + +/** + * @brief Searches for a trampoline chunk using the given `original`-function pointer. + * + * @param original A pointer to the original function. + * @param trampoline Receives the corresponding trampoline chunk, if found. + * + * @return `ZYAN_STATUS_TRUE` if the element was found, `ZYAN_STATUS_FALSE` if not or an other + * zyan status code if an error occured. + */ +ZyanStatus ZyrexTrampolineFind(const void* original, ZyrexTrampolineChunk** trampoline); + +/* ---------------------------------------------------------------------------------------------- */ + /* ============================================================================================== */ #ifdef __cplusplus diff --git a/include/Zyrex/Transaction.h b/include/Zyrex/Transaction.h index a0a8f4f..96c927b 100644 --- a/include/Zyrex/Transaction.h +++ b/include/Zyrex/Transaction.h @@ -40,6 +40,66 @@ extern "C" { /* Enums and types */ /* ============================================================================================== */ +/* ---------------------------------------------------------------------------------------------- */ +/* Hook type */ +/* ---------------------------------------------------------------------------------------------- */ + +/** + * @brief Defines the `ZyrexHookType` enum. + */ +typedef enum ZyrexHookType_ +{ + /** + * @brief Inline hook. + * + * The inline hook uses a `jmp` instruction at the begin of the target function to redirect + * code-fow to the callback function. + */ + ZYREX_HOOK_TYPE_INLINE, + /** + * @brief Exception hook. + * + * The exception hook uses a privileged instruction at the begin of the target function to + * cause an exception which is later catched by an unhandled-exception-handler that redirects + * code-flow to the callback function by modifying the instruction-pointer register of the + * calling thread. + */ + ZYREX_HOOK_TYPE_EXCEPTION, + /** + * @brief Context/HWBP hook. + * + * The context/HWBP hook modifies the x86-64 debug registers to cause an exception which is + * later catched by an unhandled-exception-handler that redirects code-flow to the callback + * function by modifying the instruction-pointer register of the calling thread. + * + * This hook + */ + ZYREX_HOOK_TYPE_CONTEXT +} ZyrexHookType; + +/* ---------------------------------------------------------------------------------------------- */ +/* Hook */ +/* ---------------------------------------------------------------------------------------------- */ + +/** + * @brief Defines the `ZyrexHook` struct. + * + * All fields in this struct should be considered as "private". Any changes may lead to unexpected + * behavior. + */ +typedef struct ZyrexHook_ +{ + /** + * @brief The hook type - just for reference. + */ + ZyrexHookType type; + /** + * @brief The address of the hooked function. + */ + void* address; + +} ZyrexHook; + /* ---------------------------------------------------------------------------------------------- */ /* Hook operation */ /* ---------------------------------------------------------------------------------------------- */ @@ -66,8 +126,8 @@ ZYREX_EXPORT ZyanStatus ZyrexTransactionBegin(); /** * @brief Adds a specific thread to the thread-update list. * - * The given thread is immediately suspended and resumed after the transaction was either committed - * or canceled. + * The given thread is immediately suspended and later on resumed after the transaction was either + * been committed or canceled. * * @param thread_id The id of the thread to add to the update list. * @@ -78,8 +138,8 @@ ZYREX_EXPORT ZyanStatus ZyrexUpdateThread(DWORD thread_id); /** * @brief Adds all threads (except the calling one) to the update list. * - * All threads are immediately suspended and resumed after the transaction was either committed - * or canceled. + * All threads are immediately suspended and later on resumed after the transaction was either + * been committed or canceled. * * @return A zyan status code. */ @@ -88,7 +148,7 @@ ZYREX_EXPORT ZyanStatus ZyrexUpdateAllThreads(); /** * @brief Commits the current transaction. * - * This function performs all pending hook attach/remove operations and updates all threads in the + * This function performs the pending hook attach/remove operations and updates all threads in the * thread-update list. * * @return A zyan status code. @@ -120,12 +180,13 @@ ZYREX_EXPORT ZyanStatus ZyrexTransactionAbort(); * * @param address The address to hook. * @param callback The callback address. - * @param original Receives the address of the original function, if the operation succeeded. + * @param trampoline Receives the address of the trampoline to the original function, if the + * operation succeeded. * * @return A zyan status code. */ ZYREX_EXPORT ZyanStatus ZyrexInstallInlineHook(void* address, const void* callback, - ZyanConstVoidPointer* original); + ZyanConstVoidPointer* trampoline); ///** // * @brief Attaches an exception hook. @@ -158,12 +219,12 @@ ZYREX_EXPORT ZyanStatus ZyrexInstallInlineHook(void* address, const void* callba /** * @brief Removes an inline hook at the given `address`. * - * @param address The address to unhook. - * @param original Receives the address of the original function, if the operation succeeded. + * @param trampoline A pointer to the trampoline address received during the hook attaching. + * Receives the address of the original function after removing the hook. * * @return A zyan status code. */ -ZYREX_EXPORT ZyanStatus ZyrexRemoveInlineHook(void* address, ZyanConstVoidPointer* original); +ZYREX_EXPORT ZyanStatus ZyrexRemoveInlineHook(ZyanConstVoidPointer* trampoline); /* ---------------------------------------------------------------------------------------------- */ diff --git a/src/Barrier.c b/src/Barrier.c index a8fc6b4..2c7fd19 100644 --- a/src/Barrier.c +++ b/src/Barrier.c @@ -72,7 +72,7 @@ typedef struct ZyrexBarrierContext_ /* ---------------------------------------------------------------------------------------------- */ /** - * @brief This function is invoked everytime a thread exists. + * @brief This function is invoked every time a thread exists. * * @param data The data currently stored in the TLS slot. */ @@ -97,7 +97,7 @@ ZYAN_THREAD_DECLARE_TLS_CALLBACK(ZyrexBarrierTlsCleanup, ZyanVector, data) * @brief Defines a comparison function for the `ZyrexBarrierContext` struct that uses the `id` * field to create an absolute order. */ -ZYAN_DECLARE_COMPARISON_FOR_FIELD(ZyrexBarrierCompareContext, ZyrexBarrierContext, id); +ZYAN_DECLARE_COMPARISON_FOR_FIELD(ZyrexBarrierCompareContext, ZyrexBarrierContext, id) /* ---------------------------------------------------------------------------------------------- */ @@ -190,7 +190,7 @@ ZyanStatus ZyrexBarrierLeave(ZyrexBarrierHandle handle) ZyanUSize found_index; const ZyanStatus status = ZyanVectorBinarySearch(vector, &context_element, &found_index, - (ZyanComparison)&ZyrexBarrierCompareContext); + (ZyanComparison)&ZyrexBarrierCompareContext); ZYAN_CHECK(status); if (status != ZYAN_STATUS_TRUE) @@ -234,7 +234,7 @@ ZyanStatus ZyrexBarrierGetRecursionDepth(ZyrexBarrierHandle handle, ZyanU32* cur ZyanUSize found_index; const ZyanStatus status = ZyanVectorBinarySearch(vector, &context_element, &found_index, - (ZyanComparison)&ZyrexBarrierCompareContext); + (ZyanComparison)&ZyrexBarrierCompareContext); ZYAN_CHECK(status); if (status == ZYAN_STATUS_TRUE) diff --git a/src/InlineHook.c b/src/InlineHook.c index c2e28ed..98dc8a5 100644 --- a/src/InlineHook.c +++ b/src/InlineHook.c @@ -24,7 +24,6 @@ ***************************************************************************************************/ -#include #include #include #include @@ -37,6 +36,8 @@ /* Runtime thread migration */ /* ---------------------------------------------------------------------------------------------- */ +#ifdef ZYAN_WINDOWS + ZyanStatus ZyrexMigrateThread(DWORD thread_id, const void* source, ZyanUSize source_length, const void* destination, ZyanUSize destination_length, const ZyrexInstructionTranslationMap* translation_map) @@ -129,6 +130,8 @@ ZyanStatus ZyrexMigrateThread(DWORD thread_id, const void* source, ZyanUSize sou return status; } +#endif + /* ---------------------------------------------------------------------------------------------- */ /* Attaching and detaching */ /* ---------------------------------------------------------------------------------------------- */ diff --git a/src/Relocation.c b/src/Relocation.c index 4aba714..8627434 100644 --- a/src/Relocation.c +++ b/src/Relocation.c @@ -383,15 +383,15 @@ static ZyanBool ZyrexShouldRewriteBranchInstruction(ZyrexRelocationContext* cont * chunk. This prevents wrong data being read due to modifications of the instructions during the * relocation process. */ -static ZyanStatus ZyrexShouldRedirectMemoryInstruction(ZyrexRelocationContext* context, - const ZyrexAnalyzedInstruction* instruction) -{ - ZYAN_ASSERT(context); - ZYAN_ASSERT(instruction); - ZYAN_ASSERT(instruction->has_relative_target); - - return !instruction->has_external_target; -} +//static ZyanStatus ZyrexShouldRedirectMemoryInstruction(ZyrexRelocationContext* context, +// const ZyrexAnalyzedInstruction* instruction) +//{ +// ZYAN_ASSERT(context); +// ZYAN_ASSERT(instruction); +// ZYAN_ASSERT(instruction->has_relative_target); +// +// return !instruction->has_external_target; +//} /* ---------------------------------------------------------------------------------------------- */ /* Relocation */ @@ -524,7 +524,7 @@ static ZyanStatus ZyrexRelocateRelativeBranchInstruction(ZyrexRelocationContext* (ZyanU8)context->bytes_written + instruction->instruction.length); // Generate `JMP` to `1` branch - ZyrexWriteRelativeJump(address, instruction->absolute_target_address); + ZyrexWriteRelativeJump(address, (ZyanUPointer)instruction->absolute_target_address); ZyrexUpdateRelocationContext(context, 2, (ZyanU8)context->bytes_read, (ZyanU8)context->bytes_written + instruction->instruction.length + 2); @@ -579,7 +579,7 @@ static ZyanStatus ZyrexRelocateRelativeBranchInstruction(ZyrexRelocationContext* // Write relative offset *(ZyanI32*)(address) = ZyrexCalculateRelativeOffset(length, (ZyanUPointer)address, - instruction->absolute_target_address); + (ZyanUPointer)instruction->absolute_target_address); // Update relocation context ZyrexUpdateRelocationContext(context, length, (ZyanU8)context->bytes_read, @@ -620,7 +620,7 @@ static ZyanStatus ZyrexRelocateRelativeMemoryInstruction(ZyrexRelocationContext* // Update the relative offset for the new instruction position const ZyanI32 value = ZyrexCalculateRelativeOffset(instruction->instruction.length, (ZyanUPointer)context->destination + context->bytes_written, - instruction->absolute_target_address); + (ZyanUPointer)instruction->absolute_target_address); switch (instruction->instruction.raw.disp.size) { diff --git a/src/Trampoline.c b/src/Trampoline.c index cc2763c..aef4604 100644 --- a/src/Trampoline.c +++ b/src/Trampoline.c @@ -24,13 +24,24 @@ ***************************************************************************************************/ -#include +#include #include #include +#include +#include #include #include #include +#if defined(ZYAN_WINDOWS) +# include +#elif defined(ZYAN_POSIX) +# include +# include +#else +# error "Unsupported platform detected" +#endif + /* ============================================================================================== */ /* Enums and types */ /* ============================================================================================== */ @@ -323,6 +334,11 @@ static ZyanBool ZyrexTrampolineRegionFindChunkInRegion(ZyrexTrampolineRegion* re // Skip the first chunk as it shares memory with the region-header for (ZyanUSize i = 1; i < g_trampoline_data.chunks_per_region; ++i) { + if (region->chunks[i].is_used) + { + continue; + } + const ZyanIPointer chunk_base = (ZyanIPointer)®ion->chunks[i]; const ZyanIPointer distance_lo_chunk = chunk_base - (ZyanIPointer)address_lo + @@ -485,13 +501,8 @@ static ZyanStatus ZyrexTrampolineRegionProtect(ZyrexTrampolineRegion* region) ZYAN_ASSERT(g_trampoline_data.is_initialized); ZYAN_ASSERT(ZYAN_IS_ALIGNED_TO((ZyanUPointer)region, g_trampoline_data.region_size)); - DWORD old_value; - if (!VirtualProtect(region, sizeof(ZyrexTrampolineChunk), PAGE_EXECUTE_READ, &old_value)) - { - return ZYAN_STATUS_BAD_SYSTEMCALL; - } - - return ZYAN_STATUS_SUCCESS; + return ZyanMemoryVirtualProtect(region, sizeof(ZyrexTrampolineChunk), + ZYAN_PAGE_EXECUTE_READ); } /** @@ -507,13 +518,8 @@ static ZyanStatus ZyrexTrampolineRegionUnprotect(ZyrexTrampolineRegion* region) ZYAN_ASSERT(g_trampoline_data.is_initialized); ZYAN_ASSERT(ZYAN_IS_ALIGNED_TO((ZyanUPointer)region, g_trampoline_data.region_size)); - DWORD old_value; - if (!VirtualProtect(region, sizeof(ZyrexTrampolineChunk), PAGE_EXECUTE_READWRITE, &old_value)) - { - return ZYAN_STATUS_BAD_SYSTEMCALL; - } - - return ZYAN_STATUS_SUCCESS; + return ZyanMemoryVirtualProtect(region, sizeof(ZyrexTrampolineChunk), + ZYAN_PAGE_EXECUTE_READWRITE); } /** @@ -534,6 +540,8 @@ static ZyanStatus ZyrexTrampolineRegionAllocate(ZyanUPointer address_lo, ZyanUPo ZYAN_ASSERT(region); ZYAN_ASSERT(g_trampoline_data.is_initialized); +#ifdef ZYAN_WINDOWS + SYSTEM_INFO system_info; GetSystemInfo(&system_info); @@ -546,8 +554,12 @@ static ZyanStatus ZyrexTrampolineRegionAllocate(ZyanUPointer address_lo, ZyanUPo MEMORY_BASIC_INFORMATION memory_info; +#endif + while (ZYAN_TRUE) { +#ifdef ZYAN_WINDOWS + // Skip reserved address regions if (alloc_address_lo < (ZyanU8*)system_info.lpMinimumApplicationAddress) { @@ -574,6 +586,11 @@ static ZyanStatus ZyrexTrampolineRegionAllocate(ZyanUPointer address_lo, ZyanUPo (const ZyanU8*)(ZYAN_ALIGN_DOWN((ZyanUPointer)alloc_address_lo, region_size)); } +//#endif + + // TODO: Only `RESERVE` the memory region and `COMMIT` pages on demand to reduce memory + // TODO: imprint + ZyanU8 c = 0; if (ZyrexTrampolineRegionInRange((ZyanUPointer)alloc_address_lo, address_lo, address_hi)) @@ -620,13 +637,19 @@ static ZyanStatus ZyrexTrampolineRegionAllocate(ZyanUPointer address_lo, ZyanUPo { return ZYAN_STATUS_OUT_OF_RANGE; } +#else + ZYAN_UNUSED(address_lo); + ZYAN_UNUSED(address_hi); +#endif } // ZYAN_UNREACHABLE; +#ifdef ZYAN_WINDOWS InitializeRegion: (*region)->header.signature = ZYREX_TRAMPOLINE_REGION_SIGNATURE; (*region)->header.number_of_unused_chunks = g_trampoline_data.chunks_per_region - 1; +#endif return ZYAN_STATUS_SUCCESS; } @@ -644,12 +667,7 @@ static ZyanStatus ZyrexTrampolineRegionFree(ZyrexTrampolineRegion* region) ZYAN_ASSERT(g_trampoline_data.is_initialized); ZYAN_ASSERT(ZYAN_IS_ALIGNED_TO((ZyanUPointer)region, g_trampoline_data.region_size)); - if (!VirtualFree(region, 0, MEM_RELEASE)) - { - return ZYAN_STATUS_BAD_SYSTEMCALL; - } - - return ZYAN_STATUS_SUCCESS; + return ZyanMemoryVirtualFree(region, g_trampoline_data.region_size); } /* ---------------------------------------------------------------------------------------------- */ @@ -687,11 +705,8 @@ static ZyanStatus ZyrexTrampolineChunkInit(ZyrexTrampolineChunk* chunk, const vo #if defined(ZYAN_X64) ZyrexWriteAbsoluteJump(&chunk->callback_jump, (ZyanUPointer)&chunk->callback_address); - if (!FlushInstructionCache(GetCurrentProcess(), &chunk->callback_jump, - ZYREX_SIZEOF_ABSOLUTE_JUMP)) - { - return ZYAN_STATUS_BAD_SYSTEMCALL; - } + ZYAN_CHECK(ZyanProcessFlushInstructionCache(&chunk->callback_jump, + ZYREX_SIZEOF_ABSOLUTE_JUMP)); #endif @@ -715,11 +730,8 @@ static ZyanStatus ZyrexTrampolineChunkInit(ZyrexTrampolineChunk* chunk, const vo bytes_remaining - ZYREX_SIZEOF_ABSOLUTE_JUMP); } - if (!FlushInstructionCache(GetCurrentProcess(), &chunk->code_buffer, - ZYREX_TRAMPOLINE_MAX_CODE_SIZE_WITH_BACKJUMP + ZYREX_TRAMPOLINE_MAX_CODE_SIZE_BONUS)) - { - return ZYAN_STATUS_BAD_SYSTEMCALL; - } + ZYAN_CHECK(ZyanProcessFlushInstructionCache(&chunk->code_buffer, + ZYREX_TRAMPOLINE_MAX_CODE_SIZE_WITH_BACKJUMP + ZYREX_TRAMPOLINE_MAX_CODE_SIZE_BONUS)); // Backup original instructions ZYAN_ASSERT(bytes_read <= ZYAN_ARRAY_LENGTH(chunk->original_code)); @@ -735,6 +747,10 @@ static ZyanStatus ZyrexTrampolineChunkInit(ZyrexTrampolineChunk* chunk, const vo /* Public functions */ /* ============================================================================================== */ +/* ---------------------------------------------------------------------------------------------- */ +/* Creation and destruction */ +/* ---------------------------------------------------------------------------------------------- */ + ZyanStatus ZyrexTrampolineCreate(const void* address, const void* callback, ZyanUSize min_bytes_to_reloc, ZyrexTrampolineChunk** trampoline) { @@ -745,20 +761,20 @@ ZyanStatus ZyrexTrampolineCreate(const void* address, const void* callback, // Check if the memory region of the target function has enough space for the hook code ZyanUSize source_size = ZYREX_TRAMPOLINE_MAX_CODE_SIZE; +#ifdef ZYAN_WINDOWS ZYAN_CHECK(ZyrexGetSizeOfReadableMemoryRegion(address, &source_size)); if (source_size < min_bytes_to_reloc) { return ZYAN_STATUS_INVALID_OPERATION; } +#endif if (!g_trampoline_data.is_initialized) { ZYAN_CHECK(ZyanVectorInit(&g_trampoline_data.regions, sizeof(ZyrexTrampolineRegion*), 8, ZYAN_NULL)); - SYSTEM_INFO system_info; - GetSystemInfo(&system_info); - g_trampoline_data.region_size = system_info.dwAllocationGranularity; + g_trampoline_data.region_size = ZyanMemoryGetSystemAllocationGranularity(); g_trampoline_data.chunks_per_region = g_trampoline_data.region_size / sizeof(ZyrexTrampolineChunk); @@ -866,7 +882,7 @@ ZyanStatus ZyrexTrampolineFree(ZyrexTrampolineChunk* trampoline) g_trampoline_data.region_size); ZyanUSize found_index; const ZyanStatus status = - ZyanVectorBinarySearch(&g_trampoline_data.regions, ®ion_address, &found_index, + ZyanVectorBinarySearch(&g_trampoline_data.regions, ®ion_address, &found_index, (ZyanComparison)&ZyanComparePointer); ZYAN_CHECK(status); @@ -900,4 +916,62 @@ ZyanStatus ZyrexTrampolineFree(ZyrexTrampolineChunk* trampoline) return ZYAN_STATUS_SUCCESS; } +/* ---------------------------------------------------------------------------------------------- */ +/* Searching */ +/* ---------------------------------------------------------------------------------------------- */ + +ZyanStatus ZyrexTrampolineFind(const void* original, ZyrexTrampolineChunk** trampoline) +{ + if (!original || !trampoline) + { + return ZYAN_STATUS_INVALID_ARGUMENT; + } + if (!g_trampoline_data.is_initialized) + { + return ZYAN_STATUS_INVALID_OPERATION; + } + + const ZyanUPointer region_address = ZYAN_ALIGN_DOWN((ZyanUPointer)original, + g_trampoline_data.region_size); + + ZyanUSize found_index; + const ZyanStatus status = + ZyanVectorBinarySearch(&g_trampoline_data.regions, ®ion_address, &found_index, + (ZyanComparison)&ZyanComparePointer); + ZYAN_CHECK(status); + + if (status == ZYAN_STATUS_FALSE) + { + return ZYAN_STATUS_FALSE; + } + + ZyrexTrampolineRegion** const element = + ZyanVectorGetMutable(&g_trampoline_data.regions, found_index); + ZYAN_ASSERT(element && *element); + + ZyrexTrampolineRegion* const region = *element; + ZYAN_ASSERT(region->header.signature == ZYREX_TRAMPOLINE_REGION_SIGNATURE); + + for (ZyanUSize i = 1; i < g_trampoline_data.chunks_per_region; ++i) + { + ZyrexTrampolineChunk* const chunk = ®ion->chunks[i]; + ZYAN_ASSERT(chunk); + + if (!chunk->is_used) + { + continue; + } + + if ((ZyanUPointer)&chunk->code_buffer == (ZyanUPointer)original) + { + *trampoline = chunk; + return ZYAN_STATUS_TRUE; + } + } + + return ZYAN_STATUS_FALSE; +} + +/* ---------------------------------------------------------------------------------------------- */ + /* ============================================================================================== */ diff --git a/src/Transaction.c b/src/Transaction.c index 7910bdb..c8b8e2e 100644 --- a/src/Transaction.c +++ b/src/Transaction.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -39,25 +40,6 @@ /* Enums and types */ /* ============================================================================================== */ -/** - * @brief Defines the `ZyrexHookType` enum. - */ -typedef enum ZyrexHookType_ -{ - /** - * @brief Inline hook. - */ - ZYREX_HOOK_TYPE_INLINE, - /** - * @brief Exception hook. - */ - ZYREX_HOOK_TYPE_EXCEPTION, - /** - * @brief Context/HWBP hook. - */ - ZYREX_HOOK_TYPE_CONTEXT -} ZyrexHookType; - /** * @brief Defines the `ZyrexOperationAction` enum. */ @@ -98,7 +80,7 @@ typedef struct ZyrexOperation_ * @brief This value points to the memory that is passed by the user to store the trampoline * pointer. */ - ZyanConstVoidPointer* trampoline_accessor; + //ZyanConstVoidPointer* trampoline_accessor; } ZyrexOperation; /* ============================================================================================== */ @@ -300,7 +282,49 @@ ZyanStatus ZyrexUpdateThread(DWORD thread_id) ZyanStatus ZyrexUpdateAllThreads() { - // TODO: + if (g_transaction_data.transaction_thread_id != GetCurrentThreadId()) + { + return ZYAN_STATUS_INVALID_OPERATION; + } + + ZYAN_ASSERT(g_transaction_data.pending_operations.data); + ZYAN_ASSERT(g_transaction_data.threads_to_update.data); + + const HANDLE h_snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); + if (h_snapshot == INVALID_HANDLE_VALUE) + { + return ZYAN_STATUS_BAD_SYSTEMCALL; + } + + THREADENTRY32 thread; + ZYAN_MEMSET(&thread, 0, sizeof(thread)); + thread.dwSize = sizeof(thread); + + if (Thread32First(h_snapshot, &thread)) + { + do + { + const HANDLE h_thread = + OpenThread(THREAD_SUSPEND_RESUME | THREAD_GET_CONTEXT | THREAD_SET_CONTEXT, + ZYAN_FALSE, thread.th32ThreadID); + if (h_thread != ZYAN_NULL) + { + if (SuspendThread(h_thread) == (DWORD)(-1)) + { + CloseHandle(h_thread); + } + else + { + ZyanVectorPushBack(&g_transaction_data.threads_to_update, &h_thread); + } + } + } while (!Thread32Next(h_snapshot, &thread)); + } + + if (!CloseHandle(h_snapshot)) + { + return ZYAN_STATUS_BAD_SYSTEMCALL; + }; return ZYAN_STATUS_SUCCESS; } @@ -336,11 +360,20 @@ ZyanStatus ZyrexTransactionCommitEx(const void** failed_operation) { case ZYREX_OPERATION_ACTION_ATTACH: { + // TODO: Check if code has changed between this call and the Attach* status = ZyrexWriteHookJump(item->address, item->trampoline); break; } case ZYREX_OPERATION_ACTION_REMOVE: status = ZyrexRestoreInstructions(item->address, item->trampoline); + if (ZYAN_SUCCESS(status)) + { + status = ZyrexTrampolineFree(item->trampoline); + if (status == ZYAN_STATUS_FALSE) + { + status = ZYAN_STATUS_NOT_FOUND; + } + } break; default: ZYAN_UNREACHABLE; @@ -407,8 +440,13 @@ ZyanStatus ZyrexTransactionAbort() /* ---------------------------------------------------------------------------------------------- */ ZyanStatus ZyrexInstallInlineHook(void* address, const void* callback, - ZyanConstVoidPointer* original) + ZyanConstVoidPointer* trampoline) { + if (!address || !callback || !trampoline) + { + return ZYAN_STATUS_INVALID_ARGUMENT; + } + if (g_transaction_data.transaction_thread_id != GetCurrentThreadId()) { return ZYAN_STATUS_INVALID_OPERATION; @@ -422,15 +460,13 @@ ZyanStatus ZyrexInstallInlineHook(void* address, const void* callback, /* type */ ZYREX_HOOK_TYPE_INLINE, /* action */ ZYREX_OPERATION_ACTION_ATTACH, /* address */ ZYAN_NULL, - /* trampoline */ ZYAN_NULL, - /* trampoline_accessor */ ZYAN_NULL + /* trampoline */ ZYAN_NULL }; operation.address = address; - operation.trampoline_accessor = original; ZYAN_CHECK(ZyrexTrampolineCreate(address, callback, ZYREX_SIZEOF_RELATIVE_JUMP, &operation.trampoline)); - *original = &operation.trampoline->code_buffer; + *trampoline = &operation.trampoline->code_buffer; return ZyanVectorPushBack(&g_transaction_data.pending_operations, &operation); } @@ -439,7 +475,7 @@ ZyanStatus ZyrexInstallInlineHook(void* address, const void* callback, /* Hook installation */ /* ---------------------------------------------------------------------------------------------- */ -ZyanStatus ZyrexRemoveInlineHook(void* address, ZyanConstVoidPointer* original) +ZyanStatus ZyrexRemoveInlineHook(ZyanConstVoidPointer* original) { if (g_transaction_data.transaction_thread_id != GetCurrentThreadId()) { @@ -449,24 +485,22 @@ ZyanStatus ZyrexRemoveInlineHook(void* address, ZyanConstVoidPointer* original) ZYAN_ASSERT(g_transaction_data.pending_operations.data); ZYAN_ASSERT(g_transaction_data.threads_to_update.data); + ZyrexTrampolineChunk* trampoline; + ZYAN_CHECK(ZyrexTrampolineFind(*original, &trampoline)); + ZyrexOperation operation = { /* type */ ZYREX_HOOK_TYPE_INLINE, /* action */ ZYREX_OPERATION_ACTION_REMOVE, /* address */ ZYAN_NULL, - /* trampoline */ ZYAN_NULL, - /* trampoline_accessor */ ZYAN_NULL + /* trampoline */ ZYAN_NULL }; operation.address = address; - operation.trampoline_accessor = original; - - // TODO: - /*ZYAN_CHECK(ZyrexTrampolineCreate(address, callback, ZYREX_SIZEOF_RELATIVE_JUMP, - &operation.trampoline));*/ + operation.trampoline = trampoline; - *original = &operation.trampoline->code_buffer; + *original = address; - return ZYAN_STATUS_SUCCESS;//ZyanVectorPushBack(&g_transaction_data.pending_operations, &operation); + return ZyanVectorPushBack(&g_transaction_data.pending_operations, &operation); } /* ---------------------------------------------------------------------------------------------- */