Tools/Launcher: Add launcher as alternative to patcher (#409)
* Tools/Launcher: Introduce version 1 of the client launcher/patcher the launcher downloads all needed modules and patches the needed one the launcher patches the RSA key in memory before WoW window opens the launcher injects the client_patcher_xx.dll inside Wow the client_patcher_xx.dll hooks LoadLibraryW and awaits Battle.net.dll load the client_patcher_xx.dll patches the Battle.net.dll memory before it is returned to process client_patcher_xx.dll unhooks itself * Tools/Launcher: MinHook only for tools and on windows
This commit is contained in:
1 parent
2fdd6742b2
commit
9d6a808ba6
41 files changed
+4675
-1
No files matched your search
@@ -36,6 +36,9 @@ endif()
|
|||||||
|
|
||||||
if(TOOLS)
|
if(TOOLS)
|
||||||
add_subdirectory(StormLib)
|
add_subdirectory(StormLib)
|
||||||
|
if(WIN32)
|
||||||
|
add_subdirectory(MinHook)
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(BUILD_TESTING)
|
if(BUILD_TESTING)
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
Tsuda Kageyu <tsuda.kageyu@gmail.com>
|
||||||
|
Creator, maintainer
|
||||||
|
|
||||||
|
Michael Maltsev <leahcimmar@gmail.com>
|
||||||
|
Added "Queue" functions. A lot of bug fixes.
|
||||||
|
|
||||||
|
Andrey Unis <uniskz@gmail.com>
|
||||||
|
Rewrote the hook engine in plain C.
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
# This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
|
||||||
|
#
|
||||||
|
# This file is free software; as a special exception the author gives
|
||||||
|
# unlimited permission to copy and/or distribute it, with or without
|
||||||
|
# modifications, as long as this notice is preserved.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful, but
|
||||||
|
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
|
||||||
|
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
|
||||||
|
set(SOURCES_MINHOOK
|
||||||
|
"src/buffer.c"
|
||||||
|
"src/hook.c"
|
||||||
|
"src/trampoline.c"
|
||||||
|
)
|
||||||
|
|
||||||
|
if(PLATFORM EQUAL 64)
|
||||||
|
set(SOURCES_HDE "src/hde/hde64.c")
|
||||||
|
else()
|
||||||
|
set(SOURCES_HDE "src/hde/hde32.c")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_library(minhook STATIC ${SOURCES_MINHOOK} ${SOURCES_HDE})
|
||||||
|
|
||||||
|
target_include_directories(minhook
|
||||||
|
PUBLIC
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/src
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/src/hde
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||||
|
|
||||||
|
target_link_libraries(minhook
|
||||||
|
PRIVATE
|
||||||
|
trinity-dependency-interface)
|
||||||
|
|
||||||
|
set_target_properties(minhook
|
||||||
|
PROPERTIES
|
||||||
|
FOLDER
|
||||||
|
"dep")
|
||||||
|
|
||||||
|
if(WIN32)
|
||||||
|
set_target_properties(minhook PROPERTIES PREFIX "")
|
||||||
|
if(PLATFORM EQUAL 64)
|
||||||
|
set_target_properties(minhook PROPERTIES DEBUG_POSTFIX ".x64d")
|
||||||
|
set_target_properties(minhook PROPERTIES RELEASE_POSTFIX ".x64")
|
||||||
|
set_target_properties(minhook PROPERTIES RELWITHDEBINFO_POSTFIX ".x64")
|
||||||
|
set_target_properties(minhook PROPERTIES MINSIZEREL_POSTFIX ".x64")
|
||||||
|
else()
|
||||||
|
set_target_properties(minhook PROPERTIES DEBUG_POSTFIX ".x32d")
|
||||||
|
set_target_properties(minhook PROPERTIES RELEASE_POSTFIX ".x32")
|
||||||
|
set_target_properties(minhook PROPERTIES RELWITHDEBINFO_POSTFIX ".x32")
|
||||||
|
set_target_properties(minhook PROPERTIES MINSIZEREL_POSTFIX ".x32")
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
set_target_properties(minhook PROPERTIES PREFIX "lib")
|
||||||
|
set_target_properties(minhook PROPERTIES POSTFIX "")
|
||||||
|
set_target_properties(minhook PROPERTIES DEBUG_POSTFIX "d")
|
||||||
|
endif()
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
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.
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
Portions of this software are Copyright (c) 2008-2009, Vyacheslav Patkov.
|
||||||
|
================================================================================
|
||||||
|
Hacker Disassembler Engine 32 C
|
||||||
|
Copyright (c) 2008-2009, Vyacheslav Patkov.
|
||||||
|
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 REGENTS 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.
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
Hacker Disassembler Engine 64 C
|
||||||
|
Copyright (c) 2008-2009, Vyacheslav Patkov.
|
||||||
|
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 REGENTS 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.
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
EXPORTS
|
||||||
|
MH_Initialize
|
||||||
|
MH_Uninitialize
|
||||||
|
|
||||||
|
MH_CreateHook
|
||||||
|
MH_CreateHookApi
|
||||||
|
MH_CreateHookApiEx
|
||||||
|
MH_RemoveHook
|
||||||
|
MH_EnableHook
|
||||||
|
MH_DisableHook
|
||||||
|
MH_QueueEnableHook
|
||||||
|
MH_QueueDisableHook
|
||||||
|
MH_ApplyQueued
|
||||||
|
MH_StatusToString
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
1 VERSIONINFO
|
||||||
|
FILEVERSION 1,3,3,0
|
||||||
|
PRODUCTVERSION 1,3,3,0
|
||||||
|
FILEFLAGSMASK 0x17L
|
||||||
|
#ifdef _DEBUG
|
||||||
|
FILEFLAGS 0x1L
|
||||||
|
#else
|
||||||
|
FILEFLAGS 0x0L
|
||||||
|
#endif
|
||||||
|
FILEOS 0x4L
|
||||||
|
FILETYPE 0x2L
|
||||||
|
FILESUBTYPE 0x0L
|
||||||
|
BEGIN
|
||||||
|
BLOCK "StringFileInfo"
|
||||||
|
BEGIN
|
||||||
|
BLOCK "040904b0"
|
||||||
|
BEGIN
|
||||||
|
VALUE "CompanyName", "Tsuda Kageyu"
|
||||||
|
VALUE "FileDescription", "MinHook - The Minimalistic API Hook Library for x64/x86"
|
||||||
|
VALUE "FileVersion", "1.3.3.0"
|
||||||
|
VALUE "InternalName", "MinHookD"
|
||||||
|
VALUE "LegalCopyright", "Copyright (C) 2009-2017 Tsuda Kageyu. All rights reserved."
|
||||||
|
VALUE "LegalTrademarks", "Tsuda Kageyu"
|
||||||
|
VALUE "ProductName", "MinHook DLL"
|
||||||
|
VALUE "ProductVersion", "1.3.3.0"
|
||||||
|
END
|
||||||
|
END
|
||||||
|
BLOCK "VarFileInfo"
|
||||||
|
BEGIN
|
||||||
|
VALUE "Translation", 0x409, 1200
|
||||||
|
END
|
||||||
|
END
|
||||||
@@ -0,0 +1,185 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
// pszProcName [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.
|
||||||
|
// pszProcName [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
|
||||||
@@ -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 <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(MEMORY_SLOT));
|
||||||
|
#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));
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
@@ -0,0 +1,324 @@
|
|||||||
|
/*
|
||||||
|
* Hacker Disassembler Engine 32 C
|
||||||
|
* Copyright (c) 2008-2009, Vyacheslav Patkov.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if defined(_M_IX86) || defined(__i386__)
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#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;
|
||||||
|
|
||||||
|
memset(hs, 0, sizeof(hde32s));
|
||||||
|
|
||||||
|
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;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
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__)
|
||||||
@@ -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_ */
|
||||||
@@ -0,0 +1,335 @@
|
|||||||
|
/*
|
||||||
|
* Hacker Disassembler Engine 64 C
|
||||||
|
* Copyright (c) 2008-2009, Vyacheslav Patkov.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if defined(_M_X64) || defined(__x86_64__)
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#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;
|
||||||
|
|
||||||
|
memset(hs, 0, sizeof(hde64s));
|
||||||
|
|
||||||
|
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;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
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__)
|
||||||
@@ -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_ */
|
||||||
@@ -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;
|
||||||
@@ -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
|
||||||
|
};
|
||||||
@@ -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
|
||||||
|
};
|
||||||
@@ -0,0 +1,923 @@
|
|||||||
|
/*
|
||||||
|
* 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 "../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 BOOL EnumerateThreads(PFROZEN_THREADS pThreads)
|
||||||
|
{
|
||||||
|
BOOL succeeded = FALSE;
|
||||||
|
|
||||||
|
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
|
||||||
|
if (hSnapshot != INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
THREADENTRY32 te;
|
||||||
|
te.dwSize = sizeof(THREADENTRY32);
|
||||||
|
if (Thread32First(hSnapshot, &te))
|
||||||
|
{
|
||||||
|
succeeded = TRUE;
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
succeeded = FALSE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (pThreads->size >= pThreads->capacity)
|
||||||
|
{
|
||||||
|
pThreads->capacity *= 2;
|
||||||
|
LPDWORD p = (LPDWORD)HeapReAlloc(
|
||||||
|
g_hHeap, 0, pThreads->pItems, pThreads->capacity * sizeof(DWORD));
|
||||||
|
if (p == NULL)
|
||||||
|
{
|
||||||
|
succeeded = FALSE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
pThreads->pItems = p;
|
||||||
|
}
|
||||||
|
pThreads->pItems[pThreads->size++] = te.th32ThreadID;
|
||||||
|
}
|
||||||
|
|
||||||
|
te.dwSize = sizeof(THREADENTRY32);
|
||||||
|
} while (Thread32Next(hSnapshot, &te));
|
||||||
|
|
||||||
|
if (succeeded && GetLastError() != ERROR_NO_MORE_FILES)
|
||||||
|
succeeded = FALSE;
|
||||||
|
|
||||||
|
if (!succeeded && pThreads->pItems != NULL)
|
||||||
|
{
|
||||||
|
HeapFree(g_hHeap, 0, pThreads->pItems);
|
||||||
|
pThreads->pItems = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CloseHandle(hSnapshot);
|
||||||
|
}
|
||||||
|
|
||||||
|
return succeeded;
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
static MH_STATUS Freeze(PFROZEN_THREADS pThreads, UINT pos, UINT action)
|
||||||
|
{
|
||||||
|
MH_STATUS status = MH_OK;
|
||||||
|
|
||||||
|
pThreads->pItems = NULL;
|
||||||
|
pThreads->capacity = 0;
|
||||||
|
pThreads->size = 0;
|
||||||
|
if (!EnumerateThreads(pThreads))
|
||||||
|
{
|
||||||
|
status = MH_ERROR_MEMORY_ALLOC;
|
||||||
|
}
|
||||||
|
else 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
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;
|
||||||
|
status = Freeze(&threads, ALL_HOOKS_POS, enable ? ACTION_ENABLE : ACTION_DISABLE);
|
||||||
|
if (status == MH_OK)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
status = Freeze(&threads, pos, ACTION_DISABLE);
|
||||||
|
if (status == MH_OK)
|
||||||
|
{
|
||||||
|
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
|
||||||
|
{
|
||||||
|
UINT pos = FindHookEntry(pTarget);
|
||||||
|
if (pos != INVALID_HOOK_POS)
|
||||||
|
{
|
||||||
|
if (g_hooks.pItems[pos].isEnabled != enable)
|
||||||
|
{
|
||||||
|
FROZEN_THREADS threads;
|
||||||
|
status = Freeze(&threads, pos, ACTION_ENABLE);
|
||||||
|
if (status == MH_OK)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
status = Freeze(&threads, ALL_HOOKS_POS, ACTION_APPLY_QUEUED);
|
||||||
|
if (status == MH_OK)
|
||||||
|
{
|
||||||
|
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)";
|
||||||
|
}
|
||||||
@@ -0,0 +1,321 @@
|
|||||||
|
/*
|
||||||
|
* 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>
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) && !defined(MINHOOK_DISABLE_INTRINSICS)
|
||||||
|
#define ALLOW_INTRINSICS
|
||||||
|
#include <intrin.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#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 ALLOW_INTRINSICS
|
||||||
|
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 ALLOW_INTRINSICS
|
||||||
|
memcpy((LPBYTE)ct->pTrampoline + newPos, pCopySrc, copySize);
|
||||||
|
#else
|
||||||
|
__movsb((LPBYTE)ct->pTrampoline + newPos, (LPBYTE)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;
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
@@ -80,3 +80,7 @@ zlib (A Massively Spiffy Yet Delicately Unobtrusive Compression Library)
|
|||||||
zmqpp (C++ binding for 0mq/zmq is a 'high-level' library that hides most of the c-style interface core 0mq provides.)
|
zmqpp (C++ binding for 0mq/zmq is a 'high-level' library that hides most of the c-style interface core 0mq provides.)
|
||||||
https://github.com/zeromq/zmqpp
|
https://github.com/zeromq/zmqpp
|
||||||
Version: 3.2.0 17e9f6afa98f56ecac1e3f3eecbfc112357a6732
|
Version: 3.2.0 17e9f6afa98f56ecac1e3f3eecbfc112357a6732
|
||||||
|
|
||||||
|
MinHook (The Minimalistic x86/x64 API Hooking Library for Windows)
|
||||||
|
https://github.com/TsudaKageyu/minhook
|
||||||
|
Version: 0f25a2449b3cf878bcbdbf91b693c38149ecf029
|
||||||
@@ -8,7 +8,14 @@
|
|||||||
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
|
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
|
||||||
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
|
||||||
add_subdirectory(connection_patcher)
|
if (WIN32)
|
||||||
|
add_subdirectory(client_launcher)
|
||||||
|
add_subdirectory(client_patcher)
|
||||||
|
add_subdirectory(connection_patcher)
|
||||||
|
else()
|
||||||
|
add_subdirectory(connection_patcher)
|
||||||
|
endif()
|
||||||
|
|
||||||
add_subdirectory(map_extractor)
|
add_subdirectory(map_extractor)
|
||||||
add_subdirectory(vmap4_assembler)
|
add_subdirectory(vmap4_assembler)
|
||||||
add_subdirectory(vmap4_extractor)
|
add_subdirectory(vmap4_extractor)
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
# This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
|
||||||
|
#
|
||||||
|
# This file is free software; as a special exception the author gives
|
||||||
|
# unlimited permission to copy and/or distribute it, with or without
|
||||||
|
# modifications, as long as this notice is preserved.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful, but
|
||||||
|
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
|
||||||
|
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
|
||||||
|
CollectSourceFiles(${CMAKE_CURRENT_SOURCE_DIR} PRIVATE_SOURCES)
|
||||||
|
|
||||||
|
list(APPEND PRIVATE_SOURCES ${sources_windows})
|
||||||
|
|
||||||
|
GroupSources(${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
|
||||||
|
if(PLATFORM EQUAL 64)
|
||||||
|
set(target_name client_launcher_64)
|
||||||
|
else()
|
||||||
|
set(target_name client_launcher_32)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_executable(${target_name} ${PRIVATE_SOURCES})
|
||||||
|
|
||||||
|
target_link_libraries(${target_name}
|
||||||
|
PRIVATE
|
||||||
|
trinity-core-interface
|
||||||
|
PUBLIC
|
||||||
|
common
|
||||||
|
)
|
||||||
|
|
||||||
|
install(TARGETS ${target_name} DESTINATION "${CMAKE_INSTALL_PREFIX}")
|
||||||
@@ -0,0 +1,165 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Helper.hpp"
|
||||||
|
#include "IoContext.h"
|
||||||
|
|
||||||
|
#include <boost/algorithm/string.hpp>
|
||||||
|
#include <boost/asio.hpp>
|
||||||
|
#include <boost/asio/ssl.hpp>
|
||||||
|
#include <boost/beast.hpp>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <SHA256.h>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <Util.h>
|
||||||
|
|
||||||
|
namespace asio = boost::asio;
|
||||||
|
namespace ssl = asio::ssl;
|
||||||
|
namespace http = boost::beast::http;
|
||||||
|
|
||||||
|
namespace ClientLauncher
|
||||||
|
{
|
||||||
|
namespace Helper
|
||||||
|
{
|
||||||
|
std::string GetFileChecksum(std::vector<unsigned char> const& data)
|
||||||
|
{
|
||||||
|
SHA256Hash h;
|
||||||
|
h.UpdateData(data.data(), data.size());
|
||||||
|
h.Finalize();
|
||||||
|
|
||||||
|
std::string checksum = ByteArrayToHexStr(h.GetDigest(), h.GetLength());
|
||||||
|
boost::algorithm::to_lower(checksum);
|
||||||
|
return checksum;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<unsigned char> ReadFile(boost::filesystem::path const& path)
|
||||||
|
{
|
||||||
|
std::ifstream ifs(path.string(), std::ifstream::binary);
|
||||||
|
if (!ifs)
|
||||||
|
throw std::runtime_error("could not open " + path.string());
|
||||||
|
|
||||||
|
std::vector<unsigned char> binary;
|
||||||
|
ifs >> std::noskipws;
|
||||||
|
ifs.seekg(0, std::ios_base::end);
|
||||||
|
binary.reserve(ifs.tellg());
|
||||||
|
ifs.seekg(0, std::ios_base::beg);
|
||||||
|
|
||||||
|
std::copy(std::istream_iterator<unsigned char>(ifs), std::istream_iterator<unsigned char>(), std::back_inserter(binary));
|
||||||
|
|
||||||
|
return binary;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetFile(std::string const& url, std::ostream& out)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Trinity::Asio::IoContext io_context;
|
||||||
|
ssl::context ssl_context(ssl::context::tlsv12_client);
|
||||||
|
|
||||||
|
ssl_context.set_verify_mode(ssl::verify_peer);
|
||||||
|
ssl_context.set_default_verify_paths();
|
||||||
|
ssl_context.set_verify_mode(ssl::verify_none);
|
||||||
|
|
||||||
|
ssl::stream<asio::ip::tcp::socket> socket(io_context, ssl_context);
|
||||||
|
asio::ip::tcp::resolver resolver(io_context);
|
||||||
|
|
||||||
|
auto const host = url.substr(url.find("://") + 3, url.find('/', 8) - (url.find("://") + 3));
|
||||||
|
auto const port = "443";
|
||||||
|
auto const target = url.substr(url.find('/', 8));
|
||||||
|
|
||||||
|
asio::connect(socket.next_layer(), resolver.resolve(host, port));
|
||||||
|
socket.handshake(ssl::stream_base::client);
|
||||||
|
|
||||||
|
http::request<http::string_body> request(http::verb::get, target, 11);
|
||||||
|
request.set(http::field::host, host);
|
||||||
|
request.set(http::field::user_agent, "TCPP ClientLauncher");
|
||||||
|
|
||||||
|
http::write(socket, request);
|
||||||
|
|
||||||
|
boost::beast::flat_buffer buffer;
|
||||||
|
http::response<http::string_body> response;
|
||||||
|
http::read(socket, buffer, response);
|
||||||
|
|
||||||
|
out << response.body();
|
||||||
|
}
|
||||||
|
catch (std::exception& e)
|
||||||
|
{
|
||||||
|
std::cerr << "ERROR: " << e.what() << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VerifyRequirements(const boost::filesystem::path& commonAppData)
|
||||||
|
{
|
||||||
|
std::vector<std::string> requiredFiles
|
||||||
|
{
|
||||||
|
{"047329d08d433da2622e9fc2ee96c8dd8f35e7770699d07cf74855b9c8ea9125.pfty"},
|
||||||
|
{"0a3afee2cade3a0e8b458c4b4660104cac7fc50e2ca9bef0d708942e77f15c1d.auth"},
|
||||||
|
{"0a3f6f3f0535ea21dbe620085192afc796203047e270e0e1b76f15a739fe1797.pfty"},
|
||||||
|
{"2753d31092f1978bdd78ebd4fae2d189364ad7108ceb22fbf1413be1f43bef04.pfty"},
|
||||||
|
{"305bbdab1953e65974a249e276867e13ad2c3cabca3668983cb5ed406251bb7b.pfty"},
|
||||||
|
{"36b27cd911b33c61730a8b82c8b2495fd16e8024fc3b2dde08861c77a852941c.auth"},
|
||||||
|
{"3e381d4f83201f4e3c482eb74da12e5ff9dd924da2413d8fb33f5eea9a02c2c2.pfty"},
|
||||||
|
{"5813f318f7e40a07a7cdfeeec9827942e6fdc5ccee0d4171148443e429ad0ead.pfty"},
|
||||||
|
{"5e298e530698af905e1247e51ef0b109b352ac310ce7802a1f63613db980ed17.auth"},
|
||||||
|
{"63bc118937e6ea2faa7b7192676daeb1b7ca87a9c24ed9f5acd60e630b4dd7a4.auth"},
|
||||||
|
{"7466b2db3f03768aa2527535d4b3c6c9ef9e8fb07c6db88b1019f3d25a2942e8.pfty"},
|
||||||
|
{"83663d54444eadad40d43725e59bde8eda10276e76fc3c4e6f2ca56332ee8f03.pfty"},
|
||||||
|
{"870f53d10b4e1b09d6b622cd5671ba4ff1ad69512dfa2c676072c52e45c7f0f9.pfty"},
|
||||||
|
{"8c43bda10be33a32abbc09fb2279126c7f5953336391276cff588565332fcd40.auth"},
|
||||||
|
{"8f52906a2c85b416a595702251570f96d3522f39237603115f2f1ab24962043c.auth"},
|
||||||
|
{"97eeb2e28e9e56ed6a22d09f44e2ff43c93315e006bbad43bafc0defaa6f50ae.auth"},
|
||||||
|
{"a2ec4b41148214037a2e89a2e557af716d085241b81f5244494bdc77a891ca38.pfty"},
|
||||||
|
{"a8c77051991b1a6c5dfe412e9f46d8f584349996fbde37c4f2a527c192163502.pfty"},
|
||||||
|
{"b72e65b6b34d8f859e79b5f28952e26553a796d5e1d75c2b5930bc0daeaa728c.pfty"},
|
||||||
|
{"c3a1ac0694979e709c3b5486927e558af1e2be02ca96e5615c5a65aacc829226.auth"},
|
||||||
|
{"e8b82becbe0a0a1bbb5561df69320edbd770897a8deaab23caa6736255e0dc33.pfty"}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::cout << "Validating required module files...\n";
|
||||||
|
|
||||||
|
for (uint32 i = 0; i < requiredFiles.size(); i++)
|
||||||
|
{
|
||||||
|
boost::filesystem::path modulePath
|
||||||
|
(commonAppData / std::string(&requiredFiles[i][0], 2) / std::string(&requiredFiles[i][2], 2));
|
||||||
|
boost::filesystem::path module(modulePath / requiredFiles[i]);
|
||||||
|
|
||||||
|
if (!boost::filesystem::exists(module.make_preferred()))
|
||||||
|
{
|
||||||
|
std::cout << requiredFiles[i] << " doesn't exist, downloading it...\n";
|
||||||
|
|
||||||
|
if (!boost::filesystem::exists(modulePath.make_preferred()))
|
||||||
|
boost::filesystem::create_directories(modulePath.make_preferred());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (module.make_preferred().stem().string().compare(Helper::GetFileChecksum(Helper::ReadFile(module.make_preferred()))) == 0)
|
||||||
|
continue;
|
||||||
|
else
|
||||||
|
std::cout << requiredFiles[i] << " is corrupted! Redownloading it...\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ofstream outFile(module.string(), std::ofstream::out | std::ofstream::binary);
|
||||||
|
GetFile("https://raw.githubusercontent.com/The-Cataclysm-Preservation-Project/AuthModules/master/Windows/ProgramData/Blizzard%20Entertainment/Battle.net/Cache/" +
|
||||||
|
std::string(&requiredFiles[i][0], 2) + "/" + std::string(&requiredFiles[i][2], 2) + "/" + requiredFiles[i], outFile);
|
||||||
|
outFile.close();
|
||||||
|
}
|
||||||
|
std::cout << "Done.\n";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef Helper_h__
|
||||||
|
#define Helper_h__
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <boost/filesystem.hpp>
|
||||||
|
|
||||||
|
namespace ClientLauncher
|
||||||
|
{
|
||||||
|
namespace Helper
|
||||||
|
{
|
||||||
|
std::string GetFileChecksum(std::vector<unsigned char> const& data);
|
||||||
|
std::vector<unsigned char> ReadFile(boost::filesystem::path const& path);
|
||||||
|
void GetFile(std::string const& url, std::ostream& out);
|
||||||
|
bool VerifyRequirements(const boost::filesystem::path& commonAppData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // Helper_h__
|
||||||
@@ -0,0 +1,198 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Patcher.hpp"
|
||||||
|
#include "Helper.hpp"
|
||||||
|
#include "Patterns/Common.hpp"
|
||||||
|
|
||||||
|
#include <boost/filesystem.hpp>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <iterator>
|
||||||
|
#include <set>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <winternl.h>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
struct NtdllFunctions
|
||||||
|
{
|
||||||
|
decltype(&::NtQueryInformationProcess) NtQueryInformationProcess;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void AssignHelper(T& func, FARPROC proc) { func = (T)proc; }
|
||||||
|
|
||||||
|
std::unique_ptr<NtdllFunctions> GetNtFunctions()
|
||||||
|
{
|
||||||
|
HMODULE ntdll = GetModuleHandleA("ntdll.dll");
|
||||||
|
|
||||||
|
std::unique_ptr<NtdllFunctions> functions = std::make_unique<NtdllFunctions>();
|
||||||
|
AssignHelper(functions->NtQueryInformationProcess, GetProcAddress(ntdll, "NtQueryInformationProcess"));
|
||||||
|
return functions;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::set<size_t> SearchOffset(std::vector<unsigned char> const& binary, std::vector<unsigned char> const& pattern)
|
||||||
|
{
|
||||||
|
std::set<size_t> offsets;
|
||||||
|
for (size_t i = 0; (i + pattern.size()) < binary.size(); i++)
|
||||||
|
{
|
||||||
|
size_t matches = 0;
|
||||||
|
|
||||||
|
for (size_t j = 0; j < pattern.size(); j++)
|
||||||
|
{
|
||||||
|
if (pattern[j] == 0)
|
||||||
|
{
|
||||||
|
matches++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (binary[i + j] != pattern[j])
|
||||||
|
break;
|
||||||
|
|
||||||
|
matches++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matches == pattern.size())
|
||||||
|
{
|
||||||
|
offsets.insert(i);
|
||||||
|
i += matches;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return offsets.empty() ? throw std::exception("SearchOffset | ERROR: unable to find pattern") : offsets;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace ClientLauncher
|
||||||
|
{
|
||||||
|
void FilePatcher::WriteFile(boost::filesystem::path const& path, std::vector<unsigned char> const& data)
|
||||||
|
{
|
||||||
|
std::ofstream ofs(path.string(), std::ofstream::binary);
|
||||||
|
if (!ofs)
|
||||||
|
throw std::runtime_error("could not open " + path.string());
|
||||||
|
|
||||||
|
ofs << std::noskipws;
|
||||||
|
|
||||||
|
std::copy(data.begin(), data.end(), std::ostream_iterator<unsigned char>(ofs));
|
||||||
|
}
|
||||||
|
|
||||||
|
void FilePatcher::Patch(std::vector<unsigned char> const& bytes, std::vector<unsigned char> const& pattern)
|
||||||
|
{
|
||||||
|
if (_binary.size() < pattern.size())
|
||||||
|
throw std::logic_error("pattern larger than binary");
|
||||||
|
|
||||||
|
if (pattern.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (size_t const offset : SearchOffset(_binary, pattern))
|
||||||
|
{
|
||||||
|
std::cout << "Found offset " << offset << "\n";
|
||||||
|
|
||||||
|
if (offset != 0 && _binary.size() >= bytes.size())
|
||||||
|
for (size_t i = 0; i < bytes.size(); i++)
|
||||||
|
_binary[offset + i] = bytes[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FilePatcher::Finish(boost::filesystem::path out)
|
||||||
|
{
|
||||||
|
if (boost::filesystem::exists(out))
|
||||||
|
boost::filesystem::remove(out);
|
||||||
|
|
||||||
|
WriteFile(out, _binary);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<unsigned char> MemoryPatcher::GetProcessMemory(HANDLE process)
|
||||||
|
{
|
||||||
|
std::unique_ptr<NtdllFunctions> ntdll = GetNtFunctions();
|
||||||
|
std::vector<uint8_t> binaryData;
|
||||||
|
ULONG dummy;
|
||||||
|
|
||||||
|
PROCESS_BASIC_INFORMATION pbi;
|
||||||
|
if (!NT_SUCCESS(ntdll->NtQueryInformationProcess(process, ProcessBasicInformation, &pbi, sizeof(pbi), &dummy)))
|
||||||
|
return binaryData;
|
||||||
|
|
||||||
|
#ifdef _M_IX86
|
||||||
|
uint32_t imageBase;
|
||||||
|
#else
|
||||||
|
uint64_t imageBase;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!ReadProcessMemory(process, &pbi.PebBaseAddress->Reserved3[1], &imageBase, sizeof(imageBase), nullptr))
|
||||||
|
return binaryData;
|
||||||
|
|
||||||
|
MEMORY_BASIC_INFORMATION mbi;
|
||||||
|
|
||||||
|
_baseAddress = imageBase;
|
||||||
|
|
||||||
|
#ifdef _M_IX86
|
||||||
|
uint32_t baseAddr = imageBase;
|
||||||
|
#else
|
||||||
|
uint64_t baseAddr = imageBase;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (!VirtualQueryEx(process, (void*)baseAddr, &mbi, sizeof(mbi)))
|
||||||
|
throw std::exception("MemoryPatcher::GetProcessMemory | ERROR: VirtualQueryEx failed.");
|
||||||
|
|
||||||
|
if (mbi.AllocationBase == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
std::vector<BYTE> temp(mbi.RegionSize);
|
||||||
|
if (!ReadProcessMemory(process, mbi.BaseAddress, temp.data(), mbi.RegionSize, nullptr))
|
||||||
|
throw std::exception("MemoryPatcher::GetProcessMemory | ERROR: ReadProcessMemory failed.");
|
||||||
|
binaryData.insert(binaryData.end(), temp.begin(), temp.end());
|
||||||
|
baseAddr += mbi.RegionSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
return binaryData;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MemoryPatcher::Patch(std::vector<unsigned char> const& bytes, std::vector<unsigned char> const& pattern)
|
||||||
|
{
|
||||||
|
if (_binary.size() < pattern.size())
|
||||||
|
throw std::logic_error("MemoryPatcher::GetProcessMemory | ERROR: pattern larger than binary");
|
||||||
|
|
||||||
|
if (pattern.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (size_t const offset : SearchOffset(_binary, pattern))
|
||||||
|
{
|
||||||
|
std::cout << "Found offset " << offset << "\n";
|
||||||
|
|
||||||
|
if (offset != 0 && _binary.size() >= bytes.size())
|
||||||
|
{
|
||||||
|
DWORD oldprotect;
|
||||||
|
VirtualProtectEx(_processHandle, (void*)(_baseAddress + offset), bytes.size(), PAGE_EXECUTE_READWRITE, &oldprotect);
|
||||||
|
WriteProcessMemory(_processHandle, (void*)(_baseAddress + offset), bytes.data(), bytes.size(), nullptr);
|
||||||
|
VirtualProtectEx(_processHandle, (void*)(_baseAddress + offset), bytes.size(), oldprotect, &oldprotect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MemoryPatcher::IsSupportedBuild()
|
||||||
|
{
|
||||||
|
std::set<size_t> offsets = SearchOffset(_binary, Patterns::Common::BinaryVersionCheck());
|
||||||
|
|
||||||
|
if (!offsets.empty())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef Patcher_h__
|
||||||
|
#define Patcher_h__
|
||||||
|
|
||||||
|
#include <boost/filesystem.hpp>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <string>
|
||||||
|
#include "Helper.hpp"
|
||||||
|
|
||||||
|
namespace ClientLauncher
|
||||||
|
{
|
||||||
|
class Patcher
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Patcher(std::vector<unsigned char> binary)
|
||||||
|
: _binary(binary)
|
||||||
|
{}
|
||||||
|
|
||||||
|
virtual void Patch(std::vector<unsigned char> const& bytes, std::vector<unsigned char> const& pattern) = 0;
|
||||||
|
std::vector<unsigned char> const& GetBinary() const { return _binary; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::vector<unsigned char> _binary;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FilePatcher : public Patcher
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FilePatcher(boost::filesystem::path file)
|
||||||
|
: Patcher(Helper::ReadFile(file))
|
||||||
|
{}
|
||||||
|
|
||||||
|
void Patch(std::vector<unsigned char> const& bytes, std::vector<unsigned char> const& pattern);
|
||||||
|
void Finish(boost::filesystem::path out);
|
||||||
|
|
||||||
|
void WriteFile(boost::filesystem::path const& path, std::vector<unsigned char> const& data);
|
||||||
|
};
|
||||||
|
|
||||||
|
class MemoryPatcher : public Patcher
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MemoryPatcher(HANDLE process)
|
||||||
|
: Patcher(GetProcessMemory(process)), _processHandle(process)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void Patch(std::vector<unsigned char> const& bytes, std::vector<unsigned char> const& pattern);
|
||||||
|
std::vector<unsigned char> GetProcessMemory(HANDLE process);
|
||||||
|
bool IsSupportedBuild();
|
||||||
|
protected:
|
||||||
|
HANDLE _processHandle;
|
||||||
|
#ifdef _M_IX86
|
||||||
|
uint32_t _baseAddress;
|
||||||
|
#else
|
||||||
|
uint64_t _baseAddress;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // Patcher_h__
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ClientLauncherPatchesCommon_h__
|
||||||
|
#define ClientLauncherPatchesCommon_h__
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace ClientLauncher
|
||||||
|
{
|
||||||
|
namespace Patches
|
||||||
|
{
|
||||||
|
struct Common
|
||||||
|
{
|
||||||
|
static const std::vector<unsigned char> Modulus()
|
||||||
|
{
|
||||||
|
return
|
||||||
|
{
|
||||||
|
0x5F, 0xD6, 0x80, 0x0B, 0xA7, 0xFF, 0x01, 0x40, 0xC7, 0xBC, 0x8E, 0xF5, 0x6B, 0x27, 0xB0, 0xBF,
|
||||||
|
0xF0, 0x1D, 0x1B, 0xFE, 0xDD, 0x0B, 0x1F, 0x3D, 0xB6, 0x6F, 0x1A, 0x48, 0x0D, 0xFB, 0x51, 0x08,
|
||||||
|
0x65, 0x58, 0x4F, 0xDB, 0x5C, 0x6E, 0xCF, 0x64, 0xCB, 0xC1, 0x6B, 0x2E, 0xB8, 0x0F, 0x5D, 0x08,
|
||||||
|
0x5D, 0x89, 0x06, 0xA9, 0x77, 0x8B, 0x9E, 0xAA, 0x04, 0xB0, 0x83, 0x10, 0xE2, 0x15, 0x4D, 0x08,
|
||||||
|
0x77, 0xD4, 0x7A, 0x0E, 0x5A, 0xB0, 0xBB, 0x00, 0x61, 0xD7, 0xA6, 0x75, 0xDF, 0x06, 0x64, 0x88,
|
||||||
|
0xBB, 0xB9, 0xCA, 0xB0, 0x18, 0x8B, 0x54, 0x13, 0xE2, 0xCB, 0x33, 0xDF, 0x17, 0xD8, 0xDA, 0xA9,
|
||||||
|
0xA5, 0x60, 0xA3, 0x1F, 0x4E, 0x27, 0x05, 0x98, 0x6F, 0xAA, 0xEE, 0x14, 0x3B, 0xF3, 0x97, 0xA8,
|
||||||
|
0x12, 0x02, 0x94, 0x0D, 0x84, 0xDC, 0x0E, 0xF1, 0x76, 0x23, 0x95, 0x36, 0x13, 0xF9, 0xA9, 0xC5,
|
||||||
|
0x48, 0xDB, 0xDA, 0x86, 0xBE, 0x29, 0x22, 0x54, 0x44, 0x9D, 0x9F, 0x80, 0x7B, 0x07, 0x80, 0x30,
|
||||||
|
0xEA, 0xD2, 0x83, 0xCC, 0xCE, 0x37, 0xD1, 0xD1, 0xCF, 0x85, 0xBE, 0x91, 0x25, 0xCE, 0xC0, 0xCC,
|
||||||
|
0x55, 0xC8, 0xC0, 0xFB, 0x38, 0xC5, 0x49, 0x03, 0x6A, 0x02, 0xA9, 0x9F, 0x9F, 0x86, 0xFB, 0xC7,
|
||||||
|
0xCB, 0xC6, 0xA5, 0x82, 0xA2, 0x30, 0xC2, 0xAC, 0xE6, 0x98, 0xDA, 0x83, 0x64, 0x43, 0x7F, 0x0D,
|
||||||
|
0x13, 0x18, 0xEB, 0x90, 0x53, 0x5B, 0x37, 0x6B, 0xE6, 0x0D, 0x80, 0x1E, 0xEF, 0xED, 0xC7, 0xB8,
|
||||||
|
0x68, 0x9B, 0x4C, 0x09, 0x7B, 0x60, 0xB2, 0x57, 0xD8, 0x59, 0x8D, 0x7F, 0xEA, 0xCD, 0xEB, 0xC4,
|
||||||
|
0x60, 0x9F, 0x45, 0x7A, 0xA9, 0x26, 0x8A, 0x2F, 0x85, 0x0C, 0xF2, 0x19, 0xC6, 0x53, 0x92, 0xF7,
|
||||||
|
0xF0, 0xB8, 0x32, 0xCB, 0x5B, 0x66, 0xCE, 0x51, 0x54, 0xB4, 0xC3, 0xD3, 0xD4, 0xDC, 0xB3, 0xEE
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //ClientLauncherPatchesCommon_h__
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ClientLauncherPatchesWindows_h__
|
||||||
|
#define ClientLauncherPatchesWindows_h__
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace ClientLauncher
|
||||||
|
{
|
||||||
|
namespace Patches
|
||||||
|
{
|
||||||
|
namespace Windows
|
||||||
|
{
|
||||||
|
struct x86
|
||||||
|
{
|
||||||
|
static const std::vector<unsigned char> Password() { return { 0x75 }; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct x64
|
||||||
|
{
|
||||||
|
static const std::vector<unsigned char> Password() { return { 0x75 }; }
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // ClientLauncherPatchesWindows_h__
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ClientLauncherPatternsCommon_h__
|
||||||
|
#define ClientLauncherPatternsCommon_h__
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace ClientLauncher
|
||||||
|
{
|
||||||
|
namespace Patterns
|
||||||
|
{
|
||||||
|
struct Common
|
||||||
|
{
|
||||||
|
static const std::vector<unsigned char> Modulus() { return { 0x91, 0xD5, 0x9B, 0xB7, 0xD4, 0xE1, 0x83, 0xA5 }; }
|
||||||
|
static const std::vector<unsigned char> BinaryVersionCheck() { return{ 0x57, 0x4F, 0x57, 0x43, 0x4F, 0x4D, 0x53, 0x41, 0x54, 0x43, 0x4C, 0x49, 0x45, 0x4E, 0x54, 0x31, 0x35, 0x35, 0x39, 0x35 }; }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // ClientLauncherPatternsCommon_h__
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ClientLauncherPatternsWindows_h__
|
||||||
|
#define ClientLauncherPatternsWindows_h__
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace ClientLauncher
|
||||||
|
{
|
||||||
|
namespace Patterns
|
||||||
|
{
|
||||||
|
namespace Windows
|
||||||
|
{
|
||||||
|
struct x86
|
||||||
|
{
|
||||||
|
static const std::vector<unsigned char> Password() { return { 0x74, 0x89, 0x8B, 0x16, 0x8B, 0x42, 0x04 }; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct x64
|
||||||
|
{
|
||||||
|
static const std::vector<unsigned char> Password() { return { 0x74, 0x84, 0x48, 0x8B, 0x03 }; }
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // ClientLauncherPatternsWindows_h__
|
||||||
@@ -0,0 +1,270 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Banner.h"
|
||||||
|
#include "Helper.hpp"
|
||||||
|
#include "Windows.h"
|
||||||
|
#include "Patcher.hpp"
|
||||||
|
#include "Patches/Common.hpp"
|
||||||
|
#include "Patches/Windows.hpp"
|
||||||
|
#include "Patterns/Common.hpp"
|
||||||
|
#include "Patterns/Windows.hpp"
|
||||||
|
|
||||||
|
#include <boost/filesystem.hpp>
|
||||||
|
#include <boost/program_options.hpp>
|
||||||
|
#include <iostream>
|
||||||
|
#include <Shlobj.h>
|
||||||
|
|
||||||
|
namespace po = boost::program_options;
|
||||||
|
|
||||||
|
namespace ClientLauncher
|
||||||
|
{
|
||||||
|
template<typename PATCH, typename PATTERN>
|
||||||
|
void do_module(const std::string& moduleName, const boost::filesystem::path& path)
|
||||||
|
{
|
||||||
|
namespace fs = boost::filesystem;
|
||||||
|
|
||||||
|
fs::path const modulePath
|
||||||
|
(path / std::string(&moduleName[0], 2) / std::string(&moduleName[2], 2));
|
||||||
|
fs::path const module(modulePath / moduleName);
|
||||||
|
|
||||||
|
std::cout << "Patching module...\n";
|
||||||
|
|
||||||
|
FilePatcher patcher(module);
|
||||||
|
|
||||||
|
std::cout << "patching Password\n";
|
||||||
|
// if (Authentication::ServerSignature::ClientValidateProof(x)) to if (true)
|
||||||
|
patcher.Patch(PATCH::Password(), PATTERN::Password());
|
||||||
|
|
||||||
|
std::string const newModuleName(Helper::GetFileChecksum(patcher.GetBinary()) + ".auth");
|
||||||
|
fs::path const newModulePath
|
||||||
|
(path / std::string(&newModuleName[0], 2) / std::string(&newModuleName[2], 2));
|
||||||
|
|
||||||
|
if (!fs::exists(newModulePath))
|
||||||
|
fs::create_directories(newModulePath);
|
||||||
|
|
||||||
|
if (fs::exists(newModulePath / newModuleName))
|
||||||
|
fs::permissions(newModulePath / newModuleName, fs::add_perms | fs::others_write | fs::group_write | fs::owner_write);
|
||||||
|
patcher.Finish(newModulePath / newModuleName);
|
||||||
|
fs::permissions(newModulePath / newModuleName, fs::remove_perms | fs::others_write | fs::group_write | fs::owner_write);
|
||||||
|
|
||||||
|
std::cout << "Patching module finished.\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename PATCH, typename PATTERN>
|
||||||
|
void do_client_patches(Patcher* patcher)
|
||||||
|
{
|
||||||
|
std::cout << "Patching client memory...\n";
|
||||||
|
|
||||||
|
std::cout << "patching redirect RSA Modulus\n";
|
||||||
|
patcher->Patch(Patches::Common::Modulus(), Patterns::Common::Modulus());
|
||||||
|
|
||||||
|
std::cout << "Patching done.\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LaunchAndInject(boost::filesystem::path binaryDir, bool patcherConsole, std::vector<std::string> const& args)
|
||||||
|
{
|
||||||
|
HANDLE processHandle = INVALID_HANDLE_VALUE;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
#ifdef _M_IX86
|
||||||
|
char const* executable = "Wow.exe";
|
||||||
|
char const* dllName = "client_patcher_32.dll";
|
||||||
|
#else
|
||||||
|
char const* executable = "Wow-64.exe";
|
||||||
|
char const* dllName = "client_patcher_64.dll";
|
||||||
|
#endif
|
||||||
|
std::string gameInstallDir = binaryDir.string();
|
||||||
|
|
||||||
|
std::cout << "Injecting " << dllName << " into " << executable << " process..\n";
|
||||||
|
|
||||||
|
char commandLine[32768] = {};
|
||||||
|
strcat(commandLine, gameInstallDir.c_str());
|
||||||
|
strcat(commandLine, "\\");
|
||||||
|
strcat(commandLine, executable);
|
||||||
|
for (std::string const& arg : args)
|
||||||
|
{
|
||||||
|
strcat(commandLine, " ");
|
||||||
|
strcat(commandLine, arg.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
char selfName[MAX_PATH] = {};
|
||||||
|
GetModuleFileNameA(nullptr, selfName, sizeof(selfName));
|
||||||
|
|
||||||
|
std::string dllPath = (boost::filesystem::path(selfName).remove_filename() / dllName).string();
|
||||||
|
|
||||||
|
if (!boost::filesystem::exists(dllPath))
|
||||||
|
throw std::exception("Launcher::LaunchAndInject | ERROR: couldn't find client_patcher dll!");
|
||||||
|
|
||||||
|
STARTUPINFOA startupInfo{ sizeof(STARTUPINFOA) };
|
||||||
|
PROCESS_INFORMATION processInfo;
|
||||||
|
if (!CreateProcessA(nullptr, commandLine, nullptr, nullptr, TRUE, CREATE_SUSPENDED, nullptr, gameInstallDir.c_str(), &startupInfo, &processInfo))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
processHandle = processInfo.hProcess;
|
||||||
|
|
||||||
|
MemoryPatcher patcher(processInfo.hProcess);
|
||||||
|
|
||||||
|
if (!patcher.IsSupportedBuild())
|
||||||
|
throw std::exception("Launcher::LaunchAndInject | ERROR: Client version different than 4.3.4.15595!");
|
||||||
|
|
||||||
|
#ifdef _M_IX86
|
||||||
|
do_client_patches<Patches::Windows::x86, Patterns::Windows::x86>(&patcher);
|
||||||
|
#else
|
||||||
|
do_client_patches<Patches::Windows::x64, Patterns::Windows::x64>(&patcher);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ResumeThread(processInfo.hThread);
|
||||||
|
|
||||||
|
HMODULE hModule = GetModuleHandle("kernel32.dll");
|
||||||
|
if (!hModule)
|
||||||
|
throw std::exception("Launcher::LaunchAndInject | ERROR: couldn't get kernel32.dll handle!");
|
||||||
|
|
||||||
|
FARPROC loadLibA = GetProcAddress(hModule, "LoadLibraryA");
|
||||||
|
if (!loadLibA)
|
||||||
|
throw std::exception("Launcher::LaunchAndInject | ERROR: couldn't get LoadLibraryA ProcAddress");
|
||||||
|
|
||||||
|
LPVOID allocatedMem = VirtualAllocEx(processInfo.hProcess, nullptr, dllPath.length() + 1, (MEM_RESERVE | MEM_COMMIT), PAGE_READWRITE);
|
||||||
|
if (!allocatedMem)
|
||||||
|
{
|
||||||
|
CloseHandle(processInfo.hProcess);
|
||||||
|
throw std::exception("Launcher::LaunchAndInject | ERROR: VirtualAllocEx failed. Errorcode " + GetLastError());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!WriteProcessMemory(processInfo.hProcess, allocatedMem, dllPath.c_str(), dllPath.length() + 1, nullptr))
|
||||||
|
{
|
||||||
|
VirtualFreeEx(processInfo.hProcess, allocatedMem, 0, MEM_RELEASE);
|
||||||
|
CloseHandle(processInfo.hProcess);
|
||||||
|
throw std::exception("Launcher::LaunchAndInject | ERROR: WriteProcessMemory failed. Errorcode " + GetLastError());
|
||||||
|
}
|
||||||
|
WaitForInputIdle(processInfo.hProcess, INFINITE);
|
||||||
|
|
||||||
|
HANDLE hRemoteThread = CreateRemoteThread(processInfo.hProcess, nullptr, CREATE_SUSPENDED, (LPTHREAD_START_ROUTINE)loadLibA, allocatedMem, 0, nullptr);
|
||||||
|
if (!hRemoteThread)
|
||||||
|
{
|
||||||
|
VirtualFreeEx(processInfo.hProcess, allocatedMem, 0, MEM_RELEASE);
|
||||||
|
CloseHandle(processInfo.hProcess);
|
||||||
|
throw std::exception("Launcher::LaunchAndInject | ERROR: CreateRemoteThread failed. Errorcode " + GetLastError());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (patcherConsole)
|
||||||
|
{
|
||||||
|
HRESULT hr = SetThreadDescription(hRemoteThread, L"console");
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
std::cout << "Launcher::LaunchAndInject | Warning: SetThreadDescription failed\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WaitForSingleObject(hRemoteThread, INFINITE);
|
||||||
|
|
||||||
|
VirtualFreeEx(processInfo.hProcess, allocatedMem, 0, MEM_RELEASE);
|
||||||
|
CloseHandle(hRemoteThread);
|
||||||
|
CloseHandle(processInfo.hProcess);
|
||||||
|
CloseHandle(processHandle);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (const std::exception& e)
|
||||||
|
{
|
||||||
|
std::cerr << "ERROR: " << e.what() << std::endl;
|
||||||
|
if (processHandle != INVALID_HANDLE_VALUE)
|
||||||
|
TerminateProcess(processHandle, 1);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<po::variables_map, std::vector<std::string>> GetConsoleArguments(int argc, char** argv)
|
||||||
|
{
|
||||||
|
po::options_description all("Allowed options");
|
||||||
|
all.add_options()
|
||||||
|
("help,h", "print usage message")
|
||||||
|
("path", po::value<std::string>()->default_value(boost::filesystem::current_path().string()), "Path to the Wow.exe")
|
||||||
|
("console", "Open patcher console")
|
||||||
|
;
|
||||||
|
|
||||||
|
po::positional_options_description pos;
|
||||||
|
pos.add("path", 1);
|
||||||
|
|
||||||
|
po::variables_map vm;
|
||||||
|
std::vector<std::string> unrecognized;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
po::basic_parsed_options<char> parsed = po::command_line_parser(argc, argv).options(all).positional(pos).allow_unregistered().run();
|
||||||
|
po::store(parsed, vm);
|
||||||
|
unrecognized = po::collect_unrecognized(parsed.options, po::exclude_positional);
|
||||||
|
po::notify(vm);
|
||||||
|
}
|
||||||
|
catch (std::exception& e)
|
||||||
|
{
|
||||||
|
std::cerr << e.what() << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vm.count("help"))
|
||||||
|
std::cout << all << "\n";
|
||||||
|
|
||||||
|
return std::make_pair(std::move(vm), std::move(unrecognized));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
using namespace ClientLauncher;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Trinity::Banner::Show("client_launcher", [](char const* text) { std::cout << text << std::endl; }, nullptr);
|
||||||
|
|
||||||
|
auto vm = GetConsoleArguments(argc, argv);
|
||||||
|
|
||||||
|
// exit if help is enabled
|
||||||
|
if (vm.first.count("help"))
|
||||||
|
{
|
||||||
|
std::cin.get();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool console = false;
|
||||||
|
if (vm.first.count("console"))
|
||||||
|
console = true;
|
||||||
|
|
||||||
|
wchar_t* commonAppData(nullptr);
|
||||||
|
SHGetKnownFolderPath(FOLDERID_ProgramData, 0, nullptr, &commonAppData);
|
||||||
|
|
||||||
|
if (!Helper::VerifyRequirements(std::wstring(commonAppData) + std::wstring(L"/Blizzard Entertainment/Battle.net/Cache/")))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
#ifdef _M_IX86
|
||||||
|
do_module<Patches::Windows::x86, Patterns::Windows::x86>("8f52906a2c85b416a595702251570f96d3522f39237603115f2f1ab24962043c.auth", std::wstring(commonAppData) + std::wstring(L"/Blizzard Entertainment/Battle.net/Cache/"));
|
||||||
|
#else
|
||||||
|
do_module<Patches::Windows::x64, Patterns::Windows::x64>("0a3afee2cade3a0e8b458c4b4660104cac7fc50e2ca9bef0d708942e77f15c1d.auth", std::wstring(commonAppData) + std::wstring(L"/Blizzard Entertainment/Battle.net/Cache/"));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
boost::filesystem::path binary_path(vm.first["path"].as<std::string>());
|
||||||
|
|
||||||
|
if (!LaunchAndInject(binary_path, console, vm.second))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
catch (std::exception const& ex)
|
||||||
|
{
|
||||||
|
std::cerr << "EX: " << ex.what() << std::endl;
|
||||||
|
std::cerr << "An error occurred. Press ENTER to continue...";
|
||||||
|
std::cin.get();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
|
||||||
|
#
|
||||||
|
# This file is free software; as a special exception the author gives
|
||||||
|
# unlimited permission to copy and/or distribute it, with or without
|
||||||
|
# modifications, as long as this notice is preserved.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful, but
|
||||||
|
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
|
||||||
|
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
|
||||||
|
CollectSourceFiles(${CMAKE_CURRENT_SOURCE_DIR} PRIVATE_SOURCES)
|
||||||
|
|
||||||
|
GroupSources(${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
|
||||||
|
if(PLATFORM EQUAL 64)
|
||||||
|
set(target_name client_patcher_64)
|
||||||
|
else()
|
||||||
|
set(target_name client_patcher_32)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_library(${target_name} SHARED ${PRIVATE_SOURCES})
|
||||||
|
|
||||||
|
target_link_libraries(${target_name}
|
||||||
|
PRIVATE
|
||||||
|
trinity-core-interface
|
||||||
|
PUBLIC
|
||||||
|
common
|
||||||
|
minhook
|
||||||
|
)
|
||||||
|
|
||||||
|
install(TARGETS ${target_name} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}")
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Helper.hpp"
|
||||||
|
#include "Patterns/Common.hpp"
|
||||||
|
#include <string>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
namespace ClientPatcher
|
||||||
|
{
|
||||||
|
namespace Helper
|
||||||
|
{
|
||||||
|
std::set<size_t> SearchOffset(std::vector<uint8> const& binary, std::vector<uint8> const& pattern)
|
||||||
|
{
|
||||||
|
std::set<size_t> offsets;
|
||||||
|
for (size_t i = 0; (i + pattern.size()) < binary.size(); i++)
|
||||||
|
{
|
||||||
|
size_t matches = 0;
|
||||||
|
|
||||||
|
for (size_t j = 0; j < pattern.size(); j++)
|
||||||
|
{
|
||||||
|
if (pattern[j] == 0)
|
||||||
|
{
|
||||||
|
matches++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (binary[i + j] != pattern[j])
|
||||||
|
break;
|
||||||
|
|
||||||
|
matches++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matches == pattern.size())
|
||||||
|
{
|
||||||
|
offsets.insert(i);
|
||||||
|
i += matches;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return offsets.empty() ? throw std::runtime_error("unable to find pattern") : offsets;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef Helper_h__
|
||||||
|
#define Helper_h__
|
||||||
|
|
||||||
|
#include "Define.h"
|
||||||
|
#include <set>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace ClientPatcher
|
||||||
|
{
|
||||||
|
namespace Helper
|
||||||
|
{
|
||||||
|
std::set<size_t> SearchOffset(std::vector<uint8> const& binary, std::vector<uint8> const& pattern);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // Helper_h__
|
||||||
@@ -0,0 +1,185 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#include <string>
|
||||||
|
#include <atlstr.h>
|
||||||
|
#include "MinHook.h"
|
||||||
|
#include <iostream>
|
||||||
|
#include <Banner.h>
|
||||||
|
|
||||||
|
#include "Helper.hpp"
|
||||||
|
#include "Patcher.hpp"
|
||||||
|
#include "Patches/Common.hpp"
|
||||||
|
#include "Patches/Windows.hpp"
|
||||||
|
#include "Patterns/Common.hpp"
|
||||||
|
#include "Patterns/Windows.hpp"
|
||||||
|
|
||||||
|
HINSTANCE DllHandle;
|
||||||
|
bool DllPatched = false;
|
||||||
|
FILE* fp;
|
||||||
|
|
||||||
|
namespace ClientPatcher
|
||||||
|
{
|
||||||
|
template<typename PATCH, typename PATTERN>
|
||||||
|
void do_dll_patches(Patcher* patcher)
|
||||||
|
{
|
||||||
|
std::cout << "Patching battle.net binary...\n";
|
||||||
|
|
||||||
|
std::cout << "patching Portal\n";
|
||||||
|
patcher->Patch(Patches::Common::Portal(), Patterns::Common::Portal());
|
||||||
|
|
||||||
|
std::cout << "patching Bnet\n";
|
||||||
|
patcher->Patch(PATCH::BNet(), PATTERN::BNet());
|
||||||
|
|
||||||
|
std::cout << "patching Signature\n";
|
||||||
|
patcher->Patch(PATCH::Signature(), PATTERN::Signature());
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef HMODULE(WINAPI* TYPE_LoadLibraryW)(LPCWSTR);
|
||||||
|
TYPE_LoadLibraryW g_loadLibraryW_original = NULL;
|
||||||
|
HMODULE WINAPI LoadLibraryW_replacement(LPCWSTR lpLibFileName)
|
||||||
|
{
|
||||||
|
HMODULE dllHandle = g_loadLibraryW_original(lpLibFileName);
|
||||||
|
|
||||||
|
if (lstrcmpiW(lpLibFileName, L"Battle.net.dll") == 0 || lstrcmpiW(lpLibFileName, L"Battle.net-64.dll") == 0)
|
||||||
|
{
|
||||||
|
std::cout << "Detected Battle.net.dll\n";
|
||||||
|
std::cout << "Start patching...\n";
|
||||||
|
|
||||||
|
MEMORY_BASIC_INFORMATION mbi;
|
||||||
|
std::vector<BYTE> binaryData;
|
||||||
|
|
||||||
|
if (!VirtualQuery(dllHandle, &mbi, sizeof(mbi)))
|
||||||
|
return dllHandle;
|
||||||
|
|
||||||
|
#ifdef _M_IX86
|
||||||
|
uint32_t baseAddr = reinterpret_cast<uint32_t>(dllHandle);;
|
||||||
|
#else
|
||||||
|
uint64_t baseAddr = reinterpret_cast<uint64_t>(dllHandle);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (!VirtualQuery((void*)baseAddr, &mbi, sizeof(mbi)))
|
||||||
|
return dllHandle;
|
||||||
|
|
||||||
|
if (mbi.AllocationBase == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
std::vector<BYTE> temp(mbi.RegionSize);
|
||||||
|
if (!ReadProcessMemory(GetCurrentProcess(), mbi.BaseAddress, temp.data(), mbi.RegionSize, nullptr))
|
||||||
|
return dllHandle;
|
||||||
|
|
||||||
|
binaryData.insert(binaryData.end(), temp.begin(), temp.end());
|
||||||
|
baseAddr += mbi.RegionSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
Patcher battleNetPatcher(binaryData, GetCurrentProcess(), dllHandle);
|
||||||
|
|
||||||
|
#ifdef _M_IX86
|
||||||
|
do_dll_patches<Patches::Windows::x86, Patterns::Windows::x86>(&battleNetPatcher);
|
||||||
|
#else
|
||||||
|
do_dll_patches<Patches::Windows::x64, Patterns::Windows::x64>(&battleNetPatcher);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
DllPatched = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dllHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD __stdcall EjectThread(LPVOID /*lpParameter*/) {
|
||||||
|
Sleep(100);
|
||||||
|
FreeLibraryAndExitThread(DllHandle, 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void shutdown(std::string reason) {
|
||||||
|
|
||||||
|
MH_Uninitialize();
|
||||||
|
std::cout << reason << std::endl;
|
||||||
|
Sleep(1000);
|
||||||
|
if (fp != nullptr)
|
||||||
|
fclose(fp);
|
||||||
|
FreeConsole();
|
||||||
|
CreateThread(0, 0, EjectThread, 0, 0, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HookThread(HINSTANCE /*hModule*/)
|
||||||
|
{
|
||||||
|
Trinity::Banner::Show("client_patcher", [](char const* text) { std::cout << text << std::endl; }, nullptr);
|
||||||
|
|
||||||
|
std::cout << "Awaiting login to patch Battle.net...\n";
|
||||||
|
|
||||||
|
MH_STATUS status = MH_Initialize();
|
||||||
|
if (status != MH_OK)
|
||||||
|
{
|
||||||
|
std::string sStatus = MH_StatusToString(status);
|
||||||
|
shutdown("Minhook init failed!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (MH_CreateHook(&LoadLibraryW, &LoadLibraryW_replacement, (LPVOID*)(&g_loadLibraryW_original)) != MH_OK)
|
||||||
|
{
|
||||||
|
shutdown("CreateHook failed!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (MH_EnableHook(&LoadLibraryW) != MH_OK)
|
||||||
|
{
|
||||||
|
shutdown("EnableHook failed!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!DllPatched) {
|
||||||
|
Sleep(50);
|
||||||
|
}
|
||||||
|
|
||||||
|
shutdown("Done!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
__declspec(dllexport) BOOL __stdcall DllMain(HINSTANCE dllInstance, DWORD reason, void* /*reserved*/)
|
||||||
|
{
|
||||||
|
switch (reason)
|
||||||
|
{
|
||||||
|
case DLL_PROCESS_ATTACH:
|
||||||
|
{
|
||||||
|
wchar_t* threadDesc;
|
||||||
|
HRESULT hr = GetThreadDescription(GetCurrentThread(), &threadDesc);
|
||||||
|
|
||||||
|
if (FAILED(hr) || wcscmp(threadDesc, L"console") == 0)
|
||||||
|
{
|
||||||
|
LocalFree(threadDesc);
|
||||||
|
AllocConsole();
|
||||||
|
freopen_s(&fp, "CONOUT$", "w", stdout);
|
||||||
|
|
||||||
|
if (FAILED(hr))
|
||||||
|
std::cout << "GetThreadDescription failed!";
|
||||||
|
}
|
||||||
|
|
||||||
|
DllHandle = dllInstance;
|
||||||
|
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ClientPatcher::HookThread, NULL, 0, NULL);
|
||||||
|
}
|
||||||
|
case DLL_THREAD_ATTACH:
|
||||||
|
case DLL_THREAD_DETACH:
|
||||||
|
case DLL_PROCESS_DETACH:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Patcher.hpp"
|
||||||
|
#include "Helper.hpp"
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace ClientPatcher
|
||||||
|
{
|
||||||
|
Patcher::Patcher(std::vector<uint8> const& data, HANDLE process, HMODULE module)
|
||||||
|
: processHandle(process), moduleHandle(module), binary(data.begin(), data.end())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Patcher::Patch(std::vector<uint8> const& bytes, std::vector<uint8> const& pattern)
|
||||||
|
{
|
||||||
|
if (binary.size() < pattern.size())
|
||||||
|
throw std::logic_error("pattern larger than binary");
|
||||||
|
|
||||||
|
if (pattern.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (size_t const offset : Helper::SearchOffset(binary, pattern))
|
||||||
|
{
|
||||||
|
std::cout << "Found offset " << offset << "\n";
|
||||||
|
|
||||||
|
if (offset != 0 && binary.size() >= bytes.size())
|
||||||
|
{
|
||||||
|
#ifdef _M_IX86
|
||||||
|
uint32_t baseAddr = reinterpret_cast<uint32_t>(moduleHandle);
|
||||||
|
#else
|
||||||
|
uint64_t baseAddr = reinterpret_cast<uint64_t>(moduleHandle);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
DWORD oldprotect;
|
||||||
|
VirtualProtectEx(processHandle, (void*)(baseAddr + offset), bytes.size(), PAGE_EXECUTE_READWRITE, &oldprotect);
|
||||||
|
memcpy((void*)(baseAddr + offset), bytes.data(), bytes.size());
|
||||||
|
VirtualProtectEx(processHandle, (void*)(baseAddr + offset), bytes.size(), oldprotect, &oldprotect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef Patcher_h__
|
||||||
|
#define Patcher_h__
|
||||||
|
|
||||||
|
#include "Define.h"
|
||||||
|
#include <vector>
|
||||||
|
#include <Windows.h>
|
||||||
|
|
||||||
|
namespace ClientPatcher
|
||||||
|
{
|
||||||
|
class Patcher
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Patcher(std::vector<uint8> const& data, HANDLE process, HMODULE module);
|
||||||
|
|
||||||
|
void Patch(std::vector<uint8> const& bytes, std::vector<uint8> const& pattern);
|
||||||
|
std::vector<uint8> const& GetBinary() const { return binary; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
HANDLE processHandle;
|
||||||
|
HMODULE moduleHandle;
|
||||||
|
std::vector<uint8> binary;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // Patcher_h__
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ClientPatcherPatchesCommon_h__
|
||||||
|
#define ClientPatcherPatchesCommon_h__
|
||||||
|
|
||||||
|
#include "Define.h"
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace ClientPatcher
|
||||||
|
{
|
||||||
|
namespace Patches
|
||||||
|
{
|
||||||
|
struct Common
|
||||||
|
{
|
||||||
|
static std::vector<uint8> Portal() { return { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // ClientPatcherPatchesCommon_h__
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ClientPatcherPatchesWindows_h__
|
||||||
|
#define ClientPatcherPatchesWindows_h__
|
||||||
|
|
||||||
|
#include "Define.h"
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace ClientPatcher
|
||||||
|
{
|
||||||
|
namespace Patches
|
||||||
|
{
|
||||||
|
namespace Windows
|
||||||
|
{
|
||||||
|
struct x86
|
||||||
|
{
|
||||||
|
static std::vector<unsigned char> BNet() { return { 0x89, 0x48, 0x08, 0xC7, 0x40, 0x0C, 0xD5, 0xF8, 0x7F, 0x82, 0x90 }; }
|
||||||
|
static std::vector<unsigned char> Signature() { return { 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0xE9 }; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct x64
|
||||||
|
{
|
||||||
|
static std::vector<unsigned char> BNet() { return { 0xB8, 0xD5, 0xF8, 0x7F, 0x82, 0x89, 0x41, 0x0C, 0x48, 0x8B, 0xC1, 0xC3 }; }
|
||||||
|
static std::vector<unsigned char> Signature() { return { 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0xE9 }; }
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // ClientPatcherPatchesWindows_h__
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ClientPatcherPatternsCommon_h__
|
||||||
|
#define ClientPatcherPatternsCommon_h__
|
||||||
|
|
||||||
|
#include "Define.h"
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace ClientPatcher
|
||||||
|
{
|
||||||
|
namespace Patterns
|
||||||
|
{
|
||||||
|
struct Common
|
||||||
|
{
|
||||||
|
static std::vector<uint8> Portal() { return { '.', 'l', 'o', 'g', 'o', 'n', '.', 'b', 'a', 't', 't', 'l', 'e', '.', 'n', 'e', 't' }; }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // ClientPatcherPatternsCommon_h__
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ClientPatcherPatternsWindows_h__
|
||||||
|
#define ClientPatcherPatternsWindows_h__
|
||||||
|
|
||||||
|
#include "Define.h"
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace ClientPatcher
|
||||||
|
{
|
||||||
|
namespace Patterns
|
||||||
|
{
|
||||||
|
namespace Windows
|
||||||
|
{
|
||||||
|
struct x86
|
||||||
|
{
|
||||||
|
static std::vector<unsigned char> BNet() { return { 0x8B, 0x55, 0x08, 0x89, 0x48, 0x08 }; }
|
||||||
|
static std::vector<unsigned char> Signature() { return { 0xE8, 0x00, 0x00, 0x00, 0x00, 0x84, 0xC0, 0x0F, 0x085, 0x00, 0x00, 0x00, 0x00, 0x8B, 0x85, 0xF0, 0xBD, 0xFF, 0xFF }; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct x64
|
||||||
|
{
|
||||||
|
static std::vector<unsigned char> BNet() { return { 0x8B, 0x02, 0x89, 0x41, 0x0C, 0x48, 0x8B, 0xC1, 0xC3, 0xCC, 0xCC, 0xCC }; }
|
||||||
|
static std::vector<unsigned char> Signature() { return { 0xE8, 0x00, 0x00, 0x00, 0x00, 0x84, 0xC0, 0x0F, 0x85, 0x00, 0x00, 0x00, 0x00, 0x45, 0x33, 0xC0 }; }
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // ClientPatcherPatternsWindows_h__
|
||||||
Reference in new issue
Block a user