1
0
Fork 0

DBG: some debug crap for TaskThread

This commit is contained in:
Duncan Ogilvie 2017-11-04 20:57:02 +01:00
parent 4d413d0f0f
commit 9f0b08af71
No known key found for this signature in database
GPG Key ID: FC89E0AAA0C1AAD8
21 changed files with 3171 additions and 37 deletions

View File

@ -8,11 +8,11 @@
static void GuiAddLogMessageAsync(_In_z_ const char* msg)
{
static StringConcatTaskThread_<void(*)(const std::string &)> task([](const std::string & msg)
static StringConcatTaskThread_<1, void(*)(const std::string &)> task([](const std::string & msg)
{
GuiAddLogMessage(msg.c_str());
});
task.WakeUp(msg);
task.WakeUp(msg, false);
}
/**

View File

@ -427,8 +427,8 @@ static DWORD WINAPI updateCallStackThread(duint ptr)
void updateCallStackAsync(duint ptr)
{
static TaskThread_<decltype(&updateCallStackThread), duint> updateCallStackTask(&updateCallStackThread);
updateCallStackTask.WakeUp(ptr);
static TaskThread_<4, decltype(&updateCallStackThread), duint> updateCallStackTask(&updateCallStackThread);
updateCallStackTask.WakeUp(ptr, false);
}
DWORD WINAPI updateSEHChainThread()
@ -441,8 +441,8 @@ DWORD WINAPI updateSEHChainThread()
void updateSEHChainAsync()
{
static TaskThread_<decltype(&updateSEHChainThread)> updateSEHChainTask(&updateSEHChainThread);
updateSEHChainTask.WakeUp();
static TaskThread_<5, decltype(&updateSEHChainThread)> updateSEHChainTask(&updateSEHChainThread);
updateSEHChainTask.WakeUp(false);
}
void DebugUpdateGui(duint disasm_addr, bool stack)
@ -528,14 +528,15 @@ void DebugUpdateGui(duint disasm_addr, bool stack)
void GuiSetDebugStateAsync(DBGSTATE state)
{
GuiSetDebugStateFast(state);
static TaskThread_<decltype(&GuiSetDebugState), DBGSTATE> GuiSetDebugStateTask(&GuiSetDebugState, 300);
GuiSetDebugStateTask.WakeUp(state);
static TaskThread_<6, decltype(&GuiSetDebugState), DBGSTATE> GuiSetDebugStateTask(&GuiSetDebugState, 300,
new(VirtualAlloc(0, sizeof(DebugStruct), MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE)) DebugStruct());
GuiSetDebugStateTask.WakeUp(state, true);
}
void DebugUpdateGuiAsync(duint disasm_addr, bool stack)
{
static TaskThread_<decltype(&DebugUpdateGui), duint, bool> DebugUpdateGuiTask(&DebugUpdateGui);
DebugUpdateGuiTask.WakeUp(disasm_addr, stack);
static TaskThread_<7, decltype(&DebugUpdateGui), duint, bool> DebugUpdateGuiTask(&DebugUpdateGui);
DebugUpdateGuiTask.WakeUp(disasm_addr, stack, false);
}
void DebugUpdateGuiSetStateAsync(duint disasm_addr, bool stack, DBGSTATE state)
@ -549,8 +550,8 @@ void DebugUpdateGuiSetStateAsync(duint disasm_addr, bool stack, DBGSTATE state)
void DebugUpdateBreakpointsViewAsync()
{
static TaskThread_<decltype(&GuiUpdateBreakpointsView)> BreakpointsUpdateGuiTask(&GuiUpdateBreakpointsView);
BreakpointsUpdateGuiTask.WakeUp();
static TaskThread_<8, decltype(&GuiUpdateBreakpointsView)> BreakpointsUpdateGuiTask(&GuiUpdateBreakpointsView);
BreakpointsUpdateGuiTask.WakeUp(false);
}
void DebugUpdateStack(duint dumpAddr, duint csp, bool forceDump)

View File

@ -258,8 +258,8 @@ static DWORD WINAPI memUpdateMap()
void MemUpdateMapAsync()
{
static TaskThread_<decltype(&memUpdateMap)> memUpdateMapTask(&memUpdateMap, 1000);
memUpdateMapTask.WakeUp();
static TaskThread_<3, decltype(&memUpdateMap)> memUpdateMapTask(&memUpdateMap, 1000);
memUpdateMapTask.WakeUp(false);
}
duint MemFindBaseAddr(duint Address, duint* Size, bool Refresh, bool FindReserved)

186
src/dbg/minhook/MinHook.h Normal file
View File

@ -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 <windows.h>
// 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

313
src/dbg/minhook/buffer.c Normal file
View File

@ -0,0 +1,313 @@
/*
* 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 <windows.h>
#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));
}

42
src/dbg/minhook/buffer.h Normal file
View File

@ -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);

391
src/dbg/minhook/hde/hde32.c Normal file
View File

@ -0,0 +1,391 @@
/*
* 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__)

108
src/dbg/minhook/hde/hde32.h Normal file
View File

@ -0,0 +1,108 @@
/*
* 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_ */

400
src/dbg/minhook/hde/hde64.c Normal file
View File

@ -0,0 +1,400 @@
/*
* 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__)

115
src/dbg/minhook/hde/hde64.h Normal file
View File

@ -0,0 +1,115 @@
/*
* 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_ */

View File

@ -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 <windows.h>
// 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;

View File

@ -0,0 +1,74 @@
/*
* 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
};

View File

@ -0,0 +1,75 @@
/*
* 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
};

891
src/dbg/minhook/hook.c Normal file
View File

@ -0,0 +1,891 @@
/*
* 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 <windows.h>
#include <tlhelp32.h>
#include <limits.h>
#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)";
}

View File

@ -0,0 +1,322 @@
/*
* 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 <windows.h>
#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;
}

View File

@ -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);

View File

@ -3,13 +3,20 @@
#include "_global.h"
#include "minhook/MinHook.h"
#include <thread>
#include <tuple>
#include <type_traits>
#include <utility>
struct DebugStruct
{
HANDLE wakeupSemaphore = nullptr;
};
const size_t TASK_THREAD_DEFAULT_SLEEP_TIME = 100;
template <typename F, typename... Args>
template <int N, typename F, typename... Args>
class TaskThread_
{
protected:
@ -23,7 +30,7 @@ protected:
size_t minSleepTimeMs = 0;
size_t wakeups = 0;
size_t execs = 0;
void Loop();
void Loop(DebugStruct* debug);
// Given new args, compress it into old args.
virtual std::tuple<Args...> CompressArguments(Args && ... args);
@ -31,13 +38,13 @@ protected:
// Reset called after we latch in a value
virtual void ResetArgs() { }
public:
void WakeUp(Args...);
explicit TaskThread_(F, size_t minSleepTimeMs = TASK_THREAD_DEFAULT_SLEEP_TIME);
void WakeUp(Args..., bool debug);
explicit TaskThread_(F, size_t minSleepTimeMs = TASK_THREAD_DEFAULT_SLEEP_TIME, DebugStruct* debug = nullptr);
virtual ~TaskThread_();
};
template <typename F>
class StringConcatTaskThread_ : public TaskThread_<F, std::string>
template <int N, typename F>
class StringConcatTaskThread_ : public TaskThread_<N, F, std::string>
{
virtual std::tuple<std::string> CompressArguments(std::string && msg) override
{
@ -52,7 +59,7 @@ class StringConcatTaskThread_ : public TaskThread_<F, std::string>
}
public:
explicit StringConcatTaskThread_(F fn, size_t minSleepTimeMs = TASK_THREAD_DEFAULT_SLEEP_TIME)
: TaskThread_<F, std::string>(fn, minSleepTimeMs) {}
: TaskThread_<N, F, std::string>(fn, minSleepTimeMs) {}
};
// using aliases for cleaner syntax
@ -90,28 +97,37 @@ DECLTYPE_AND_RETURN(apply_tuple_impl(std::forward<F>(fn), std::forward<Tuple>(t)
std::tuple_size<typename std::remove_reference<Tuple>::type>::value
> ()));
template <typename F, typename... Args>
std::tuple<Args...> TaskThread_<F, Args...>::CompressArguments(Args && ... _args)
template <int N, typename F, typename... Args>
std::tuple<Args...> TaskThread_<N, F, Args...>::CompressArguments(Args && ... _args)
{
return std::make_tuple<Args...>(std::forward<Args>(_args)...);
}
template <typename F, typename... Args> void TaskThread_<F, Args...>::WakeUp(Args... _args)
template <int N, typename F, typename... Args>
void TaskThread_<N, F, Args...>::WakeUp(Args... _args, bool debug)
{
++this->wakeups;
EnterCriticalSection(&this->access);
this->args = CompressArguments(std::forward<Args>(_args)...);
LeaveCriticalSection(&this->access);
// This will fail silently if it's redundant, which is what we want.
ReleaseSemaphore(this->wakeupSemaphore, 1, nullptr);
if(!ReleaseSemaphore(this->wakeupSemaphore, 1, nullptr) && debug)
__debugbreak();
}
template <typename F, typename... Args> void TaskThread_<F, Args...>::Loop()
template <int N, typename F, typename... Args>
void TaskThread_<N, F, Args...>::Loop(DebugStruct* debug)
{
std::tuple<Args...> argLatch;
while(this->active)
{
WaitForSingleObject(this->wakeupSemaphore, INFINITE);
if(debug)
{
if(WaitForSingleObject(this->wakeupSemaphore, INFINITE) != 0)
__debugbreak();
}
else
WaitForSingleObject(this->wakeupSemaphore, INFINITE);
EnterCriticalSection(&this->access);
argLatch = this->args;
@ -127,21 +143,53 @@ template <typename F, typename... Args> void TaskThread_<F, Args...>::Loop()
}
}
template <typename F, typename... Args>
TaskThread_<F, Args...>::TaskThread_(F fn,
size_t minSleepTimeMs) : fn(fn), minSleepTimeMs(minSleepTimeMs)
static DebugStruct* g_Debug = nullptr;
typedef BOOL(WINAPI* CLOSEHANDLE)(HANDLE hObject);
static CLOSEHANDLE fpCloseHandle = nullptr;
static BOOL WINAPI CloseHandleHook(HANDLE hObject)
{
this->wakeupSemaphore = CreateSemaphoreW(nullptr, 0, 1, nullptr);
if(g_Debug && g_Debug->wakeupSemaphore == hObject)
__debugbreak();
return fpCloseHandle(hObject);
}
static void DoHook()
{
if(MH_Initialize() != MH_OK)
__debugbreak();
if(MH_CreateHook(GetProcAddress(GetModuleHandleW(L"kernelbase.dll"), "CloseHandle"), &CloseHandleHook, (LPVOID*)&fpCloseHandle) != MH_OK)
__debugbreak();
if(MH_EnableHook(MH_ALL_HOOKS) != MH_OK)
__debugbreak();
}
template <int N, typename F, typename... Args>
TaskThread_<N, F, Args...>::TaskThread_(F fn,
size_t minSleepTimeMs, DebugStruct* debug) : fn(fn), minSleepTimeMs(minSleepTimeMs)
{
wchar_t name[256];
swprintf_s(name, L"_TaskThread%d_%p", N, debug);
this->wakeupSemaphore = CreateSemaphoreW(nullptr, 0, 1, name);
if(debug)
{
g_Debug = debug;
SetEnvironmentVariableA("TASKTHREAD_DEBUG", StringUtils::sprintf("%p", debug).c_str());
DoHook();
if(!this->wakeupSemaphore)
__debugbreak();
debug->wakeupSemaphore = this->wakeupSemaphore;
}
InitializeCriticalSection(&this->access);
this->thread = std::thread([this]
this->thread = std::thread([this, debug]
{
this->Loop();
this->Loop(debug);
});
}
template <typename F, typename... Args>
TaskThread_<F, Args...>::~TaskThread_()
template <int N, typename F, typename... Args>
TaskThread_<N, F, Args...>::~TaskThread_()
{
EnterCriticalSection(&this->access);
this->active = false;

View File

@ -105,8 +105,8 @@ void WatchExpr::modifyName(const char* newName)
void GuiUpdateWatchViewAsync()
{
static TaskThread_<decltype(&GuiUpdateWatchView)> task(&GuiUpdateWatchView);
task.WakeUp();
static TaskThread_<2, decltype(&GuiUpdateWatchView)> task(&GuiUpdateWatchView);
task.WakeUp(false);
}
// Global functions

View File

@ -85,6 +85,11 @@
<ClCompile Include="loop.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="memory.cpp" />
<ClCompile Include="minhook\buffer.c" />
<ClCompile Include="minhook\hde\hde32.c" />
<ClCompile Include="minhook\hde\hde64.c" />
<ClCompile Include="minhook\hook.c" />
<ClCompile Include="minhook\trampoline.c" />
<ClCompile Include="mnemonichelp.cpp" />
<ClCompile Include="module.cpp" />
<ClCompile Include="msgqueue.cpp" />

View File

@ -96,6 +96,9 @@
<Filter Include="Header Files\btparser">
<UniqueIdentifier>{d20554d2-b3de-4e73-ac55-217da06783ba}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\minhook">
<UniqueIdentifier>{2936bd10-14a6-4af7-86af-5b7b5d91d4b8}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="main.cpp">
@ -431,6 +434,21 @@
<ClCompile Include="formatfunctions.cpp">
<Filter>Source Files\Core</Filter>
</ClCompile>
<ClCompile Include="minhook\buffer.c">
<Filter>Source Files\minhook</Filter>
</ClCompile>
<ClCompile Include="minhook\hook.c">
<Filter>Source Files\minhook</Filter>
</ClCompile>
<ClCompile Include="minhook\trampoline.c">
<Filter>Source Files\minhook</Filter>
</ClCompile>
<ClCompile Include="minhook\hde\hde32.c">
<Filter>Source Files\minhook</Filter>
</ClCompile>
<ClCompile Include="minhook\hde\hde64.c">
<Filter>Source Files\minhook</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="dbghelp\dbghelp.h">

View File

@ -33,6 +33,7 @@ BOOL
void CrashDumpInitialize()
{
return;
// Get handles to kernel32 and dbghelp
HMODULE hKernel32 = GetModuleHandleA("kernel32.dll");
HMODULE hDbghelp = LoadLibraryA("dbghelp.dll");