From ef8783d82d9defa23016502a302b91db58e032e9 Mon Sep 17 00:00:00 2001 From: Akash Mozumdar Date: Sat, 16 Feb 2019 03:17:16 -0500 Subject: [PATCH] move minhook into libs folder --- vnrhook/CMakeLists.txt | 10 +- vnrhook/MinHook.h | 186 ------- vnrhook/libminhook/CMakeLists.txt | 17 - vnrhook/libminhook/buffer.c | 312 ----------- vnrhook/libminhook/buffer.h | 42 -- vnrhook/libminhook/hde/hde32.c | 326 ----------- vnrhook/libminhook/hde/hde32.h | 105 ---- vnrhook/libminhook/hde/hde64.c | 337 ----------- vnrhook/libminhook/hde/hde64.h | 112 ---- vnrhook/libminhook/hde/pstdint.h | 39 -- vnrhook/libminhook/hde/table32.h | 73 --- vnrhook/libminhook/hde/table64.h | 74 --- vnrhook/libminhook/hook.c | 889 ------------------------------ vnrhook/libminhook/trampoline.c | 316 ----------- vnrhook/libminhook/trampoline.h | 105 ---- vnrhook/main.cc | 1 - vnrhook/main.h | 38 ++ vnrhook/texthook.cc | 1 - x64libs/minhook.lib | Bin 0 -> 126998 bytes x86libs/minhook.lib | Bin 0 -> 118908 bytes 20 files changed, 39 insertions(+), 2944 deletions(-) delete mode 100644 vnrhook/MinHook.h delete mode 100644 vnrhook/libminhook/CMakeLists.txt delete mode 100644 vnrhook/libminhook/buffer.c delete mode 100644 vnrhook/libminhook/buffer.h delete mode 100644 vnrhook/libminhook/hde/hde32.c delete mode 100644 vnrhook/libminhook/hde/hde32.h delete mode 100644 vnrhook/libminhook/hde/hde64.c delete mode 100644 vnrhook/libminhook/hde/hde64.h delete mode 100644 vnrhook/libminhook/hde/pstdint.h delete mode 100644 vnrhook/libminhook/hde/table32.h delete mode 100644 vnrhook/libminhook/hde/table64.h delete mode 100644 vnrhook/libminhook/hook.c delete mode 100644 vnrhook/libminhook/trampoline.c delete mode 100644 vnrhook/libminhook/trampoline.h create mode 100644 x64libs/minhook.lib create mode 100644 x86libs/minhook.lib diff --git a/vnrhook/CMakeLists.txt b/vnrhook/CMakeLists.txt index 842b371..4bc3975 100644 --- a/vnrhook/CMakeLists.txt +++ b/vnrhook/CMakeLists.txt @@ -1,6 +1,6 @@ include_directories(. util) -if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "8") +if(${CMAKE_SIZEOF_VOID_P} EQUAL 8) set(vnrhook_src main.cc texthook.cc @@ -23,20 +23,12 @@ set(vnrhook_src ) endif() -add_subdirectory(libminhook) - add_library(vnrhook SHARED ${vnrhook_src}) -enable_language(ASM_MASM) - set_target_properties(vnrhook PROPERTIES LINK_FLAGS "/SUBSYSTEM:WINDOWS /MANIFEST:NO" ) -target_compile_options(vnrhook PRIVATE - /wd4819 -) - set(vnrhook_libs Version.lib minhook diff --git a/vnrhook/MinHook.h b/vnrhook/MinHook.h deleted file mode 100644 index 15c0a87..0000000 --- a/vnrhook/MinHook.h +++ /dev/null @@ -1,186 +0,0 @@ -/* - * MinHook - The Minimalistic API Hooking Library for x64/x86 - * Copyright (C) 2009-2017 Tsuda Kageyu. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER - * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#if !(defined _M_IX86) && !(defined _M_X64) && !(defined __i386__) && !(defined __x86_64__) - #error MinHook supports only x86 and x64 systems. -#endif - -#include - -// MinHook Error Codes. -typedef enum MH_STATUS -{ - // Unknown error. Should not be returned. - MH_UNKNOWN = -1, - - // Successful. - MH_OK = 0, - - // MinHook is already initialized. - MH_ERROR_ALREADY_INITIALIZED, - - // MinHook is not initialized yet, or already uninitialized. - MH_ERROR_NOT_INITIALIZED, - - // The hook for the specified target function is already created. - MH_ERROR_ALREADY_CREATED, - - // The hook for the specified target function is not created yet. - MH_ERROR_NOT_CREATED, - - // The hook for the specified target function is already enabled. - MH_ERROR_ENABLED, - - // The hook for the specified target function is not enabled yet, or already - // disabled. - MH_ERROR_DISABLED, - - // The specified pointer is invalid. It points the address of non-allocated - // and/or non-executable region. - MH_ERROR_NOT_EXECUTABLE, - - // The specified target function cannot be hooked. - MH_ERROR_UNSUPPORTED_FUNCTION, - - // Failed to allocate memory. - MH_ERROR_MEMORY_ALLOC, - - // Failed to change the memory protection. - MH_ERROR_MEMORY_PROTECT, - - // The specified module is not loaded. - MH_ERROR_MODULE_NOT_FOUND, - - // The specified function is not found. - MH_ERROR_FUNCTION_NOT_FOUND -} -MH_STATUS; - -// Can be passed as a parameter to MH_EnableHook, MH_DisableHook, -// MH_QueueEnableHook or MH_QueueDisableHook. -#define MH_ALL_HOOKS NULL - -#ifdef __cplusplus -extern "C" { -#endif - - // Initialize the MinHook library. You must call this function EXACTLY ONCE - // at the beginning of your program. - MH_STATUS WINAPI MH_Initialize(VOID); - - // Uninitialize the MinHook library. You must call this function EXACTLY - // ONCE at the end of your program. - MH_STATUS WINAPI MH_Uninitialize(VOID); - - // Creates a Hook for the specified target function, in disabled state. - // Parameters: - // pTarget [in] A pointer to the target function, which will be - // overridden by the detour function. - // pDetour [in] A pointer to the detour function, which will override - // the target function. - // ppOriginal [out] A pointer to the trampoline function, which will be - // used to call the original target function. - // This parameter can be NULL. - MH_STATUS WINAPI MH_CreateHook(LPVOID pTarget, LPVOID pDetour, LPVOID *ppOriginal); - - // Creates a Hook for the specified API function, in disabled state. - // Parameters: - // pszModule [in] A pointer to the loaded module name which contains the - // target function. - // pszTarget [in] A pointer to the target function name, which will be - // overridden by the detour function. - // pDetour [in] A pointer to the detour function, which will override - // the target function. - // ppOriginal [out] A pointer to the trampoline function, which will be - // used to call the original target function. - // This parameter can be NULL. - MH_STATUS WINAPI MH_CreateHookApi( - LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, LPVOID *ppOriginal); - - // Creates a Hook for the specified API function, in disabled state. - // Parameters: - // pszModule [in] A pointer to the loaded module name which contains the - // target function. - // pszTarget [in] A pointer to the target function name, which will be - // overridden by the detour function. - // pDetour [in] A pointer to the detour function, which will override - // the target function. - // ppOriginal [out] A pointer to the trampoline function, which will be - // used to call the original target function. - // This parameter can be NULL. - // ppTarget [out] A pointer to the target function, which will be used - // with other functions. - // This parameter can be NULL. - MH_STATUS WINAPI MH_CreateHookApiEx( - LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, LPVOID *ppOriginal, LPVOID *ppTarget); - - // Removes an already created hook. - // Parameters: - // pTarget [in] A pointer to the target function. - MH_STATUS WINAPI MH_RemoveHook(LPVOID pTarget); - - // Enables an already created hook. - // Parameters: - // pTarget [in] A pointer to the target function. - // If this parameter is MH_ALL_HOOKS, all created hooks are - // enabled in one go. - MH_STATUS WINAPI MH_EnableHook(LPVOID pTarget); - - // Disables an already created hook. - // Parameters: - // pTarget [in] A pointer to the target function. - // If this parameter is MH_ALL_HOOKS, all created hooks are - // disabled in one go. - MH_STATUS WINAPI MH_DisableHook(LPVOID pTarget); - - // Queues to enable an already created hook. - // Parameters: - // pTarget [in] A pointer to the target function. - // If this parameter is MH_ALL_HOOKS, all created hooks are - // queued to be enabled. - MH_STATUS WINAPI MH_QueueEnableHook(LPVOID pTarget); - - // Queues to disable an already created hook. - // Parameters: - // pTarget [in] A pointer to the target function. - // If this parameter is MH_ALL_HOOKS, all created hooks are - // queued to be disabled. - MH_STATUS WINAPI MH_QueueDisableHook(LPVOID pTarget); - - // Applies all queued changes in one go. - MH_STATUS WINAPI MH_ApplyQueued(VOID); - - // Translates the MH_STATUS to its name as a string. - const char * WINAPI MH_StatusToString(MH_STATUS status); - -#ifdef __cplusplus -} -#endif - diff --git a/vnrhook/libminhook/CMakeLists.txt b/vnrhook/libminhook/CMakeLists.txt deleted file mode 100644 index 07beaf8..0000000 --- a/vnrhook/libminhook/CMakeLists.txt +++ /dev/null @@ -1,17 +0,0 @@ -if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "8") -set(minhook_src - buffer.c - hook.c - trampoline.c - hde/hde64.c -) -else() -set(minhook_src - buffer.c - hook.c - trampoline.c - hde/hde32.c -) -endif() - -add_library(minhook ${minhook_src}) \ No newline at end of file diff --git a/vnrhook/libminhook/buffer.c b/vnrhook/libminhook/buffer.c deleted file mode 100644 index 8f9fbce..0000000 --- a/vnrhook/libminhook/buffer.c +++ /dev/null @@ -1,312 +0,0 @@ -/* - * MinHook - The Minimalistic API Hooking Library for x64/x86 - * Copyright (C) 2009-2017 Tsuda Kageyu. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER - * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include -#include "buffer.h" - -// Size of each memory block. (= page size of VirtualAlloc) -#define MEMORY_BLOCK_SIZE 0x1000 - -// Max range for seeking a memory block. (= 1024MB) -#define MAX_MEMORY_RANGE 0x40000000 - -// Memory protection flags to check the executable address. -#define PAGE_EXECUTE_FLAGS \ - (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY) - -// Memory slot. -typedef struct _MEMORY_SLOT -{ - union - { - struct _MEMORY_SLOT *pNext; - UINT8 buffer[MEMORY_SLOT_SIZE]; - }; -} MEMORY_SLOT, *PMEMORY_SLOT; - -// Memory block info. Placed at the head of each block. -typedef struct _MEMORY_BLOCK -{ - struct _MEMORY_BLOCK *pNext; - PMEMORY_SLOT pFree; // First element of the free slot list. - UINT usedCount; -} MEMORY_BLOCK, *PMEMORY_BLOCK; - -//------------------------------------------------------------------------- -// Global Variables: -//------------------------------------------------------------------------- - -// First element of the memory block list. -PMEMORY_BLOCK g_pMemoryBlocks; - -//------------------------------------------------------------------------- -VOID InitializeBuffer(VOID) -{ - // Nothing to do for now. -} - -//------------------------------------------------------------------------- -VOID UninitializeBuffer(VOID) -{ - PMEMORY_BLOCK pBlock = g_pMemoryBlocks; - g_pMemoryBlocks = NULL; - - while (pBlock) - { - PMEMORY_BLOCK pNext = pBlock->pNext; - VirtualFree(pBlock, 0, MEM_RELEASE); - pBlock = pNext; - } -} - -//------------------------------------------------------------------------- -#if defined(_M_X64) || defined(__x86_64__) -static LPVOID FindPrevFreeRegion(LPVOID pAddress, LPVOID pMinAddr, DWORD dwAllocationGranularity) -{ - ULONG_PTR tryAddr = (ULONG_PTR)pAddress; - - // Round down to the allocation granularity. - tryAddr -= tryAddr % dwAllocationGranularity; - - // Start from the previous allocation granularity multiply. - tryAddr -= dwAllocationGranularity; - - while (tryAddr >= (ULONG_PTR)pMinAddr) - { - MEMORY_BASIC_INFORMATION mbi; - if (VirtualQuery((LPVOID)tryAddr, &mbi, sizeof(mbi)) == 0) - break; - - if (mbi.State == MEM_FREE) - return (LPVOID)tryAddr; - - if ((ULONG_PTR)mbi.AllocationBase < dwAllocationGranularity) - break; - - tryAddr = (ULONG_PTR)mbi.AllocationBase - dwAllocationGranularity; - } - - return NULL; -} -#endif - -//------------------------------------------------------------------------- -#if defined(_M_X64) || defined(__x86_64__) -static LPVOID FindNextFreeRegion(LPVOID pAddress, LPVOID pMaxAddr, DWORD dwAllocationGranularity) -{ - ULONG_PTR tryAddr = (ULONG_PTR)pAddress; - - // Round down to the allocation granularity. - tryAddr -= tryAddr % dwAllocationGranularity; - - // Start from the next allocation granularity multiply. - tryAddr += dwAllocationGranularity; - - while (tryAddr <= (ULONG_PTR)pMaxAddr) - { - MEMORY_BASIC_INFORMATION mbi; - if (VirtualQuery((LPVOID)tryAddr, &mbi, sizeof(mbi)) == 0) - break; - - if (mbi.State == MEM_FREE) - return (LPVOID)tryAddr; - - tryAddr = (ULONG_PTR)mbi.BaseAddress + mbi.RegionSize; - - // Round up to the next allocation granularity. - tryAddr += dwAllocationGranularity - 1; - tryAddr -= tryAddr % dwAllocationGranularity; - } - - return NULL; -} -#endif - -//------------------------------------------------------------------------- -static PMEMORY_BLOCK GetMemoryBlock(LPVOID pOrigin) -{ - PMEMORY_BLOCK pBlock; -#if defined(_M_X64) || defined(__x86_64__) - ULONG_PTR minAddr; - ULONG_PTR maxAddr; - - SYSTEM_INFO si; - GetSystemInfo(&si); - minAddr = (ULONG_PTR)si.lpMinimumApplicationAddress; - maxAddr = (ULONG_PTR)si.lpMaximumApplicationAddress; - - // pOrigin ± 512MB - if ((ULONG_PTR)pOrigin > MAX_MEMORY_RANGE && minAddr < (ULONG_PTR)pOrigin - MAX_MEMORY_RANGE) - minAddr = (ULONG_PTR)pOrigin - MAX_MEMORY_RANGE; - - if (maxAddr > (ULONG_PTR)pOrigin + MAX_MEMORY_RANGE) - maxAddr = (ULONG_PTR)pOrigin + MAX_MEMORY_RANGE; - - // Make room for MEMORY_BLOCK_SIZE bytes. - maxAddr -= MEMORY_BLOCK_SIZE - 1; -#endif - - // Look the registered blocks for a reachable one. - for (pBlock = g_pMemoryBlocks; pBlock != NULL; pBlock = pBlock->pNext) - { -#if defined(_M_X64) || defined(__x86_64__) - // Ignore the blocks too far. - if ((ULONG_PTR)pBlock < minAddr || (ULONG_PTR)pBlock >= maxAddr) - continue; -#endif - // The block has at least one unused slot. - if (pBlock->pFree != NULL) - return pBlock; - } - -#if defined(_M_X64) || defined(__x86_64__) - // Alloc a new block above if not found. - { - LPVOID pAlloc = pOrigin; - while ((ULONG_PTR)pAlloc >= minAddr) - { - pAlloc = FindPrevFreeRegion(pAlloc, (LPVOID)minAddr, si.dwAllocationGranularity); - if (pAlloc == NULL) - break; - - pBlock = (PMEMORY_BLOCK)VirtualAlloc( - pAlloc, MEMORY_BLOCK_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); - if (pBlock != NULL) - break; - } - } - - // Alloc a new block below if not found. - if (pBlock == NULL) - { - LPVOID pAlloc = pOrigin; - while ((ULONG_PTR)pAlloc <= maxAddr) - { - pAlloc = FindNextFreeRegion(pAlloc, (LPVOID)maxAddr, si.dwAllocationGranularity); - if (pAlloc == NULL) - break; - - pBlock = (PMEMORY_BLOCK)VirtualAlloc( - pAlloc, MEMORY_BLOCK_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); - if (pBlock != NULL) - break; - } - } -#else - // In x86 mode, a memory block can be placed anywhere. - pBlock = (PMEMORY_BLOCK)VirtualAlloc( - NULL, MEMORY_BLOCK_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); -#endif - - if (pBlock != NULL) - { - // Build a linked list of all the slots. - PMEMORY_SLOT pSlot = (PMEMORY_SLOT)pBlock + 1; - pBlock->pFree = NULL; - pBlock->usedCount = 0; - do - { - pSlot->pNext = pBlock->pFree; - pBlock->pFree = pSlot; - pSlot++; - } while ((ULONG_PTR)pSlot - (ULONG_PTR)pBlock <= MEMORY_BLOCK_SIZE - MEMORY_SLOT_SIZE); - - pBlock->pNext = g_pMemoryBlocks; - g_pMemoryBlocks = pBlock; - } - - return pBlock; -} - -//------------------------------------------------------------------------- -LPVOID AllocateBuffer(LPVOID pOrigin) -{ - PMEMORY_SLOT pSlot; - PMEMORY_BLOCK pBlock = GetMemoryBlock(pOrigin); - if (pBlock == NULL) - return NULL; - - // Remove an unused slot from the list. - pSlot = pBlock->pFree; - pBlock->pFree = pSlot->pNext; - pBlock->usedCount++; -#ifdef _DEBUG - // Fill the slot with INT3 for debugging. - memset(pSlot, 0xCC, sizeof(MEMORY_SLOT)); -#endif - return pSlot; -} - -//------------------------------------------------------------------------- -VOID FreeBuffer(LPVOID pBuffer) -{ - PMEMORY_BLOCK pBlock = g_pMemoryBlocks; - PMEMORY_BLOCK pPrev = NULL; - ULONG_PTR pTargetBlock = ((ULONG_PTR)pBuffer / MEMORY_BLOCK_SIZE) * MEMORY_BLOCK_SIZE; - - while (pBlock != NULL) - { - if ((ULONG_PTR)pBlock == pTargetBlock) - { - PMEMORY_SLOT pSlot = (PMEMORY_SLOT)pBuffer; -#ifdef _DEBUG - // Clear the released slot for debugging. - memset(pSlot, 0x00, sizeof(*pSlot)); -#endif - // Restore the released slot to the list. - pSlot->pNext = pBlock->pFree; - pBlock->pFree = pSlot; - pBlock->usedCount--; - - // Free if unused. - if (pBlock->usedCount == 0) - { - if (pPrev) - pPrev->pNext = pBlock->pNext; - else - g_pMemoryBlocks = pBlock->pNext; - - VirtualFree(pBlock, 0, MEM_RELEASE); - } - - break; - } - - pPrev = pBlock; - pBlock = pBlock->pNext; - } -} - -//------------------------------------------------------------------------- -BOOL IsExecutableAddress(LPVOID pAddress) -{ - MEMORY_BASIC_INFORMATION mi; - VirtualQuery(pAddress, &mi, sizeof(mi)); - - return (mi.State == MEM_COMMIT && (mi.Protect & PAGE_EXECUTE_FLAGS)); -} diff --git a/vnrhook/libminhook/buffer.h b/vnrhook/libminhook/buffer.h deleted file mode 100644 index 204d551..0000000 --- a/vnrhook/libminhook/buffer.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * MinHook - The Minimalistic API Hooking Library for x64/x86 - * Copyright (C) 2009-2017 Tsuda Kageyu. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER - * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -// Size of each memory slot. -#if defined(_M_X64) || defined(__x86_64__) - #define MEMORY_SLOT_SIZE 64 -#else - #define MEMORY_SLOT_SIZE 32 -#endif - -VOID InitializeBuffer(VOID); -VOID UninitializeBuffer(VOID); -LPVOID AllocateBuffer(LPVOID pOrigin); -VOID FreeBuffer(LPVOID pBuffer); -BOOL IsExecutableAddress(LPVOID pAddress); diff --git a/vnrhook/libminhook/hde/hde32.c b/vnrhook/libminhook/hde/hde32.c deleted file mode 100644 index 08fa25b..0000000 --- a/vnrhook/libminhook/hde/hde32.c +++ /dev/null @@ -1,326 +0,0 @@ -/* - * Hacker Disassembler Engine 32 C - * Copyright (c) 2008-2009, Vyacheslav Patkov. - * All rights reserved. - * - */ - -#if defined(_M_IX86) || defined(__i386__) - -#include "hde32.h" -#include "table32.h" - -unsigned int hde32_disasm(const void *code, hde32s *hs) -{ - uint8_t x, c, *p = (uint8_t *)code, cflags, opcode, pref = 0; - uint8_t *ht = hde32_table, m_mod, m_reg, m_rm, disp_size = 0; - - // Avoid using memset to reduce the footprint. -#ifndef _MSC_VER - memset((LPBYTE)hs, 0, sizeof(hde32s)); -#else - __stosb((LPBYTE)hs, 0, sizeof(hde32s)); -#endif - - for (x = 16; x; x--) - switch (c = *p++) { - case 0xf3: - hs->p_rep = c; - pref |= PRE_F3; - break; - case 0xf2: - hs->p_rep = c; - pref |= PRE_F2; - break; - case 0xf0: - hs->p_lock = c; - pref |= PRE_LOCK; - break; - case 0x26: case 0x2e: case 0x36: - case 0x3e: case 0x64: case 0x65: - hs->p_seg = c; - pref |= PRE_SEG; - break; - case 0x66: - hs->p_66 = c; - pref |= PRE_66; - break; - case 0x67: - hs->p_67 = c; - pref |= PRE_67; - break; - default: - goto pref_done; - } - pref_done: - - hs->flags = (uint32_t)pref << 23; - - if (!pref) - pref |= PRE_NONE; - - if ((hs->opcode = c) == 0x0f) { - hs->opcode2 = c = *p++; - ht += DELTA_OPCODES; - } else if (c >= 0xa0 && c <= 0xa3) { - if (pref & PRE_67) - pref |= PRE_66; - else - pref &= ~PRE_66; - } - - opcode = c; - cflags = ht[ht[opcode / 4] + (opcode % 4)]; - - if (cflags == C_ERROR) { - hs->flags |= F_ERROR | F_ERROR_OPCODE; - cflags = 0; - if ((opcode & -3) == 0x24) - cflags++; - } - - x = 0; - if (cflags & C_GROUP) { - uint16_t t; - t = *(uint16_t *)(ht + (cflags & 0x7f)); - cflags = (uint8_t)t; - x = (uint8_t)(t >> 8); - } - - if (hs->opcode2) { - ht = hde32_table + DELTA_PREFIXES; - if (ht[ht[opcode / 4] + (opcode % 4)] & pref) - hs->flags |= F_ERROR | F_ERROR_OPCODE; - } - - if (cflags & C_MODRM) { - hs->flags |= F_MODRM; - hs->modrm = c = *p++; - hs->modrm_mod = m_mod = c >> 6; - hs->modrm_rm = m_rm = c & 7; - hs->modrm_reg = m_reg = (c & 0x3f) >> 3; - - if (x && ((x << m_reg) & 0x80)) - hs->flags |= F_ERROR | F_ERROR_OPCODE; - - if (!hs->opcode2 && opcode >= 0xd9 && opcode <= 0xdf) { - uint8_t t = opcode - 0xd9; - if (m_mod == 3) { - ht = hde32_table + DELTA_FPU_MODRM + t*8; - t = ht[m_reg] << m_rm; - } else { - ht = hde32_table + DELTA_FPU_REG; - t = ht[t] << m_reg; - } - if (t & 0x80) - hs->flags |= F_ERROR | F_ERROR_OPCODE; - } - - if (pref & PRE_LOCK) { - if (m_mod == 3) { - hs->flags |= F_ERROR | F_ERROR_LOCK; - } else { - uint8_t *table_end, op = opcode; - if (hs->opcode2) { - ht = hde32_table + DELTA_OP2_LOCK_OK; - table_end = ht + DELTA_OP_ONLY_MEM - DELTA_OP2_LOCK_OK; - } else { - ht = hde32_table + DELTA_OP_LOCK_OK; - table_end = ht + DELTA_OP2_LOCK_OK - DELTA_OP_LOCK_OK; - op &= -2; - } - for (; ht != table_end; ht++) - if (*ht++ == op) { - if (!((*ht << m_reg) & 0x80)) - goto no_lock_error; - else - break; - } - hs->flags |= F_ERROR | F_ERROR_LOCK; - no_lock_error: - ; - } - } - - if (hs->opcode2) { - switch (opcode) { - case 0x20: case 0x22: - m_mod = 3; - if (m_reg > 4 || m_reg == 1) - goto error_operand; - else - goto no_error_operand; - case 0x21: case 0x23: - m_mod = 3; - if (m_reg == 4 || m_reg == 5) - goto error_operand; - else - goto no_error_operand; - } - } else { - switch (opcode) { - case 0x8c: - if (m_reg > 5) - goto error_operand; - else - goto no_error_operand; - case 0x8e: - if (m_reg == 1 || m_reg > 5) - goto error_operand; - else - goto no_error_operand; - } - } - - if (m_mod == 3) { - uint8_t *table_end; - if (hs->opcode2) { - ht = hde32_table + DELTA_OP2_ONLY_MEM; - table_end = ht + sizeof(hde32_table) - DELTA_OP2_ONLY_MEM; - } else { - ht = hde32_table + DELTA_OP_ONLY_MEM; - table_end = ht + DELTA_OP2_ONLY_MEM - DELTA_OP_ONLY_MEM; - } - for (; ht != table_end; ht += 2) - if (*ht++ == opcode) { - if (*ht++ & pref && !((*ht << m_reg) & 0x80)) - goto error_operand; - else - break; - } - goto no_error_operand; - } else if (hs->opcode2) { - switch (opcode) { - case 0x50: case 0xd7: case 0xf7: - if (pref & (PRE_NONE | PRE_66)) - goto error_operand; - break; - case 0xd6: - if (pref & (PRE_F2 | PRE_F3)) - goto error_operand; - break; - case 0xc5: - goto error_operand; - } - goto no_error_operand; - } else - goto no_error_operand; - - error_operand: - hs->flags |= F_ERROR | F_ERROR_OPERAND; - no_error_operand: - - c = *p++; - if (m_reg <= 1) { - if (opcode == 0xf6) - cflags |= C_IMM8; - else if (opcode == 0xf7) - cflags |= C_IMM_P66; - } - - switch (m_mod) { - case 0: - if (pref & PRE_67) { - if (m_rm == 6) - disp_size = 2; - } else - if (m_rm == 5) - disp_size = 4; - break; - case 1: - disp_size = 1; - break; - case 2: - disp_size = 2; - if (!(pref & PRE_67)) - disp_size <<= 1; - } - - if (m_mod != 3 && m_rm == 4 && !(pref & PRE_67)) { - hs->flags |= F_SIB; - p++; - hs->sib = c; - hs->sib_scale = c >> 6; - hs->sib_index = (c & 0x3f) >> 3; - if ((hs->sib_base = c & 7) == 5 && !(m_mod & 1)) - disp_size = 4; - } - - p--; - switch (disp_size) { - case 1: - hs->flags |= F_DISP8; - hs->disp.disp8 = *p; - break; - case 2: - hs->flags |= F_DISP16; - hs->disp.disp16 = *(uint16_t *)p; - break; - case 4: - hs->flags |= F_DISP32; - hs->disp.disp32 = *(uint32_t *)p; - } - p += disp_size; - } else if (pref & PRE_LOCK) - hs->flags |= F_ERROR | F_ERROR_LOCK; - - if (cflags & C_IMM_P66) { - if (cflags & C_REL32) { - if (pref & PRE_66) { - hs->flags |= F_IMM16 | F_RELATIVE; - hs->imm.imm16 = *(uint16_t *)p; - p += 2; - goto disasm_done; - } - goto rel32_ok; - } - if (pref & PRE_66) { - hs->flags |= F_IMM16; - hs->imm.imm16 = *(uint16_t *)p; - p += 2; - } else { - hs->flags |= F_IMM32; - hs->imm.imm32 = *(uint32_t *)p; - p += 4; - } - } - - if (cflags & C_IMM16) { - if (hs->flags & F_IMM32) { - hs->flags |= F_IMM16; - hs->disp.disp16 = *(uint16_t *)p; - } else if (hs->flags & F_IMM16) { - hs->flags |= F_2IMM16; - hs->disp.disp16 = *(uint16_t *)p; - } else { - hs->flags |= F_IMM16; - hs->imm.imm16 = *(uint16_t *)p; - } - p += 2; - } - if (cflags & C_IMM8) { - hs->flags |= F_IMM8; - hs->imm.imm8 = *p++; - } - - if (cflags & C_REL32) { - rel32_ok: - hs->flags |= F_IMM32 | F_RELATIVE; - hs->imm.imm32 = *(uint32_t *)p; - p += 4; - } else if (cflags & C_REL8) { - hs->flags |= F_IMM8 | F_RELATIVE; - hs->imm.imm8 = *p++; - } - - disasm_done: - - if ((hs->len = (uint8_t)(p-(uint8_t *)code)) > 15) { - hs->flags |= F_ERROR | F_ERROR_LENGTH; - hs->len = 15; - } - - return (unsigned int)hs->len; -} - -#endif // defined(_M_IX86) || defined(__i386__) diff --git a/vnrhook/libminhook/hde/hde32.h b/vnrhook/libminhook/hde/hde32.h deleted file mode 100644 index 1112450..0000000 --- a/vnrhook/libminhook/hde/hde32.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Hacker Disassembler Engine 32 - * Copyright (c) 2006-2009, Vyacheslav Patkov. - * All rights reserved. - * - * hde32.h: C/C++ header file - * - */ - -#ifndef _HDE32_H_ -#define _HDE32_H_ - -/* stdint.h - C99 standard header - * http://en.wikipedia.org/wiki/stdint.h - * - * if your compiler doesn't contain "stdint.h" header (for - * example, Microsoft Visual C++), you can download file: - * http://www.azillionmonkeys.com/qed/pstdint.h - * and change next line to: - * #include "pstdint.h" - */ -#include "pstdint.h" - -#define F_MODRM 0x00000001 -#define F_SIB 0x00000002 -#define F_IMM8 0x00000004 -#define F_IMM16 0x00000008 -#define F_IMM32 0x00000010 -#define F_DISP8 0x00000020 -#define F_DISP16 0x00000040 -#define F_DISP32 0x00000080 -#define F_RELATIVE 0x00000100 -#define F_2IMM16 0x00000800 -#define F_ERROR 0x00001000 -#define F_ERROR_OPCODE 0x00002000 -#define F_ERROR_LENGTH 0x00004000 -#define F_ERROR_LOCK 0x00008000 -#define F_ERROR_OPERAND 0x00010000 -#define F_PREFIX_REPNZ 0x01000000 -#define F_PREFIX_REPX 0x02000000 -#define F_PREFIX_REP 0x03000000 -#define F_PREFIX_66 0x04000000 -#define F_PREFIX_67 0x08000000 -#define F_PREFIX_LOCK 0x10000000 -#define F_PREFIX_SEG 0x20000000 -#define F_PREFIX_ANY 0x3f000000 - -#define PREFIX_SEGMENT_CS 0x2e -#define PREFIX_SEGMENT_SS 0x36 -#define PREFIX_SEGMENT_DS 0x3e -#define PREFIX_SEGMENT_ES 0x26 -#define PREFIX_SEGMENT_FS 0x64 -#define PREFIX_SEGMENT_GS 0x65 -#define PREFIX_LOCK 0xf0 -#define PREFIX_REPNZ 0xf2 -#define PREFIX_REPX 0xf3 -#define PREFIX_OPERAND_SIZE 0x66 -#define PREFIX_ADDRESS_SIZE 0x67 - -#pragma pack(push,1) - -typedef struct { - uint8_t len; - uint8_t p_rep; - uint8_t p_lock; - uint8_t p_seg; - uint8_t p_66; - uint8_t p_67; - uint8_t opcode; - uint8_t opcode2; - uint8_t modrm; - uint8_t modrm_mod; - uint8_t modrm_reg; - uint8_t modrm_rm; - uint8_t sib; - uint8_t sib_scale; - uint8_t sib_index; - uint8_t sib_base; - union { - uint8_t imm8; - uint16_t imm16; - uint32_t imm32; - } imm; - union { - uint8_t disp8; - uint16_t disp16; - uint32_t disp32; - } disp; - uint32_t flags; -} hde32s; - -#pragma pack(pop) - -#ifdef __cplusplus -extern "C" { -#endif - -/* __cdecl */ -unsigned int hde32_disasm(const void *code, hde32s *hs); - -#ifdef __cplusplus -} -#endif - -#endif /* _HDE32_H_ */ diff --git a/vnrhook/libminhook/hde/hde64.c b/vnrhook/libminhook/hde/hde64.c deleted file mode 100644 index c23e2fc..0000000 --- a/vnrhook/libminhook/hde/hde64.c +++ /dev/null @@ -1,337 +0,0 @@ -/* - * Hacker Disassembler Engine 64 C - * Copyright (c) 2008-2009, Vyacheslav Patkov. - * All rights reserved. - * - */ - -#if defined(_M_X64) || defined(__x86_64__) - -#include "hde64.h" -#include "table64.h" - -unsigned int hde64_disasm(const void *code, hde64s *hs) -{ - uint8_t x, c, *p = (uint8_t *)code, cflags, opcode, pref = 0; - uint8_t *ht = hde64_table, m_mod, m_reg, m_rm, disp_size = 0; - uint8_t op64 = 0; - - // Avoid using memset to reduce the footprint. -#ifndef _MSC_VER - memset((LPBYTE)hs, 0, sizeof(hde64s)); -#else - __stosb((LPBYTE)hs, 0, sizeof(hde64s)); -#endif - - for (x = 16; x; x--) - switch (c = *p++) { - case 0xf3: - hs->p_rep = c; - pref |= PRE_F3; - break; - case 0xf2: - hs->p_rep = c; - pref |= PRE_F2; - break; - case 0xf0: - hs->p_lock = c; - pref |= PRE_LOCK; - break; - case 0x26: case 0x2e: case 0x36: - case 0x3e: case 0x64: case 0x65: - hs->p_seg = c; - pref |= PRE_SEG; - break; - case 0x66: - hs->p_66 = c; - pref |= PRE_66; - break; - case 0x67: - hs->p_67 = c; - pref |= PRE_67; - break; - default: - goto pref_done; - } - pref_done: - - hs->flags = (uint32_t)pref << 23; - - if (!pref) - pref |= PRE_NONE; - - if ((c & 0xf0) == 0x40) { - hs->flags |= F_PREFIX_REX; - if ((hs->rex_w = (c & 0xf) >> 3) && (*p & 0xf8) == 0xb8) - op64++; - hs->rex_r = (c & 7) >> 2; - hs->rex_x = (c & 3) >> 1; - hs->rex_b = c & 1; - if (((c = *p++) & 0xf0) == 0x40) { - opcode = c; - goto error_opcode; - } - } - - if ((hs->opcode = c) == 0x0f) { - hs->opcode2 = c = *p++; - ht += DELTA_OPCODES; - } else if (c >= 0xa0 && c <= 0xa3) { - op64++; - if (pref & PRE_67) - pref |= PRE_66; - else - pref &= ~PRE_66; - } - - opcode = c; - cflags = ht[ht[opcode / 4] + (opcode % 4)]; - - if (cflags == C_ERROR) { - error_opcode: - hs->flags |= F_ERROR | F_ERROR_OPCODE; - cflags = 0; - if ((opcode & -3) == 0x24) - cflags++; - } - - x = 0; - if (cflags & C_GROUP) { - uint16_t t; - t = *(uint16_t *)(ht + (cflags & 0x7f)); - cflags = (uint8_t)t; - x = (uint8_t)(t >> 8); - } - - if (hs->opcode2) { - ht = hde64_table + DELTA_PREFIXES; - if (ht[ht[opcode / 4] + (opcode % 4)] & pref) - hs->flags |= F_ERROR | F_ERROR_OPCODE; - } - - if (cflags & C_MODRM) { - hs->flags |= F_MODRM; - hs->modrm = c = *p++; - hs->modrm_mod = m_mod = c >> 6; - hs->modrm_rm = m_rm = c & 7; - hs->modrm_reg = m_reg = (c & 0x3f) >> 3; - - if (x && ((x << m_reg) & 0x80)) - hs->flags |= F_ERROR | F_ERROR_OPCODE; - - if (!hs->opcode2 && opcode >= 0xd9 && opcode <= 0xdf) { - uint8_t t = opcode - 0xd9; - if (m_mod == 3) { - ht = hde64_table + DELTA_FPU_MODRM + t*8; - t = ht[m_reg] << m_rm; - } else { - ht = hde64_table + DELTA_FPU_REG; - t = ht[t] << m_reg; - } - if (t & 0x80) - hs->flags |= F_ERROR | F_ERROR_OPCODE; - } - - if (pref & PRE_LOCK) { - if (m_mod == 3) { - hs->flags |= F_ERROR | F_ERROR_LOCK; - } else { - uint8_t *table_end, op = opcode; - if (hs->opcode2) { - ht = hde64_table + DELTA_OP2_LOCK_OK; - table_end = ht + DELTA_OP_ONLY_MEM - DELTA_OP2_LOCK_OK; - } else { - ht = hde64_table + DELTA_OP_LOCK_OK; - table_end = ht + DELTA_OP2_LOCK_OK - DELTA_OP_LOCK_OK; - op &= -2; - } - for (; ht != table_end; ht++) - if (*ht++ == op) { - if (!((*ht << m_reg) & 0x80)) - goto no_lock_error; - else - break; - } - hs->flags |= F_ERROR | F_ERROR_LOCK; - no_lock_error: - ; - } - } - - if (hs->opcode2) { - switch (opcode) { - case 0x20: case 0x22: - m_mod = 3; - if (m_reg > 4 || m_reg == 1) - goto error_operand; - else - goto no_error_operand; - case 0x21: case 0x23: - m_mod = 3; - if (m_reg == 4 || m_reg == 5) - goto error_operand; - else - goto no_error_operand; - } - } else { - switch (opcode) { - case 0x8c: - if (m_reg > 5) - goto error_operand; - else - goto no_error_operand; - case 0x8e: - if (m_reg == 1 || m_reg > 5) - goto error_operand; - else - goto no_error_operand; - } - } - - if (m_mod == 3) { - uint8_t *table_end; - if (hs->opcode2) { - ht = hde64_table + DELTA_OP2_ONLY_MEM; - table_end = ht + sizeof(hde64_table) - DELTA_OP2_ONLY_MEM; - } else { - ht = hde64_table + DELTA_OP_ONLY_MEM; - table_end = ht + DELTA_OP2_ONLY_MEM - DELTA_OP_ONLY_MEM; - } - for (; ht != table_end; ht += 2) - if (*ht++ == opcode) { - if (*ht++ & pref && !((*ht << m_reg) & 0x80)) - goto error_operand; - else - break; - } - goto no_error_operand; - } else if (hs->opcode2) { - switch (opcode) { - case 0x50: case 0xd7: case 0xf7: - if (pref & (PRE_NONE | PRE_66)) - goto error_operand; - break; - case 0xd6: - if (pref & (PRE_F2 | PRE_F3)) - goto error_operand; - break; - case 0xc5: - goto error_operand; - } - goto no_error_operand; - } else - goto no_error_operand; - - error_operand: - hs->flags |= F_ERROR | F_ERROR_OPERAND; - no_error_operand: - - c = *p++; - if (m_reg <= 1) { - if (opcode == 0xf6) - cflags |= C_IMM8; - else if (opcode == 0xf7) - cflags |= C_IMM_P66; - } - - switch (m_mod) { - case 0: - if (pref & PRE_67) { - if (m_rm == 6) - disp_size = 2; - } else - if (m_rm == 5) - disp_size = 4; - break; - case 1: - disp_size = 1; - break; - case 2: - disp_size = 2; - if (!(pref & PRE_67)) - disp_size <<= 1; - } - - if (m_mod != 3 && m_rm == 4) { - hs->flags |= F_SIB; - p++; - hs->sib = c; - hs->sib_scale = c >> 6; - hs->sib_index = (c & 0x3f) >> 3; - if ((hs->sib_base = c & 7) == 5 && !(m_mod & 1)) - disp_size = 4; - } - - p--; - switch (disp_size) { - case 1: - hs->flags |= F_DISP8; - hs->disp.disp8 = *p; - break; - case 2: - hs->flags |= F_DISP16; - hs->disp.disp16 = *(uint16_t *)p; - break; - case 4: - hs->flags |= F_DISP32; - hs->disp.disp32 = *(uint32_t *)p; - } - p += disp_size; - } else if (pref & PRE_LOCK) - hs->flags |= F_ERROR | F_ERROR_LOCK; - - if (cflags & C_IMM_P66) { - if (cflags & C_REL32) { - if (pref & PRE_66) { - hs->flags |= F_IMM16 | F_RELATIVE; - hs->imm.imm16 = *(uint16_t *)p; - p += 2; - goto disasm_done; - } - goto rel32_ok; - } - if (op64) { - hs->flags |= F_IMM64; - hs->imm.imm64 = *(uint64_t *)p; - p += 8; - } else if (!(pref & PRE_66)) { - hs->flags |= F_IMM32; - hs->imm.imm32 = *(uint32_t *)p; - p += 4; - } else - goto imm16_ok; - } - - - if (cflags & C_IMM16) { - imm16_ok: - hs->flags |= F_IMM16; - hs->imm.imm16 = *(uint16_t *)p; - p += 2; - } - if (cflags & C_IMM8) { - hs->flags |= F_IMM8; - hs->imm.imm8 = *p++; - } - - if (cflags & C_REL32) { - rel32_ok: - hs->flags |= F_IMM32 | F_RELATIVE; - hs->imm.imm32 = *(uint32_t *)p; - p += 4; - } else if (cflags & C_REL8) { - hs->flags |= F_IMM8 | F_RELATIVE; - hs->imm.imm8 = *p++; - } - - disasm_done: - - if ((hs->len = (uint8_t)(p-(uint8_t *)code)) > 15) { - hs->flags |= F_ERROR | F_ERROR_LENGTH; - hs->len = 15; - } - - return (unsigned int)hs->len; -} - -#endif // defined(_M_X64) || defined(__x86_64__) diff --git a/vnrhook/libminhook/hde/hde64.h b/vnrhook/libminhook/hde/hde64.h deleted file mode 100644 index ecbf4df..0000000 --- a/vnrhook/libminhook/hde/hde64.h +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Hacker Disassembler Engine 64 - * Copyright (c) 2008-2009, Vyacheslav Patkov. - * All rights reserved. - * - * hde64.h: C/C++ header file - * - */ - -#ifndef _HDE64_H_ -#define _HDE64_H_ - -/* stdint.h - C99 standard header - * http://en.wikipedia.org/wiki/stdint.h - * - * if your compiler doesn't contain "stdint.h" header (for - * example, Microsoft Visual C++), you can download file: - * http://www.azillionmonkeys.com/qed/pstdint.h - * and change next line to: - * #include "pstdint.h" - */ -#include "pstdint.h" - -#define F_MODRM 0x00000001 -#define F_SIB 0x00000002 -#define F_IMM8 0x00000004 -#define F_IMM16 0x00000008 -#define F_IMM32 0x00000010 -#define F_IMM64 0x00000020 -#define F_DISP8 0x00000040 -#define F_DISP16 0x00000080 -#define F_DISP32 0x00000100 -#define F_RELATIVE 0x00000200 -#define F_ERROR 0x00001000 -#define F_ERROR_OPCODE 0x00002000 -#define F_ERROR_LENGTH 0x00004000 -#define F_ERROR_LOCK 0x00008000 -#define F_ERROR_OPERAND 0x00010000 -#define F_PREFIX_REPNZ 0x01000000 -#define F_PREFIX_REPX 0x02000000 -#define F_PREFIX_REP 0x03000000 -#define F_PREFIX_66 0x04000000 -#define F_PREFIX_67 0x08000000 -#define F_PREFIX_LOCK 0x10000000 -#define F_PREFIX_SEG 0x20000000 -#define F_PREFIX_REX 0x40000000 -#define F_PREFIX_ANY 0x7f000000 - -#define PREFIX_SEGMENT_CS 0x2e -#define PREFIX_SEGMENT_SS 0x36 -#define PREFIX_SEGMENT_DS 0x3e -#define PREFIX_SEGMENT_ES 0x26 -#define PREFIX_SEGMENT_FS 0x64 -#define PREFIX_SEGMENT_GS 0x65 -#define PREFIX_LOCK 0xf0 -#define PREFIX_REPNZ 0xf2 -#define PREFIX_REPX 0xf3 -#define PREFIX_OPERAND_SIZE 0x66 -#define PREFIX_ADDRESS_SIZE 0x67 - -#pragma pack(push,1) - -typedef struct { - uint8_t len; - uint8_t p_rep; - uint8_t p_lock; - uint8_t p_seg; - uint8_t p_66; - uint8_t p_67; - uint8_t rex; - uint8_t rex_w; - uint8_t rex_r; - uint8_t rex_x; - uint8_t rex_b; - uint8_t opcode; - uint8_t opcode2; - uint8_t modrm; - uint8_t modrm_mod; - uint8_t modrm_reg; - uint8_t modrm_rm; - uint8_t sib; - uint8_t sib_scale; - uint8_t sib_index; - uint8_t sib_base; - union { - uint8_t imm8; - uint16_t imm16; - uint32_t imm32; - uint64_t imm64; - } imm; - union { - uint8_t disp8; - uint16_t disp16; - uint32_t disp32; - } disp; - uint32_t flags; -} hde64s; - -#pragma pack(pop) - -#ifdef __cplusplus -extern "C" { -#endif - -/* __cdecl */ -unsigned int hde64_disasm(const void *code, hde64s *hs); - -#ifdef __cplusplus -} -#endif - -#endif /* _HDE64_H_ */ diff --git a/vnrhook/libminhook/hde/pstdint.h b/vnrhook/libminhook/hde/pstdint.h deleted file mode 100644 index 84d82a0..0000000 --- a/vnrhook/libminhook/hde/pstdint.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * MinHook - The Minimalistic API Hooking Library for x64/x86 - * Copyright (C) 2009-2017 Tsuda Kageyu. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include - -// Integer types for HDE. -typedef INT8 int8_t; -typedef INT16 int16_t; -typedef INT32 int32_t; -typedef INT64 int64_t; -typedef UINT8 uint8_t; -typedef UINT16 uint16_t; -typedef UINT32 uint32_t; -typedef UINT64 uint64_t; diff --git a/vnrhook/libminhook/hde/table32.h b/vnrhook/libminhook/hde/table32.h deleted file mode 100644 index 7b3e12e..0000000 --- a/vnrhook/libminhook/hde/table32.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Hacker Disassembler Engine 32 C - * Copyright (c) 2008-2009, Vyacheslav Patkov. - * All rights reserved. - * - */ - -#define C_NONE 0x00 -#define C_MODRM 0x01 -#define C_IMM8 0x02 -#define C_IMM16 0x04 -#define C_IMM_P66 0x10 -#define C_REL8 0x20 -#define C_REL32 0x40 -#define C_GROUP 0x80 -#define C_ERROR 0xff - -#define PRE_ANY 0x00 -#define PRE_NONE 0x01 -#define PRE_F2 0x02 -#define PRE_F3 0x04 -#define PRE_66 0x08 -#define PRE_67 0x10 -#define PRE_LOCK 0x20 -#define PRE_SEG 0x40 -#define PRE_ALL 0xff - -#define DELTA_OPCODES 0x4a -#define DELTA_FPU_REG 0xf1 -#define DELTA_FPU_MODRM 0xf8 -#define DELTA_PREFIXES 0x130 -#define DELTA_OP_LOCK_OK 0x1a1 -#define DELTA_OP2_LOCK_OK 0x1b9 -#define DELTA_OP_ONLY_MEM 0x1cb -#define DELTA_OP2_ONLY_MEM 0x1da - -unsigned char hde32_table[] = { - 0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3, - 0xa8,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xac,0xaa,0xb2,0xaa,0x9f,0x9f, - 0x9f,0x9f,0xb5,0xa3,0xa3,0xa4,0xaa,0xaa,0xba,0xaa,0x96,0xaa,0xa8,0xaa,0xc3, - 0xc3,0x96,0x96,0xb7,0xae,0xd6,0xbd,0xa3,0xc5,0xa3,0xa3,0x9f,0xc3,0x9c,0xaa, - 0xaa,0xac,0xaa,0xbf,0x03,0x7f,0x11,0x7f,0x01,0x7f,0x01,0x3f,0x01,0x01,0x90, - 0x82,0x7d,0x97,0x59,0x59,0x59,0x59,0x59,0x7f,0x59,0x59,0x60,0x7d,0x7f,0x7f, - 0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x9a,0x88,0x7d, - 0x59,0x50,0x50,0x50,0x50,0x59,0x59,0x59,0x59,0x61,0x94,0x61,0x9e,0x59,0x59, - 0x85,0x59,0x92,0xa3,0x60,0x60,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59, - 0x59,0x59,0x9f,0x01,0x03,0x01,0x04,0x03,0xd5,0x03,0xcc,0x01,0xbc,0x03,0xf0, - 0x10,0x10,0x10,0x10,0x50,0x50,0x50,0x50,0x14,0x20,0x20,0x20,0x20,0x01,0x01, - 0x01,0x01,0xc4,0x02,0x10,0x00,0x00,0x00,0x00,0x01,0x01,0xc0,0xc2,0x10,0x11, - 0x02,0x03,0x11,0x03,0x03,0x04,0x00,0x00,0x14,0x00,0x02,0x00,0x00,0xc6,0xc8, - 0x02,0x02,0x02,0x02,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xca, - 0x01,0x01,0x01,0x00,0x06,0x00,0x04,0x00,0xc0,0xc2,0x01,0x01,0x03,0x01,0xff, - 0xff,0x01,0x00,0x03,0xc4,0xc4,0xc6,0x03,0x01,0x01,0x01,0xff,0x03,0x03,0x03, - 0xc8,0x40,0x00,0x0a,0x00,0x04,0x00,0x00,0x00,0x00,0x7f,0x00,0x33,0x01,0x00, - 0x00,0x00,0x00,0x00,0x00,0xff,0xbf,0xff,0xff,0x00,0x00,0x00,0x00,0x07,0x00, - 0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0xff,0xff,0x00,0x00,0x00,0xbf,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x7f,0x00,0x00,0xff,0x4a,0x4a,0x4a,0x4a,0x4b,0x52,0x4a,0x4a,0x4a,0x4a,0x4f, - 0x4c,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x55,0x45,0x40,0x4a,0x4a,0x4a, - 0x45,0x59,0x4d,0x46,0x4a,0x5d,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a, - 0x4a,0x4a,0x4a,0x4a,0x4a,0x61,0x63,0x67,0x4e,0x4a,0x4a,0x6b,0x6d,0x4a,0x4a, - 0x45,0x6d,0x4a,0x4a,0x44,0x45,0x4a,0x4a,0x00,0x00,0x00,0x02,0x0d,0x06,0x06, - 0x06,0x06,0x0e,0x00,0x00,0x00,0x00,0x06,0x06,0x06,0x00,0x06,0x06,0x02,0x06, - 0x00,0x0a,0x0a,0x07,0x07,0x06,0x02,0x05,0x05,0x02,0x02,0x00,0x00,0x04,0x04, - 0x04,0x04,0x00,0x00,0x00,0x0e,0x05,0x06,0x06,0x06,0x01,0x06,0x00,0x00,0x08, - 0x00,0x10,0x00,0x18,0x00,0x20,0x00,0x28,0x00,0x30,0x00,0x80,0x01,0x82,0x01, - 0x86,0x00,0xf6,0xcf,0xfe,0x3f,0xab,0x00,0xb0,0x00,0xb1,0x00,0xb3,0x00,0xba, - 0xf8,0xbb,0x00,0xc0,0x00,0xc1,0x00,0xc7,0xbf,0x62,0xff,0x00,0x8d,0xff,0x00, - 0xc4,0xff,0x00,0xc5,0xff,0x00,0xff,0xff,0xeb,0x01,0xff,0x0e,0x12,0x08,0x00, - 0x13,0x09,0x00,0x16,0x08,0x00,0x17,0x09,0x00,0x2b,0x09,0x00,0xae,0xff,0x07, - 0xb2,0xff,0x00,0xb4,0xff,0x00,0xb5,0xff,0x00,0xc3,0x01,0x00,0xc7,0xff,0xbf, - 0xe7,0x08,0x00,0xf0,0x02,0x00 -}; diff --git a/vnrhook/libminhook/hde/table64.h b/vnrhook/libminhook/hde/table64.h deleted file mode 100644 index 01d4541..0000000 --- a/vnrhook/libminhook/hde/table64.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Hacker Disassembler Engine 64 C - * Copyright (c) 2008-2009, Vyacheslav Patkov. - * All rights reserved. - * - */ - -#define C_NONE 0x00 -#define C_MODRM 0x01 -#define C_IMM8 0x02 -#define C_IMM16 0x04 -#define C_IMM_P66 0x10 -#define C_REL8 0x20 -#define C_REL32 0x40 -#define C_GROUP 0x80 -#define C_ERROR 0xff - -#define PRE_ANY 0x00 -#define PRE_NONE 0x01 -#define PRE_F2 0x02 -#define PRE_F3 0x04 -#define PRE_66 0x08 -#define PRE_67 0x10 -#define PRE_LOCK 0x20 -#define PRE_SEG 0x40 -#define PRE_ALL 0xff - -#define DELTA_OPCODES 0x4a -#define DELTA_FPU_REG 0xfd -#define DELTA_FPU_MODRM 0x104 -#define DELTA_PREFIXES 0x13c -#define DELTA_OP_LOCK_OK 0x1ae -#define DELTA_OP2_LOCK_OK 0x1c6 -#define DELTA_OP_ONLY_MEM 0x1d8 -#define DELTA_OP2_ONLY_MEM 0x1e7 - -unsigned char hde64_table[] = { - 0xa5,0xaa,0xa5,0xb8,0xa5,0xaa,0xa5,0xaa,0xa5,0xb8,0xa5,0xb8,0xa5,0xb8,0xa5, - 0xb8,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xac,0xc0,0xcc,0xc0,0xa1,0xa1, - 0xa1,0xa1,0xb1,0xa5,0xa5,0xa6,0xc0,0xc0,0xd7,0xda,0xe0,0xc0,0xe4,0xc0,0xea, - 0xea,0xe0,0xe0,0x98,0xc8,0xee,0xf1,0xa5,0xd3,0xa5,0xa5,0xa1,0xea,0x9e,0xc0, - 0xc0,0xc2,0xc0,0xe6,0x03,0x7f,0x11,0x7f,0x01,0x7f,0x01,0x3f,0x01,0x01,0xab, - 0x8b,0x90,0x64,0x5b,0x5b,0x5b,0x5b,0x5b,0x92,0x5b,0x5b,0x76,0x90,0x92,0x92, - 0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x6a,0x73,0x90, - 0x5b,0x52,0x52,0x52,0x52,0x5b,0x5b,0x5b,0x5b,0x77,0x7c,0x77,0x85,0x5b,0x5b, - 0x70,0x5b,0x7a,0xaf,0x76,0x76,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b, - 0x5b,0x5b,0x86,0x01,0x03,0x01,0x04,0x03,0xd5,0x03,0xd5,0x03,0xcc,0x01,0xbc, - 0x03,0xf0,0x03,0x03,0x04,0x00,0x50,0x50,0x50,0x50,0xff,0x20,0x20,0x20,0x20, - 0x01,0x01,0x01,0x01,0xc4,0x02,0x10,0xff,0xff,0xff,0x01,0x00,0x03,0x11,0xff, - 0x03,0xc4,0xc6,0xc8,0x02,0x10,0x00,0xff,0xcc,0x01,0x01,0x01,0x00,0x00,0x00, - 0x00,0x01,0x01,0x03,0x01,0xff,0xff,0xc0,0xc2,0x10,0x11,0x02,0x03,0x01,0x01, - 0x01,0xff,0xff,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0x10, - 0x10,0x10,0x10,0x02,0x10,0x00,0x00,0xc6,0xc8,0x02,0x02,0x02,0x02,0x06,0x00, - 0x04,0x00,0x02,0xff,0x00,0xc0,0xc2,0x01,0x01,0x03,0x03,0x03,0xca,0x40,0x00, - 0x0a,0x00,0x04,0x00,0x00,0x00,0x00,0x7f,0x00,0x33,0x01,0x00,0x00,0x00,0x00, - 0x00,0x00,0xff,0xbf,0xff,0xff,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0xff,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff, - 0x00,0x00,0x00,0xbf,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7f,0x00,0x00, - 0xff,0x40,0x40,0x40,0x40,0x41,0x49,0x40,0x40,0x40,0x40,0x4c,0x42,0x40,0x40, - 0x40,0x40,0x40,0x40,0x40,0x40,0x4f,0x44,0x53,0x40,0x40,0x40,0x44,0x57,0x43, - 0x5c,0x40,0x60,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40, - 0x40,0x40,0x64,0x66,0x6e,0x6b,0x40,0x40,0x6a,0x46,0x40,0x40,0x44,0x46,0x40, - 0x40,0x5b,0x44,0x40,0x40,0x00,0x00,0x00,0x00,0x06,0x06,0x06,0x06,0x01,0x06, - 0x06,0x02,0x06,0x06,0x00,0x06,0x00,0x0a,0x0a,0x00,0x00,0x00,0x02,0x07,0x07, - 0x06,0x02,0x0d,0x06,0x06,0x06,0x0e,0x05,0x05,0x02,0x02,0x00,0x00,0x04,0x04, - 0x04,0x04,0x05,0x06,0x06,0x06,0x00,0x00,0x00,0x0e,0x00,0x00,0x08,0x00,0x10, - 0x00,0x18,0x00,0x20,0x00,0x28,0x00,0x30,0x00,0x80,0x01,0x82,0x01,0x86,0x00, - 0xf6,0xcf,0xfe,0x3f,0xab,0x00,0xb0,0x00,0xb1,0x00,0xb3,0x00,0xba,0xf8,0xbb, - 0x00,0xc0,0x00,0xc1,0x00,0xc7,0xbf,0x62,0xff,0x00,0x8d,0xff,0x00,0xc4,0xff, - 0x00,0xc5,0xff,0x00,0xff,0xff,0xeb,0x01,0xff,0x0e,0x12,0x08,0x00,0x13,0x09, - 0x00,0x16,0x08,0x00,0x17,0x09,0x00,0x2b,0x09,0x00,0xae,0xff,0x07,0xb2,0xff, - 0x00,0xb4,0xff,0x00,0xb5,0xff,0x00,0xc3,0x01,0x00,0xc7,0xff,0xbf,0xe7,0x08, - 0x00,0xf0,0x02,0x00 -}; diff --git a/vnrhook/libminhook/hook.c b/vnrhook/libminhook/hook.c deleted file mode 100644 index 793c176..0000000 --- a/vnrhook/libminhook/hook.c +++ /dev/null @@ -1,889 +0,0 @@ -/* - * MinHook - The Minimalistic API Hooking Library for x64/x86 - * Copyright (C) 2009-2017 Tsuda Kageyu. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER - * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include -#include -#include - -#include "MinHook.h" -#include "buffer.h" -#include "trampoline.h" - -#ifndef ARRAYSIZE - #define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) -#endif - -// Initial capacity of the HOOK_ENTRY buffer. -#define INITIAL_HOOK_CAPACITY 32 - -// Initial capacity of the thread IDs buffer. -#define INITIAL_THREAD_CAPACITY 128 - -// Special hook position values. -#define INVALID_HOOK_POS UINT_MAX -#define ALL_HOOKS_POS UINT_MAX - -// Freeze() action argument defines. -#define ACTION_DISABLE 0 -#define ACTION_ENABLE 1 -#define ACTION_APPLY_QUEUED 2 - -// Thread access rights for suspending/resuming threads. -#define THREAD_ACCESS \ - (THREAD_SUSPEND_RESUME | THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION | THREAD_SET_CONTEXT) - -// Hook information. -typedef struct _HOOK_ENTRY -{ - LPVOID pTarget; // Address of the target function. - LPVOID pDetour; // Address of the detour or relay function. - LPVOID pTrampoline; // Address of the trampoline function. - UINT8 backup[8]; // Original prologue of the target function. - - UINT8 patchAbove : 1; // Uses the hot patch area. - UINT8 isEnabled : 1; // Enabled. - UINT8 queueEnable : 1; // Queued for enabling/disabling when != isEnabled. - - UINT nIP : 4; // Count of the instruction boundaries. - UINT8 oldIPs[8]; // Instruction boundaries of the target function. - UINT8 newIPs[8]; // Instruction boundaries of the trampoline function. -} HOOK_ENTRY, *PHOOK_ENTRY; - -// Suspended threads for Freeze()/Unfreeze(). -typedef struct _FROZEN_THREADS -{ - LPDWORD pItems; // Data heap - UINT capacity; // Size of allocated data heap, items - UINT size; // Actual number of data items -} FROZEN_THREADS, *PFROZEN_THREADS; - -//------------------------------------------------------------------------- -// Global Variables: -//------------------------------------------------------------------------- - -// Spin lock flag for EnterSpinLock()/LeaveSpinLock(). -volatile LONG g_isLocked = FALSE; - -// Private heap handle. If not NULL, this library is initialized. -HANDLE g_hHeap = NULL; - -// Hook entries. -struct -{ - PHOOK_ENTRY pItems; // Data heap - UINT capacity; // Size of allocated data heap, items - UINT size; // Actual number of data items -} g_hooks; - -//------------------------------------------------------------------------- -// Returns INVALID_HOOK_POS if not found. -static UINT FindHookEntry(LPVOID pTarget) -{ - UINT i; - for (i = 0; i < g_hooks.size; ++i) - { - if ((ULONG_PTR)pTarget == (ULONG_PTR)g_hooks.pItems[i].pTarget) - return i; - } - - return INVALID_HOOK_POS; -} - -//------------------------------------------------------------------------- -static PHOOK_ENTRY AddHookEntry() -{ - if (g_hooks.pItems == NULL) - { - g_hooks.capacity = INITIAL_HOOK_CAPACITY; - g_hooks.pItems = (PHOOK_ENTRY)HeapAlloc( - g_hHeap, 0, g_hooks.capacity * sizeof(HOOK_ENTRY)); - if (g_hooks.pItems == NULL) - return NULL; - } - else if (g_hooks.size >= g_hooks.capacity) - { - PHOOK_ENTRY p = (PHOOK_ENTRY)HeapReAlloc( - g_hHeap, 0, g_hooks.pItems, (g_hooks.capacity * 2) * sizeof(HOOK_ENTRY)); - if (p == NULL) - return NULL; - - g_hooks.capacity *= 2; - g_hooks.pItems = p; - } - - return &g_hooks.pItems[g_hooks.size++]; -} - -//------------------------------------------------------------------------- -static void DeleteHookEntry(UINT pos) -{ - if (pos < g_hooks.size - 1) - g_hooks.pItems[pos] = g_hooks.pItems[g_hooks.size - 1]; - - g_hooks.size--; - - if (g_hooks.capacity / 2 >= INITIAL_HOOK_CAPACITY && g_hooks.capacity / 2 >= g_hooks.size) - { - PHOOK_ENTRY p = (PHOOK_ENTRY)HeapReAlloc( - g_hHeap, 0, g_hooks.pItems, (g_hooks.capacity / 2) * sizeof(HOOK_ENTRY)); - if (p == NULL) - return; - - g_hooks.capacity /= 2; - g_hooks.pItems = p; - } -} - -//------------------------------------------------------------------------- -static DWORD_PTR FindOldIP(PHOOK_ENTRY pHook, DWORD_PTR ip) -{ - UINT i; - - if (pHook->patchAbove && ip == ((DWORD_PTR)pHook->pTarget - sizeof(JMP_REL))) - return (DWORD_PTR)pHook->pTarget; - - for (i = 0; i < pHook->nIP; ++i) - { - if (ip == ((DWORD_PTR)pHook->pTrampoline + pHook->newIPs[i])) - return (DWORD_PTR)pHook->pTarget + pHook->oldIPs[i]; - } - -#if defined(_M_X64) || defined(__x86_64__) - // Check relay function. - if (ip == (DWORD_PTR)pHook->pDetour) - return (DWORD_PTR)pHook->pTarget; -#endif - - return 0; -} - -//------------------------------------------------------------------------- -static DWORD_PTR FindNewIP(PHOOK_ENTRY pHook, DWORD_PTR ip) -{ - UINT i; - for (i = 0; i < pHook->nIP; ++i) - { - if (ip == ((DWORD_PTR)pHook->pTarget + pHook->oldIPs[i])) - return (DWORD_PTR)pHook->pTrampoline + pHook->newIPs[i]; - } - - return 0; -} - -//------------------------------------------------------------------------- -static void ProcessThreadIPs(HANDLE hThread, UINT pos, UINT action) -{ - // If the thread suspended in the overwritten area, - // move IP to the proper address. - - CONTEXT c; -#if defined(_M_X64) || defined(__x86_64__) - DWORD64 *pIP = &c.Rip; -#else - DWORD *pIP = &c.Eip; -#endif - UINT count; - - c.ContextFlags = CONTEXT_CONTROL; - if (!GetThreadContext(hThread, &c)) - return; - - if (pos == ALL_HOOKS_POS) - { - pos = 0; - count = g_hooks.size; - } - else - { - count = pos + 1; - } - - for (; pos < count; ++pos) - { - PHOOK_ENTRY pHook = &g_hooks.pItems[pos]; - BOOL enable; - DWORD_PTR ip; - - switch (action) - { - case ACTION_DISABLE: - enable = FALSE; - break; - - case ACTION_ENABLE: - enable = TRUE; - break; - - default: // ACTION_APPLY_QUEUED - enable = pHook->queueEnable; - break; - } - if (pHook->isEnabled == enable) - continue; - - if (enable) - ip = FindNewIP(pHook, *pIP); - else - ip = FindOldIP(pHook, *pIP); - - if (ip != 0) - { - *pIP = ip; - SetThreadContext(hThread, &c); - } - } -} - -//------------------------------------------------------------------------- -static VOID EnumerateThreads(PFROZEN_THREADS pThreads) -{ - HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); - if (hSnapshot != INVALID_HANDLE_VALUE) - { - THREADENTRY32 te; - te.dwSize = sizeof(THREADENTRY32); - if (Thread32First(hSnapshot, &te)) - { - do - { - if (te.dwSize >= (FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID) + sizeof(DWORD)) - && te.th32OwnerProcessID == GetCurrentProcessId() - && te.th32ThreadID != GetCurrentThreadId()) - { - if (pThreads->pItems == NULL) - { - pThreads->capacity = INITIAL_THREAD_CAPACITY; - pThreads->pItems - = (LPDWORD)HeapAlloc(g_hHeap, 0, pThreads->capacity * sizeof(DWORD)); - if (pThreads->pItems == NULL) - break; - } - else if (pThreads->size >= pThreads->capacity) - { - LPDWORD p = (LPDWORD)HeapReAlloc( - g_hHeap, 0, pThreads->pItems, (pThreads->capacity * 2) * sizeof(DWORD)); - if (p == NULL) - break; - - pThreads->capacity *= 2; - pThreads->pItems = p; - } - pThreads->pItems[pThreads->size++] = te.th32ThreadID; - } - - te.dwSize = sizeof(THREADENTRY32); - } while (Thread32Next(hSnapshot, &te)); - } - CloseHandle(hSnapshot); - } -} - -//------------------------------------------------------------------------- -static VOID Freeze(PFROZEN_THREADS pThreads, UINT pos, UINT action) -{ - pThreads->pItems = NULL; - pThreads->capacity = 0; - pThreads->size = 0; - EnumerateThreads(pThreads); - - if (pThreads->pItems != NULL) - { - UINT i; - for (i = 0; i < pThreads->size; ++i) - { - HANDLE hThread = OpenThread(THREAD_ACCESS, FALSE, pThreads->pItems[i]); - if (hThread != NULL) - { - SuspendThread(hThread); - ProcessThreadIPs(hThread, pos, action); - CloseHandle(hThread); - } - } - } -} - -//------------------------------------------------------------------------- -static VOID Unfreeze(PFROZEN_THREADS pThreads) -{ - if (pThreads->pItems != NULL) - { - UINT i; - for (i = 0; i < pThreads->size; ++i) - { - HANDLE hThread = OpenThread(THREAD_ACCESS, FALSE, pThreads->pItems[i]); - if (hThread != NULL) - { - ResumeThread(hThread); - CloseHandle(hThread); - } - } - - HeapFree(g_hHeap, 0, pThreads->pItems); - } -} - -//------------------------------------------------------------------------- -static MH_STATUS EnableHookLL(UINT pos, BOOL enable) -{ - PHOOK_ENTRY pHook = &g_hooks.pItems[pos]; - DWORD oldProtect; - SIZE_T patchSize = sizeof(JMP_REL); - LPBYTE pPatchTarget = (LPBYTE)pHook->pTarget; - - if (pHook->patchAbove) - { - pPatchTarget -= sizeof(JMP_REL); - patchSize += sizeof(JMP_REL_SHORT); - } - - if (!VirtualProtect(pPatchTarget, patchSize, PAGE_EXECUTE_READWRITE, &oldProtect)) - return MH_ERROR_MEMORY_PROTECT; - - if (enable) - { - PJMP_REL pJmp = (PJMP_REL)pPatchTarget; - pJmp->opcode = 0xE9; - pJmp->operand = (UINT32)((LPBYTE)pHook->pDetour - (pPatchTarget + sizeof(JMP_REL))); - - if (pHook->patchAbove) - { - PJMP_REL_SHORT pShortJmp = (PJMP_REL_SHORT)pHook->pTarget; - pShortJmp->opcode = 0xEB; - pShortJmp->operand = (UINT8)(0 - (sizeof(JMP_REL_SHORT) + sizeof(JMP_REL))); - } - } - else - { - if (pHook->patchAbove) - memcpy(pPatchTarget, pHook->backup, sizeof(JMP_REL) + sizeof(JMP_REL_SHORT)); - else - memcpy(pPatchTarget, pHook->backup, sizeof(JMP_REL)); - } - - VirtualProtect(pPatchTarget, patchSize, oldProtect, &oldProtect); - - // Just-in-case measure. - FlushInstructionCache(GetCurrentProcess(), pPatchTarget, patchSize); - - pHook->isEnabled = enable; - pHook->queueEnable = enable; - - return MH_OK; -} - -//------------------------------------------------------------------------- -static MH_STATUS EnableAllHooksLL(BOOL enable) -{ - MH_STATUS status = MH_OK; - UINT i, first = INVALID_HOOK_POS; - - for (i = 0; i < g_hooks.size; ++i) - { - if (g_hooks.pItems[i].isEnabled != enable) - { - first = i; - break; - } - } - - if (first != INVALID_HOOK_POS) - { - FROZEN_THREADS threads; - Freeze(&threads, ALL_HOOKS_POS, enable ? ACTION_ENABLE : ACTION_DISABLE); - - for (i = first; i < g_hooks.size; ++i) - { - if (g_hooks.pItems[i].isEnabled != enable) - { - status = EnableHookLL(i, enable); - if (status != MH_OK) - break; - } - } - - Unfreeze(&threads); - } - - return status; -} - -//------------------------------------------------------------------------- -static VOID EnterSpinLock(VOID) -{ - SIZE_T spinCount = 0; - - // Wait until the flag is FALSE. - while (InterlockedCompareExchange(&g_isLocked, TRUE, FALSE) != FALSE) - { - // No need to generate a memory barrier here, since InterlockedCompareExchange() - // generates a full memory barrier itself. - - // Prevent the loop from being too busy. - if (spinCount < 32) - Sleep(0); - else - Sleep(1); - - spinCount++; - } -} - -//------------------------------------------------------------------------- -static VOID LeaveSpinLock(VOID) -{ - // No need to generate a memory barrier here, since InterlockedExchange() - // generates a full memory barrier itself. - - InterlockedExchange(&g_isLocked, FALSE); -} - -//------------------------------------------------------------------------- -MH_STATUS WINAPI MH_Initialize(VOID) -{ - MH_STATUS status = MH_OK; - - EnterSpinLock(); - - if (g_hHeap == NULL) - { - g_hHeap = HeapCreate(0, 0, 0); - if (g_hHeap != NULL) - { - // Initialize the internal function buffer. - InitializeBuffer(); - } - else - { - status = MH_ERROR_MEMORY_ALLOC; - } - } - else - { - status = MH_ERROR_ALREADY_INITIALIZED; - } - - LeaveSpinLock(); - - return status; -} - -//------------------------------------------------------------------------- -MH_STATUS WINAPI MH_Uninitialize(VOID) -{ - MH_STATUS status = MH_OK; - - EnterSpinLock(); - - if (g_hHeap != NULL) - { - status = EnableAllHooksLL(FALSE); - if (status == MH_OK) - { - // Free the internal function buffer. - - // HeapFree is actually not required, but some tools detect a false - // memory leak without HeapFree. - - UninitializeBuffer(); - - HeapFree(g_hHeap, 0, g_hooks.pItems); - HeapDestroy(g_hHeap); - - g_hHeap = NULL; - - g_hooks.pItems = NULL; - g_hooks.capacity = 0; - g_hooks.size = 0; - } - } - else - { - status = MH_ERROR_NOT_INITIALIZED; - } - - LeaveSpinLock(); - - return status; -} - -//------------------------------------------------------------------------- -MH_STATUS WINAPI MH_CreateHook(LPVOID pTarget, LPVOID pDetour, LPVOID *ppOriginal) -{ - MH_STATUS status = MH_OK; - - EnterSpinLock(); - - if (g_hHeap != NULL) - { - if (IsExecutableAddress(pTarget) && IsExecutableAddress(pDetour)) - { - UINT pos = FindHookEntry(pTarget); - if (pos == INVALID_HOOK_POS) - { - LPVOID pBuffer = AllocateBuffer(pTarget); - if (pBuffer != NULL) - { - TRAMPOLINE ct; - - ct.pTarget = pTarget; - ct.pDetour = pDetour; - ct.pTrampoline = pBuffer; - if (CreateTrampolineFunction(&ct)) - { - PHOOK_ENTRY pHook = AddHookEntry(); - if (pHook != NULL) - { - pHook->pTarget = ct.pTarget; -#if defined(_M_X64) || defined(__x86_64__) - pHook->pDetour = ct.pRelay; -#else - pHook->pDetour = ct.pDetour; -#endif - pHook->pTrampoline = ct.pTrampoline; - pHook->patchAbove = ct.patchAbove; - pHook->isEnabled = FALSE; - pHook->queueEnable = FALSE; - pHook->nIP = ct.nIP; - memcpy(pHook->oldIPs, ct.oldIPs, ARRAYSIZE(ct.oldIPs)); - memcpy(pHook->newIPs, ct.newIPs, ARRAYSIZE(ct.newIPs)); - - // Back up the target function. - - if (ct.patchAbove) - { - memcpy( - pHook->backup, - (LPBYTE)pTarget - sizeof(JMP_REL), - sizeof(JMP_REL) + sizeof(JMP_REL_SHORT)); - } - else - { - memcpy(pHook->backup, pTarget, sizeof(JMP_REL)); - } - - if (ppOriginal != NULL) - *ppOriginal = pHook->pTrampoline; - } - else - { - status = MH_ERROR_MEMORY_ALLOC; - } - } - else - { - status = MH_ERROR_UNSUPPORTED_FUNCTION; - } - - if (status != MH_OK) - { - FreeBuffer(pBuffer); - } - } - else - { - status = MH_ERROR_MEMORY_ALLOC; - } - } - else - { - status = MH_ERROR_ALREADY_CREATED; - } - } - else - { - status = MH_ERROR_NOT_EXECUTABLE; - } - } - else - { - status = MH_ERROR_NOT_INITIALIZED; - } - - LeaveSpinLock(); - - return status; -} - -//------------------------------------------------------------------------- -MH_STATUS WINAPI MH_RemoveHook(LPVOID pTarget) -{ - MH_STATUS status = MH_OK; - - EnterSpinLock(); - - if (g_hHeap != NULL) - { - UINT pos = FindHookEntry(pTarget); - if (pos != INVALID_HOOK_POS) - { - if (g_hooks.pItems[pos].isEnabled) - { - FROZEN_THREADS threads; - Freeze(&threads, pos, ACTION_DISABLE); - - status = EnableHookLL(pos, FALSE); - - Unfreeze(&threads); - } - - if (status == MH_OK) - { - FreeBuffer(g_hooks.pItems[pos].pTrampoline); - DeleteHookEntry(pos); - } - } - else - { - status = MH_ERROR_NOT_CREATED; - } - } - else - { - status = MH_ERROR_NOT_INITIALIZED; - } - - LeaveSpinLock(); - - return status; -} - -//------------------------------------------------------------------------- -static MH_STATUS EnableHook(LPVOID pTarget, BOOL enable) -{ - MH_STATUS status = MH_OK; - - EnterSpinLock(); - - if (g_hHeap != NULL) - { - if (pTarget == MH_ALL_HOOKS) - { - status = EnableAllHooksLL(enable); - } - else - { - FROZEN_THREADS threads; - UINT pos = FindHookEntry(pTarget); - if (pos != INVALID_HOOK_POS) - { - if (g_hooks.pItems[pos].isEnabled != enable) - { - Freeze(&threads, pos, ACTION_ENABLE); - - status = EnableHookLL(pos, enable); - - Unfreeze(&threads); - } - else - { - status = enable ? MH_ERROR_ENABLED : MH_ERROR_DISABLED; - } - } - else - { - status = MH_ERROR_NOT_CREATED; - } - } - } - else - { - status = MH_ERROR_NOT_INITIALIZED; - } - - LeaveSpinLock(); - - return status; -} - -//------------------------------------------------------------------------- -MH_STATUS WINAPI MH_EnableHook(LPVOID pTarget) -{ - return EnableHook(pTarget, TRUE); -} - -//------------------------------------------------------------------------- -MH_STATUS WINAPI MH_DisableHook(LPVOID pTarget) -{ - return EnableHook(pTarget, FALSE); -} - -//------------------------------------------------------------------------- -static MH_STATUS QueueHook(LPVOID pTarget, BOOL queueEnable) -{ - MH_STATUS status = MH_OK; - - EnterSpinLock(); - - if (g_hHeap != NULL) - { - if (pTarget == MH_ALL_HOOKS) - { - UINT i; - for (i = 0; i < g_hooks.size; ++i) - g_hooks.pItems[i].queueEnable = queueEnable; - } - else - { - UINT pos = FindHookEntry(pTarget); - if (pos != INVALID_HOOK_POS) - { - g_hooks.pItems[pos].queueEnable = queueEnable; - } - else - { - status = MH_ERROR_NOT_CREATED; - } - } - } - else - { - status = MH_ERROR_NOT_INITIALIZED; - } - - LeaveSpinLock(); - - return status; -} - -//------------------------------------------------------------------------- -MH_STATUS WINAPI MH_QueueEnableHook(LPVOID pTarget) -{ - return QueueHook(pTarget, TRUE); -} - -//------------------------------------------------------------------------- -MH_STATUS WINAPI MH_QueueDisableHook(LPVOID pTarget) -{ - return QueueHook(pTarget, FALSE); -} - -//------------------------------------------------------------------------- -MH_STATUS WINAPI MH_ApplyQueued(VOID) -{ - MH_STATUS status = MH_OK; - UINT i, first = INVALID_HOOK_POS; - - EnterSpinLock(); - - if (g_hHeap != NULL) - { - for (i = 0; i < g_hooks.size; ++i) - { - if (g_hooks.pItems[i].isEnabled != g_hooks.pItems[i].queueEnable) - { - first = i; - break; - } - } - - if (first != INVALID_HOOK_POS) - { - FROZEN_THREADS threads; - Freeze(&threads, ALL_HOOKS_POS, ACTION_APPLY_QUEUED); - - for (i = first; i < g_hooks.size; ++i) - { - PHOOK_ENTRY pHook = &g_hooks.pItems[i]; - if (pHook->isEnabled != pHook->queueEnable) - { - status = EnableHookLL(i, pHook->queueEnable); - if (status != MH_OK) - break; - } - } - - Unfreeze(&threads); - } - } - else - { - status = MH_ERROR_NOT_INITIALIZED; - } - - LeaveSpinLock(); - - return status; -} - -//------------------------------------------------------------------------- -MH_STATUS WINAPI MH_CreateHookApiEx( - LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, - LPVOID *ppOriginal, LPVOID *ppTarget) -{ - HMODULE hModule; - LPVOID pTarget; - - hModule = GetModuleHandleW(pszModule); - if (hModule == NULL) - return MH_ERROR_MODULE_NOT_FOUND; - - pTarget = (LPVOID)GetProcAddress(hModule, pszProcName); - if (pTarget == NULL) - return MH_ERROR_FUNCTION_NOT_FOUND; - - if(ppTarget != NULL) - *ppTarget = pTarget; - - return MH_CreateHook(pTarget, pDetour, ppOriginal); -} - -//------------------------------------------------------------------------- -MH_STATUS WINAPI MH_CreateHookApi( - LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, LPVOID *ppOriginal) -{ - return MH_CreateHookApiEx(pszModule, pszProcName, pDetour, ppOriginal, NULL); -} - -//------------------------------------------------------------------------- -const char * WINAPI MH_StatusToString(MH_STATUS status) -{ -#define MH_ST2STR(x) \ - case x: \ - return #x; - - switch (status) { - MH_ST2STR(MH_UNKNOWN) - MH_ST2STR(MH_OK) - MH_ST2STR(MH_ERROR_ALREADY_INITIALIZED) - MH_ST2STR(MH_ERROR_NOT_INITIALIZED) - MH_ST2STR(MH_ERROR_ALREADY_CREATED) - MH_ST2STR(MH_ERROR_NOT_CREATED) - MH_ST2STR(MH_ERROR_ENABLED) - MH_ST2STR(MH_ERROR_DISABLED) - MH_ST2STR(MH_ERROR_NOT_EXECUTABLE) - MH_ST2STR(MH_ERROR_UNSUPPORTED_FUNCTION) - MH_ST2STR(MH_ERROR_MEMORY_ALLOC) - MH_ST2STR(MH_ERROR_MEMORY_PROTECT) - MH_ST2STR(MH_ERROR_MODULE_NOT_FOUND) - MH_ST2STR(MH_ERROR_FUNCTION_NOT_FOUND) - } - -#undef MH_ST2STR - - return "(unknown)"; -} diff --git a/vnrhook/libminhook/trampoline.c b/vnrhook/libminhook/trampoline.c deleted file mode 100644 index ac37f0f..0000000 --- a/vnrhook/libminhook/trampoline.c +++ /dev/null @@ -1,316 +0,0 @@ -/* - * MinHook - The Minimalistic API Hooking Library for x64/x86 - * Copyright (C) 2009-2017 Tsuda Kageyu. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER - * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include - -#ifndef ARRAYSIZE - #define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) -#endif - -#if defined(_M_X64) || defined(__x86_64__) - #include "./hde/hde64.h" - typedef hde64s HDE; - #define HDE_DISASM(code, hs) hde64_disasm(code, hs) -#else - #include "./hde/hde32.h" - typedef hde32s HDE; - #define HDE_DISASM(code, hs) hde32_disasm(code, hs) -#endif - -#include "trampoline.h" -#include "buffer.h" - -// Maximum size of a trampoline function. -#if defined(_M_X64) || defined(__x86_64__) - #define TRAMPOLINE_MAX_SIZE (MEMORY_SLOT_SIZE - sizeof(JMP_ABS)) -#else - #define TRAMPOLINE_MAX_SIZE MEMORY_SLOT_SIZE -#endif - -//------------------------------------------------------------------------- -static BOOL IsCodePadding(LPBYTE pInst, UINT size) -{ - UINT i; - - if (pInst[0] != 0x00 && pInst[0] != 0x90 && pInst[0] != 0xCC) - return FALSE; - - for (i = 1; i < size; ++i) - { - if (pInst[i] != pInst[0]) - return FALSE; - } - return TRUE; -} - -//------------------------------------------------------------------------- -BOOL CreateTrampolineFunction(PTRAMPOLINE ct) -{ -#if defined(_M_X64) || defined(__x86_64__) - CALL_ABS call = { - 0xFF, 0x15, 0x00000002, // FF15 00000002: CALL [RIP+8] - 0xEB, 0x08, // EB 08: JMP +10 - 0x0000000000000000ULL // Absolute destination address - }; - JMP_ABS jmp = { - 0xFF, 0x25, 0x00000000, // FF25 00000000: JMP [RIP+6] - 0x0000000000000000ULL // Absolute destination address - }; - JCC_ABS jcc = { - 0x70, 0x0E, // 7* 0E: J** +16 - 0xFF, 0x25, 0x00000000, // FF25 00000000: JMP [RIP+6] - 0x0000000000000000ULL // Absolute destination address - }; -#else - CALL_REL call = { - 0xE8, // E8 xxxxxxxx: CALL +5+xxxxxxxx - 0x00000000 // Relative destination address - }; - JMP_REL jmp = { - 0xE9, // E9 xxxxxxxx: JMP +5+xxxxxxxx - 0x00000000 // Relative destination address - }; - JCC_REL jcc = { - 0x0F, 0x80, // 0F8* xxxxxxxx: J** +6+xxxxxxxx - 0x00000000 // Relative destination address - }; -#endif - - UINT8 oldPos = 0; - UINT8 newPos = 0; - ULONG_PTR jmpDest = 0; // Destination address of an internal jump. - BOOL finished = FALSE; // Is the function completed? -#if defined(_M_X64) || defined(__x86_64__) - UINT8 instBuf[16]; -#endif - - ct->patchAbove = FALSE; - ct->nIP = 0; - - do - { - HDE hs; - UINT copySize; - LPVOID pCopySrc; - ULONG_PTR pOldInst = (ULONG_PTR)ct->pTarget + oldPos; - ULONG_PTR pNewInst = (ULONG_PTR)ct->pTrampoline + newPos; - - copySize = HDE_DISASM((LPVOID)pOldInst, &hs); - if (hs.flags & F_ERROR) - return FALSE; - - pCopySrc = (LPVOID)pOldInst; - if (oldPos >= sizeof(JMP_REL)) - { - // The trampoline function is long enough. - // Complete the function with the jump to the target function. -#if defined(_M_X64) || defined(__x86_64__) - jmp.address = pOldInst; -#else - jmp.operand = (UINT32)(pOldInst - (pNewInst + sizeof(jmp))); -#endif - pCopySrc = &jmp; - copySize = sizeof(jmp); - - finished = TRUE; - } -#if defined(_M_X64) || defined(__x86_64__) - else if ((hs.modrm & 0xC7) == 0x05) - { - // Instructions using RIP relative addressing. (ModR/M = 00???101B) - - // Modify the RIP relative address. - PUINT32 pRelAddr; - - // Avoid using memcpy to reduce the footprint. -#ifndef _MSC_VER - memcpy(instBuf, (LPBYTE)pOldInst, copySize); -#else - __movsb(instBuf, (LPBYTE)pOldInst, copySize); -#endif - pCopySrc = instBuf; - - // Relative address is stored at (instruction length - immediate value length - 4). - pRelAddr = (PUINT32)(instBuf + hs.len - ((hs.flags & 0x3C) >> 2) - 4); - *pRelAddr - = (UINT32)((pOldInst + hs.len + (INT32)hs.disp.disp32) - (pNewInst + hs.len)); - - // Complete the function if JMP (FF /4). - if (hs.opcode == 0xFF && hs.modrm_reg == 4) - finished = TRUE; - } -#endif - else if (hs.opcode == 0xE8) - { - // Direct relative CALL - ULONG_PTR dest = pOldInst + hs.len + (INT32)hs.imm.imm32; -#if defined(_M_X64) || defined(__x86_64__) - call.address = dest; -#else - call.operand = (UINT32)(dest - (pNewInst + sizeof(call))); -#endif - pCopySrc = &call; - copySize = sizeof(call); - } - else if ((hs.opcode & 0xFD) == 0xE9) - { - // Direct relative JMP (EB or E9) - ULONG_PTR dest = pOldInst + hs.len; - - if (hs.opcode == 0xEB) // isShort jmp - dest += (INT8)hs.imm.imm8; - else - dest += (INT32)hs.imm.imm32; - - // Simply copy an internal jump. - if ((ULONG_PTR)ct->pTarget <= dest - && dest < ((ULONG_PTR)ct->pTarget + sizeof(JMP_REL))) - { - if (jmpDest < dest) - jmpDest = dest; - } - else - { -#if defined(_M_X64) || defined(__x86_64__) - jmp.address = dest; -#else - jmp.operand = (UINT32)(dest - (pNewInst + sizeof(jmp))); -#endif - pCopySrc = &jmp; - copySize = sizeof(jmp); - - // Exit the function If it is not in the branch - finished = (pOldInst >= jmpDest); - } - } - else if ((hs.opcode & 0xF0) == 0x70 - || (hs.opcode & 0xFC) == 0xE0 - || (hs.opcode2 & 0xF0) == 0x80) - { - // Direct relative Jcc - ULONG_PTR dest = pOldInst + hs.len; - - if ((hs.opcode & 0xF0) == 0x70 // Jcc - || (hs.opcode & 0xFC) == 0xE0) // LOOPNZ/LOOPZ/LOOP/JECXZ - dest += (INT8)hs.imm.imm8; - else - dest += (INT32)hs.imm.imm32; - - // Simply copy an internal jump. - if ((ULONG_PTR)ct->pTarget <= dest - && dest < ((ULONG_PTR)ct->pTarget + sizeof(JMP_REL))) - { - if (jmpDest < dest) - jmpDest = dest; - } - else if ((hs.opcode & 0xFC) == 0xE0) - { - // LOOPNZ/LOOPZ/LOOP/JCXZ/JECXZ to the outside are not supported. - return FALSE; - } - else - { - UINT8 cond = ((hs.opcode != 0x0F ? hs.opcode : hs.opcode2) & 0x0F); -#if defined(_M_X64) || defined(__x86_64__) - // Invert the condition in x64 mode to simplify the conditional jump logic. - jcc.opcode = 0x71 ^ cond; - jcc.address = dest; -#else - jcc.opcode1 = 0x80 | cond; - jcc.operand = (UINT32)(dest - (pNewInst + sizeof(jcc))); -#endif - pCopySrc = &jcc; - copySize = sizeof(jcc); - } - } - else if ((hs.opcode & 0xFE) == 0xC2) - { - // RET (C2 or C3) - - // Complete the function if not in a branch. - finished = (pOldInst >= jmpDest); - } - - // Can't alter the instruction length in a branch. - if (pOldInst < jmpDest && copySize != hs.len) - return FALSE; - - // Trampoline function is too large. - if ((newPos + copySize) > TRAMPOLINE_MAX_SIZE) - return FALSE; - - // Trampoline function has too many instructions. - if (ct->nIP >= ARRAYSIZE(ct->oldIPs)) - return FALSE; - - ct->oldIPs[ct->nIP] = oldPos; - ct->newIPs[ct->nIP] = newPos; - ct->nIP++; - - // Avoid using memcpy to reduce the footprint. -#ifndef _MSC_VER - memcpy((LPBYTE)ct->pTrampoline + newPos, pCopySrc, copySize); -#else - __movsb((LPBYTE)ct->pTrampoline + newPos, pCopySrc, copySize); -#endif - newPos += copySize; - oldPos += hs.len; - } - while (!finished); - - // Is there enough place for a long jump? - if (oldPos < sizeof(JMP_REL) - && !IsCodePadding((LPBYTE)ct->pTarget + oldPos, sizeof(JMP_REL) - oldPos)) - { - // Is there enough place for a short jump? - if (oldPos < sizeof(JMP_REL_SHORT) - && !IsCodePadding((LPBYTE)ct->pTarget + oldPos, sizeof(JMP_REL_SHORT) - oldPos)) - { - return FALSE; - } - - // Can we place the long jump above the function? - if (!IsExecutableAddress((LPBYTE)ct->pTarget - sizeof(JMP_REL))) - return FALSE; - - if (!IsCodePadding((LPBYTE)ct->pTarget - sizeof(JMP_REL), sizeof(JMP_REL))) - return FALSE; - - ct->patchAbove = TRUE; - } - -#if defined(_M_X64) || defined(__x86_64__) - // Create a relay function. - jmp.address = (ULONG_PTR)ct->pDetour; - - ct->pRelay = (LPBYTE)ct->pTrampoline + newPos; - memcpy(ct->pRelay, &jmp, sizeof(jmp)); -#endif - - return TRUE; -} diff --git a/vnrhook/libminhook/trampoline.h b/vnrhook/libminhook/trampoline.h deleted file mode 100644 index bdffdac..0000000 --- a/vnrhook/libminhook/trampoline.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * MinHook - The Minimalistic API Hooking Library for x64/x86 - * Copyright (C) 2009-2017 Tsuda Kageyu. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER - * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#pragma pack(push, 1) - -// Structs for writing x86/x64 instructions. - -// 8-bit relative jump. -typedef struct _JMP_REL_SHORT -{ - UINT8 opcode; // EB xx: JMP +2+xx - UINT8 operand; -} JMP_REL_SHORT, *PJMP_REL_SHORT; - -// 32-bit direct relative jump/call. -typedef struct _JMP_REL -{ - UINT8 opcode; // E9/E8 xxxxxxxx: JMP/CALL +5+xxxxxxxx - UINT32 operand; // Relative destination address -} JMP_REL, *PJMP_REL, CALL_REL; - -// 64-bit indirect absolute jump. -typedef struct _JMP_ABS -{ - UINT8 opcode0; // FF25 00000000: JMP [+6] - UINT8 opcode1; - UINT32 dummy; - UINT64 address; // Absolute destination address -} JMP_ABS, *PJMP_ABS; - -// 64-bit indirect absolute call. -typedef struct _CALL_ABS -{ - UINT8 opcode0; // FF15 00000002: CALL [+6] - UINT8 opcode1; - UINT32 dummy0; - UINT8 dummy1; // EB 08: JMP +10 - UINT8 dummy2; - UINT64 address; // Absolute destination address -} CALL_ABS; - -// 32-bit direct relative conditional jumps. -typedef struct _JCC_REL -{ - UINT8 opcode0; // 0F8* xxxxxxxx: J** +6+xxxxxxxx - UINT8 opcode1; - UINT32 operand; // Relative destination address -} JCC_REL; - -// 64bit indirect absolute conditional jumps that x64 lacks. -typedef struct _JCC_ABS -{ - UINT8 opcode; // 7* 0E: J** +16 - UINT8 dummy0; - UINT8 dummy1; // FF25 00000000: JMP [+6] - UINT8 dummy2; - UINT32 dummy3; - UINT64 address; // Absolute destination address -} JCC_ABS; - -#pragma pack(pop) - -typedef struct _TRAMPOLINE -{ - LPVOID pTarget; // [In] Address of the target function. - LPVOID pDetour; // [In] Address of the detour function. - LPVOID pTrampoline; // [In] Buffer address for the trampoline and relay function. - -#if defined(_M_X64) || defined(__x86_64__) - LPVOID pRelay; // [Out] Address of the relay function. -#endif - BOOL patchAbove; // [Out] Should use the hot patch area? - UINT nIP; // [Out] Number of the instruction boundaries. - UINT8 oldIPs[8]; // [Out] Instruction boundaries of the target function. - UINT8 newIPs[8]; // [Out] Instruction boundaries of the trampoline function. -} TRAMPOLINE, *PTRAMPOLINE; - -BOOL CreateTrampolineFunction(PTRAMPOLINE ct); diff --git a/vnrhook/main.cc b/vnrhook/main.cc index 0a237eb..4d4a530 100644 --- a/vnrhook/main.cc +++ b/vnrhook/main.cc @@ -6,7 +6,6 @@ #include "main.h" #include "defs.h" #include "text.h" -#include "MinHook.h" #include "engine/match.h" #include "texthook.h" #include "util.h" diff --git a/vnrhook/main.h b/vnrhook/main.h index 99253e3..be7ec8d 100644 --- a/vnrhook/main.h +++ b/vnrhook/main.h @@ -13,6 +13,44 @@ void NotifyHookRemove(uint64_t addr); void NewHook(HookParam hp, LPCSTR name, DWORD flag = HOOK_ENGINE); void RemoveHook(uint64_t addr, int maxOffset = 9); +extern "C" // minhook library +{ + enum MH_STATUS + { + MH_OK, + MH_ERROR_ALREADY_INITIALIZED, + MH_ERROR_NOT_INITIALIZED, + MH_ERROR_ALREADY_CREATED, + MH_ERROR_NOT_CREATED, + MH_ERROR_ENABLED, + MH_ERROR_DISABLED, + MH_ERROR_NOT_EXECUTABLE, + MH_ERROR_UNSUPPORTED_FUNCTION, + MH_ERROR_MEMORY_ALLOC, + MH_ERROR_MEMORY_PROTECT, + MH_ERROR_MODULE_NOT_FOUND, + MH_ERROR_FUNCTION_NOT_FOUND + }; + + MH_STATUS WINAPI MH_Initialize(VOID); + MH_STATUS WINAPI MH_Uninitialize(VOID); + + // Creates a Hook for the specified target function, in disabled state. + // Parameters: + // pTarget [in] A pointer to the target function, which will be + // overridden by the detour function. + // pDetour [in] A pointer to the detour function, which will override + // the target function. + // ppOriginal [out] A pointer to the trampoline function, which will be + // used to call the original target function. + // This parameter can be NULL. + MH_STATUS WINAPI MH_CreateHook(LPVOID pTarget, LPVOID pDetour, LPVOID *ppOriginal); + MH_STATUS WINAPI MH_EnableHook(LPVOID pTarget); + MH_STATUS WINAPI MH_DisableHook(LPVOID pTarget); + MH_STATUS WINAPI MH_RemoveHook(LPVOID pTarget); + const char* WINAPI MH_StatusToString(MH_STATUS status); +} + #define ITH_RAISE (*(int*)0 = 0) // raise C000005, for debugging only #define ITH_TRY __try #define ITH_EXCEPT __except(EXCEPTION_EXECUTE_HANDLER) diff --git a/vnrhook/texthook.cc b/vnrhook/texthook.cc index 12423b4..9ea6f95 100644 --- a/vnrhook/texthook.cc +++ b/vnrhook/texthook.cc @@ -4,7 +4,6 @@ // 8/24/2013 TODO: Clean up this file #include "texthook.h" -#include "MinHook.h" #include "engine/match.h" #include "main.h" #include "const.h" diff --git a/x64libs/minhook.lib b/x64libs/minhook.lib new file mode 100644 index 0000000000000000000000000000000000000000..1ae7f03af4cc8becaf89946fe8c9dfb2483b4ba5 GIT binary patch literal 126998 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 literal 0 HcmV?d00001 diff --git a/x86libs/minhook.lib b/x86libs/minhook.lib new file mode 100644 index 0000000000000000000000000000000000000000..5add6f94d6f7b002d1c80492056b82f424c43b08 GIT binary patch literal 118908 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 nIO;0}W%|f