From 4b3edeef07a1cf2096814e450c20de1cedef7cb3 Mon Sep 17 00:00:00 2001 From: Akash Mozumdar Date: Fri, 24 Aug 2018 11:33:01 -0400 Subject: [PATCH 1/4] add minhook --- CMakeLists.txt | 1 + minhook/CMakeLists.txt | 17 + minhook/MinHook.h | 186 +++++++++ minhook/buffer.c | 312 +++++++++++++++ minhook/buffer.h | 42 ++ minhook/hde/hde32.c | 326 +++++++++++++++ minhook/hde/hde32.h | 105 +++++ minhook/hde/hde64.c | 337 ++++++++++++++++ minhook/hde/hde64.h | 112 ++++++ minhook/hde/pstdint.h | 39 ++ minhook/hde/table32.h | 73 ++++ minhook/hde/table64.h | 74 ++++ minhook/hook.c | 889 +++++++++++++++++++++++++++++++++++++++++ minhook/trampoline.c | 316 +++++++++++++++ minhook/trampoline.h | 105 +++++ vnrhook/CMakeLists.txt | 1 + 16 files changed, 2935 insertions(+) create mode 100644 minhook/CMakeLists.txt create mode 100644 minhook/MinHook.h create mode 100644 minhook/buffer.c create mode 100644 minhook/buffer.h create mode 100644 minhook/hde/hde32.c create mode 100644 minhook/hde/hde32.h create mode 100644 minhook/hde/hde64.c create mode 100644 minhook/hde/hde64.h create mode 100644 minhook/hde/pstdint.h create mode 100644 minhook/hde/table32.h create mode 100644 minhook/hde/table64.h create mode 100644 minhook/hook.c create mode 100644 minhook/trampoline.c create mode 100644 minhook/trampoline.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 2b647da..cbc9ce4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,5 +32,6 @@ set(CMAKE_CONFIGURATION_TYPES Debug Release) add_subdirectory(host) add_subdirectory(GUI) +add_subdirectory(minhook) add_subdirectory(vnrhook) add_subdirectory(extensions) diff --git a/minhook/CMakeLists.txt b/minhook/CMakeLists.txt new file mode 100644 index 0000000..07beaf8 --- /dev/null +++ b/minhook/CMakeLists.txt @@ -0,0 +1,17 @@ +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/minhook/MinHook.h b/minhook/MinHook.h new file mode 100644 index 0000000..15c0a87 --- /dev/null +++ b/minhook/MinHook.h @@ -0,0 +1,186 @@ +/* + * 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/minhook/buffer.c b/minhook/buffer.c new file mode 100644 index 0000000..8f9fbce --- /dev/null +++ b/minhook/buffer.c @@ -0,0 +1,312 @@ +/* + * 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/minhook/buffer.h b/minhook/buffer.h new file mode 100644 index 0000000..204d551 --- /dev/null +++ b/minhook/buffer.h @@ -0,0 +1,42 @@ +/* + * 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/minhook/hde/hde32.c b/minhook/hde/hde32.c new file mode 100644 index 0000000..08fa25b --- /dev/null +++ b/minhook/hde/hde32.c @@ -0,0 +1,326 @@ +/* + * 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/minhook/hde/hde32.h b/minhook/hde/hde32.h new file mode 100644 index 0000000..1112450 --- /dev/null +++ b/minhook/hde/hde32.h @@ -0,0 +1,105 @@ +/* + * 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/minhook/hde/hde64.c b/minhook/hde/hde64.c new file mode 100644 index 0000000..c23e2fc --- /dev/null +++ b/minhook/hde/hde64.c @@ -0,0 +1,337 @@ +/* + * 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/minhook/hde/hde64.h b/minhook/hde/hde64.h new file mode 100644 index 0000000..ecbf4df --- /dev/null +++ b/minhook/hde/hde64.h @@ -0,0 +1,112 @@ +/* + * 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/minhook/hde/pstdint.h b/minhook/hde/pstdint.h new file mode 100644 index 0000000..84d82a0 --- /dev/null +++ b/minhook/hde/pstdint.h @@ -0,0 +1,39 @@ +/* + * 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/minhook/hde/table32.h b/minhook/hde/table32.h new file mode 100644 index 0000000..7b3e12e --- /dev/null +++ b/minhook/hde/table32.h @@ -0,0 +1,73 @@ +/* + * 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/minhook/hde/table64.h b/minhook/hde/table64.h new file mode 100644 index 0000000..01d4541 --- /dev/null +++ b/minhook/hde/table64.h @@ -0,0 +1,74 @@ +/* + * 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/minhook/hook.c b/minhook/hook.c new file mode 100644 index 0000000..793c176 --- /dev/null +++ b/minhook/hook.c @@ -0,0 +1,889 @@ +/* + * 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/minhook/trampoline.c b/minhook/trampoline.c new file mode 100644 index 0000000..ac37f0f --- /dev/null +++ b/minhook/trampoline.c @@ -0,0 +1,316 @@ +/* + * 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/minhook/trampoline.h b/minhook/trampoline.h new file mode 100644 index 0000000..bdffdac --- /dev/null +++ b/minhook/trampoline.h @@ -0,0 +1,105 @@ +/* + * 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/CMakeLists.txt b/vnrhook/CMakeLists.txt index 796ccd9..633f091 100644 --- a/vnrhook/CMakeLists.txt +++ b/vnrhook/CMakeLists.txt @@ -48,6 +48,7 @@ target_compile_options(vnrhook PRIVATE set(vnrhook_libs ntdll.lib Version.lib + minhook ) target_link_libraries(vnrhook ${vnrhook_libs}) From 70ae03279c4f2b99ed2f1d9e0a7413eb592770ba Mon Sep 17 00:00:00 2001 From: Akash Mozumdar Date: Fri, 24 Aug 2018 15:09:37 -0400 Subject: [PATCH 2/4] move minhook --- {minhook => include}/MinHook.h | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {minhook => include}/MinHook.h (100%) diff --git a/minhook/MinHook.h b/include/MinHook.h similarity index 100% rename from minhook/MinHook.h rename to include/MinHook.h From a93a7dc4444e928aca8c9684e4126b33535a00c9 Mon Sep 17 00:00:00 2001 From: Akash Mozumdar Date: Sat, 25 Aug 2018 15:45:25 -0400 Subject: [PATCH 3/4] rewrite hooking logic --- host/host.cc | 12 +- include/types.h | 2 - vnrhook/engine/engine.cc | 65 ++-- vnrhook/engine/match.cc | 18 +- vnrhook/engine/pchooks.cc | 2 +- vnrhook/hijack/texthook.cc | 686 ++++++++++++------------------------- vnrhook/hijack/texthook.h | 42 +-- vnrhook/main.cc | 77 ++--- vnrhook/main.h | 18 +- vnrhook/pipe.cc | 20 +- vnrhook/pipe.h | 8 + 11 files changed, 326 insertions(+), 624 deletions(-) create mode 100644 vnrhook/pipe.h diff --git a/host/host.cc b/host/host.cc index 27e3536..ba28d21 100644 --- a/host/host.cc +++ b/host/host.cc @@ -7,6 +7,9 @@ #include "defs.h" #include "../vnrhook/hijack/texthook.h" + +bool operator==(const ThreadParam& one, const ThreadParam& two) { return one.pid == two.pid && one.hook == two.hook && one.retn == two.retn && one.spl == two.spl; } + namespace { struct ProcessRecord @@ -21,7 +24,6 @@ namespace ThreadEventCallback OnCreate, OnRemove; ProcessEventCallback OnAttach, OnDetach; - bool operator==(const ThreadParam& one, const ThreadParam& two) { return one.pid == two.pid && one.hook == two.hook && one.retn == two.retn && one.spl == two.spl; } std::unordered_map textThreadsByParams; std::unordered_map processRecordsByIds; @@ -228,7 +230,7 @@ namespace Host WaitForSingleObject(pr.sectionMutex, 0); const TextHook* hooks = (const TextHook*)pr.sectionMap; for (int i = 0; i < MAX_HOOK; ++i) - if (hooks[i].Address() == addr) + if (hooks[i].hp.address == addr) ret = hooks[i].hp; ReleaseMutex(pr.sectionMutex); return ret; @@ -246,10 +248,10 @@ namespace Host WaitForSingleObject(pr.sectionMutex, 0); const TextHook* hooks = (const TextHook*)pr.sectionMap; for (int i = 0; i < MAX_HOOK; ++i) - if (hooks[i].Address() == addr) + if (hooks[i].hp.address == addr) { - buffer.resize(hooks[i].NameLength()); - ReadProcessMemory(pr.processHandle, hooks[i].Name(), &buffer[0], hooks[i].NameLength(), nullptr); + buffer.resize(hooks[i].name_length); + ReadProcessMemory(pr.processHandle, hooks[i].hook_name, &buffer[0], hooks[i].name_length, nullptr); } ReleaseMutex(pr.sectionMutex); USES_CONVERSION; diff --git a/include/types.h b/include/types.h index f47f4a6..d952b99 100644 --- a/include/types.h +++ b/include/types.h @@ -25,8 +25,6 @@ struct HookParam filter_fun_t filter_fun; hook_fun_t hook_fun; - BYTE hook_len, // ? - recover_len; // ? HANDLE readerHandle; // Artikash 8/4/2018: handle for reader thread }; diff --git a/vnrhook/engine/engine.cc b/vnrhook/engine/engine.cc index 7594fed..f02e26b 100644 --- a/vnrhook/engine/engine.cc +++ b/vnrhook/engine/engine.cc @@ -546,7 +546,7 @@ bool InsertKiriKiriHook() // 9/20/2014 jichi: change return type to bool //RegisterEngineType(ENGINE_KIRIKIRI); if (k1 && k2) { ConsoleOutput("vnreng:KiriKiri1: disable GDI hooks"); - DisableGDIHooks(); + } return k1 || k2; } @@ -1324,7 +1324,7 @@ void NewKiriKiriZHook(DWORD addr) NewHook(hp, "KiriKiriZ"); ConsoleOutput("vnreng:KiriKiriZ: disable GDI hooks"); - DisableGDIHooks(); + } bool KiriKiriZHook1(DWORD esp_base, HookParam *) @@ -1998,7 +1998,7 @@ bool InsertBGI2Hook() // Disable TextOutA, which is cached and hence missing characters. ConsoleOutput("vnreng:BGI2: disable GDI hooks"); - DisableGDIHooks(); + return true; } @@ -2177,7 +2177,7 @@ static bool InsertRealliveDynamicHook(LPVOID addr, DWORD frame, DWORD stack) NewHook(hp, "RealLive"); //RegisterEngineType(ENGINE_REALLIVE); ConsoleOutput("vnreng:RealLive: disable GDI hooks"); - DisableGDIHooks(); + return true; } } @@ -2299,7 +2299,7 @@ bool InsertSiglus3Hook() NewHook(hp, "SiglusEngine3"); ConsoleOutput("vnreng:Siglus3: disable GDI hooks"); - DisableGDIHooks(); + return true; } @@ -2450,7 +2450,7 @@ bool InsertSiglus4Hook() NewHook(hp, "SiglusEngine4"); ConsoleOutput("vnreng:Siglus4: disable GDI hooks"); - DisableGDIHooks(); + return true; } @@ -2640,7 +2640,7 @@ bool InsertSiglus4Hook() NewHook(hp, "SiglusEngine4"); ConsoleOutput("vnreng:Siglus4: disable GDI hooks"); - DisableGDIHooks(); + return true; } #endif // 0 @@ -4735,7 +4735,7 @@ static bool InsertSystem43OldHook(ULONG startAddress, ULONG stopAddress, LPCSTR NewHook(hp, hookName); ConsoleOutput("vnreng:System43: disable GDI hooks"); // disable hooking to TextOutA, which is cached - DisableGDIHooks(); + return true; } @@ -5473,7 +5473,7 @@ static bool InsertSystem43NewHook(ULONG startAddress, ULONG stopAddress, LPCSTR NewHook(hp, hookName); ConsoleOutput("vnreng:System43+: disable GDI hooks"); // disable hooking to TextOutA, which is cached - DisableGDIHooks(); + return true; } @@ -6635,7 +6635,7 @@ bool InsertMalieHook2() // jichi 8/20/2013: Change return type to boolean NewHook(hp, "Malie"); //RegisterEngineType(ENGINE_MALIE); ConsoleOutput("vnreng:Malie2: disable GDI hooks"); - DisableGDIHooks(); + return true; } @@ -6964,7 +6964,7 @@ bool InsertMalie3Hook() ConsoleOutput("vnreng: INSERT Malie3"); NewHook(hp, "Malie3"); ConsoleOutput("vnreng:Malie3: disable GDI hooks"); - DisableGDIHooks(); + return true; } @@ -7042,7 +7042,7 @@ bool InsertMalieHook() if (ok) { ConsoleOutput("vnreng:Malie: disable GDI hooks"); - DisableGDIHooks(); + } return ok; } @@ -8077,7 +8077,7 @@ bool InsertApricoTHook() //RegisterEngineType(ENGINE_APRICOT); // jichi 2/14/2015: disable cached GDI functions ConsoleOutput("vnreng:ApRicoT: disable GDI hooks"); - DisableGDIHooks(); + return true; } @@ -8203,9 +8203,6 @@ bool InsertDebonosuScenarioHook() hp.type = USING_STRING|NO_CONTEXT|USING_SPLIT|FIXING_SPLIT; // there is only one thread ConsoleOutput("vnreng: INSERT Debonosu"); NewHook(hp, "Debonosu"); - //RegisterEngineType(ENGINE_DEBONOSU); - ConsoleOutput("vnreng:Debonosu: disable GDI+ hooks"); - DisableGDIPlusHooks(); return true; } } @@ -8439,7 +8436,7 @@ bool InsertSystemAoiDynamicHook(LPVOID addr, DWORD frame, DWORD stack) else NewHook(hp, "SystemAoi"); // jichi 7/8/2014: renamed, see: ja.wikipedia.org/wiki/ソフトハウスキャラ ConsoleOutput("vnreng:SystemAoi: disable GDI hooks"); - DisableGDIHooks(); + } else ConsoleOutput("vnreng: failed to detect SystemAoi"); //RegisterEngineType(ENGINE_SOFTHOUSE); @@ -8494,7 +8491,7 @@ bool InsertSystemAoiStatic(HMODULE hModule, bool wideChar) // attach scenario else NewHook(hp, "SystemAoiA"); ConsoleOutput("vnreng:SystemAoiStatic: disable GDI hooks"); - DisableGDIHooks(); + return true; } } // unnamed namespace @@ -9675,7 +9672,7 @@ static bool InsertGXP1Hook() // jichi 5/13/2015: Disable hooking to GetGlyphOutlineW // FIXME: GetGlyphOutlineW can extract name, but GXP cannot ConsoleOutput("vnreng:GXP: disable GDI hooks"); - DisableGDIHooks(); + return true; } } @@ -9711,7 +9708,7 @@ static bool InsertGXP2Hook() ConsoleOutput("vnreng: INSERT GXP2"); NewHook(hp, "GXP2"); ConsoleOutput("vnreng:GXP: disable GDI hooks"); - DisableGDIHooks(); + return true; } @@ -9928,7 +9925,7 @@ bool InsertNextonHook() NewHook(hp, "NEXTON"); //ConsoleOutput("vnreng:NEXTON: disable GDI hooks"); // There are no GDI functions hooked though - //DisableGDIHooks(); // disable GetGlyphOutlineA + // // disable GetGlyphOutlineA return true; } @@ -13415,7 +13412,7 @@ bool InsertExpHook() NewHook(hp, "EXP"); // FIXME: text displayed line by line ConsoleOutput("vnreng:EXP: disable GDI hooks"); // There are no GDI functions hooked though - DisableGDIHooks(); + return true; } @@ -13709,7 +13706,7 @@ bool Insert5pbHook1() // GDI functions are not used by 5pb games anyway. //ConsoleOutput("vnreng:5pb: disable GDI hooks"); - //DisableGDIHooks(); + // return true; } @@ -13760,7 +13757,7 @@ bool Insert5pbHook2() // GDI functions are not used by 5pb games anyway. //ConsoleOutput("vnreng:5pb: disable GDI hooks"); - //DisableGDIHooks(); + // return true; } @@ -13914,7 +13911,7 @@ bool Insert5pbHook3() NewHook(hp, "5pb3"); // GDI functions are not used by 5pb games anyway. //ConsoleOutput("vnreng:5pb: disable GDI hooks"); - //DisableGDIHooks(); + // return true; } } // unnamed namespace @@ -14062,7 +14059,7 @@ static bool InsertMinkDynamicHook(LPVOID fun, DWORD frame, DWORD stack) NewHook(hp, "Mink"); ConsoleOutput("vnreng:Mink: disable GDI hooks"); - DisableGDIHooks(); + return true; } #endif // 0 @@ -14119,7 +14116,7 @@ bool InsertMinkHook() NewHook(hp, "Mink"); //ConsoleOutput("vnreng:Mink: disable GDI hooks"); - //DisableGDIHooks(); + // return true; } @@ -14544,7 +14541,7 @@ bool InsertLeafHook() NewHook(hp, "Leaf"); //ConsoleOutput("vnreng:Leaf: disable GDI hooks"); - //DisableGDIHooks(); + // return true; } @@ -14598,7 +14595,7 @@ bool InsertNekopackHook() // Disable GDIHook(um.. ?), which is cached and hence missing characters. //ConsoleOutput("vnreng:NekoPack: disable GDI hooks"); - //DisableGDIHooks(); + // return true; } @@ -14710,7 +14707,7 @@ bool InsertLunaSoftHook() // There are no GDI functions anyway //ConsoleOutput("vnreng:LunaSoft: disable GDI hooks"); - //DisableGDIHooks(); + // return true; } @@ -14853,7 +14850,7 @@ bool InsertFocasLensHook() NewHook(hp, "FocasLens"); // GDI functions are kept in case the font is not cached - //DisableGDIHooks(); + // return true; } @@ -15026,7 +15023,7 @@ bool InsertSyuntadaHook() // TextOutA will produce repeated texts ConsoleOutput("vnreng:Syuntada: disable GDI hooks"); - DisableGDIHooks(); + return true; } @@ -15239,7 +15236,7 @@ bool InsertBootupGDIHook() NewHook(hp, widechar ? "BootupW" : "BootupA"); ConsoleOutput("vnreng:BootupGDI: disable GDI hooks"); - DisableGDIHooks(); + return true; } bool InsertBootupLstrHook() // for character name @@ -16394,7 +16391,7 @@ bool InsertAdobeFlash10Hook() NewHook(hp, "Adobe Flash 10"); ConsoleOutput("vnreng:AdobeFlash10: disable GDI hooks"); - DisableGDIHooks(); + return true; } diff --git a/vnrhook/engine/match.cc b/vnrhook/engine/match.cc index b1ce028..7840eba 100644 --- a/vnrhook/engine/match.cc +++ b/vnrhook/engine/match.cc @@ -22,14 +22,13 @@ enum { MAX_REL_ADDR = 0x200000 }; // jichi 8/18/2013: maximum relative address // - Global variables - +DWORD processStartAddress, processStopAddress; + namespace Engine { WCHAR *processName, // cached processPath[MAX_PATH]; // cached -DWORD process_base, - process_limit; - //LPVOID trigger_addr; trigger_fun_t trigger_fun_; @@ -85,7 +84,7 @@ bool DeterminePCEngine() // PC games PcHooks::hookGDIFunctions(); - EnableGDIPlusHooks(); + PcHooks::hookGDIPlusFunctions(); return false; } @@ -866,8 +865,6 @@ bool DetermineEngineType() seh_with_eh(ExceptHandler, found = UnsafeDetermineEngineType()); #endif // ITH_HAS_SEH - if (::GDIPlusHooksEnabled()) - PcHooks::hookGDIPlusFunctions(); if (!found) { // jichi 10/2/2013: Only enable it if no game engine is detected PcHooks::hookLstrFunctions(); PcHooks::hookCharNextFunctions(); @@ -891,6 +888,15 @@ void Hijack() GetModuleFileNameW(nullptr, processPath, MAX_PATH); processName = wcsrchr(processPath, L'\\') + 1; + ::processStartAddress = ::processStopAddress = (DWORD)GetModuleHandleW(nullptr); + MEMORY_BASIC_INFORMATION info; + do + { + VirtualQuery((void*)::processStopAddress, &info, sizeof(info)); + ::processStopAddress = (DWORD)info.BaseAddress + info.RegionSize; + } while (info.Protect > PAGE_NOACCESS); + processStopAddress -= info.RegionSize; + DetermineEngineType(); hijacked = true; } diff --git a/vnrhook/engine/pchooks.cc b/vnrhook/engine/pchooks.cc index 7ea8365..a03f31f 100644 --- a/vnrhook/engine/pchooks.cc +++ b/vnrhook/engine/pchooks.cc @@ -123,7 +123,7 @@ void PcHooks::hookGDIPlusFunctions() { HMODULE hModule = ::GetModuleHandleA("gdiplus.dll"); if (!hModule) { - ConsoleOutput("not loaded"); + ConsoleOutput("gdi+: not loaded"); return; } diff --git a/vnrhook/hijack/texthook.cc b/vnrhook/hijack/texthook.cc index 38f0229..243a72f 100644 --- a/vnrhook/hijack/texthook.cc +++ b/vnrhook/hijack/texthook.cc @@ -10,218 +10,92 @@ #endif // _MSC_VER #include "hijack/texthook.h" +#include "MinHook.h" #include "engine/match.h" #include "except.h" #include "main.h" +#include "pipe.h" #include "const.h" #include "ithsys/ithsys.h" #include "disasm/disasm.h" #include "growl.h" -//#include "winseh/winseh.h" +#include -//#define ConsoleOutput(...) (void)0 // jichi 9/17/2013: I don't need this >< - -// - Global variables - - -// 10/14/2014 jichi: disable GDI hooks -static bool gdi_hook_enabled_ = true; // enable GDI by default -static bool gdiplus_hook_enabled_ = false; // disable GDIPlus by default -bool GDIHooksEnabled() { return ::gdi_hook_enabled_; } -bool GDIPlusHooksEnabled() { return ::gdiplus_hook_enabled_; } -void EnableGDIHooks() { ::gdi_hook_enabled_ = true; } -void EnableGDIPlusHooks() { ::gdiplus_hook_enabled_ = true; } -void DisableGDIHooks() { ::gdi_hook_enabled_ = false; } -void DisableGDIPlusHooks() { ::gdiplus_hook_enabled_ = false; } - -//FilterRange filter[8]; - -DWORD flag, - enter_count; - -TextHook *hookman, - *current_available; +TextHook *hookman; // - Unnamed helpers - #ifndef _WIN64 namespace { // unnamed -//provide const time hook entry. -int userhook_count; -const BYTE common_hook[] = { - 0x9c, // pushfd - 0x60, // pushad - 0x9c, // pushfd - 0x8d,0x54,0x24,0x28, // lea edx,[esp+0x28] ; esp value - 0x8b,0x32, // mov esi,[edx] ; return address - 0xb9, 0,0,0,0, // mov ecx, $ ; pointer to TextHook - 0xe8, 0,0,0,0, // call @hook - 0x9d, // popfd - 0x61, // popad - 0x9d // popfd -}; + const BYTE common_hook[] = { + 0x9c, // pushfd + 0x60, // pushad + 0x9c, // pushfd + 0x8d,0x54,0x24,0x28, // lea edx,[esp+0x28] ; esp value + 0x8b,0x32, // mov esi,[edx] ; return address + 0xb9, 0,0,0,0, // mov ecx, $ ; pointer to TextHook + 0xe8, 0,0,0,0, // call @hook + 0x9d, // popfd + 0x61, // popad + 0x9d, // popfd + 0xe9 // jmp @original + }; -/** - * jichi 7/19/2014 - * - * @param original_addr - * @param new_addr - * @param hook_len - * @param original_len - * @return -1 if failed, else 0 if ?, else ? - */ -int MapInstruction(DWORD original_addr, DWORD new_addr, BYTE &hook_len, BYTE &original_len) -{ - int flag = 0; - DWORD l = 0; - const BYTE *r = (const BYTE *)original_addr; // 7/19/2014 jichi: original address is not modified - BYTE *c = (BYTE *)new_addr; // 7/19/2014 jichi: but new address might be modified - while((r - (BYTE *) original_addr) < 5) { - l = ::disasm(r); - if (l == 0) { - ConsoleOutput("vnrcli:MapInstruction: FAILED: failed to disasm"); - return -1; - } + DWORD Hash(std::wstring module) + { + DWORD hash = 0; + for (auto i : module) hash = _rotr(hash, 7) + i; + return hash; + } - ::memcpy(c, r, l); - if (*r >= 0x70 && *r < 0x80) { - c[0] = 0xf; - c[1] = *r + 0x10; - c += 6; - __asm - { - mov eax,r - add eax,2 - movsx edx,byte ptr [eax-1] - add edx,eax - mov eax,c - sub edx,eax - mov [eax-4],edx - } - } else if (*r == 0xeb) { - c[0] = 0xe9; - c += 5; - __asm - { - mov eax,r - add eax,2 - movsx edx,[eax-1] - add edx,eax - mov eax,c - sub edx,eax - mov [eax-4],edx - } - if (r - (BYTE *)original_addr < 5 - l) { - ConsoleOutput("vnrcli:MapInstruction: not safe to move instruction right after short jmp"); - return -1; // Not safe to move instruction right after short jmp. - } else - flag = 1; - } else if (*r == 0xe8 || *r == 0xe9) { - c[0]=*r; - c += 5; - flag = (*r == 0xe9); - __asm - { - mov eax,r - add eax,5 - mov edx,[eax-4] - add edx,eax - mov eax,c - sub edx,eax - mov [eax-4],edx - } - } else if (*r == 0xf && (*(r + 1) >> 4) == 0x8) { - c += 6; - __asm - { - mov eax,r - mov edx,dword ptr [eax+2] - add eax,6 - add eax,edx - mov edx,c - sub eax,edx - mov [edx-4],eax - } - } - else - c += l; - r += l; - } - original_len = r - (BYTE *)original_addr; - hook_len = c - (BYTE *)new_addr; - return flag; -} + //copy original instruction + //jmp back + DWORD GetModuleBase(DWORD hash) + { + HMODULE allModules[1000]; + DWORD size; + EnumProcessModules(GetCurrentProcess(), allModules, sizeof(allModules), &size); + wchar_t name[MAX_PATH]; + for (int i = 0; i < size / sizeof(HMODULE); ++i) + { + GetModuleFileNameW(allModules[i], name, MAX_PATH); + _wcslwr(name); + if (Hash(wcsrchr(name, L'\\') + 1) == hash) return (DWORD)allModules[i]; + } + return 0; + } -//copy original instruction -//jmp back -DWORD GetModuleBase(DWORD hash) -{ - __asm - { - mov eax,fs:[0x30] - mov eax,[eax+0xc] - mov esi,[eax+0x14] - mov edi,_wcslwr -listfind: - mov edx,[esi+0x28] - test edx,edx - jz notfound - push edx - call edi - pop edx - xor eax,eax -calc: - movzx ecx, word ptr [edx] - test cl,cl - jz fin - ror eax,7 - add eax,ecx - add edx,2 - jmp calc -fin: - cmp eax,[hash] - je found - mov esi,[esi] - jmp listfind -notfound: - xor eax,eax - jmp termin -found: - mov eax,[esi+0x10] -termin: - } -} - -__declspec(naked) // jichi 10/2/2013: No prolog and epilog -int ProcessHook(DWORD dwDataBase, DWORD dwRetn, TextHook *hook) // Use SEH to ensure normal execution even bad hook inserted. -{ - // jichi 12/17/2013: The function parameters here are meaning leass. The parameters are in esi and edi - __asm - { - push esi - push edx - call TextHook::Send - retn // jichi 12/13/2013: return near, see: http://stackoverflow.com/questions/1396909/ret-retn-retf-how-to-use-them - } -} + __declspec(naked) // jichi 10/2/2013: No prolog and epilog + int ProcessHook(DWORD dwDataBase, DWORD dwRetn, TextHook *hook) // Use SEH to ensure normal execution even bad hook inserted. + { + // jichi 12/17/2013: The function parameters here are meaning leass. The parameters are in esi and edi + __asm + { + push esi + push edx + call TextHook::Send + retn // jichi 12/13/2013: return near, see: http://stackoverflow.com/questions/1396909/ret-retn-retf-how-to-use-them + } + } } // unnamed namespace #endif // _WIN32 // - TextHook methods - -int TextHook::InsertHook() +bool TextHook::InsertHook() { - int ok = 1; - //ConsoleOutput("vnrcli:InsertHook: enter"); - WaitForSingleObject(hmMutex, 0); - if (hp.type & DIRECT_READ) ok = InsertReadCode(); + bool ret = false; + //ConsoleOutput("vnrcli:InsertHook: enter"); + WaitForSingleObject(hmMutex, 0); + if (hp.type & DIRECT_READ) ret = InsertReadCode(); #ifndef _WIN64 - else ok = InsertHookCode(); + else ret = InsertHookCode(); #endif - ReleaseMutex(hmMutex); - //ConsoleOutput("vnrcli:InsertHook: leave"); - return ok; + ReleaseMutex(hmMutex); + //ConsoleOutput("vnrcli:InsertHook: leave"); + return ret; } #ifndef _WIN64 @@ -234,211 +108,136 @@ int TextHook::InsertHook() // - dwRetn: the return address of the hook DWORD TextHook::Send(DWORD dwDataBase, DWORD dwRetn) { - DWORD ret = 0; - ITH_WITH_SEH(ret = UnsafeSend(dwDataBase, dwRetn)); - return ret; + DWORD ret = 0; + ITH_WITH_SEH(ret = UnsafeSend(dwDataBase, dwRetn)); + return ret; } DWORD TextHook::UnsafeSend(DWORD dwDataBase, DWORD dwRetn) { - DWORD dwCount, - dwAddr, - dwDataIn, - dwSplit; - BYTE pbData[PIPE_BUFFER_SIZE]; - DWORD dwType = hp.type; + DWORD dwCount, + dwAddr, + dwDataIn, + dwSplit; + BYTE pbData[PIPE_BUFFER_SIZE]; + DWORD dwType = hp.type; - dwAddr = hp.address; + dwAddr = hp.address; - /** jichi 12/24/2014 - * @param addr function address - * @param frame real address of the function, supposed to be the same as addr - * @param stack address of current stack - 4 - * @return If success, which is reverted - */ - if (::trigger) - ::trigger = Engine::InsertDynamicHook((LPVOID)dwAddr, *(DWORD *)(dwDataBase - 0x1c), *(DWORD *)(dwDataBase-0x18)); + /** jichi 12/24/2014 + * @param addr function address + * @param frame real address of the function, supposed to be the same as addr + * @param stack address of current stack - 4 + * @return If success, which is reverted + */ + if (::trigger) + ::trigger = Engine::InsertDynamicHook((LPVOID)dwAddr, *(DWORD *)(dwDataBase - 0x1c), *(DWORD *)(dwDataBase - 0x18)); - // jichi 10/24/2014: generic hook function - if (hp.hook_fun && !hp.hook_fun(dwDataBase, &hp)) - hp.hook_fun = nullptr; + // jichi 10/24/2014: generic hook function + if (hp.hook_fun && !hp.hook_fun(dwDataBase, &hp)) + hp.hook_fun = nullptr; - if (dwType & HOOK_EMPTY) // jichi 10/24/2014: dummy hook only for dynamic hook - return 0; + if (dwType & HOOK_EMPTY) // jichi 10/24/2014: dummy hook only for dynamic hook + return 0; - dwCount = 0; - dwSplit = 0; - dwDataIn = *(DWORD *)(dwDataBase + hp.offset); // default value + dwCount = 0; + dwSplit = 0; + dwDataIn = *(DWORD *)(dwDataBase + hp.offset); // default value - if (hp.text_fun) { - hp.text_fun(dwDataBase, &hp, 0, &dwDataIn, &dwSplit, &dwCount); - } else { - if (dwDataIn == 0) - return 0; - if (dwType & FIXING_SPLIT) - dwSplit = FIXED_SPLIT_VALUE; // fuse all threads, and prevent floating - else if (dwType & USING_SPLIT) { - dwSplit = *(DWORD *)(dwDataBase + hp.split); - if (dwType & SPLIT_INDIRECT) { - if (IthGetMemoryRange((LPVOID)(dwSplit + hp.split_index), 0, 0)) - dwSplit = *(DWORD *)(dwSplit + hp.split_index); - else - return 0; - } - } - if (dwType & DATA_INDIRECT) { - if (IthGetMemoryRange((LPVOID)(dwDataIn + hp.index), 0, 0)) - dwDataIn = *(DWORD *)(dwDataIn + hp.index); - else - return 0; - } - dwCount = GetLength(dwDataBase, dwDataIn); - } - // jichi 12/25/2013: validate data size - if (dwCount == 0 || dwCount > PIPE_BUFFER_SIZE - sizeof(ThreadParam)) - return 0; + if (hp.text_fun) { + hp.text_fun(dwDataBase, &hp, 0, &dwDataIn, &dwSplit, &dwCount); + } + else { + if (dwDataIn == 0) + return 0; + if (dwType & FIXING_SPLIT) + dwSplit = FIXED_SPLIT_VALUE; // fuse all threads, and prevent floating + else if (dwType & USING_SPLIT) { + dwSplit = *(DWORD *)(dwDataBase + hp.split); + if (dwType & SPLIT_INDIRECT) { + if (IthGetMemoryRange((LPVOID)(dwSplit + hp.split_index), 0, 0)) + dwSplit = *(DWORD *)(dwSplit + hp.split_index); + else + return 0; + } + } + if (dwType & DATA_INDIRECT) { + if (IthGetMemoryRange((LPVOID)(dwDataIn + hp.index), 0, 0)) + dwDataIn = *(DWORD *)(dwDataIn + hp.index); + else + return 0; + } + dwCount = GetLength(dwDataBase, dwDataIn); + } + // jichi 12/25/2013: validate data size + if (dwCount == 0 || dwCount > PIPE_BUFFER_SIZE - sizeof(ThreadParam)) + return 0; - if (hp.length_offset == 1) { - dwDataIn &= 0xffff; - if ((dwType & BIG_ENDIAN) && (dwDataIn >> 8)) - dwDataIn = _byteswap_ushort(dwDataIn & 0xffff); - if (dwCount == 1) - dwDataIn &= 0xff; - *(WORD *)(pbData + sizeof(ThreadParam)) = dwDataIn & 0xffff; - } - else - ::memcpy(pbData + sizeof(ThreadParam), (void *)dwDataIn, dwCount); + if (hp.length_offset == 1) { + dwDataIn &= 0xffff; + if ((dwType & BIG_ENDIAN) && (dwDataIn >> 8)) + dwDataIn = _byteswap_ushort(dwDataIn & 0xffff); + if (dwCount == 1) + dwDataIn &= 0xff; + *(WORD *)(pbData + sizeof(ThreadParam)) = dwDataIn & 0xffff; + } + else + ::memcpy(pbData + sizeof(ThreadParam), (void *)dwDataIn, dwCount); - // jichi 10/14/2014: Add filter function - if (hp.filter_fun && !hp.filter_fun(pbData + sizeof(ThreadParam), &dwCount, &hp, 0) || dwCount <= 0) { - return 0; - } + // jichi 10/14/2014: Add filter function + if (hp.filter_fun && !hp.filter_fun(pbData + sizeof(ThreadParam), &dwCount, &hp, 0) || dwCount <= 0) { + return 0; + } - if (dwType & (NO_CONTEXT|FIXING_SPLIT)) - dwRetn = 0; + if (dwType & (NO_CONTEXT | FIXING_SPLIT)) + dwRetn = 0; *(ThreadParam*)pbData = { GetCurrentProcessId(), dwAddr, dwRetn, dwSplit }; - if (dwCount) { + if (dwCount) { DWORD unused; - //CliLockPipe(); - WriteFile(::hookPipe, pbData, dwCount + sizeof(ThreadParam), &unused, nullptr); - //CliUnlockPipe(); - } - return 0; + //CliLockPipe(); + WriteFile(::hookPipe, pbData, dwCount + sizeof(ThreadParam), &unused, nullptr); + //CliUnlockPipe(); + } + return 0; } -int TextHook::InsertHookCode() +bool TextHook::InsertHookCode() { - DWORD ret = no; - // jichi 9/17/2013: might raise 0xC0000005 AccessViolationException on win7 - ITH_WITH_SEH(ret = UnsafeInsertHookCode()); - //if (ret == no) - // ITH_WARN(L"Failed to insert hook"); - return ret; + bool ret = false; + // jichi 9/17/2013: might raise 0xC0000005 AccessViolationException on win7 + __try { ret = UnsafeInsertHookCode(); } + __except (1) {}; + return ret; } -int TextHook::UnsafeInsertHookCode() +bool TextHook::UnsafeInsertHookCode() { - //ConsoleOutput("vnrcli:UnsafeInsertHookCode: enter"); - if (hp.module && (hp.type & MODULE_OFFSET)) { // Map hook offset to real address. - if (DWORD base = GetModuleBase(hp.module)) { - hp.address += base; - } - else { - currentHook--; - ConsoleOutput("vnrcli:UnsafeInsertHookCode: FAILED: module not present"); - return no; - } - hp.type &= ~MODULE_OFFSET; - } + if (hp.module && (hp.type & MODULE_OFFSET)) // Map hook offset to real address. + { + if (DWORD base = GetModuleBase(hp.module)) hp.address += base; + else return ConsoleOutput("NextHooker: UnsafeInsertHookCode: FAILED: module not present"), false; + hp.type &= ~MODULE_OFFSET; + } + BYTE* original; + if (MH_CreateHook((void*)hp.address, (void*)trampoline, (void**)&original) != MH_OK) return false; - { - TextHook *it = hookman; - for (int i = 0; (i < currentHook) && it; it++) { // Check if there is a collision. - if (it->Address()) - i++; - //it = hookman + i; - if (it == this) - continue; - if (it->Address() <= hp.address && - it->Address() + it->Length() > hp.address) { - it->ClearHook(); - break; - } - } - } + void* thisPtr = (void*)this; + void* funcPtr = (void*)((BYTE*)ProcessHook - (BYTE*)(trampoline + 19)); + DWORD dist = original - (trampoline + sizeof(common_hook)) - 4; - // Verify hp.address. - if (!IthGetMemoryRange((LPCVOID)hp.address, nullptr, nullptr)) - { - ConsoleOutput("NextHooker: FAILED: cannot access requested memory"); - return no; - } + memcpy(trampoline, common_hook, sizeof(common_hook)); + memcpy(trampoline + 10, &thisPtr, sizeof(void*)); + memcpy(trampoline + 15, &funcPtr, sizeof(void*)); + memcpy(trampoline + sizeof(common_hook), &dist, sizeof(dist)); - memcpy(recover, common_hook, sizeof(common_hook)); - void* thisPtr = (void*)this; - void* funcPtr = (void*)((BYTE*)ProcessHook - (BYTE*)(recover + 19)); - memcpy(recover + 10, &thisPtr, sizeof(void*)); - memcpy(recover + 15, &funcPtr, sizeof(void*)); - BYTE *c = (BYTE *)hp.address, - *r = recover; - BYTE inst[] = // jichi 9/27/2013: Why 8? Only 5 bytes will be written using NtWriteVirtualMemory - { - 0xe9, 0, 0, 0, 0, // jmp recover - 0xcc, 0xcc, 0xcc // int3 - }; - void* relRecover = (void*)(recover - (BYTE*)hp.address - 5); - memcpy(inst + 1, &relRecover, sizeof(void*)); - r += sizeof(common_hook); - hp.hook_len = 5; - int address = hp.address; - switch (MapInstruction(hp.address, (DWORD)r, hp.hook_len, hp.recover_len)) { - case -1: - ConsoleOutput("vnrcli:UnsafeInsertHookCode: FAILED: failed to map instruction"); - return no; - case 0: - __asm - { - mov ecx,this - movzx eax,[ecx]hp.hook_len - movzx edx,[ecx]hp.recover_len - add edx,address - add eax,r - add eax,5 - sub edx,eax - mov [eax-5],0xe9 // jichi 9/27/2013: 0xe9 is jump - mov [eax-4],edx - } - } - // jichi 9/27/2013: Save the original instructions in the memory - memcpy(original, (LPVOID)hp.address, hp.recover_len); - //Check if the new hook range conflict with existing ones. Clear older if conflict. - { - TextHook *it = hookman; - for (int i = 0; i < currentHook; it++) { - if (it->Address()) - i++; - if (it == this) - continue; - if (it->Address() >= hp.address && - it->Address() < hp.hook_len + hp.address) { - it->ClearHook(); - break; - } - } - } + if (MH_EnableHook((void*)hp.address) != MH_OK) return false; - DWORD old; - LPVOID addr = (void*)hp.address; - VirtualProtect(addr, sizeof(inst), PAGE_EXECUTE_READWRITE, &old); - memcpy(addr, inst, hp.recover_len); - FlushInstructionCache(GetCurrentProcess(), addr, hp.recover_len); - - return 0; + return true; } #endif // _WIN32 @@ -478,132 +277,97 @@ DWORD WINAPI ReaderThread(LPVOID threadParam) return 0; } -int TextHook::InsertReadCode() +bool TextHook::InsertReadCode() { - hp.hook_len = 0x40; - //Check if the new hook range conflict with existing ones. Clear older if conflict. - TextHook *it = hookman; - for (int i = 0; i < currentHook; it++) { - if (it->Address()) - i++; - if (it == this) - continue; - if ((it->Address() >= hp.address && it->Address() < hp.hook_len + hp.address) || (it->Address() <= hp.address && it->Address() + it->Length() > hp.address)) - it->ClearHook(); - } + RemoveHook(hp.address); // Artikash 8/25/2018: clear existing if (!IthGetMemoryRange((LPCVOID)hp.address, 0, 0)) { - ConsoleOutput("cannot access read address"); - return no; + ConsoleOutput("NextHooker:InsertReadCode failed: cannot access read address"); + return false; } hp.readerHandle = CreateThread(nullptr, 0, ReaderThread, this, 0, nullptr); - return yes; - + return true; } -int TextHook::InitHook(const HookParam &h, LPCSTR name, WORD set_flag) +void TextHook::InitHook(const HookParam &h, LPCSTR name, WORD set_flag) { - WaitForSingleObject(hmMutex, 0); - hp = h; - hp.type |= set_flag; - if (name && name != hook_name) { - SetHookName(name); - } - currentHook++; - current_available = this+1; - while (current_available->Address()) - current_available++; - ReleaseMutex(hmMutex); - return 1; + WaitForSingleObject(hmMutex, 0); + hp = h; + hp.type |= set_flag; + if (name && name != hook_name) SetHookName(name); + ReleaseMutex(hmMutex); } -int TextHook::RemoveHookCode() +void TextHook::RemoveHookCode() { - if (!hp.address) - return no; - - DWORD l = hp.hook_len; - - memcpy((void*)hp.address, original, hp.recover_len); - FlushInstructionCache(GetCurrentProcess(), (void*)hp.address, hp.recover_len); - return yes; + MH_DisableHook((void*)hp.address); } -int TextHook::RemoveReadCode() +void TextHook::RemoveReadCode() { - if (!hp.address) return no; TerminateThread(hp.readerHandle, 0); CloseHandle(hp.readerHandle); - return yes; } -int TextHook::ClearHook() +void TextHook::ClearHook() { - int err; - WaitForSingleObject(hmMutex, 0); - ConsoleOutput("vnrcli:RemoveHook: enter"); - if (hp.type & DIRECT_READ) err = RemoveReadCode(); - else err = RemoveHookCode(); - NotifyHookRemove(hp.address); - if (hook_name) { - delete[] hook_name; - hook_name = nullptr; - } - memset(this, 0, sizeof(TextHook)); // jichi 11/30/2013: This is the original code of ITH - //if (current_available>this) - // current_available = this; - currentHook--; - ConsoleOutput("vnrcli:RemoveHook: leave"); - ReleaseMutex(hmMutex); - return err; + WaitForSingleObject(hmMutex, 0); + ConsoleOutput("NextHooker:RemoveHook: enter"); + if (hp.type & DIRECT_READ) RemoveReadCode(); + else RemoveHookCode(); + NotifyHookRemove(hp.address); + if (hook_name) delete[] hook_name; + memset(this, 0, sizeof(TextHook)); // jichi 11/30/2013: This is the original code of ITH + //if (current_available>this) + // current_available = this; + currentHook--; + ConsoleOutput("NextHooker:RemoveHook: leave"); + ReleaseMutex(hmMutex); } -int TextHook::SetHookName(LPCSTR name) +void TextHook::SetHookName(LPCSTR name) { - name_length = strlen(name) + 1; - if (hook_name) - delete[] hook_name; - hook_name = new char[name_length]; - //ITH_MEMSET_HEAP(hook_name, 0, sizeof(wchar_t) * name_length); // jichi 9/26/2013: zero memory - strcpy(hook_name, name); - return 0; + name_length = strlen(name) + 1; + if (hook_name) delete[] hook_name; + hook_name = new char[name_length]; + strcpy(hook_name, name); } int TextHook::GetLength(DWORD base, DWORD in) { - if (base == 0) - return 0; - int len; - switch (hp.length_offset) { - default: // jichi 12/26/2013: I should not put this default branch to the end - len = *((int *)base + hp.length_offset); - if (len >= 0) { - if (hp.type & USING_UNICODE) - len <<= 1; - break; - } - else if (len != -1) - break; - //len == -1 then continue to case 0. - case 0: - if (hp.type & USING_UNICODE) - len = wcslen((const wchar_t *)in) << 1; - else - len = strlen((const char *)in); - break; - case 1: - if (hp.type & USING_UNICODE) - len = 2; - else { - if (hp.type & BIG_ENDIAN) - in >>= 8; - len = LeadByteTable[in & 0xff]; //Slightly faster than IsDBCSLeadByte - } - break; - } - // jichi 12/25/2013: This function originally return -1 if failed - //return len; - return max(0, len); + if (base == 0) + return 0; + int len; + switch (hp.length_offset) { + default: // jichi 12/26/2013: I should not put this default branch to the end + len = *((int *)base + hp.length_offset); + if (len >= 0) { + if (hp.type & USING_UNICODE) + len <<= 1; + break; + } + else if (len != -1) + break; + //len == -1 then continue to case 0. + case 0: + if (hp.type & USING_UNICODE) + len = wcslen((const wchar_t *)in) << 1; + else + len = strlen((const char *)in); + break; + case 1: + if (hp.type & USING_UNICODE) + len = 2; + else { + if (hp.type & BIG_ENDIAN) + in >>= 8; + len = LeadByteTable[in & 0xff]; //Slightly faster than IsDBCSLeadByte + } + break; + } + // jichi 12/25/2013: This function originally return -1 if failed + //return len; + return max(0, len); } // EOF diff --git a/vnrhook/hijack/texthook.h b/vnrhook/hijack/texthook.h index 86dddec..82b7e78 100644 --- a/vnrhook/hijack/texthook.h +++ b/vnrhook/hijack/texthook.h @@ -18,47 +18,33 @@ extern DWORD trigger; class TextHook { - int InsertHookCode(); - int InsertReadCode(); - int UnsafeInsertHookCode(); + bool InsertHookCode(); + bool InsertReadCode(); + bool UnsafeInsertHookCode(); DWORD UnsafeSend(DWORD dwDataBase, DWORD dwRetn); - int RemoveHookCode(); - int RemoveReadCode(); - int SetHookName(LPCSTR name); + void RemoveHookCode(); + void RemoveReadCode(); + void SetHookName(LPCSTR name); public: HookParam hp; LPSTR hook_name; int name_length; - BYTE recover[0x68 - sizeof(HookParam)]; - BYTE original[0x10]; + BYTE trampoline[80]; - unsigned __int64 Address() const { return hp.address; } - DWORD Type() const { return hp.type; } - WORD Length() const { return hp.hook_len; } - LPSTR Name() const { return hook_name; } - int NameLength() const { return name_length; } - int InsertHook(); - int InitHook(const HookParam &hp, LPCSTR name = 0, WORD set_flag = 0); + bool InsertHook(); + void InitHook(const HookParam &hp, LPCSTR name = 0, WORD set_flag = 0); DWORD Send(DWORD dwDataBase, DWORD dwRetn); - int ClearHook(); + void ClearHook(); int GetLength(DWORD base, DWORD in); // jichi 12/25/2013: Return 0 if failed }; -// jichi 1/16/2015: Though called max hook, it means max number of text threads -enum { MAX_HOOK = 64 }; +enum { MAX_HOOK = 300 }; enum { HOOK_SECTION_SIZE = MAX_HOOK * sizeof(TextHook) * 2, HOOK_BUFFER_SIZE = MAX_HOOK * sizeof(TextHook) }; -extern TextHook *hookman, -*current_available; +extern TextHook *hookman; -extern bool running, -live; +extern bool running; -extern HANDLE hookPipe, -hmMutex; - -DWORD WINAPI PipeManager(LPVOID unused); - -enum : int { yes = 0, no = 1 }; +extern HANDLE hookPipe, hmMutex; // EOF diff --git a/vnrhook/main.cc b/vnrhook/main.cc index 38f8a7e..b74e71c 100644 --- a/vnrhook/main.cc +++ b/vnrhook/main.cc @@ -10,29 +10,18 @@ #include "main.h" #include "defs.h" +#include "MinHook.h" +#include "pipe.h" #include "engine/engine.h" #include "engine/match.h" #include "hijack/texthook.h" #include "util/growl.h" -// Global variables - -// jichi 6/3/2014: memory range of the current module -DWORD processStartAddress, -processStopAddress; - -WCHAR hm_section[0x100]; HANDLE hSection; bool running; -int currentHook = 0, -user_hook_count = 0; +int currentHook = 0, userhookCount = 0; DWORD trigger = 0; -HANDLE -hFile, -hMutex, -hmMutex; - -void CreatePipe(); +HANDLE hmMutex; BOOL WINAPI DllMain(HINSTANCE hModule, DWORD fdwReason, LPVOID unused) { @@ -47,19 +36,11 @@ BOOL WINAPI DllMain(HINSTANCE hModule, DWORD fdwReason, LPVOID unused) // jichi 9/25/2013: Interprocedural communication with vnrsrv. hSection = CreateFileMappingW(INVALID_HANDLE_VALUE, nullptr, PAGE_EXECUTE_READWRITE, 0, HOOK_SECTION_SIZE, (ITH_SECTION_ + std::to_wstring(GetCurrentProcessId())).c_str()); ::hookman = (TextHook*)MapViewOfFile(hSection, FILE_MAP_ALL_ACCESS | FILE_MAP_EXECUTE, 0, 0, HOOK_BUFFER_SIZE); + memset(::hookman, 0, HOOK_BUFFER_SIZE); - ::processStartAddress = ::processStopAddress = (DWORD)GetModuleHandleW(nullptr); - - MEMORY_BASIC_INFORMATION info; - do - { - VirtualQuery((void*)::processStopAddress, &info, sizeof(info)); - ::processStopAddress = (DWORD)info.BaseAddress + info.RegionSize; - } while (info.Protect > PAGE_NOACCESS); - processStopAddress -= info.RegionSize; + MH_Initialize(); ::running = true; - ::current_available = ::hookman; CreatePipe(); } @@ -67,14 +48,13 @@ BOOL WINAPI DllMain(HINSTANCE hModule, DWORD fdwReason, LPVOID unused) case DLL_PROCESS_DETACH: { ::running = false; - - for (TextHook *man = ::hookman; man < ::hookman + MAX_HOOK; man++) if (man->Address()) man->ClearHook(); + MH_Uninitialize(); + for (TextHook *man = ::hookman; man < ::hookman + MAX_HOOK; man++) if (man->hp.address) man->ClearHook(); //if (ith_has_section) UnmapViewOfFile(::hookman); CloseHandle(::hookPipe); CloseHandle(hSection); - CloseHandle(hMutex); CloseHandle(hmMutex); //} ITH_EXCEPT {} } @@ -84,47 +64,32 @@ BOOL WINAPI DllMain(HINSTANCE hModule, DWORD fdwReason, LPVOID unused) } //extern "C" { -DWORD NewHook(const HookParam &hp, LPCSTR lpname, DWORD flag) +void NewHook(const HookParam &hp, LPCSTR lpname, DWORD flag) { std::string name = lpname; - int current = ::current_available - ::hookman; - if (current < MAX_HOOK) { - //flag &= 0xffff; - //if ((flag & HOOK_AUXILIARY) == 0) - flag |= HOOK_ADDITIONAL; - if (name[0] == '\0') - { - name = "UserHook" + std::to_string(user_hook_count++); - } - - ConsoleOutput(("vnrcli:NewHook: try inserting hook: " + name).c_str()); + if (++currentHook < MAX_HOOK) + { + if (name[0] == '\0') name = "UserHook" + std::to_string(userhookCount++); + ConsoleOutput(("NextHooker: try inserting hook: " + name).c_str()); // jichi 7/13/2014: This function would raise when too many hooks added - ::hookman[current].InitHook(hp, name.c_str(), flag & 0xffff); - - if (::hookman[current].InsertHook() == 0) { - ConsoleOutput(("vnrcli:NewHook: inserted hook: " + name).c_str()); - NotifyHookInsert(hp, name.c_str()); - } - else - ConsoleOutput("vnrcli:NewHook:WARNING: failed to insert hook"); + ::hookman[currentHook].InitHook(hp, name.c_str(), flag); + if (::hookman[currentHook].InsertHook()) ConsoleOutput(("NextHooker: inserted hook: " + name).c_str()); + else ConsoleOutput("NextHooker:WARNING: failed to insert hook"); } - return 0; + else ConsoleOutput("NextHooker: too many hooks: can't insert"); } -DWORD RemoveHook(unsigned __int64 addr) + +void RemoveHook(unsigned __int64 addr) { for (int i = 0; i < MAX_HOOK; i++) - if (::hookman[i].Address() == addr) { + if (abs((long long)(::hookman[i].hp.address - addr)) < 9) ::hookman[i].ClearHook(); - return 0; - } - return 0; } -DWORD SwitchTrigger(DWORD t) +void SwitchTrigger(DWORD t) { trigger = t; - return 0; } // EOF \ No newline at end of file diff --git a/vnrhook/main.h b/vnrhook/main.h index 5e694f7..5d41f19 100644 --- a/vnrhook/main.h +++ b/vnrhook/main.h @@ -6,20 +6,10 @@ #include "common.h" #include "types.h" +#include "pipe.h" -void ConsoleOutput(LPCSTR text); // jichi 12/25/2013: Used to return length of sent text -void NotifyHookInsert(HookParam hp, LPCSTR name); -void NotifyHookRemove(unsigned __int64 addr); -DWORD NewHook(const HookParam &hp, LPCSTR name, DWORD flag = HOOK_ENGINE); -DWORD RemoveHook(unsigned __int64 addr); -DWORD SwitchTrigger(DWORD on); - -// 10/14/2014 jichi: disable GDI hooks -void EnableGDIHooks(); -void EnableGDIPlusHooks(); -void DisableGDIHooks(); -void DisableGDIPlusHooks(); -bool GDIHooksEnabled(); -bool GDIPlusHooksEnabled(); +void NewHook(const HookParam &hp, LPCSTR name, DWORD flag = HOOK_ENGINE); +void RemoveHook(unsigned __int64 addr); +void SwitchTrigger(DWORD on); // EOF diff --git a/vnrhook/pipe.cc b/vnrhook/pipe.cc index 8dd8cb2..462f97f 100644 --- a/vnrhook/pipe.cc +++ b/vnrhook/pipe.cc @@ -7,7 +7,7 @@ # pragma warning (disable:4100) // C4100: unreference formal parameter #endif // _MSC_VER -#include "types.h" +#include "pipe.h" #include "main.h" #include "hijack/texthook.h" #include "engine/match.h" @@ -52,7 +52,7 @@ void CreatePipe() ReleaseMutex(pipeAcquisitionMutex); CloseHandle(pipeAcquisitionMutex); - ConsoleOutput("vnrcli:WaitForPipe: pipe connected"); + ConsoleOutput("NextHooker: pipe connected"); #ifdef _WIN64 ConsoleOutput("Hooks don't work on x64, only read codes work. Engine disabled."); #else @@ -71,9 +71,7 @@ void CreatePipe() case HOST_COMMAND_REMOVE_HOOK: { auto info = *(RemoveHookCmd*)buffer; - for (int i = 0; i < MAX_HOOK; ++i) - if (::hookman[i].Address() == info.address) - ::hookman[i].ClearHook(); + RemoveHook(info.address); } break; case HOST_COMMAND_DETACH: @@ -96,18 +94,6 @@ void ConsoleOutput(LPCSTR text) WriteFile(::hookPipe, &info, strlen(text) + sizeof(info), DUMMY, nullptr); } -void NotifyHookInsert(HookParam hp, LPCSTR name) -{ - //BYTE buffer[PIPE_BUFFER_SIZE]; - //*(DWORD*)buffer = HOST_NOTIFICATION; - //*(DWORD*)(buffer + sizeof(DWORD)) = HOST_NOTIFICATION_NEWHOOK; - //*(HookParam*)(buffer + sizeof(DWORD) * 2) = hp; - //strcpy((char*)buffer + sizeof(DWORD) * 2 + sizeof(HookParam), name); - //DWORD unused; - //WriteFile(::hookPipe, buffer, strlen(name) + sizeof(DWORD) * 2 + sizeof(HookParam), &unused, nullptr); - //return; -} - void NotifyHookRemove(unsigned __int64 addr) { auto info = HookRemovedNotif(addr); diff --git a/vnrhook/pipe.h b/vnrhook/pipe.h new file mode 100644 index 0000000..b57658b --- /dev/null +++ b/vnrhook/pipe.h @@ -0,0 +1,8 @@ +#pragma once + +#include "common.h" +#include "types.h" + +void CreatePipe(); +void NotifyHookRemove(unsigned __int64 addr); +void ConsoleOutput(LPCSTR text); // jichi 12/25/2013: Used to return length of sent text From f8ba6102e3120ffb8bf131a6ab4b33ec81332250 Mon Sep 17 00:00:00 2001 From: Akash Mozumdar Date: Sat, 25 Aug 2018 15:45:35 -0400 Subject: [PATCH 4/4] update readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 432c44a..bbacc72 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,7 @@ GPL v3 - ITH updating by [Andys](https://github.com/AndyScull) - ITHVNR new GUI & VNR engine migration by [Stomp](http://www.hongfire.com/forum/member/325894-stomp) - ITHVNR updating by [mireado](https://github.com/mireado) and [Eguni](https://github.com/Eguni) +- MinHook library made by [TsudaKageyu](https://github.com/TsudaKageyu) - NextHooker creation/updating by [Me](https://github.com/Artikash) and [DoumanAsh](https://github.com/DoumanAsh) ## Special Thanks