From 109501550e3f0cbcccbf5534da4cf936cca8dd86 Mon Sep 17 00:00:00 2001 From: doomertheboomer Date: Wed, 26 Jul 2023 17:47:00 +0700 Subject: [PATCH] init commit, hooks ported from spicetools --- .gitignore | 5 + README.md | 6 + depthrush.sln | 31 + depthrush/depthrush.vcxproj | 193 ++++ depthrush/depthrush.vcxproj.filters | 112 +++ depthrush/depthrush.vcxproj.user | 6 + depthrush/drs.cpp | 166 ++++ depthrush/drs.h | 43 + depthrush/includes.h | 13 + depthrush/kiero/injector/assembly.hpp | 178 ++++ depthrush/kiero/injector/calling.hpp | 127 +++ depthrush/kiero/injector/gvm/gvm.hpp | 125 +++ depthrush/kiero/injector/gvm/translator.hpp | 203 ++++ depthrush/kiero/injector/hooking.hpp | 687 ++++++++++++++ depthrush/kiero/injector/injector.hpp | 661 +++++++++++++ depthrush/kiero/injector/utility.hpp | 56 ++ depthrush/kiero/kiero.cpp | 720 ++++++++++++++ depthrush/kiero/kiero.h | 78 ++ .../kiero/minhook/dll_resources/MinHook.def | 14 + .../kiero/minhook/dll_resources/MinHook.rc | 32 + depthrush/kiero/minhook/include/MinHook.h | 186 ++++ depthrush/kiero/minhook/src/buffer.c | 312 ++++++ depthrush/kiero/minhook/src/buffer.h | 42 + depthrush/kiero/minhook/src/hde/hde32.c | 326 +++++++ depthrush/kiero/minhook/src/hde/hde32.h | 105 +++ depthrush/kiero/minhook/src/hde/hde64.c | 337 +++++++ depthrush/kiero/minhook/src/hde/hde64.h | 112 +++ depthrush/kiero/minhook/src/hde/pstdint.h | 39 + depthrush/kiero/minhook/src/hde/table32.h | 73 ++ depthrush/kiero/minhook/src/hde/table64.h | 74 ++ depthrush/kiero/minhook/src/hook.c | 889 ++++++++++++++++++ depthrush/kiero/minhook/src/trampoline.c | 316 +++++++ depthrush/kiero/minhook/src/trampoline.h | 105 +++ depthrush/main.cpp | 53 ++ 34 files changed, 6425 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 depthrush.sln create mode 100644 depthrush/depthrush.vcxproj create mode 100644 depthrush/depthrush.vcxproj.filters create mode 100644 depthrush/depthrush.vcxproj.user create mode 100644 depthrush/drs.cpp create mode 100644 depthrush/drs.h create mode 100644 depthrush/includes.h create mode 100644 depthrush/kiero/injector/assembly.hpp create mode 100644 depthrush/kiero/injector/calling.hpp create mode 100644 depthrush/kiero/injector/gvm/gvm.hpp create mode 100644 depthrush/kiero/injector/gvm/translator.hpp create mode 100644 depthrush/kiero/injector/hooking.hpp create mode 100644 depthrush/kiero/injector/injector.hpp create mode 100644 depthrush/kiero/injector/utility.hpp create mode 100644 depthrush/kiero/kiero.cpp create mode 100644 depthrush/kiero/kiero.h create mode 100644 depthrush/kiero/minhook/dll_resources/MinHook.def create mode 100644 depthrush/kiero/minhook/dll_resources/MinHook.rc create mode 100644 depthrush/kiero/minhook/include/MinHook.h create mode 100644 depthrush/kiero/minhook/src/buffer.c create mode 100644 depthrush/kiero/minhook/src/buffer.h create mode 100644 depthrush/kiero/minhook/src/hde/hde32.c create mode 100644 depthrush/kiero/minhook/src/hde/hde32.h create mode 100644 depthrush/kiero/minhook/src/hde/hde64.c create mode 100644 depthrush/kiero/minhook/src/hde/hde64.h create mode 100644 depthrush/kiero/minhook/src/hde/pstdint.h create mode 100644 depthrush/kiero/minhook/src/hde/table32.h create mode 100644 depthrush/kiero/minhook/src/hde/table64.h create mode 100644 depthrush/kiero/minhook/src/hook.c create mode 100644 depthrush/kiero/minhook/src/trampoline.c create mode 100644 depthrush/kiero/minhook/src/trampoline.h create mode 100644 depthrush/main.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..33de78e --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.vs/ +x64/ +depthrush/x64/ +Release/ +depthrush/Release/ \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..5e4b2ab --- /dev/null +++ b/README.md @@ -0,0 +1,6 @@ +# depthrush +Work in progress Kinect driver for Dancerush Stardom. + +## Installation +1. Patch SpiceTools to disable all DRS pad hooks. +2. Copy `depthrush.dll` to the same folder as spice64 and use spicecfg to inject depthrush. diff --git a/depthrush.sln b/depthrush.sln new file mode 100644 index 0000000..9cbc62f --- /dev/null +++ b/depthrush.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.2.32616.157 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "depthrush", "depthrush\depthrush.vcxproj", "{04B17470-CB82-4724-904B-25445926AB86}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {04B17470-CB82-4724-904B-25445926AB86}.Debug|x64.ActiveCfg = Debug|x64 + {04B17470-CB82-4724-904B-25445926AB86}.Debug|x64.Build.0 = Debug|x64 + {04B17470-CB82-4724-904B-25445926AB86}.Debug|x86.ActiveCfg = Debug|Win32 + {04B17470-CB82-4724-904B-25445926AB86}.Debug|x86.Build.0 = Debug|Win32 + {04B17470-CB82-4724-904B-25445926AB86}.Release|x64.ActiveCfg = Release|x64 + {04B17470-CB82-4724-904B-25445926AB86}.Release|x64.Build.0 = Release|x64 + {04B17470-CB82-4724-904B-25445926AB86}.Release|x86.ActiveCfg = Release|Win32 + {04B17470-CB82-4724-904B-25445926AB86}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {A99829DB-1A71-44FC-BA06-638D3412F1BC} + EndGlobalSection +EndGlobal diff --git a/depthrush/depthrush.vcxproj b/depthrush/depthrush.vcxproj new file mode 100644 index 0000000..2b858a7 --- /dev/null +++ b/depthrush/depthrush.vcxproj @@ -0,0 +1,193 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + {04B17470-CB82-4724-904B-25445926AB86} + ImGuiDirectX11KieroHook + 10.0 + depthrush + + + + DynamicLibrary + true + v143 + MultiByte + + + DynamicLibrary + false + v143 + true + MultiByte + + + DynamicLibrary + true + v143 + MultiByte + + + DynamicLibrary + false + v143 + true + MultiByte + + + + + + + + + + + + + + + + + + + + + C:\Program Files %28x86%29\Microsoft DirectX SDK %28June 2010%29\Lib\x86;$(LibraryPath) + C:\Program Files %28x86%29\Microsoft DirectX SDK %28June 2010%29\Include;$(IncludePath) + + + C:\Program Files %28x86%29\Microsoft DirectX SDK %28June 2010%29\Lib\x86;$(LibraryPath) + C:\Program Files %28x86%29\Microsoft DirectX SDK %28June 2010%29\Include;$(IncludePath) + + + C:\Program Files %28x86%29\Microsoft DirectX SDK %28June 2010%29\Lib\x64;$(LibraryPath) + C:\Program Files %28x86%29\Microsoft DirectX SDK %28June 2010%29\Include;$(IncludePath) + + + C:\Program Files %28x86%29\Microsoft DirectX SDK %28June 2010%29\Lib\x64;$(LibraryPath) + C:\Program Files %28x86%29\Microsoft DirectX SDK %28June 2010%29\Include;$(IncludePath) + + + + Level3 + Disabled + true + true + + + Console + d3d11.lib;dxgi.lib;%(AdditionalDependencies) + + + + + Level3 + Disabled + true + true + Disabled + Speed + false + + + Console + d3d11.lib;dxgi.lib;Ws2_32.lib;%(AdditionalDependencies) + + + + + Level3 + MaxSpeed + true + true + true + true + + + Console + true + true + d3d11.lib;dxgi.lib;%(AdditionalDependencies) + + + + + Level3 + Disabled + true + false + true + true + stdcpp20 + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + Disabled + Speed + false + false + + + Console + true + true + d3d11.lib;dxgi.lib;Ws2_32.lib;%(AdditionalDependencies) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/depthrush/depthrush.vcxproj.filters b/depthrush/depthrush.vcxproj.filters new file mode 100644 index 0000000..e55c07d --- /dev/null +++ b/depthrush/depthrush.vcxproj.filters @@ -0,0 +1,112 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {c914abae-6128-4a2a-8821-07a38f656930} + + + + + kiero + + + kiero + + + kiero + + + kiero + + + kiero + + + kiero + + + Source Files + + + Source Files + + + + + Resource Files + + + + + kiero + + + kiero + + + kiero + + + kiero + + + kiero + + + kiero + + + kiero + + + kiero + + + kiero + + + Header Files + + + kiero + + + kiero + + + kiero + + + kiero + + + kiero + + + kiero + + + kiero + + + Header Files + + + + + kiero + + + \ No newline at end of file diff --git a/depthrush/depthrush.vcxproj.user b/depthrush/depthrush.vcxproj.user new file mode 100644 index 0000000..5df420f --- /dev/null +++ b/depthrush/depthrush.vcxproj.user @@ -0,0 +1,6 @@ + + + + false + + \ No newline at end of file diff --git a/depthrush/drs.cpp b/depthrush/drs.cpp new file mode 100644 index 0000000..439c998 --- /dev/null +++ b/depthrush/drs.cpp @@ -0,0 +1,166 @@ +#include "drs.h" +#include "includes.h" + +#include +#include + +#pragma pack(push) + +typedef struct { + union { + struct { + WORD unk1; + WORD unk2; + WORD device_id; + WORD vid; + WORD pid; + WORD pvn; + WORD max_point_num; + }; + uint8_t raw[2356]; + }; +} dev_info_t; + +typedef struct { + DWORD cid; + DWORD type; + DWORD unused; + DWORD y; + DWORD x; + DWORD height; + DWORD width; + DWORD unk8; +} touch_data_t; + +#pragma pack(pop) + +enum touch_type { + TS_DOWN = 1, + TS_MOVE = 2, + TS_UP = 3, +}; + +void *user_data = nullptr; +void (*touch_callback)( + dev_info_t *dev_info, + const touch_data_t *touch_data, + int touch_points, + int unk1, + const void *user_data); + +namespace drs { + + void* TouchSDK_Constructor(void* in) { + return in; + } + + bool TouchSDK_SendData(dev_info_t*, + unsigned char* const, int, unsigned char* const, + unsigned char* output, int output_size) { + + // fake success + if (output_size >= 4) { + output[0] = 0xfc; + output[1] = 0xa5; + } + return true; + } + + bool TouchSDK_SetSignalInit(dev_info_t*, int) { + return true; + } + + void TouchSDK_Destructor(void* This) { + } + + int TouchSDK_GetYLedTotal(dev_info_t*, int) { + return 53; + } + + int TouchSDK_GetXLedTotal(dev_info_t*, int) { + return 41; + } + + bool TouchSDK_DisableTouch(dev_info_t*, int) { + return true; + } + + bool TouchSDK_DisableDrag(dev_info_t*, int) { + return true; + } + + bool TouchSDK_DisableWheel(dev_info_t*, int) { + return true; + } + + bool TouchSDK_DisableRightClick(dev_info_t*, int) { + return true; + } + + bool TouchSDK_SetMultiTouchMode(dev_info_t*, int) { + return true; + } + + bool TouchSDK_EnableTouchWidthData(dev_info_t*, int) { + return true; + } + + bool TouchSDK_EnableRawData(dev_info_t*, int) { + return true; + } + + bool TouchSDK_SetAllEnable(dev_info_t*, bool, int) { + return true; + } + + int TouchSDK_GetTouchDeviceCount(void* This) { + return 1; + } + + unsigned int TouchSDK_GetTouchSDKVersion(void) { + return 0x01030307; + } + + int TouchSDK_InitTouch(void* This, dev_info_t* devices, int max_devices, void* touch_event_cb, + void* hotplug_callback, void* userdata) { + + // fake touch device + memset(devices, 0, sizeof(devices[0].raw)); + devices[0].unk1 = 0x1122; + devices[0].unk2 = 0x3344; + devices[0].device_id = 0; + devices[0].vid = 0xDEAD; + devices[0].pid = 0xBEEF; + devices[0].pvn = 0xC0DE; + devices[0].max_point_num = 16; + + // remember provided callback and userdata + touch_callback = (decltype(touch_callback))touch_event_cb; + user_data = userdata; + + // success + return 1; + } + + void hookDancepad() { + MH_Initialize(); + MH_CreateHookApi(L"TouchSDKDll.dll", "??0TouchSDK@@QEAA@XZ", TouchSDK_Constructor, NULL); + MH_CreateHookApi(L"TouchSDKDll.dll", "?SendData@TouchSDK@@QEAA_NU_DeviceInfo@@QEAEH1HH@Z", TouchSDK_SendData, NULL); + MH_CreateHookApi(L"TouchSDKDll.dll", "?SetSignalInit@TouchSDK@@QEAA_NU_DeviceInfo@@H@Z", TouchSDK_SetSignalInit, NULL); + MH_CreateHookApi(L"TouchSDKDll.dll", "?GetYLedTotal@TouchSDK@@QEAAHU_DeviceInfo@@H@Z", TouchSDK_GetYLedTotal, NULL); + MH_CreateHookApi(L"TouchSDKDll.dll", "?GetXLedTotal@TouchSDK@@QEAAHU_DeviceInfo@@H@Z", TouchSDK_GetXLedTotal, NULL); + MH_CreateHookApi(L"TouchSDKDll.dll", "?DisableTouch@TouchSDK@@QEAA_NU_DeviceInfo@@H@Z", TouchSDK_DisableTouch, NULL); + MH_CreateHookApi(L"TouchSDKDll.dll", "?DisableDrag@TouchSDK@@QEAA_NU_DeviceInfo@@H@Z", TouchSDK_DisableDrag, NULL); + MH_CreateHookApi(L"TouchSDKDll.dll", "?DisableWheel@TouchSDK@@QEAA_NU_DeviceInfo@@H@Z", TouchSDK_DisableWheel, NULL); + MH_CreateHookApi(L"TouchSDKDll.dll", "?DisableRightClick@TouchSDK@@QEAA_NU_DeviceInfo@@H@Z", TouchSDK_DisableRightClick, NULL); + MH_CreateHookApi(L"TouchSDKDll.dll", "?SetMultiTouchMode@TouchSDK@@QEAA_NU_DeviceInfo@@H@Z", TouchSDK_SetMultiTouchMode, NULL); + MH_CreateHookApi(L"TouchSDKDll.dll", "?EnableTouchWidthData@TouchSDK@@QEAA_NU_DeviceInfo@@H@Z", TouchSDK_EnableTouchWidthData, NULL); + MH_CreateHookApi(L"TouchSDKDll.dll", "?EnableRawData@TouchSDK@@QEAA_NU_DeviceInfo@@H@Z", TouchSDK_EnableRawData, NULL); + MH_CreateHookApi(L"TouchSDKDll.dll", "?SetAllEnable@TouchSDK@@QEAA_NU_DeviceInfo@@_NH@Z", TouchSDK_SetAllEnable, NULL); + MH_CreateHookApi(L"TouchSDKDll.dll", "?GetTouchDeviceCount@TouchSDK@@QEAAHXZ", TouchSDK_GetTouchDeviceCount, NULL); + MH_CreateHookApi(L"TouchSDKDll.dll", "?GetTouchSDKVersion@TouchSDK@@QEAAIXZ", TouchSDK_GetTouchSDKVersion, NULL); + MH_CreateHookApi(L"TouchSDKDll.dll", "?InitTouch@TouchSDK@@QEAAHPEAU_DeviceInfo@@HP6AXU2@PEBU_TouchPointData@@HHPEBX@ZP6AX1_N3@ZPEAX@Z", TouchSDK_InitTouch, NULL); + + MH_EnableHook(MH_ALL_HOOKS); + } +} \ No newline at end of file diff --git a/depthrush/drs.h b/depthrush/drs.h new file mode 100644 index 0000000..719c4d6 --- /dev/null +++ b/depthrush/drs.h @@ -0,0 +1,43 @@ +#pragma once + +namespace drs { + + enum DRS_TOUCH_TYPE { + DRS_DOWN = 0, + DRS_UP = 1, + DRS_MOVE = 2, + }; + + typedef struct drs_touch_t { + int type = DRS_UP; + int id = 0; + double x = 0.0; + double y = 0.0; + double width = 1; + double height = 1; + }; + + struct VRFoot { + unsigned int id; + unsigned int index = 2; + float length = 3.1; + float size_base = 0.05; + float size_scale = 0.1; + float height = 3; + //linalg::aliases::float4 rotation {0, 0, 0, 1}; + drs_touch_t event{}; + //unsigned int get_index(); + //linalg::aliases::float3 to_world(linalg::aliases::float3 pos); + }; + + extern char DRS_TAPELED[38 * 49][3]; + //extern linalg::aliases::float3 VR_SCALE; + //extern linalg::aliases::float3 VR_OFFSET; + extern float VR_ROTATION; + extern VRFoot VR_FOOTS[2]; + + //void fire_touches(drs_touch_t* events, size_t event_count); + //void start_vr(); + + void hookDancepad(); +} diff --git a/depthrush/includes.h b/depthrush/includes.h new file mode 100644 index 0000000..39445d6 --- /dev/null +++ b/depthrush/includes.h @@ -0,0 +1,13 @@ +#pragma once +#include +#include +#include +#include +#include "kiero/kiero.h" +#include "kiero/minhook/include/MinHook.h" +#include "kiero/injector/injector.hpp" +#include "d3d9.h" + + +typedef LRESULT(CALLBACK* WNDPROC)(HWND, UINT, WPARAM, LPARAM); +typedef uintptr_t PTR; \ No newline at end of file diff --git a/depthrush/kiero/injector/assembly.hpp b/depthrush/kiero/injector/assembly.hpp new file mode 100644 index 0000000..622f57e --- /dev/null +++ b/depthrush/kiero/injector/assembly.hpp @@ -0,0 +1,178 @@ +/* + * Injectors - Useful Assembly Stuff + * + * Copyright (C) 2012-2014 LINK/2012 + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + * + */ +#pragma once + +// This header is very restrict about compiler and architecture +#ifndef _MSC_VER // MSVC is much more flexible when we're talking about inline assembly +#error Cannot use this header in another compiler other than MSVC +#endif +#ifndef _M_IX86 +#error Supported only in x86 +#endif + +// +#include "injector.hpp" + +namespace injector +{ + struct reg_pack + { + // The ordering is very important, don't change + // The first field is the last to be pushed and first to be poped + + // PUSHFD / POPFD + uint32_t ef; + + // PUSHAD/POPAD -- must be the lastest fields (because of esp) + union + { + uint32_t arr[8]; + struct { uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax; }; + }; + + enum reg_name { + reg_edi, reg_esi, reg_ebp, reg_esp, reg_ebx, reg_edx, reg_ecx, reg_eax + }; + + enum ef_flag { + carry_flag = 0, parity_flag = 2, adjust_flag = 4, zero_flag = 6, sign_flag = 7, + direction_flag = 10, overflow_flag = 11 + }; + + uint32_t& operator[](size_t i) + { return this->arr[i]; } + const uint32_t& operator[](size_t i) const + { return this->arr[i]; } + + template // bit starts from 0, use ef_flag enum + bool flag() + { + return (this->ef & (1 << bit)) != 0; + } + + bool jnb() + { + return flag() == false; + } + }; + + // Lowest level stuff (actual assembly) goes on the following namespace + // PRIVATE! Skip this, not interesting for you. + namespace injector_asm + { + // Wrapper functor, so the assembly can use some templating + template + struct wrapper + { + static void call(reg_pack* regs) + { + T fun; fun(*regs); + } + }; + + // Constructs a reg_pack and calls the wrapper functor + template // where W is of type wrapper + inline void __declspec(naked) make_reg_pack_and_call() + { + _asm + { + // Construct the reg_pack structure on the stack + pushad // Pushes general purposes registers to reg_pack + add [esp+12], 4 // Add 4 to reg_pack::esp 'cuz of our return pointer, let it be as before this func is called + pushfd // Pushes EFLAGS to reg_pack + + // Call wrapper sending reg_pack as parameter + push esp + call W::call + add esp, 4 + + // Destructs the reg_pack from the stack + sub [esp+12+4], 4 // Fix reg_pack::esp before popping it (doesn't make a difference though) (+4 because eflags) + popfd // Warning: Do not use any instruction that changes EFLAGS after this (-> sub affects EF!! <-) + popad + + // Back to normal flow + ret + } + } + }; + + + /* + * MakeInline + * Makes inline assembly (but not assembly, an actual functor of type FuncT) at address + */ + template + void MakeInline(memory_pointer_tr at) + { + typedef injector_asm::wrapper functor; + if(false) functor::call(nullptr); // To instantiate the template, if not done _asm will fail + MakeCALL(at, injector_asm::make_reg_pack_and_call); + } + + /* + * MakeInline + * Same as above, but it NOPs everything between at and end (exclusive), then performs MakeInline + */ + template + void MakeInline(memory_pointer_tr at, memory_pointer_tr end) + { + MakeRangedNOP(at, end); + MakeInline(at); + } + + /* + * MakeInline + * Same as above, but (at,end) are template parameters. + * On this case the functor can be passed as argument since there will be one func instance for each at,end not just for each FuncT + */ + template + void MakeInline(FuncT func) + { + static FuncT static_func = func; // Stores the func object + static_func = func; // + + // Encapsulates the call to static_func + struct Caps + { + void operator()(reg_pack& regs) + { static_func(regs); } + }; + + // Does the actual MakeInline + return MakeInline(lazy_pointer::get(), lazy_pointer::get()); + } + + /* + * MakeInline + * Same as above, but (end) is calculated by the length of a call instruction + */ + template + void MakeInline(FuncT func) + { + return MakeInline(func); + } +}; diff --git a/depthrush/kiero/injector/calling.hpp b/depthrush/kiero/injector/calling.hpp new file mode 100644 index 0000000..0a1dd62 --- /dev/null +++ b/depthrush/kiero/injector/calling.hpp @@ -0,0 +1,127 @@ +/* + * Injectors - Function Calls Using Variadic Templates + * + * Copyright (C) 2014 LINK/2012 + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + * + */ +#pragma once +#include "injector.hpp" +#include +#include + +#if __cplusplus >= 201103L || _MSC_VER >= 1800 // MSVC 2013 +#else +#error "This feature is not supported on this compiler" +#endif + +namespace injector +{ + template + struct cstd; + + template + struct cstd + { + // Call function at @p returning @Ret with args @Args + static Ret call(memory_pointer_tr p, Args... a) + { + auto fn = (Ret(*)(Args...)) p.get(); + return fn(std::forward(a)...); + } + + template // Uses lazy pointer + static Ret call(Args... a) + { + return call(lazy_pointer::get(), std::forward(a)...); + } + }; + + template + struct stdcall; + + template + struct stdcall + { + // Call function at @p returning @Ret with args @Args + static Ret call(memory_pointer_tr p, Args... a) + { + auto fn = (Ret(__stdcall *)(Args...)) p.get(); + return fn(std::forward(a)...); + } + + template // Uses lazy pointer + static Ret call(Args... a) + { + return call(lazy_pointer::get(), std::forward(a)...); + } + }; + + template + struct fastcall; + + template + struct fastcall + { + // Call function at @p returning @Ret with args @Args + static Ret call(memory_pointer_tr p, Args... a) + { + auto fn = (Ret(__fastcall *)(Args...)) p.get();; + return fn(std::forward(a)...); + } + + template // Uses lazy pointer + static Ret call(Args... a) + { + return call(lazy_pointer::get(), std::forward(a)...); + } + }; + + template + struct thiscall; + + template + struct thiscall + { + // Call function at @p returning @Ret with args @Args + static Ret call(memory_pointer_tr p, Args... a) + { + auto fn = (Ret(__thiscall *)(Args...)) p.get(); + return fn(std::forward(a)...); + } + + // Call function at the index @i from the vtable of the object @a[0] + template + static Ret vtbl(Args... a) + { + auto obj = raw_ptr(std::get<0>(std::forward_as_tuple(a...))); + auto p = raw_ptr( (*obj.template get()) [i] ); + return call(p, std::forward(a)...); + } + + template // Uses lazy pointer + static Ret call(Args... a) + { + return call(lazy_pointer::get(), std::forward(a)...); + } + }; +} + diff --git a/depthrush/kiero/injector/gvm/gvm.hpp b/depthrush/kiero/injector/gvm/gvm.hpp new file mode 100644 index 0000000..9c1a772 --- /dev/null +++ b/depthrush/kiero/injector/gvm/gvm.hpp @@ -0,0 +1,125 @@ +/* + * Injectors - Base Header + * + * Copyright (C) 2012-2014 LINK/2012 + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + * + */ +#pragma once + +#include +#include +#include + +namespace injector +{ + +#if 1 // GVM and Address Translator, Not very interesting for the users, so skip reading those... + +/* + * game_version_manager + * Detects the game, the game version and the game region + * This assumes the executable is decrypted, so, Silent's ASI Loader is recommended. + */ +#ifndef INJECTOR_OWN_GVM +class game_version_manager +{ + public: + bool Detect() { return true; } +}; +#endif // INJECTOR_OWN_GVM + + +/* + * address_manager + * Address translator from 1.0 executables to other executables offsets + * Inherits from game_version_manager ;) + */ +class address_manager : public game_version_manager +{ + private: + address_manager() + { + this->Detect(); + } + + // You could implement your translator for the address your plugin uses + // If not implemented, the translator won't translate anything, just return the samething as before + #ifdef INJECTOR_GVM_HAS_TRANSLATOR + void* translator(void* p); + #else + void* translator(void* p) { return p; } + #endif + + public: + // Translates address p to the running executable pointer + void* translate(void* p) + { + return translator(p); + } + + + public: + // Address manager singleton + static address_manager& singleton() + { + static address_manager m; + return m; + } + + // Static version of translate() + static void* translate_address(void* p) + { + return singleton().translate(p); + } + + public: + // Functors for memory translation: + + // Translates aslr translator + struct fn_mem_translator_aslr + { + void* operator()(void* p) const + { + static uintptr_t module = (uintptr_t)GetModuleHandle(NULL); + return (void*)((uintptr_t)(p)-(0x400000 - module)); + } + }; + + // Translates nothing translator + struct fn_mem_translator_nop + { + void* operator()(void* p) const + { return p; } + }; + + // Real translator + struct fn_mem_translator + { + void* operator()(void* p) const + { return translate_address(p); } + }; +}; + +#endif // #if 1 + + +} \ No newline at end of file diff --git a/depthrush/kiero/injector/gvm/translator.hpp b/depthrush/kiero/injector/gvm/translator.hpp new file mode 100644 index 0000000..c6ac430 --- /dev/null +++ b/depthrush/kiero/injector/gvm/translator.hpp @@ -0,0 +1,203 @@ +/* + * Injectors - Address Translation Management + * + * Copyright (C) 2014 LINK/2012 + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + * + */ +#pragma once + +#if !defined(INJECTOR_GVM_HAS_TRANSLATOR) +#error Missing INJECTOR_GVM_HAS_TRANSLATOR on compiler definitions +#endif + +/* + * This is a quick solution for address translations if you're too lazy to implement a proper address_manager::translator by yourself + * So, just call address_translator_manager::singleton().translate(p) from your address_manager::translator and that's it. + * It'll translate addresses based on 'address_translator' objects, when one gets constructed it turns into a possible translator. + * At the constructor of your derived 'address_translator' make the map object to have [addr_to_translate] = translated_addr; + * There's also the virtual method 'fallback' that will get called when the translation wasn't possible, you can do some fallback stuff here + * (such as return the pointer as is or output a error message) + */ + +#include "../injector.hpp" +#include +#include +#include + +namespace injector +{ + /* + * address_translator + * Base for an address translator + */ + class address_translator + { + private: + bool enabled; + void add(); + void remove(); + + protected: + friend class address_translator_manager; + std::map map; + + public: + address_translator() : enabled(true) + { + // Must have bounds filled with min ptr and max ptr to have search working properly + map.insert(std::make_pair(raw_ptr(0x00000000u), raw_ptr(0x00000000u))); + map.insert(std::make_pair(raw_ptr(0xffffffffu), raw_ptr(0xffffffffu))); + add(); + } + + ~address_translator() + { + remove(); + } + + virtual void* fallback(void*) const + { + return nullptr; + } + + + // Enables or disables this translator + void enable(bool enable_it) + { + if(enable_it) this->enable(); + else this->disable(); + } + + // Enables this translator + void enable() + { + this->enabled = true; + } + + // Disables this translator + void disable() + { + this->enabled = false; + } + + // Checks if this translator is enabled + bool is_enabled() const + { + return enabled; + } + }; + + /* + * address_translator_manager + * Manages the address_translator objects + */ + class address_translator_manager + { + protected: + friend class address_manager; + friend class address_translator; + + std::list translators; + + void add(const address_translator& t) + { + translators.push_front(&t); + } + + void remove(const address_translator& t) + { + translators.remove(&t); + } + + public: + // Translates the address p + void* translator(void* p); + + // Singleton object + static address_translator_manager& singleton() + { + static address_translator_manager mgr; + return mgr; + } + }; + + + + inline void* address_translator_manager::translator(void* p_) + { + static const size_t max_ptr_dist = 7; + + // Tries to find an address in a translator map + auto try_map = [](const std::map& map, memory_pointer_raw p) -> memory_pointer_raw + { + memory_pointer_raw result = nullptr; + + // Find first element in the map that is greater than or equal to p + auto it = map.lower_bound(p); + if(it != map.end()) + { + // If it's not exactly the address, get back one position on the table + if(it->first != p) --it; + + auto diff = (p - it->first).as_int(); // What's the difference between p and that address? + if(diff <= max_ptr_dist) // Could we live with this difference in hands? + result = it->second + raw_ptr(diff); // Yes, we can! + } + + return result; + }; + + + // + memory_pointer_raw result = nullptr; + + // Try to find translation for this pointer + auto& mgr = address_translator_manager::singleton().translators; + for(auto it = mgr.begin(); result == nullptr && it != mgr.end(); ++it) + { + auto& t = **it; + if(t.is_enabled()) result = try_map(t.map, p_); + } + + // If we couldn't translate the address, notify and try to fallback + if(result.is_null()) + { + for(auto it = mgr.begin(); result == nullptr && it != mgr.end(); ++it) + { + auto& t = **it; + if(t.is_enabled()) result = t.fallback(p_); + } + } + + return result.get(); + } + + inline void address_translator::add() + { + address_translator_manager::singleton().add(*this); + } + + inline void address_translator::remove() + { + address_translator_manager::singleton().remove(*this); + } +} diff --git a/depthrush/kiero/injector/hooking.hpp b/depthrush/kiero/injector/hooking.hpp new file mode 100644 index 0000000..ec7d797 --- /dev/null +++ b/depthrush/kiero/injector/hooking.hpp @@ -0,0 +1,687 @@ +/* + * Injectors - Classes for making your hooking life easy + * + * Copyright (C) 2013-2014 LINK/2012 + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + * + */ +#pragma once +#include "injector.hpp" +#include +#include +#include // for std::shared_ptr +#include + +namespace injector +{ + /* + * scoped_base + * Base for any scoped hooking type + * !!!! NOTICE !!!! --> Any derived which implements/reimplements restore() should implement a destructor calling it + */ + class scoped_base + { + public: + virtual ~scoped_base() {} + virtual void restore() = 0; + }; + + /* + * scoped_basic + * Base for scoped types which will need a buffer to save/restore stuff + */ + template // TODO specialize bufsize=0 to be dynamic + class scoped_basic : public scoped_base + { + private: + uint8_t buf[bufsize];// Saved content + memory_pointer_raw addr; // Data saved from this address + size_t size; // Size saved + bool saved; // Something saved? + bool vp; // Virtual protect? + + public: + + static const bool is_dynamic = false; + + // Restore the previosly saved data + // Problems may arise if someone else hooked the same place using the same method + virtual void restore() + { + #ifndef INJECTOR_SCOPED_NOSAVE_NORESTORE + if(this->saved) + { + WriteMemoryRaw(this->addr, this->buf, this->size, this->vp); + this->saved = false; + } + #endif + } + + // Save buffer at @addr with @size and virtual protect @vp + virtual void save(memory_pointer_tr addr, size_t size, bool vp) + { + #ifndef INJECTOR_SCOPED_NOSAVE_NORESTORE + assert(size <= bufsize); // Debug Safeness + this->restore(); // Restore anything we have saved + this->saved = true; // Mark that we have data save + this->addr = addr.get(); // Save address + this->size = size; // Save size + this->vp = vp; // Save virtual protect + ReadMemoryRaw(addr, buf, size, vp); // Save buffer + #endif + } + + public: + // Constructor, initialises + scoped_basic() : saved(false) + {} + + ~scoped_basic() + { + this->restore(); + } + + // No copy construction, we can't do this! Sure we can move construct :) + scoped_basic(const scoped_basic&) = delete; + scoped_basic(scoped_basic&& rhs) + { + *this = std::move(rhs); + } + + scoped_basic& operator=(const scoped_basic& rhs) = delete; + scoped_basic& operator=(scoped_basic&& rhs) + { + if(this->saved = rhs.saved) + { + assert(bufsize >= rhs.size); + + this->addr = rhs.addr; + this->size = rhs.size; + this->vp = rhs.vp; + memcpy(buf, rhs.buf, rhs.size); + + rhs.saved = false; + } + return *this; + } + }; + + /* + * RAII wrapper for memory writes + * Can save only basic and POD types + */ + template + class scoped_write : public scoped_basic + { + public: + // Save buffer at @addr with @size and virtual protect @vp and then overwrite it with @value + void write(memory_pointer_tr addr, void* value, size_t size, bool vp) + { + this->save(addr, size, vp); + return WriteMemoryRaw(addr, value, size, vp); + } + + // Save buffer at @addr with size sizeof(@value) and virtual protect @vp and then overwrite it with @value + template + void write(memory_pointer_tr addr, T value, bool vp = false) + { + this->save(addr, sizeof(T), vp); + return WriteMemory(addr, value, vp); + } + + // Constructors, move constructors, assigment operators........ + scoped_write() = default; + scoped_write(const scoped_write&) = delete; + scoped_write(scoped_write&& rhs) : scoped_basic(std::move(rhs)) {} + scoped_write& operator=(const scoped_write& rhs) = delete; + scoped_write& operator=(scoped_write&& rhs) + { scoped_basic::operator=(std::move(rhs)); return *this; } + }; + + /* + * RAII wrapper for filling + */ + template + class scoped_fill : public scoped_basic + { + public: + // Fills memory at @addr with value @value and size @size and virtual protect @vp + void fill(memory_pointer_tr addr, uint8_t value, size_t size, bool vp) + { + this->save(addr, size, vp); + return MemoryFill(addr, value, size, vp); + } + + // Constructors, move constructors, assigment operators........ + scoped_fill() = default; + scoped_fill(const scoped_fill&) = delete; + scoped_fill(scoped_fill&& rhs) : scoped_basic(std::move(rhs)) {} + scoped_fill& operator=(const scoped_fill& rhs) = delete; + scoped_fill& operator=(scoped_fill&& rhs) + { scoped_basic::operator=(std::move(rhs)); return *this; } + + scoped_fill(memory_pointer_tr addr, uint8_t value, size_t size, bool vp) + { fill(addr, value, vp); } + }; + + /* + * RAII wrapper for nopping + */ + template + class scoped_nop : public scoped_basic + { + public: + // Makes NOP at @addr with value @value and size @size and virtual protect @vp + void make_nop(memory_pointer_tr addr, size_t size = 1, bool vp = true) + { + this->save(addr, size, vp); + return MakeNOP(addr, size, vp); + } + + // Constructors, move constructors, assigment operators........ + scoped_nop() = default; + scoped_nop(const scoped_nop&) = delete; + scoped_nop(scoped_nop&& rhs) : scoped_basic(std::move(rhs)) {} + scoped_nop& operator=(const scoped_nop& rhs) = delete; + scoped_nop& operator=(scoped_nop&& rhs) + { scoped_basic::operator=(std::move(rhs)); return *this; } + + scoped_nop(memory_pointer_tr addr, size_t size = 1, bool vp = true) + { make_nop(addr, size, vp); } + }; + + /* + * RAII wrapper for MakeJMP + */ + class scoped_jmp : public scoped_basic<5> + { + public: + // Makes NOP at @addr with value @value and size @size and virtual protect @vp + memory_pointer_raw make_jmp(memory_pointer_tr at, memory_pointer_raw dest, bool vp = true) + { + this->save(at, 5, vp); + return MakeJMP(at, dest, vp); + } + + // Constructors, move constructors, assigment operators........ + scoped_jmp() = default; + scoped_jmp(const scoped_jmp&) = delete; + scoped_jmp(scoped_jmp&& rhs) : scoped_basic<5>(std::move(rhs)) {} + scoped_jmp& operator=(const scoped_jmp& rhs) = delete; + scoped_jmp& operator=(scoped_jmp&& rhs) + { scoped_basic<5>::operator=(std::move(rhs)); return *this; } + + scoped_jmp(memory_pointer_tr at, memory_pointer_raw dest, bool vp = true) + { make_jmp(at, dest, vp); } + }; + + /* + * RAII wrapper for MakeCALL + */ + class scoped_call : public scoped_basic<5> + { + public: + // Makes NOP at @addr with value @value and size @size and virtual protect @vp + memory_pointer_raw make_call(memory_pointer_tr at, memory_pointer_raw dest, bool vp = true) + { + this->save(at, 5, vp); + return MakeCALL(at, dest, vp); + } + + // Constructors, move constructors, assigment operators........ + scoped_call() = default; + scoped_call(const scoped_call&) = delete; + scoped_call(scoped_call&& rhs) : scoped_basic<5>(std::move(rhs)) {} + scoped_call& operator=(const scoped_call& rhs) = delete; + scoped_call& operator=(scoped_call&& rhs) + { scoped_basic<5>::operator=(std::move(rhs)); return *this; } + + scoped_call(memory_pointer_tr at, memory_pointer_raw dest, bool vp = true) + { make_call(at, dest, vp); } + }; + + +#if __cplusplus >= 201103L || _MSC_VER >= 1800 // C++11 or MSVC 2013 required for variadic templates + + /* + * function_hooker_manager + * Manages many function_hookers that points to the same address + * The need for this function arises because otherwise we would only be able to allow one hook per address using function_hookers + * This manager takes care of the amount of hooks placed in a particular address, calls the hooks and unhooks when necessary. + */ + template + class function_hooker_manager : protected scoped_call + { + private: + using func_type_raw = typename ToManage::func_type_raw; + using func_type = typename ToManage::func_type; + using functor_type = typename ToManage::functor_type; + using assoc_type = std::list>; + + // Only construction is allowed... by myself ofcourse... + function_hooker_manager() = default; + function_hooker_manager(const function_hooker_manager&) = delete; + function_hooker_manager(function_hooker_manager&&) = delete; + + // + func_type_raw original; // Pointer to the original function we've replaced + assoc_type assoc; // Association between owners of a hook and the hook (map) + bool has_hooked = false; // Is the hook already in place? + + // Find assoc iterator for the content owned by 'owned' + typename assoc_type::iterator find_assoc(const ToManage& owner) + { + for(auto it = assoc.begin(); it != assoc.end(); ++it) + if(it->first == &owner) return it; + return assoc.end(); + } + + // Adds a new item to the association map (or override if already in the map) + void add(const ToManage& hooker, functor_type functor) + { + auto it = find_assoc(hooker); + if(it != assoc.end()) + it->second = std::move(functor); + else + assoc.emplace_back(&hooker, std::move(functor)); + } + + public: + // Forwards the call to all the installed hooks + static Ret call_hooks(Args&... args) + { + auto& manager = *instance(); + + if(manager.assoc.size() == 0) // This may be uncommon but may happen (?), no hook installed + return manager.original(args...); + + // Functor for the original call + func_type original = [&manager](Args... args) -> Ret { + return manager.original(args...); + }; + + if(manager.assoc.size() == 1) + { + // We have only one hook, just use it directly no need to go further in complexity + auto& functor = manager.assoc.begin()->second; + return functor(std::move(original), args...); + } + else + { + // Build a serie of functors which captures the previous functor sending it to the next functor, + // that's what would happen if the hooks took place independent of the template staticness (AAAAAAA) + func_type next = std::move(original); + for(auto it = manager.assoc.begin(); it != manager.assoc.end(); ++it) + { + auto& functor = it->second; + next = [functor, next](Args... args) -> Ret + { + return functor(next, args...); + }; + } + return next(args...); + } + } + + public: + + // Installs a hook associated with the function_hooker 'hooker' which would call the specified 'functor' + // We need an auxiliar function pointer 'ptr' (to abstract calling conventions) which should forward itself to ^call_hooks + void make_call(const ToManage& hooker, functor_type functor, memory_pointer_raw ptr) + { + this->add(hooker, std::move(functor)); + + // Make sure we only hook this address for the manager once + if(!this->has_hooked) + { + // (the following cast is needed for __thiscall functions) + this->original = (func_type_raw) (void*) scoped_call::make_call(hooker.addr, ptr).get(); + this->has_hooked = true; + } + } + + // Restores the state of the call we've replaced in the game code + // All installed hooks gets uninstalled after this + void restore() + { + if(this->has_hooked) + { + this->has_hooked = false; + this->assoc.clear(); + return scoped_call::restore(); + } + } + + // Replaces the hook associated with 'from' to be associated with 'to' + // After this call the 'from' object has no association in this manager + void replace(const ToManage& from, const ToManage& to) + { + auto it = find_assoc(from); + if(it != assoc.end()) + { + auto functor = std::move(it->second); + assoc.erase(it); + this->add(to, std::move(functor)); + } + } + + // Removes the hook associated with the specified 'hooker' + // If the number of hooks reaches zero after the remotion, a restore will take place + void remove(const ToManage& hooker) + { + auto it = find_assoc(hooker); + if(it != assoc.end()) + { + assoc.erase(it); + if(assoc.size() == 0) this->restore(); + } + } + + // The instance of this specific manager + // This work as a shared pointer to avoid the static destruction of the manager even when a static function_hooker + // still wants to use it. + static std::shared_ptr instance() + { + static auto fm_ptr = std::shared_ptr(new function_hooker_manager()); + return fm_ptr; + } + + }; + + + /* + * function_hooker_base + * Base for any function_hooker, this class manages the relationship with the function hooker manager + */ + template + class function_hooker_base : public scoped_base + { + public: + static const uintptr_t addr = addr1; + + using func_type_raw = FuncType; + using func_type = std::function; + using functor_type = std::function; + using manager_type = function_hooker_manager; + + public: + // Constructors, move constructors, assigment operators........ + function_hooker_base(const function_hooker_base&) = delete; + function_hooker_base& operator=(const function_hooker_base& rhs) = delete; + + function_hooker_base() : manager(manager_type::instance()) + {} + + // + virtual ~function_hooker_base() + { + this->restore(); + } + + // The move constructor should do a replace in the manager + function_hooker_base(function_hooker_base&& rhs) + : scoped_base(std::move(rhs)), has_call(rhs.has_call), + manager(rhs.manager) // (don't move the manager!, every function_hooker should own one) + { + manager->replace(rhs, *this); + } + + // The move assignment operator should also do a replace in the manager + function_hooker_base& operator=(function_hooker_base&& rhs) + { + scoped_base::operator=(std::move(rhs)); + manager->replace(rhs, *this); + this->has_call = rhs.has_call; + this->manager = rhs.manager; // (don't move the manager! every function_hooker should own one) + return *this; + } + + // Deriveds should implement a proper make_call (yeah it's virtual so derived-deriveds can do some fest) + virtual void make_call(functor_type functor) = 0; + + // Restores the state of the call we've replaced in the game code + virtual void restore() + { + this->has_call = false; + manager->remove(*this); + } + + // Checkers whether a hook is installed + bool has_hooked() + { + return this->has_call; + } + + private: + bool has_call = false; // Has a hook installed? + std::shared_ptr manager; // **EVERY** function_hooker should have a ownership over it's manager_type + // this prevents the static destruction of the manager_type while it may be still needed. + + protected: // Forwarders to the function hooker manager + + void make_call(functor_type functor, memory_pointer_raw ptr) + { + this->has_call = true; + manager->make_call(*this, std::move(functor), ptr); + } + + static Ret call_hooks(Args&... a) + { + return manager_type::call_hooks(a...); + } + }; + + + + + /* + * function_hooker + * For standard conventions (usually __cdecl) + */ + template + struct function_hooker; + + template + class function_hooker + : public function_hooker_base + { + private: + using base = function_hooker_base; + + // The hook caller + static Ret call(Args... a) + { + return base::call_hooks(a...); + } + + public: + // Constructors, move constructors, assigment operators........ + function_hooker() = default; + function_hooker(const function_hooker&) = delete; + function_hooker(function_hooker&& rhs) : base(std::move(rhs)) {} + function_hooker& operator=(const function_hooker& rhs) = delete; + function_hooker& operator=(function_hooker&& rhs) + { base::operator=(std::move(rhs)); return *this; } + + // Makes the hook + void make_call(typename base::functor_type functor) + { + return base::make_call(std::move(functor), raw_ptr(call)); + } + }; + + + /* + * function_hooker_stdcall + * For stdcall conventions (__stdcall) + */ + template + struct function_hooker_stdcall; + + template + struct function_hooker_stdcall + : public function_hooker_base + { + private: + using base = function_hooker_base; + + // The hook caller + static Ret __stdcall call(Args... a) + { + return base::call_hooks(a...); + } + + public: + // Constructors, move constructors, assigment operators........ + function_hooker_stdcall() = default; + function_hooker_stdcall(const function_hooker_stdcall&) = delete; + function_hooker_stdcall(function_hooker_stdcall&& rhs) : base(std::move(rhs)) {} + function_hooker_stdcall& operator=(const function_hooker_stdcall& rhs) = delete; + function_hooker_stdcall& operator=(function_hooker_stdcall&& rhs) + { base::operator=(std::move(rhs)); return *this; } + + // Makes the hook + void make_call(typename base::functor_type functor) + { + return base::make_call(std::move(functor), raw_ptr(call)); + } + }; + + + /* + * function_hooker_fastcall + * For fastcall conventions (__fastcall) + */ + template + struct function_hooker_fastcall; + + template + struct function_hooker_fastcall + : public function_hooker_base + { + private: + using base = function_hooker_base; + + // The hook caller + static Ret __fastcall call(Args... a) + { + return base::call_hooks(a...); + } + + public: + // Constructors, move constructors, assigment operators........ + function_hooker_fastcall() = default; + function_hooker_fastcall(const function_hooker_fastcall&) = delete; + function_hooker_fastcall(function_hooker_fastcall&& rhs) : base(std::move(rhs)) {} + function_hooker_fastcall& operator=(const function_hooker_fastcall& rhs) = delete; + function_hooker_fastcall& operator=(function_hooker_fastcall&& rhs) + { base::operator=(std::move(rhs)); return *this; } + + // Makes the hook + void make_call(typename base::functor_type functor) + { + return base::make_call(std::move(functor), raw_ptr(call)); + } + }; + + + /* + * function_hooker_thiscall + * For thiscall conventions (__thiscall, class methods) + */ + template + struct function_hooker_thiscall; + + template + struct function_hooker_thiscall + : public function_hooker_base + { + private: + using base = function_hooker_base; + + // The hook caller + static Ret __thiscall call(Args... a) + { + return base::call_hooks(a...); + } + + public: + // Constructors, move constructors, assigment operators........ + function_hooker_thiscall() = default; + function_hooker_thiscall(const function_hooker_thiscall&) = delete; + function_hooker_thiscall(function_hooker_thiscall&& rhs) : base(std::move(rhs)) {} + function_hooker_thiscall& operator=(const function_hooker_thiscall& rhs) = delete; + function_hooker_thiscall& operator=(function_hooker_thiscall&& rhs) + { base::operator=(std::move(rhs)); return *this; } + + // Makes the hook + void make_call(typename base::functor_type functor) + { + return base::make_call(std::move(functor), raw_ptr(call)); + } + }; + + + + /******************* HELPERS ******************/ + + /* + * Adds a hook to be alive for the entire program lifetime + * That means the hook received will be alive until the program dies. + * Note: Parameter must be a rvalue + */ + template inline + T& add_static_hook(T&& hooker) + { + static std::list a; + return *a.emplace(a.end(), std::move(hooker)); + } + + /* + * Makes a hook which is alive until it gets out of scope + * 'T' must be any function_hooker object + */ + template inline + T make_function_hook(F functor) + { + T a; + a.make_call(std::move(functor)); + return a; + } + + /* + * Makes a hook which is alive for the entire lifetime of this program + * 'T' must be any function_hooker object + */ + template inline + T& make_static_hook(F functor) + { + return add_static_hook(make_function_hook(std::move(functor))); + } + + + // TODO when we have access to C++14 add a make_function_hook, make_stdcall_function_hook, and so on + // the problem behind implement it with C++11 is that lambdas cannot be generic and the first param of a hook is a functor pointing + // to the previous call pointer + +#endif + +} diff --git a/depthrush/kiero/injector/injector.hpp b/depthrush/kiero/injector/injector.hpp new file mode 100644 index 0000000..ce84314 --- /dev/null +++ b/depthrush/kiero/injector/injector.hpp @@ -0,0 +1,661 @@ +/* + * Injectors - Base Header + * + * Copyright (C) 2012-2014 LINK/2012 + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + * + */ +#pragma once +#define INJECTOR_HAS_INJECTOR_HPP +#include +#include +#include +#include "gvm/gvm.hpp" +/* + The following macros (#define) are relevant on this header: + + INJECTOR_GVM_HAS_TRANSLATOR + If defined, the user should provide their own address_manager::translator function. + That function is responssible for translating a void pointer (that mayn't be an actual pointer) into an actual address. + The meaning of that void pointer will be made by YOU when you send it to the functions that receive pointers on this library. + The default translator does nothing but returns that void pointer as the address. + + INJECTOR_GVM_OWN_DETECT + If defined, the user should provide it's own game detection function thought game_version_manager::Detect + By default it provide an good detection for the Grand Theft Auto series. + + INJECTOR_GVM_PLUGIN_NAME + If this is defined, it will be used as the plugin name used at error messages. + By default it will use ""Unknown Plugin Name" + + INJECTOR_GVM_DUMMY + If defined, the game_version_manager will be a dummy object + By default it provides a nice gvm for Grand Theft Auto series + + INJECTOR_OWN_GVM + If defined, the game_version_manager should be implemented by the user before including this library. + By default it provides a nice gvm for Grand Theft Auto series +*/ +#include "gvm/gvm.hpp" + + + +namespace injector +{ + + +/* + * auto_pointer + * Casts itself to another pointer type in the lhs + */ +union auto_pointer +{ + protected: + friend union memory_pointer_tr; + template friend union basic_memory_pointer; + + void* p; + uintptr_t a; + + public: + auto_pointer() : p(0) {} + auto_pointer(const auto_pointer& x) : p(x.p) {} + explicit auto_pointer(void* x) : p(x) {} + explicit auto_pointer(uint32_t x) : a(x) {} + + bool is_null() const { return this->p != nullptr; } + + #if __cplusplus >= 201103L || _MSC_VER >= 1800 + explicit operator bool() const { return is_null(); } + #endif + + auto_pointer get() const { return *this; } + template T* get() const { return (T*) this->p; } + template T* get_raw() const { return (T*) this->p; } + + template + operator T*() const { return reinterpret_cast(p); } +}; + +/* + * basic_memory_pointer + * A memory pointer class that is capable of many operations, including address translation + * MemTranslator is the translator functor + */ +template +union basic_memory_pointer +{ + protected: + void* p; + uintptr_t a; + + // Translates address p to the running executable pointer + static auto_pointer memory_translate(void* p) + { + return auto_pointer(MemTranslator()(p)); + } + + public: + basic_memory_pointer() : p(nullptr) {} + basic_memory_pointer(std::nullptr_t) : p(nullptr) {} + basic_memory_pointer(uintptr_t x) : a(x) {} + basic_memory_pointer(const auto_pointer& x) : p(x.p) {} + basic_memory_pointer(const basic_memory_pointer& rhs) : p(rhs.p) {} + + template + basic_memory_pointer(T* x) : p((void*)x) {} + + + + + // Gets the translated pointer (plus automatic casting to lhs) + auto_pointer get() const { return memory_translate(p); } + + // Gets the translated pointer (casted to T*) + template T* get() const { return get(); } + + // Gets the raw pointer, without translation (casted to T*) + template T* get_raw() const { return auto_pointer(p); } + + // This type can get assigned from void* and uintptr_t + basic_memory_pointer& operator=(void* x) { return p = x, *this; } + basic_memory_pointer& operator=(uintptr_t x) { return a = x, *this; } + + /* Arithmetic */ + basic_memory_pointer operator+(const basic_memory_pointer& rhs) const + { return basic_memory_pointer(this->a + rhs.a); } + + basic_memory_pointer operator-(const basic_memory_pointer& rhs) const + { return basic_memory_pointer(this->a - rhs.a); } + + basic_memory_pointer operator*(const basic_memory_pointer& rhs) const + { return basic_memory_pointer(this->a * rhs.a); } + + basic_memory_pointer operator/(const basic_memory_pointer& rhs) const + { return basic_memory_pointer(this->a / rhs.a); } + + + /* Comparision */ + bool operator==(const basic_memory_pointer& rhs) const + { return this->a == rhs.a; } + + bool operator!=(const basic_memory_pointer& rhs) const + { return this->a != rhs.a; } + + bool operator<(const basic_memory_pointer& rhs) const + { return this->a < rhs.a; } + + bool operator<=(const basic_memory_pointer& rhs) const + { return this->a <= rhs.a; } + + bool operator>(const basic_memory_pointer& rhs) const + { return this->a > rhs.a; } + + bool operator>=(const basic_memory_pointer& rhs) const + { return this->a >=rhs.a; } + + bool is_null() const { return this->p == nullptr; } + uintptr_t as_int() const { return this->a; } // does not perform translation + + + +#if __cplusplus >= 201103L || _MSC_VER >= 1800 // MSVC 2013 + /* Conversion to other types */ + explicit operator uintptr_t() const + { return this->a; } // does not perform translation + explicit operator bool() const + { return this->p != nullptr; } +#else + //operator bool() -------------- Causes casting problems because of implicitness, use !is_null() + //{ return this->p != nullptr; } +#endif + +}; + + // Typedefs including memory translator for the above type +typedef basic_memory_pointer memory_pointer; +typedef basic_memory_pointer memory_pointer_raw; +typedef basic_memory_pointer memory_pointer_aslr; + + + +/* + * memory_pointer_tr + * Stores a basic_memory_pointer as a raw pointer from translated pointer + */ +union memory_pointer_tr +{ + protected: + void* p; + uintptr_t a; + + public: + template + memory_pointer_tr(const basic_memory_pointer& ptr) + : p(ptr.get()) + {} // Constructs from a basic_memory_pointer + + memory_pointer_tr(const auto_pointer& ptr) + : p(ptr.p) + {} // Constructs from a auto_pointer, probably comming from basic_memory_pointer::get + + memory_pointer_tr(const memory_pointer_tr& rhs) + : p(rhs.p) + {} // Constructs from my own type, copy constructor + + memory_pointer_tr(uintptr_t x) + : p(memory_pointer(x).get()) + {} // Constructs from a integer, translating the address + + memory_pointer_tr(void* x) + : p(memory_pointer(x).get()) + {} // Constructs from a void pointer, translating the address + + // Just to be method-compatible with basic_memory_pointer ... + auto_pointer get() { return auto_pointer(p); } + template T* get() { return get(); } + template T* get_raw() { return get(); } + + memory_pointer_tr operator+(const uintptr_t& rhs) const + { return memory_pointer_raw(this->a + rhs); } + + memory_pointer_tr operator-(const uintptr_t& rhs) const + { return memory_pointer_raw(this->a - rhs); } + + memory_pointer_tr operator*(const uintptr_t& rhs) const + { return memory_pointer_raw(this->a * rhs); } + + memory_pointer_tr operator/(const uintptr_t& rhs) const + { return memory_pointer_raw(this->a / rhs); } + + bool is_null() const { return this->p == nullptr; } + uintptr_t as_int() const { return this->a; } + +#if __cplusplus >= 201103L + explicit operator uintptr_t() const + { return this->a; } +#else +#endif + +}; + + + + + + + +/* + * ProtectMemory + * Makes the address @addr have a protection of @protection + */ +inline bool ProtectMemory(memory_pointer_tr addr, size_t size, DWORD protection) +{ + return VirtualProtect(addr.get(), size, protection, &protection) != 0; +} + +/* + * UnprotectMemory + * Unprotect the memory at @addr with size @size so it have all accesses (execute, read and write) + * Returns the old protection to out_oldprotect + */ +inline bool UnprotectMemory(memory_pointer_tr addr, size_t size, DWORD& out_oldprotect) +{ + return VirtualProtect(addr.get(), size, PAGE_EXECUTE_READWRITE, &out_oldprotect) != 0; +} + +/* + * scoped_unprotect + * RAII wrapper for UnprotectMemory + * On construction unprotects the memory, on destruction reprotects the memory + */ +struct scoped_unprotect +{ + memory_pointer_raw addr; + size_t size; + DWORD dwOldProtect; + bool bUnprotected; + + scoped_unprotect(memory_pointer_tr addr, size_t size) + { + if(size == 0) bUnprotected = false; + else bUnprotected = UnprotectMemory(this->addr = addr.get(), this->size = size, dwOldProtect); + } + + ~scoped_unprotect() + { + if(bUnprotected) ProtectMemory(this->addr.get(), this->size, this->dwOldProtect); + } +}; + + + + + + + + +/* + * WriteMemoryRaw + * Writes into memory @addr the content of @value with a sizeof @size + * Does memory unprotection if @vp is true + */ +inline void WriteMemoryRaw(memory_pointer_tr addr, void* value, size_t size, bool vp) +{ + scoped_unprotect xprotect(addr, vp? size : 0); + memcpy(addr.get(), value, size); +} + +/* + * ReadMemoryRaw + * Reads the memory at @addr with a sizeof @size into address @ret + * Does memory unprotection if @vp is true + */ +inline void ReadMemoryRaw(memory_pointer_tr addr, void* ret, size_t size, bool vp) +{ + scoped_unprotect xprotect(addr, vp? size : 0); + memcpy(ret, addr.get(), size); +} + +/* + * MemoryFill + * Fills the memory at @addr with the byte @value doing it @size times + * Does memory unprotection if @vp is true + */ +inline void MemoryFill(memory_pointer_tr addr, uint8_t value, size_t size, bool vp) +{ + scoped_unprotect xprotect(addr, vp? size : 0); + memset(addr.get(), value, size); +} + +/* + * WriteObject + * Assigns the object @value into the same object type at @addr + * Does memory unprotection if @vp is true + */ +template +inline T& WriteObject(memory_pointer_tr addr, const T& value, bool vp = false) +{ + scoped_unprotect xprotect(addr, vp? sizeof(value) : 0); + return (*addr.get() = value); +} + +/* + * ReadObject + * Assigns the object @value with the value of the same object type at @addr + * Does memory unprotection if @vp is true + */ +template +inline T& ReadObject(memory_pointer_tr addr, T& value, bool vp = false) +{ + scoped_unprotect xprotect(addr, vp? sizeof(value) : 0); + return (value = *addr.get()); +} + + +/* + * WriteMemory + * Writes the object of type T into the address @addr + * Does memory unprotection if @vp is true + */ +template +inline void WriteMemory(memory_pointer_tr addr, T value, bool vp = false) +{ + WriteObject(addr, value, vp); +} + +/* + * ReadMemory + * Reads the object type T at address @addr + * Does memory unprotection if @vp is true + */ +template +inline T ReadMemory(memory_pointer_tr addr, bool vp = false) +{ + T value; + return ReadObject(addr, value, vp); +} + +/* + * AdjustPointer + * Searches in the range [@addr, @addr + @max_search] for a pointer in the range [@default_base, @default_end] and replaces + * it with the proper offset in the pointer @replacement_base. + * Does memory unprotection if @vp is true. + */ + inline memory_pointer_raw AdjustPointer(memory_pointer_tr addr, + memory_pointer_raw replacement_base, memory_pointer_tr default_base, memory_pointer_tr default_end, + size_t max_search = 8, bool vp = true) + { + scoped_unprotect xprotect(addr, vp? max_search + sizeof(void*) : 0); + for(size_t i = 0; i < max_search; ++i) + { + memory_pointer_raw ptr = ReadMemory(addr + i); + if(ptr >= default_base.get() && ptr <= default_end.get()) + { + auto result = replacement_base + (ptr - default_base.get()); + WriteMemory(addr + i, result.get()); + return result; + } + } + return nullptr; + } + + + + + + +/* + * GetAbsoluteOffset + * Gets absolute address based on relative offset @rel_value from instruction that ends at @end_of_instruction + */ +inline memory_pointer_raw GetAbsoluteOffset(int rel_value, memory_pointer_tr end_of_instruction) +{ + return end_of_instruction.get() + rel_value; +} + +/* + * GetRelativeOffset + * Gets relative offset based on absolute address @abs_value for instruction that ends at @end_of_instruction + */ +inline int GetRelativeOffset(memory_pointer_tr abs_value, memory_pointer_tr end_of_instruction) +{ + return uintptr_t(abs_value.get() - end_of_instruction.get()); +} + +/* + * ReadRelativeOffset + * Reads relative offset from address @at + */ +inline memory_pointer_raw ReadRelativeOffset(memory_pointer_tr at, size_t sizeof_addr = 4, bool vp = true) +{ + switch(sizeof_addr) + { + case 1: return (GetAbsoluteOffset(ReadMemory (at, vp), at+sizeof_addr)); + case 2: return (GetAbsoluteOffset(ReadMemory(at, vp), at+sizeof_addr)); + case 4: return (GetAbsoluteOffset(ReadMemory(at, vp), at+sizeof_addr)); + } + return nullptr; +} + +/* + * MakeRelativeOffset + * Writes relative offset into @at based on absolute destination @dest + */ +inline void MakeRelativeOffset(memory_pointer_tr at, memory_pointer_tr dest, size_t sizeof_addr = 4, bool vp = true) +{ + switch(sizeof_addr) + { + case 1: WriteMemory (at, static_cast (GetRelativeOffset(dest, at+sizeof_addr)), vp); + case 2: WriteMemory(at, static_cast(GetRelativeOffset(dest, at+sizeof_addr)), vp); + case 4: WriteMemory(at, static_cast(GetRelativeOffset(dest, at+sizeof_addr)), vp); + } +} + +/* + * GetBranchDestination + * Gets the destination of a branch instruction at address @at + * *** Works only with JMP and CALL for now *** + */ +inline memory_pointer_raw GetBranchDestination(memory_pointer_tr at, bool vp = true) +{ + switch(ReadMemory(at, vp)) + { + // We need to handle other instructions (and prefixes) later... + case 0xE8: // call rel + case 0xE9: // jmp rel + return ReadRelativeOffset(at + 1, 4, vp); + + case 0xFF: + switch(ReadMemory(at + 1, vp)) + { + case 0x15: // call dword ptr [addr] + case 0x25: // jmp dword ptr [addr] + return *(ReadMemory(at + 2, vp)); + } + break; + } + return nullptr; +} + +/* + * MakeJMP + * Creates a JMP instruction at address @at that jumps into address @dest + * If there was already a branch instruction there, returns the previosly destination of the branch + */ +inline memory_pointer_raw MakeJMP(memory_pointer_tr at, memory_pointer_raw dest, bool vp = true) +{ + auto p = GetBranchDestination(at, vp); + WriteMemory(at, 0xE9, vp); + MakeRelativeOffset(at+1, dest, 4, vp); + return p; +} + +/* + * MakeCALL + * Creates a CALL instruction at address @at that jumps into address @dest + * If there was already a branch instruction there, returns the previosly destination of the branch + */ +inline memory_pointer_raw MakeCALL(memory_pointer_tr at, memory_pointer_raw dest, bool vp = true) +{ + auto p = GetBranchDestination(at, vp); + WriteMemory(at, 0xE8, vp); + MakeRelativeOffset(at+1, dest, 4, vp); + return p; +} + +/* + * MakeJA + * Creates a JA instruction at address @at that jumps if above into address @dest + * If there was already a branch instruction there, returns the previosly destination of the branch + */ +inline void MakeJA(memory_pointer_tr at, memory_pointer_raw dest, bool vp = true) +{ + WriteMemory(at, 0x87F0, vp); + MakeRelativeOffset(at+2, dest, 4, vp); +} + +/* + * MakeNOP + * Creates a bunch of NOP instructions at address @at + */ +inline void MakeNOP(memory_pointer_tr at, size_t count = 1, bool vp = true) +{ + MemoryFill(at, 0x90, count, vp); +} + +/* + * MakeRangedNOP + * Creates a bunch of NOP instructions at address @at until address @until + */ +inline void MakeRangedNOP(memory_pointer_tr at, memory_pointer_tr until, bool vp = true) +{ + return MakeNOP(at, size_t(until.get_raw() - at.get_raw()), vp); +} + + +/* + * MakeRET + * Creates a RET instruction at address @at popping @pop values from the stack + * If @pop is equal to 0 it will use the 1 byte form of the instruction + */ +inline void MakeRET(memory_pointer_tr at, uint16_t pop = 0, bool vp = true) +{ + WriteMemory(at, pop? 0xC2 : 0xC3, vp); + if(pop) WriteMemory(at+1, pop, vp); +} + + + + + +/* + * lazy_pointer + * Lazy pointer, where it's final value will get evaluated only once when finally needed. + */ + template + struct lazy_pointer + { + public: + // Returns the final raw pointer + static auto_pointer get() + { + return xget().get(); + } + + template + static T* get() + { + return get().get(); + } + + private: + // Returns the final pointer + static memory_pointer_raw xget() + { + static void* ptr = nullptr; + if(!ptr) ptr = memory_pointer(addr).get(); + return memory_pointer_raw(ptr); + } +}; + + /* + * lazy_object + * Lazy object, where it's final object will get evaluated only once when finally needed. + */ + template + struct lazy_object + { + static T& get() + { + static T data; + static bool has_data = false; + if(!has_data) + { + ReadObject(addr, data, true); + has_data = true; + } + return data; + } + }; + + + /* + Helpers + */ + + template +inline memory_pointer mem_ptr(T p) +{ + return memory_pointer(p); +} + +template +inline memory_pointer_raw raw_ptr(T p) +{ + return memory_pointer_raw(p); +} + +template +inline memory_pointer_raw raw_ptr(basic_memory_pointer p) +{ + return raw_ptr(p.get()); +} + +template +inline memory_pointer_raw lazy_ptr() +{ + return lazy_pointer::get(); +} + +template +inline memory_pointer_aslr aslr_ptr(T p) +{ + return memory_pointer_aslr(p); +} + + + + + + +} // namespace + diff --git a/depthrush/kiero/injector/utility.hpp b/depthrush/kiero/injector/utility.hpp new file mode 100644 index 0000000..0a5eafc --- /dev/null +++ b/depthrush/kiero/injector/utility.hpp @@ -0,0 +1,56 @@ +/* + * Injectors - Utility / Helpers + * + * Copyright (C) 2014 LINK/2012 + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + * + */ +#pragma once + +namespace injector +{ + template + T return_value() + { + return value; + } + + template + void* force_ptr(const T& fun) + { + auto ptr = fun; + return *(void**)&ptr; + } + + + // Helper structure to help calling back what was there before a hook + // e.g. hb.fun = MakeCALL(0x0, raw_ptr(my_hook)); + template + struct hook_back + { + typedef FuncType func_type; + + func_type fun; + + hook_back() : fun(nullptr) + {} + }; +}; diff --git a/depthrush/kiero/kiero.cpp b/depthrush/kiero/kiero.cpp new file mode 100644 index 0000000..fbac5ab --- /dev/null +++ b/depthrush/kiero/kiero.cpp @@ -0,0 +1,720 @@ +#include "kiero.h" +#include +#include + +#if KIERO_INCLUDE_D3D9 +# include +#endif + +#if KIERO_INCLUDE_D3D10 +# include +# include +# include +#endif + +#if KIERO_INCLUDE_D3D11 +# include +# include +#endif + +#if KIERO_INCLUDE_D3D12 +# include +# include +#endif + +#if KIERO_INCLUDE_OPENGL +# include +#endif + +#if KIERO_INCLUDE_VULKAN +# include +#endif + +#if KIERO_USE_MINHOOK +# include "minhook/include/MinHook.h" +#endif + +#ifdef _UNICODE +# define KIERO_TEXT(text) L##text +#else +# define KIERO_TEXT(text) text +#endif + +#define KIERO_ARRAY_SIZE(arr) ((size_t)(sizeof(arr)/sizeof(arr[0]))) + +static kiero::RenderType::Enum g_renderType = kiero::RenderType::None; +static uint150_t* g_methodsTable = NULL; + +kiero::Status::Enum kiero::init(RenderType::Enum _renderType) +{ + if (g_renderType != RenderType::None) + { + return Status::AlreadyInitializedError; + } + + if (_renderType != RenderType::None) + { + if (_renderType >= RenderType::D3D9 && _renderType <= RenderType::D3D12) + { + WNDCLASSEX windowClass; + windowClass.cbSize = sizeof(WNDCLASSEX); + windowClass.style = CS_HREDRAW | CS_VREDRAW; + windowClass.lpfnWndProc = DefWindowProc; + windowClass.cbClsExtra = 0; + windowClass.cbWndExtra = 0; + windowClass.hInstance = GetModuleHandle(NULL); + windowClass.hIcon = NULL; + windowClass.hCursor = NULL; + windowClass.hbrBackground = NULL; + windowClass.lpszMenuName = NULL; + windowClass.lpszClassName = KIERO_TEXT("Kiero"); + windowClass.hIconSm = NULL; + + ::RegisterClassEx(&windowClass); + + HWND window = ::CreateWindow(windowClass.lpszClassName, KIERO_TEXT("Kiero DirectX Window"), WS_OVERLAPPEDWINDOW, 0, 0, 100, 100, NULL, NULL, windowClass.hInstance, NULL); + + if (_renderType == RenderType::D3D9) + { +#if KIERO_INCLUDE_D3D9 + HMODULE libD3D9; + if ((libD3D9 = ::GetModuleHandle(KIERO_TEXT("d3d9.dll"))) == NULL) + { + ::DestroyWindow(window); + ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + return Status::ModuleNotFoundError; + } + + void* Direct3DCreate9; + if ((Direct3DCreate9 = ::GetProcAddress(libD3D9, "Direct3DCreate9")) == NULL) + { + ::DestroyWindow(window); + ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + return Status::UnknownError; + } + + LPDIRECT3D9 direct3D9; + if ((direct3D9 = ((LPDIRECT3D9(__stdcall*)(uint32_t))(Direct3DCreate9))(D3D_SDK_VERSION)) == NULL) + { + ::DestroyWindow(window); + ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + return Status::UnknownError; + } + + D3DDISPLAYMODE displayMode; + if (direct3D9->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &displayMode) < 0) + { + ::DestroyWindow(window); + ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + return Status::UnknownError; + } + + D3DPRESENT_PARAMETERS params; + params.BackBufferWidth = 0; + params.BackBufferHeight = 0; + params.BackBufferFormat = displayMode.Format; + params.BackBufferCount = 0; + params.MultiSampleType = D3DMULTISAMPLE_NONE; + params.MultiSampleQuality = NULL; + params.SwapEffect = D3DSWAPEFFECT_DISCARD; + params.hDeviceWindow = window; + params.Windowed = 1; + params.EnableAutoDepthStencil = 0; + params.AutoDepthStencilFormat = D3DFMT_UNKNOWN; + params.Flags = NULL; + params.FullScreen_RefreshRateInHz = 0; + params.PresentationInterval = 0; + + LPDIRECT3DDEVICE9 device; + if (direct3D9->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, window, D3DCREATE_SOFTWARE_VERTEXPROCESSING | D3DCREATE_DISABLE_DRIVER_MANAGEMENT, ¶ms, &device) < 0) + { + direct3D9->Release(); + ::DestroyWindow(window); + ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + return Status::UnknownError; + } + + g_methodsTable = (uint150_t*)::calloc(119, sizeof(uint150_t)); + ::memcpy(g_methodsTable, *(uint150_t**)device, 119 * sizeof(uint150_t)); + +#if KIERO_USE_MINHOOK + MH_Initialize(); +#endif + + direct3D9->Release(); + direct3D9 = NULL; + + device->Release(); + device = NULL; + + g_renderType = RenderType::D3D9; + + ::DestroyWindow(window); + ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + + return Status::Success; +#endif + } + else if (_renderType == RenderType::D3D10) + { +#if KIERO_INCLUDE_D3D10 + HMODULE libDXGI; + HMODULE libD3D10; + if ((libDXGI = ::GetModuleHandle(KIERO_TEXT("dxgi.dll"))) == NULL || (libD3D10 = ::GetModuleHandle(KIERO_TEXT("d3d10.dll"))) == NULL) + { + ::DestroyWindow(window); + ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + return Status::ModuleNotFoundError; + } + + void* CreateDXGIFactory; + if ((CreateDXGIFactory = ::GetProcAddress(libDXGI, "CreateDXGIFactory")) == NULL) + { + ::DestroyWindow(window); + ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + return Status::UnknownError; + } + + IDXGIFactory* factory; + if (((long(__stdcall*)(const IID&, void**))(CreateDXGIFactory))(__uuidof(IDXGIFactory), (void**)&factory) < 0) + { + ::DestroyWindow(window); + ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + return Status::UnknownError; + } + + IDXGIAdapter* adapter; + if (factory->EnumAdapters(0, &adapter) == DXGI_ERROR_NOT_FOUND) + { + ::DestroyWindow(window); + ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + return Status::UnknownError; + } + + void* D3D10CreateDeviceAndSwapChain; + if ((D3D10CreateDeviceAndSwapChain = ::GetProcAddress(libD3D10, "D3D10CreateDeviceAndSwapChain")) == NULL) + { + ::DestroyWindow(window); + ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + return Status::UnknownError; + } + + DXGI_RATIONAL refreshRate; + refreshRate.Numerator = 60; + refreshRate.Denominator = 1; + + DXGI_MODE_DESC bufferDesc; + bufferDesc.Width = 100; + bufferDesc.Height = 100; + bufferDesc.RefreshRate = refreshRate; + bufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + bufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; + bufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED; + + DXGI_SAMPLE_DESC sampleDesc; + sampleDesc.Count = 1; + sampleDesc.Quality = 0; + + DXGI_SWAP_CHAIN_DESC swapChainDesc; + swapChainDesc.BufferDesc = bufferDesc; + swapChainDesc.SampleDesc = sampleDesc; + swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swapChainDesc.BufferCount = 1; + swapChainDesc.OutputWindow = window; + swapChainDesc.Windowed = 1; + swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; + swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH; + + IDXGISwapChain* swapChain; + ID3D10Device* device; + + if (((long(__stdcall*)( + IDXGIAdapter*, + D3D10_DRIVER_TYPE, + HMODULE, + UINT, + UINT, + DXGI_SWAP_CHAIN_DESC*, + IDXGISwapChain**, + ID3D10Device**))(D3D10CreateDeviceAndSwapChain))(adapter, D3D10_DRIVER_TYPE_HARDWARE, NULL, 0, D3D10_SDK_VERSION, &swapChainDesc, &swapChain, &device) < 0) + { + ::DestroyWindow(window); + ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + return Status::UnknownError; + } + + g_methodsTable = (uint150_t*)::calloc(116, sizeof(uint150_t)); + ::memcpy(g_methodsTable, *(uint150_t**)swapChain, 18 * sizeof(uint150_t)); + ::memcpy(g_methodsTable + 18, *(uint150_t**)device, 98 * sizeof(uint150_t)); + +#if KIERO_USE_MINHOOK + MH_Initialize(); +#endif + + swapChain->Release(); + swapChain = NULL; + + device->Release(); + device = NULL; + + ::DestroyWindow(window); + ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + + g_renderType = RenderType::D3D10; + + return Status::Success; +#endif + } + else if (_renderType == RenderType::D3D11) + { +#if KIERO_INCLUDE_D3D11 + HMODULE libD3D11; + if ((libD3D11 = ::GetModuleHandle(KIERO_TEXT("d3d11.dll"))) == NULL) + { + ::DestroyWindow(window); + ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + return Status::ModuleNotFoundError; + } + + void* D3D11CreateDeviceAndSwapChain; + if ((D3D11CreateDeviceAndSwapChain = ::GetProcAddress(libD3D11, "D3D11CreateDeviceAndSwapChain")) == NULL) + { + ::DestroyWindow(window); + ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + return Status::UnknownError; + } + + D3D_FEATURE_LEVEL featureLevel; + const D3D_FEATURE_LEVEL featureLevels[] = { D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_11_0 }; + + DXGI_RATIONAL refreshRate; + refreshRate.Numerator = 60; + refreshRate.Denominator = 1; + + DXGI_MODE_DESC bufferDesc; + bufferDesc.Width = 100; + bufferDesc.Height = 100; + bufferDesc.RefreshRate = refreshRate; + bufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + bufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; + bufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED; + + DXGI_SAMPLE_DESC sampleDesc; + sampleDesc.Count = 1; + sampleDesc.Quality = 0; + + DXGI_SWAP_CHAIN_DESC swapChainDesc; + swapChainDesc.BufferDesc = bufferDesc; + swapChainDesc.SampleDesc = sampleDesc; + swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swapChainDesc.BufferCount = 1; + swapChainDesc.OutputWindow = window; + swapChainDesc.Windowed = 1; + swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; + swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH; + + IDXGISwapChain* swapChain; + ID3D11Device* device; + ID3D11DeviceContext* context; + + if (((long(__stdcall*)( + IDXGIAdapter*, + D3D_DRIVER_TYPE, + HMODULE, + UINT, + const D3D_FEATURE_LEVEL*, + UINT, + UINT, + const DXGI_SWAP_CHAIN_DESC*, + IDXGISwapChain**, + ID3D11Device**, + D3D_FEATURE_LEVEL*, + ID3D11DeviceContext**))(D3D11CreateDeviceAndSwapChain))(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, 0, featureLevels, 1, D3D11_SDK_VERSION, &swapChainDesc, &swapChain, &device, &featureLevel, &context) < 0) + { + ::DestroyWindow(window); + ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + return Status::UnknownError; + } + + g_methodsTable = (uint150_t*)::calloc(205, sizeof(uint150_t)); + ::memcpy(g_methodsTable, *(uint150_t**)swapChain, 18 * sizeof(uint150_t)); + ::memcpy(g_methodsTable + 18, *(uint150_t**)device, 43 * sizeof(uint150_t)); + ::memcpy(g_methodsTable + 18 + 43, *(uint150_t**)context, 144 * sizeof(uint150_t)); + +#if KIERO_USE_MINHOOK + MH_Initialize(); +#endif + + swapChain->Release(); + swapChain = NULL; + + device->Release(); + device = NULL; + + context->Release(); + context = NULL; + + ::DestroyWindow(window); + ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + + g_renderType = RenderType::D3D11; + + return Status::Success; +#endif + } + else if (_renderType == RenderType::D3D12) + { +#if KIERO_INCLUDE_D3D12 + HMODULE libDXGI; + HMODULE libD3D12; + if ((libDXGI = ::GetModuleHandle(KIERO_TEXT("dxgi.dll"))) == NULL || (libD3D12 = ::GetModuleHandle(KIERO_TEXT("d3d12.dll"))) == NULL) + { + ::DestroyWindow(window); + ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + return Status::ModuleNotFoundError; + } + + void* CreateDXGIFactory; + if ((CreateDXGIFactory = ::GetProcAddress(libDXGI, "CreateDXGIFactory")) == NULL) + { + ::DestroyWindow(window); + ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + return Status::UnknownError; + } + + IDXGIFactory* factory; + if (((long(__stdcall*)(const IID&, void**))(CreateDXGIFactory))(__uuidof(IDXGIFactory), (void**)&factory) < 0) + { + ::DestroyWindow(window); + ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + return Status::UnknownError; + } + + IDXGIAdapter* adapter; + if (factory->EnumAdapters(0, &adapter) == DXGI_ERROR_NOT_FOUND) + { + ::DestroyWindow(window); + ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + return Status::UnknownError; + } + + void* D3D12CreateDevice; + if ((D3D12CreateDevice = ::GetProcAddress(libD3D12, "D3D12CreateDevice")) == NULL) + { + ::DestroyWindow(window); + ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + return Status::UnknownError; + } + + ID3D12Device* device; + if (((long(__stdcall*)(IUnknown*, D3D_FEATURE_LEVEL, const IID&, void**))(D3D12CreateDevice))(adapter, D3D_FEATURE_LEVEL_11_0, __uuidof(ID3D12Device), (void**)&device) < 0) + { + ::DestroyWindow(window); + ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + return Status::UnknownError; + } + + D3D12_COMMAND_QUEUE_DESC queueDesc; + queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; + queueDesc.Priority = 0; + queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; + queueDesc.NodeMask = 0; + + ID3D12CommandQueue* commandQueue; + if (device->CreateCommandQueue(&queueDesc, __uuidof(ID3D12CommandQueue), (void**)&commandQueue) < 0) + { + ::DestroyWindow(window); + ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + return Status::UnknownError; + } + + ID3D12CommandAllocator* commandAllocator; + if (device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, __uuidof(ID3D12CommandAllocator), (void**)&commandAllocator) < 0) + { + ::DestroyWindow(window); + ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + return Status::UnknownError; + } + + ID3D12GraphicsCommandList* commandList; + if (device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, commandAllocator, NULL, __uuidof(ID3D12GraphicsCommandList), (void**)&commandList) < 0) + { + ::DestroyWindow(window); + ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + return Status::UnknownError; + } + + DXGI_RATIONAL refreshRate; + refreshRate.Numerator = 60; + refreshRate.Denominator = 1; + + DXGI_MODE_DESC bufferDesc; + bufferDesc.Width = 100; + bufferDesc.Height = 100; + bufferDesc.RefreshRate = refreshRate; + bufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + bufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; + bufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED; + + DXGI_SAMPLE_DESC sampleDesc; + sampleDesc.Count = 1; + sampleDesc.Quality = 0; + + DXGI_SWAP_CHAIN_DESC swapChainDesc = {}; + swapChainDesc.BufferDesc = bufferDesc; + swapChainDesc.SampleDesc = sampleDesc; + swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swapChainDesc.BufferCount = 2; + swapChainDesc.OutputWindow = window; + swapChainDesc.Windowed = 1; + swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; + swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH; + + IDXGISwapChain* swapChain; + if (factory->CreateSwapChain(commandQueue, &swapChainDesc, &swapChain) < 0) + { + ::DestroyWindow(window); + ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + return Status::UnknownError; + } + + g_methodsTable = (uint150_t*)::calloc(150, sizeof(uint150_t)); + ::memcpy(g_methodsTable, *(uint150_t**)device, 44 * sizeof(uint150_t)); + ::memcpy(g_methodsTable + 44, *(uint150_t**)commandQueue, 19 * sizeof(uint150_t)); + ::memcpy(g_methodsTable + 44 + 19, *(uint150_t**)commandAllocator, 9 * sizeof(uint150_t)); + ::memcpy(g_methodsTable + 44 + 19 + 9, *(uint150_t**)commandList, 60 * sizeof(uint150_t)); + ::memcpy(g_methodsTable + 44 + 19 + 9 + 60, *(uint150_t**)swapChain, 18 * sizeof(uint150_t)); + +#if KIERO_USE_MINHOOK + MH_Initialize(); +#endif + + device->Release(); + device = NULL; + + commandQueue->Release(); + commandQueue = NULL; + + commandAllocator->Release(); + commandAllocator = NULL; + + commandList->Release(); + commandList = NULL; + + swapChain->Release(); + swapChain = NULL; + + ::DestroyWindow(window); + ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + + g_renderType = RenderType::D3D12; + + return Status::Success; +#endif + } + + ::DestroyWindow(window); + ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + + return Status::NotSupportedError; + } + else if (_renderType == RenderType::OpenGL) + { +#if KIERO_INCLUDE_OPENGL + HMODULE libOpenGL32; + if ((libOpenGL32 = ::GetModuleHandle(KIERO_TEXT("opengl32.dll"))) == NULL) + { + return Status::ModuleNotFoundError; + } + + const char* const methodsNames[] = { + "glAccum", "glAlphaFunc", "glAreTexturesResident", "glArrayElement", "glBegin", "glBindTexture", "glBitmap", "glBlendFunc", "glCallList", "glCallLists", "glClear", "glClearAccum", + "glClearColor", "glClearDepth", "glClearIndex", "glClearStencil", "glClipPlane", "glColor3b", "glColor3bv", "glColor3d", "glColor3dv", "glColor3f", "glColor3fv", "glColor3i", "glColor3iv", + "glColor3s", "glColor3sv", "glColor3ub", "glColor3ubv", "glColor3ui", "glColor3uiv", "glColor3us", "glColor3usv", "glColor4b", "glColor4bv", "glColor4d", "glColor4dv", "glColor4f", + "glColor4fv", "glColor4i", "glColor4iv", "glColor4s", "glColor4sv", "glColor4ub", "glColor4ubv", "glColor4ui", "glColor4uiv", "glColor4us", "glColor4usv", "glColorMask", "glColorMaterial", + "glColorPointer", "glCopyPixels", "glCopyTexImage1D", "glCopyTexImage2D", "glCopyTexSubImage1D", "glCopyTexSubImage2D", "glCullFaceglCullFace", "glDeleteLists", "glDeleteTextures", + "glDepthFunc", "glDepthMask", "glDepthRange", "glDisable", "glDisableClientState", "glDrawArrays", "glDrawBuffer", "glDrawElements", "glDrawPixels", "glEdgeFlag", "glEdgeFlagPointer", + "glEdgeFlagv", "glEnable", "glEnableClientState", "glEnd", "glEndList", "glEvalCoord1d", "glEvalCoord1dv", "glEvalCoord1f", "glEvalCoord1fv", "glEvalCoord2d", "glEvalCoord2dv", + "glEvalCoord2f", "glEvalCoord2fv", "glEvalMesh1", "glEvalMesh2", "glEvalPoint1", "glEvalPoint2", "glFeedbackBuffer", "glFinish", "glFlush", "glFogf", "glFogfv", "glFogi", "glFogiv", + "glFrontFace", "glFrustum", "glGenLists", "glGenTextures", "glGetBooleanv", "glGetClipPlane", "glGetDoublev", "glGetError", "glGetFloatv", "glGetIntegerv", "glGetLightfv", "glGetLightiv", + "glGetMapdv", "glGetMapfv", "glGetMapiv", "glGetMaterialfv", "glGetMaterialiv", "glGetPixelMapfv", "glGetPixelMapuiv", "glGetPixelMapusv", "glGetPointerv", "glGetPolygonStipple", + "glGetString", "glGetTexEnvfv", "glGetTexEnviv", "glGetTexGendv", "glGetTexGenfv", "glGetTexGeniv", "glGetTexImage", "glGetTexLevelParameterfv", "glGetTexLevelParameteriv", + "glGetTexParameterfv", "glGetTexParameteriv", "glHint", "glIndexMask", "glIndexPointer", "glIndexd", "glIndexdv", "glIndexf", "glIndexfv", "glIndexi", "glIndexiv", "glIndexs", "glIndexsv", + "glIndexub", "glIndexubv", "glInitNames", "glInterleavedArrays", "glIsEnabled", "glIsList", "glIsTexture", "glLightModelf", "glLightModelfv", "glLightModeli", "glLightModeliv", "glLightf", + "glLightfv", "glLighti", "glLightiv", "glLineStipple", "glLineWidth", "glListBase", "glLoadIdentity", "glLoadMatrixd", "glLoadMatrixf", "glLoadName", "glLogicOp", "glMap1d", "glMap1f", + "glMap2d", "glMap2f", "glMapGrid1d", "glMapGrid1f", "glMapGrid2d", "glMapGrid2f", "glMaterialf", "glMaterialfv", "glMateriali", "glMaterialiv", "glMatrixMode", "glMultMatrixd", + "glMultMatrixf", "glNewList", "glNormal3b", "glNormal3bv", "glNormal3d", "glNormal3dv", "glNormal3f", "glNormal3fv", "glNormal3i", "glNormal3iv", "glNormal3s", "glNormal3sv", + "glNormalPointer", "glOrtho", "glPassThrough", "glPixelMapfv", "glPixelMapuiv", "glPixelMapusv", "glPixelStoref", "glPixelStorei", "glPixelTransferf", "glPixelTransferi", "glPixelZoom", + "glPointSize", "glPolygonMode", "glPolygonOffset", "glPolygonStipple", "glPopAttrib", "glPopClientAttrib", "glPopMatrix", "glPopName", "glPrioritizeTextures", "glPushAttrib", + "glPushClientAttrib", "glPushMatrix", "glPushName", "glRasterPos2d", "glRasterPos2dv", "glRasterPos2f", "glRasterPos2fv", "glRasterPos2i", "glRasterPos2iv", "glRasterPos2s", + "glRasterPos2sv", "glRasterPos3d", "glRasterPos3dv", "glRasterPos3f", "glRasterPos3fv", "glRasterPos3i", "glRasterPos3iv", "glRasterPos3s", "glRasterPos3sv", "glRasterPos4d", + "glRasterPos4dv", "glRasterPos4f", "glRasterPos4fv", "glRasterPos4i", "glRasterPos4iv", "glRasterPos4s", "glRasterPos4sv", "glReadBuffer", "glReadPixels", "glRectd", "glRectdv", "glRectf", + "glRectfv", "glRecti", "glRectiv", "glRects", "glRectsv", "glRenderMode", "glRotated", "glRotatef", "glScaled", "glScalef", "glScissor", "glSelectBuffer", "glShadeModel", "glStencilFunc", + "glStencilMask", "glStencilOp", "glTexCoord1d", "glTexCoord1dv", "glTexCoord1f", "glTexCoord1fv", "glTexCoord1i", "glTexCoord1iv", "glTexCoord1s", "glTexCoord1sv", "glTexCoord2d", + "glTexCoord2dv", "glTexCoord2f", "glTexCoord2fv", "glTexCoord2i", "glTexCoord2iv", "glTexCoord2s", "glTexCoord2sv", "glTexCoord3d", "glTexCoord3dv", "glTexCoord3f", "glTexCoord3fv", + "glTexCoord3i", "glTexCoord3iv", "glTexCoord3s", "glTexCoord3sv", "glTexCoord4d", "glTexCoord4dv", "glTexCoord4f", "glTexCoord4fv", "glTexCoord4i", "glTexCoord4iv", "glTexCoord4s", + "glTexCoord4sv", "glTexCoordPointer", "glTexEnvf", "glTexEnvfv", "glTexEnvi", "glTexEnviv", "glTexGend", "glTexGendv", "glTexGenf", "glTexGenfv", "glTexGeni", "glTexGeniv", "glTexImage1D", + "glTexImage2D", "glTexParameterf", "glTexParameterfv", "glTexParameteri", "glTexParameteriv", "glTexSubImage1D", "glTexSubImage2D", "glTranslated", "glTranslatef", "glVertex2d", + "glVertex2dv", "glVertex2f", "glVertex2fv", "glVertex2i", "glVertex2iv", "glVertex2s", "glVertex2sv", "glVertex3d", "glVertex3dv", "glVertex3f", "glVertex3fv", "glVertex3i", "glVertex3iv", + "glVertex3s", "glVertex3sv", "glVertex4d", "glVertex4dv", "glVertex4f", "glVertex4fv", "glVertex4i", "glVertex4iv", "glVertex4s", "glVertex4sv", "glVertexPointer", "glViewport" + }; + + size_t size = KIERO_ARRAY_SIZE(methodsNames); + + g_methodsTable = (uint150_t*)::calloc(size, sizeof(uint150_t)); + + for (int i = 0; i < size; i++) + { + g_methodsTable[i] = (uint150_t)::GetProcAddress(libOpenGL32, methodsNames[i]); + } + +#if KIERO_USE_MINHOOK + MH_Initialize(); +#endif + + g_renderType = RenderType::OpenGL; + + return Status::Success; +#endif + } + else if (_renderType == RenderType::Vulkan) + { +#if KIERO_INCLUDE_VULKAN + HMODULE libVulkan; + if ((libVulkan = GetModuleHandle(KIERO_TEXT("vulcan-1.dll"))) == NULL) + { + return Status::ModuleNotFoundError; + } + + const char* const methodsNames[] = { + "vkCreateInstance", "vkDestroyInstance", "vkEnumeratePhysicalDevices", "vkGetPhysicalDeviceFeatures", "vkGetPhysicalDeviceFormatProperties", "vkGetPhysicalDeviceImageFormatProperties", + "vkGetPhysicalDeviceProperties", "vkGetPhysicalDeviceQueueFamilyProperties", "vkGetPhysicalDeviceMemoryProperties", "vkGetInstanceProcAddr", "vkGetDeviceProcAddr", "vkCreateDevice", + "vkDestroyDevice", "vkEnumerateInstanceExtensionProperties", "vkEnumerateDeviceExtensionProperties", "vkEnumerateDeviceLayerProperties", "vkGetDeviceQueue", "vkQueueSubmit", "vkQueueWaitIdle", + "vkDeviceWaitIdle", "vkAllocateMemory", "vkFreeMemory", "vkMapMemory", "vkUnmapMemory", "vkFlushMappedMemoryRanges", "vkInvalidateMappedMemoryRanges", "vkGetDeviceMemoryCommitment", + "vkBindBufferMemory", "vkBindImageMemory", "vkGetBufferMemoryRequirements", "vkGetImageMemoryRequirements", "vkGetImageSparseMemoryRequirements", "vkGetPhysicalDeviceSparseImageFormatProperties", + "vkQueueBindSparse", "vkCreateFence", "vkDestroyFence", "vkResetFences", "vkGetFenceStatus", "vkWaitForFences", "vkCreateSemaphore", "vkDestroySemaphore", "vkCreateEvent", "vkDestroyEvent", + "vkGetEventStatus", "vkSetEvent", "vkResetEvent", "vkCreateQueryPool", "vkDestroyQueryPool", "vkGetQueryPoolResults", "vkCreateBuffer", "vkDestroyBuffer", "vkCreateBufferView", "vkDestroyBufferView", + "vkCreateImage", "vkDestroyImage", "vkGetImageSubresourceLayout", "vkCreateImageView", "vkDestroyImageView", "vkCreateShaderModule", "vkDestroyShaderModule", "vkCreatePipelineCache", + "vkDestroyPipelineCache", "vkGetPipelineCacheData", "vkMergePipelineCaches", "vkCreateGraphicsPipelines", "vkCreateComputePipelines", "vkDestroyPipeline", "vkCreatePipelineLayout", + "vkDestroyPipelineLayout", "vkCreateSampler", "vkDestroySampler", "vkCreateDescriptorSetLayout", "vkDestroyDescriptorSetLayout", "vkCreateDescriptorPool", "vkDestroyDescriptorPool", + "vkResetDescriptorPool", "vkAllocateDescriptorSets", "vkFreeDescriptorSets", "vkUpdateDescriptorSets", "vkCreateFramebuffer", "vkDestroyFramebuffer", "vkCreateRenderPass", "vkDestroyRenderPass", + "vkGetRenderAreaGranularity", "vkCreateCommandPool", "vkDestroyCommandPool", "vkResetCommandPool", "vkAllocateCommandBuffers", "vkFreeCommandBuffers", "vkBeginCommandBuffer", "vkEndCommandBuffer", + "vkResetCommandBuffer", "vkCmdBindPipeline", "vkCmdSetViewport", "vkCmdSetScissor", "vkCmdSetLineWidth", "vkCmdSetDepthBias", "vkCmdSetBlendConstants", "vkCmdSetDepthBounds", + "vkCmdSetStencilCompareMask", "vkCmdSetStencilWriteMask", "vkCmdSetStencilReference", "vkCmdBindDescriptorSets", "vkCmdBindIndexBuffer", "vkCmdBindVertexBuffers", "vkCmdDraw", "vkCmdDrawIndexed", + "vkCmdDrawIndirect", "vkCmdDrawIndexedIndirect", "vkCmdDispatch", "vkCmdDispatchIndirect", "vkCmdCopyBuffer", "vkCmdCopyImage", "vkCmdBlitImage", "vkCmdCopyBufferToImage", "vkCmdCopyImageToBuffer", + "vkCmdUpdateBuffer", "vkCmdFillBuffer", "vkCmdClearColorImage", "vkCmdClearDepthStencilImage", "vkCmdClearAttachments", "vkCmdResolveImage", "vkCmdSetEvent", "vkCmdResetEvent", "vkCmdWaitEvents", + "vkCmdPipelineBarrier", "vkCmdBeginQuery", "vkCmdEndQuery", "vkCmdResetQueryPool", "vkCmdWriteTimestamp", "vkCmdCopyQueryPoolResults", "vkCmdPushConstants", "vkCmdBeginRenderPass", "vkCmdNextSubpass", + "vkCmdEndRenderPass", "vkCmdExecuteCommands" + }; + + size_t size = KIERO_ARRAY_SIZE(methodsNames); + + g_methodsTable = (uint150_t*)::calloc(size, sizeof(uint150_t)); + + for (int i = 0; i < size; i++) + { + g_methodsTable[i] = (uint150_t)::GetProcAddress(libVulkan, methodsNames[i]); + } + +#if KIERO_USE_MINHOOK + MH_Initialize(); +#endif + + g_renderType = RenderType::Vulkan; + + return Status::Success; +#endif + } + else if (_renderType == RenderType::Auto) + { + RenderType::Enum type = RenderType::None; + + if (::GetModuleHandle(KIERO_TEXT("d3d9.dll")) != NULL) + { + type = RenderType::D3D9; + } + else if (::GetModuleHandle(KIERO_TEXT("d3d10.dll")) != NULL) + { + type = RenderType::D3D10; + } + else if (::GetModuleHandle(KIERO_TEXT("d3d11.dll")) != NULL) + { + type = RenderType::D3D11; + } + else if (::GetModuleHandle(KIERO_TEXT("d3d12.dll")) != NULL) + { + type = RenderType::D3D12; + } + else if (::GetModuleHandle(KIERO_TEXT("opengl32.dll")) != NULL) + { + type = RenderType::OpenGL; + } + else if (::GetModuleHandle(KIERO_TEXT("vulcan-1.dll")) != NULL) + { + type = RenderType::Vulkan; + } + + return init(type); + } + } + + return Status::Success; +} + +void kiero::shutdown() +{ + if (g_renderType != RenderType::None) + { +#if KIERO_USE_MINHOOK + MH_DisableHook(MH_ALL_HOOKS); +#endif + + ::free(g_methodsTable); + g_methodsTable = NULL; + g_renderType = RenderType::None; + } +} + +kiero::Status::Enum kiero::bind(uint16_t _index, void** _original, void* _function) +{ + // TODO: Need own detour function + + assert(_index >= 0 && _original != NULL && _function != NULL); + + if (g_renderType != RenderType::None) + { +#if KIERO_USE_MINHOOK + void* target = (void*)g_methodsTable[_index]; + if (MH_CreateHook(target, _function, _original) != MH_OK || MH_EnableHook(target) != MH_OK) + { + return Status::UnknownError; + } +#endif + + return Status::Success; + } + + return Status::NotInitializedError; +} + +void kiero::unbind(uint16_t _index) +{ + assert(_index >= 0); + + if (g_renderType != RenderType::None) + { +#if KIERO_USE_MINHOOK + MH_DisableHook((void*)g_methodsTable[_index]); +#endif + } +} + +kiero::RenderType::Enum kiero::getRenderType() +{ + return g_renderType; +} + +uint150_t* kiero::getMethodsTable() +{ + return g_methodsTable; +} \ No newline at end of file diff --git a/depthrush/kiero/kiero.h b/depthrush/kiero/kiero.h new file mode 100644 index 0000000..f75f6a2 --- /dev/null +++ b/depthrush/kiero/kiero.h @@ -0,0 +1,78 @@ +#ifndef __KIERO_H__ +#define __KIERO_H__ + +#include + +#define KIERO_VERSION "1.2.6" + +#define KIERO_INCLUDE_D3D9 0 // 1 if you need D3D9 hook +#define KIERO_INCLUDE_D3D10 0 // 1 if you need D3D10 hook +#define KIERO_INCLUDE_D3D11 1 // 1 if you need D3D11 hook +#define KIERO_INCLUDE_D3D12 0 // 1 if you need D3D12 hook +#define KIERO_INCLUDE_OPENGL 0 // 1 if you need OpenGL hook +#define KIERO_INCLUDE_VULKAN 0 // 1 if you need Vulkan hook +#define KIERO_USE_MINHOOK 1 // 1 if you will use kiero::bind function + +#define KIERO_ARCH_X64 0 +#define KIERO_ARCH_X86 0 + +#if defined(_M_X64) +# undef KIERO_ARCH_X64 +# define KIERO_ARCH_X64 1 +#else +# undef KIERO_ARCH_X86 +# define KIERO_ARCH_X86 1 +#endif + +#if KIERO_ARCH_X64 +typedef uint64_t uint150_t; +#else +typedef uint32_t uint150_t; +#endif + +namespace kiero +{ + struct Status + { + enum Enum + { + UnknownError = -1, + NotSupportedError = -2, + ModuleNotFoundError = -3, + + AlreadyInitializedError = -4, + NotInitializedError = -5, + + Success = 0, + }; + }; + + struct RenderType + { + enum Enum + { + None, + + D3D9, + D3D10, + D3D11, + D3D12, + + OpenGL, + Vulkan, + + Auto + }; + }; + + Status::Enum init(RenderType::Enum renderType); + void shutdown(); + + Status::Enum bind(uint16_t index, void** original, void* function); + void unbind(uint16_t index); + + RenderType::Enum getRenderType(); + uint150_t* getMethodsTable(); +} + +#endif // __KIERO_H__ \ No newline at end of file diff --git a/depthrush/kiero/minhook/dll_resources/MinHook.def b/depthrush/kiero/minhook/dll_resources/MinHook.def new file mode 100644 index 0000000..c6af698 --- /dev/null +++ b/depthrush/kiero/minhook/dll_resources/MinHook.def @@ -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 diff --git a/depthrush/kiero/minhook/dll_resources/MinHook.rc b/depthrush/kiero/minhook/dll_resources/MinHook.rc new file mode 100644 index 0000000..7cbacb7 --- /dev/null +++ b/depthrush/kiero/minhook/dll_resources/MinHook.rc @@ -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 diff --git a/depthrush/kiero/minhook/include/MinHook.h b/depthrush/kiero/minhook/include/MinHook.h new file mode 100644 index 0000000..15c0a87 --- /dev/null +++ b/depthrush/kiero/minhook/include/MinHook.h @@ -0,0 +1,186 @@ +/* + * MinHook - The Minimalistic API Hooking Library for x64/x86 + * Copyright (C) 2009-2017 Tsuda Kageyu. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if !(defined _M_IX86) && !(defined _M_X64) && !(defined __i386__) && !(defined __x86_64__) + #error MinHook supports only x86 and x64 systems. +#endif + +#include + +// MinHook Error Codes. +typedef enum MH_STATUS +{ + // Unknown error. Should not be returned. + MH_UNKNOWN = -1, + + // Successful. + MH_OK = 0, + + // MinHook is already initialized. + MH_ERROR_ALREADY_INITIALIZED, + + // MinHook is not initialized yet, or already uninitialized. + MH_ERROR_NOT_INITIALIZED, + + // The hook for the specified target function is already created. + MH_ERROR_ALREADY_CREATED, + + // The hook for the specified target function is not created yet. + MH_ERROR_NOT_CREATED, + + // The hook for the specified target function is already enabled. + MH_ERROR_ENABLED, + + // The hook for the specified target function is not enabled yet, or already + // disabled. + MH_ERROR_DISABLED, + + // The specified pointer is invalid. It points the address of non-allocated + // and/or non-executable region. + MH_ERROR_NOT_EXECUTABLE, + + // The specified target function cannot be hooked. + MH_ERROR_UNSUPPORTED_FUNCTION, + + // Failed to allocate memory. + MH_ERROR_MEMORY_ALLOC, + + // Failed to change the memory protection. + MH_ERROR_MEMORY_PROTECT, + + // The specified module is not loaded. + MH_ERROR_MODULE_NOT_FOUND, + + // The specified function is not found. + MH_ERROR_FUNCTION_NOT_FOUND +} +MH_STATUS; + +// Can be passed as a parameter to MH_EnableHook, MH_DisableHook, +// MH_QueueEnableHook or MH_QueueDisableHook. +#define MH_ALL_HOOKS NULL + +#ifdef __cplusplus +extern "C" { +#endif + + // Initialize the MinHook library. You must call this function EXACTLY ONCE + // at the beginning of your program. + MH_STATUS WINAPI MH_Initialize(VOID); + + // Uninitialize the MinHook library. You must call this function EXACTLY + // ONCE at the end of your program. + MH_STATUS WINAPI MH_Uninitialize(VOID); + + // Creates a Hook for the specified target function, in disabled state. + // Parameters: + // pTarget [in] A pointer to the target function, which will be + // overridden by the detour function. + // pDetour [in] A pointer to the detour function, which will override + // the target function. + // ppOriginal [out] A pointer to the trampoline function, which will be + // used to call the original target function. + // This parameter can be NULL. + MH_STATUS WINAPI MH_CreateHook(LPVOID pTarget, LPVOID pDetour, LPVOID *ppOriginal); + + // Creates a Hook for the specified API function, in disabled state. + // Parameters: + // pszModule [in] A pointer to the loaded module name which contains the + // target function. + // pszTarget [in] A pointer to the target function name, which will be + // overridden by the detour function. + // pDetour [in] A pointer to the detour function, which will override + // the target function. + // ppOriginal [out] A pointer to the trampoline function, which will be + // used to call the original target function. + // This parameter can be NULL. + MH_STATUS WINAPI MH_CreateHookApi( + LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, LPVOID *ppOriginal); + + // Creates a Hook for the specified API function, in disabled state. + // Parameters: + // pszModule [in] A pointer to the loaded module name which contains the + // target function. + // pszTarget [in] A pointer to the target function name, which will be + // overridden by the detour function. + // pDetour [in] A pointer to the detour function, which will override + // the target function. + // ppOriginal [out] A pointer to the trampoline function, which will be + // used to call the original target function. + // This parameter can be NULL. + // ppTarget [out] A pointer to the target function, which will be used + // with other functions. + // This parameter can be NULL. + MH_STATUS WINAPI MH_CreateHookApiEx( + LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, LPVOID *ppOriginal, LPVOID *ppTarget); + + // Removes an already created hook. + // Parameters: + // pTarget [in] A pointer to the target function. + MH_STATUS WINAPI MH_RemoveHook(LPVOID pTarget); + + // Enables an already created hook. + // Parameters: + // pTarget [in] A pointer to the target function. + // If this parameter is MH_ALL_HOOKS, all created hooks are + // enabled in one go. + MH_STATUS WINAPI MH_EnableHook(LPVOID pTarget); + + // Disables an already created hook. + // Parameters: + // pTarget [in] A pointer to the target function. + // If this parameter is MH_ALL_HOOKS, all created hooks are + // disabled in one go. + MH_STATUS WINAPI MH_DisableHook(LPVOID pTarget); + + // Queues to enable an already created hook. + // Parameters: + // pTarget [in] A pointer to the target function. + // If this parameter is MH_ALL_HOOKS, all created hooks are + // queued to be enabled. + MH_STATUS WINAPI MH_QueueEnableHook(LPVOID pTarget); + + // Queues to disable an already created hook. + // Parameters: + // pTarget [in] A pointer to the target function. + // If this parameter is MH_ALL_HOOKS, all created hooks are + // queued to be disabled. + MH_STATUS WINAPI MH_QueueDisableHook(LPVOID pTarget); + + // Applies all queued changes in one go. + MH_STATUS WINAPI MH_ApplyQueued(VOID); + + // Translates the MH_STATUS to its name as a string. + const char * WINAPI MH_StatusToString(MH_STATUS status); + +#ifdef __cplusplus +} +#endif + diff --git a/depthrush/kiero/minhook/src/buffer.c b/depthrush/kiero/minhook/src/buffer.c new file mode 100644 index 0000000..8f9fbce --- /dev/null +++ b/depthrush/kiero/minhook/src/buffer.c @@ -0,0 +1,312 @@ +/* + * MinHook - The Minimalistic API Hooking Library for x64/x86 + * Copyright (C) 2009-2017 Tsuda Kageyu. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include "buffer.h" + +// Size of each memory block. (= page size of VirtualAlloc) +#define MEMORY_BLOCK_SIZE 0x1000 + +// Max range for seeking a memory block. (= 1024MB) +#define MAX_MEMORY_RANGE 0x40000000 + +// Memory protection flags to check the executable address. +#define PAGE_EXECUTE_FLAGS \ + (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY) + +// Memory slot. +typedef struct _MEMORY_SLOT +{ + union + { + struct _MEMORY_SLOT *pNext; + UINT8 buffer[MEMORY_SLOT_SIZE]; + }; +} MEMORY_SLOT, *PMEMORY_SLOT; + +// Memory block info. Placed at the head of each block. +typedef struct _MEMORY_BLOCK +{ + struct _MEMORY_BLOCK *pNext; + PMEMORY_SLOT pFree; // First element of the free slot list. + UINT usedCount; +} MEMORY_BLOCK, *PMEMORY_BLOCK; + +//------------------------------------------------------------------------- +// Global Variables: +//------------------------------------------------------------------------- + +// First element of the memory block list. +PMEMORY_BLOCK g_pMemoryBlocks; + +//------------------------------------------------------------------------- +VOID InitializeBuffer(VOID) +{ + // Nothing to do for now. +} + +//------------------------------------------------------------------------- +VOID UninitializeBuffer(VOID) +{ + PMEMORY_BLOCK pBlock = g_pMemoryBlocks; + g_pMemoryBlocks = NULL; + + while (pBlock) + { + PMEMORY_BLOCK pNext = pBlock->pNext; + VirtualFree(pBlock, 0, MEM_RELEASE); + pBlock = pNext; + } +} + +//------------------------------------------------------------------------- +#if defined(_M_X64) || defined(__x86_64__) +static LPVOID FindPrevFreeRegion(LPVOID pAddress, LPVOID pMinAddr, DWORD dwAllocationGranularity) +{ + ULONG_PTR tryAddr = (ULONG_PTR)pAddress; + + // Round down to the allocation granularity. + tryAddr -= tryAddr % dwAllocationGranularity; + + // Start from the previous allocation granularity multiply. + tryAddr -= dwAllocationGranularity; + + while (tryAddr >= (ULONG_PTR)pMinAddr) + { + MEMORY_BASIC_INFORMATION mbi; + if (VirtualQuery((LPVOID)tryAddr, &mbi, sizeof(mbi)) == 0) + break; + + if (mbi.State == MEM_FREE) + return (LPVOID)tryAddr; + + if ((ULONG_PTR)mbi.AllocationBase < dwAllocationGranularity) + break; + + tryAddr = (ULONG_PTR)mbi.AllocationBase - dwAllocationGranularity; + } + + return NULL; +} +#endif + +//------------------------------------------------------------------------- +#if defined(_M_X64) || defined(__x86_64__) +static LPVOID FindNextFreeRegion(LPVOID pAddress, LPVOID pMaxAddr, DWORD dwAllocationGranularity) +{ + ULONG_PTR tryAddr = (ULONG_PTR)pAddress; + + // Round down to the allocation granularity. + tryAddr -= tryAddr % dwAllocationGranularity; + + // Start from the next allocation granularity multiply. + tryAddr += dwAllocationGranularity; + + while (tryAddr <= (ULONG_PTR)pMaxAddr) + { + MEMORY_BASIC_INFORMATION mbi; + if (VirtualQuery((LPVOID)tryAddr, &mbi, sizeof(mbi)) == 0) + break; + + if (mbi.State == MEM_FREE) + return (LPVOID)tryAddr; + + tryAddr = (ULONG_PTR)mbi.BaseAddress + mbi.RegionSize; + + // Round up to the next allocation granularity. + tryAddr += dwAllocationGranularity - 1; + tryAddr -= tryAddr % dwAllocationGranularity; + } + + return NULL; +} +#endif + +//------------------------------------------------------------------------- +static PMEMORY_BLOCK GetMemoryBlock(LPVOID pOrigin) +{ + PMEMORY_BLOCK pBlock; +#if defined(_M_X64) || defined(__x86_64__) + ULONG_PTR minAddr; + ULONG_PTR maxAddr; + + SYSTEM_INFO si; + GetSystemInfo(&si); + minAddr = (ULONG_PTR)si.lpMinimumApplicationAddress; + maxAddr = (ULONG_PTR)si.lpMaximumApplicationAddress; + + // pOrigin ± 512MB + if ((ULONG_PTR)pOrigin > MAX_MEMORY_RANGE && minAddr < (ULONG_PTR)pOrigin - MAX_MEMORY_RANGE) + minAddr = (ULONG_PTR)pOrigin - MAX_MEMORY_RANGE; + + if (maxAddr > (ULONG_PTR)pOrigin + MAX_MEMORY_RANGE) + maxAddr = (ULONG_PTR)pOrigin + MAX_MEMORY_RANGE; + + // Make room for MEMORY_BLOCK_SIZE bytes. + maxAddr -= MEMORY_BLOCK_SIZE - 1; +#endif + + // Look the registered blocks for a reachable one. + for (pBlock = g_pMemoryBlocks; pBlock != NULL; pBlock = pBlock->pNext) + { +#if defined(_M_X64) || defined(__x86_64__) + // Ignore the blocks too far. + if ((ULONG_PTR)pBlock < minAddr || (ULONG_PTR)pBlock >= maxAddr) + continue; +#endif + // The block has at least one unused slot. + if (pBlock->pFree != NULL) + return pBlock; + } + +#if defined(_M_X64) || defined(__x86_64__) + // Alloc a new block above if not found. + { + LPVOID pAlloc = pOrigin; + while ((ULONG_PTR)pAlloc >= minAddr) + { + pAlloc = FindPrevFreeRegion(pAlloc, (LPVOID)minAddr, si.dwAllocationGranularity); + if (pAlloc == NULL) + break; + + pBlock = (PMEMORY_BLOCK)VirtualAlloc( + pAlloc, MEMORY_BLOCK_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); + if (pBlock != NULL) + break; + } + } + + // Alloc a new block below if not found. + if (pBlock == NULL) + { + LPVOID pAlloc = pOrigin; + while ((ULONG_PTR)pAlloc <= maxAddr) + { + pAlloc = FindNextFreeRegion(pAlloc, (LPVOID)maxAddr, si.dwAllocationGranularity); + if (pAlloc == NULL) + break; + + pBlock = (PMEMORY_BLOCK)VirtualAlloc( + pAlloc, MEMORY_BLOCK_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); + if (pBlock != NULL) + break; + } + } +#else + // In x86 mode, a memory block can be placed anywhere. + pBlock = (PMEMORY_BLOCK)VirtualAlloc( + NULL, MEMORY_BLOCK_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); +#endif + + if (pBlock != NULL) + { + // Build a linked list of all the slots. + PMEMORY_SLOT pSlot = (PMEMORY_SLOT)pBlock + 1; + pBlock->pFree = NULL; + pBlock->usedCount = 0; + do + { + pSlot->pNext = pBlock->pFree; + pBlock->pFree = pSlot; + pSlot++; + } while ((ULONG_PTR)pSlot - (ULONG_PTR)pBlock <= MEMORY_BLOCK_SIZE - MEMORY_SLOT_SIZE); + + pBlock->pNext = g_pMemoryBlocks; + g_pMemoryBlocks = pBlock; + } + + return pBlock; +} + +//------------------------------------------------------------------------- +LPVOID AllocateBuffer(LPVOID pOrigin) +{ + PMEMORY_SLOT pSlot; + PMEMORY_BLOCK pBlock = GetMemoryBlock(pOrigin); + if (pBlock == NULL) + return NULL; + + // Remove an unused slot from the list. + pSlot = pBlock->pFree; + pBlock->pFree = pSlot->pNext; + pBlock->usedCount++; +#ifdef _DEBUG + // Fill the slot with INT3 for debugging. + memset(pSlot, 0xCC, sizeof(MEMORY_SLOT)); +#endif + return pSlot; +} + +//------------------------------------------------------------------------- +VOID FreeBuffer(LPVOID pBuffer) +{ + PMEMORY_BLOCK pBlock = g_pMemoryBlocks; + PMEMORY_BLOCK pPrev = NULL; + ULONG_PTR pTargetBlock = ((ULONG_PTR)pBuffer / MEMORY_BLOCK_SIZE) * MEMORY_BLOCK_SIZE; + + while (pBlock != NULL) + { + if ((ULONG_PTR)pBlock == pTargetBlock) + { + PMEMORY_SLOT pSlot = (PMEMORY_SLOT)pBuffer; +#ifdef _DEBUG + // Clear the released slot for debugging. + memset(pSlot, 0x00, sizeof(*pSlot)); +#endif + // Restore the released slot to the list. + pSlot->pNext = pBlock->pFree; + pBlock->pFree = pSlot; + pBlock->usedCount--; + + // Free if unused. + if (pBlock->usedCount == 0) + { + if (pPrev) + pPrev->pNext = pBlock->pNext; + else + g_pMemoryBlocks = pBlock->pNext; + + VirtualFree(pBlock, 0, MEM_RELEASE); + } + + break; + } + + pPrev = pBlock; + pBlock = pBlock->pNext; + } +} + +//------------------------------------------------------------------------- +BOOL IsExecutableAddress(LPVOID pAddress) +{ + MEMORY_BASIC_INFORMATION mi; + VirtualQuery(pAddress, &mi, sizeof(mi)); + + return (mi.State == MEM_COMMIT && (mi.Protect & PAGE_EXECUTE_FLAGS)); +} diff --git a/depthrush/kiero/minhook/src/buffer.h b/depthrush/kiero/minhook/src/buffer.h new file mode 100644 index 0000000..204d551 --- /dev/null +++ b/depthrush/kiero/minhook/src/buffer.h @@ -0,0 +1,42 @@ +/* + * MinHook - The Minimalistic API Hooking Library for x64/x86 + * Copyright (C) 2009-2017 Tsuda Kageyu. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +// Size of each memory slot. +#if defined(_M_X64) || defined(__x86_64__) + #define MEMORY_SLOT_SIZE 64 +#else + #define MEMORY_SLOT_SIZE 32 +#endif + +VOID InitializeBuffer(VOID); +VOID UninitializeBuffer(VOID); +LPVOID AllocateBuffer(LPVOID pOrigin); +VOID FreeBuffer(LPVOID pBuffer); +BOOL IsExecutableAddress(LPVOID pAddress); diff --git a/depthrush/kiero/minhook/src/hde/hde32.c b/depthrush/kiero/minhook/src/hde/hde32.c new file mode 100644 index 0000000..08fa25b --- /dev/null +++ b/depthrush/kiero/minhook/src/hde/hde32.c @@ -0,0 +1,326 @@ +/* + * Hacker Disassembler Engine 32 C + * Copyright (c) 2008-2009, Vyacheslav Patkov. + * All rights reserved. + * + */ + +#if defined(_M_IX86) || defined(__i386__) + +#include "hde32.h" +#include "table32.h" + +unsigned int hde32_disasm(const void *code, hde32s *hs) +{ + uint8_t x, c, *p = (uint8_t *)code, cflags, opcode, pref = 0; + uint8_t *ht = hde32_table, m_mod, m_reg, m_rm, disp_size = 0; + + // Avoid using memset to reduce the footprint. +#ifndef _MSC_VER + memset((LPBYTE)hs, 0, sizeof(hde32s)); +#else + __stosb((LPBYTE)hs, 0, sizeof(hde32s)); +#endif + + for (x = 16; x; x--) + switch (c = *p++) { + case 0xf3: + hs->p_rep = c; + pref |= PRE_F3; + break; + case 0xf2: + hs->p_rep = c; + pref |= PRE_F2; + break; + case 0xf0: + hs->p_lock = c; + pref |= PRE_LOCK; + break; + case 0x26: case 0x2e: case 0x36: + case 0x3e: case 0x64: case 0x65: + hs->p_seg = c; + pref |= PRE_SEG; + break; + case 0x66: + hs->p_66 = c; + pref |= PRE_66; + break; + case 0x67: + hs->p_67 = c; + pref |= PRE_67; + break; + default: + goto pref_done; + } + pref_done: + + hs->flags = (uint32_t)pref << 23; + + if (!pref) + pref |= PRE_NONE; + + if ((hs->opcode = c) == 0x0f) { + hs->opcode2 = c = *p++; + ht += DELTA_OPCODES; + } else if (c >= 0xa0 && c <= 0xa3) { + if (pref & PRE_67) + pref |= PRE_66; + else + pref &= ~PRE_66; + } + + opcode = c; + cflags = ht[ht[opcode / 4] + (opcode % 4)]; + + if (cflags == C_ERROR) { + hs->flags |= F_ERROR | F_ERROR_OPCODE; + cflags = 0; + if ((opcode & -3) == 0x24) + cflags++; + } + + x = 0; + if (cflags & C_GROUP) { + uint16_t t; + t = *(uint16_t *)(ht + (cflags & 0x7f)); + cflags = (uint8_t)t; + x = (uint8_t)(t >> 8); + } + + if (hs->opcode2) { + ht = hde32_table + DELTA_PREFIXES; + if (ht[ht[opcode / 4] + (opcode % 4)] & pref) + hs->flags |= F_ERROR | F_ERROR_OPCODE; + } + + if (cflags & C_MODRM) { + hs->flags |= F_MODRM; + hs->modrm = c = *p++; + hs->modrm_mod = m_mod = c >> 6; + hs->modrm_rm = m_rm = c & 7; + hs->modrm_reg = m_reg = (c & 0x3f) >> 3; + + if (x && ((x << m_reg) & 0x80)) + hs->flags |= F_ERROR | F_ERROR_OPCODE; + + if (!hs->opcode2 && opcode >= 0xd9 && opcode <= 0xdf) { + uint8_t t = opcode - 0xd9; + if (m_mod == 3) { + ht = hde32_table + DELTA_FPU_MODRM + t*8; + t = ht[m_reg] << m_rm; + } else { + ht = hde32_table + DELTA_FPU_REG; + t = ht[t] << m_reg; + } + if (t & 0x80) + hs->flags |= F_ERROR | F_ERROR_OPCODE; + } + + if (pref & PRE_LOCK) { + if (m_mod == 3) { + hs->flags |= F_ERROR | F_ERROR_LOCK; + } else { + uint8_t *table_end, op = opcode; + if (hs->opcode2) { + ht = hde32_table + DELTA_OP2_LOCK_OK; + table_end = ht + DELTA_OP_ONLY_MEM - DELTA_OP2_LOCK_OK; + } else { + ht = hde32_table + DELTA_OP_LOCK_OK; + table_end = ht + DELTA_OP2_LOCK_OK - DELTA_OP_LOCK_OK; + op &= -2; + } + for (; ht != table_end; ht++) + if (*ht++ == op) { + if (!((*ht << m_reg) & 0x80)) + goto no_lock_error; + else + break; + } + hs->flags |= F_ERROR | F_ERROR_LOCK; + no_lock_error: + ; + } + } + + if (hs->opcode2) { + switch (opcode) { + case 0x20: case 0x22: + m_mod = 3; + if (m_reg > 4 || m_reg == 1) + goto error_operand; + else + goto no_error_operand; + case 0x21: case 0x23: + m_mod = 3; + if (m_reg == 4 || m_reg == 5) + goto error_operand; + else + goto no_error_operand; + } + } else { + switch (opcode) { + case 0x8c: + if (m_reg > 5) + goto error_operand; + else + goto no_error_operand; + case 0x8e: + if (m_reg == 1 || m_reg > 5) + goto error_operand; + else + goto no_error_operand; + } + } + + if (m_mod == 3) { + uint8_t *table_end; + if (hs->opcode2) { + ht = hde32_table + DELTA_OP2_ONLY_MEM; + table_end = ht + sizeof(hde32_table) - DELTA_OP2_ONLY_MEM; + } else { + ht = hde32_table + DELTA_OP_ONLY_MEM; + table_end = ht + DELTA_OP2_ONLY_MEM - DELTA_OP_ONLY_MEM; + } + for (; ht != table_end; ht += 2) + if (*ht++ == opcode) { + if (*ht++ & pref && !((*ht << m_reg) & 0x80)) + goto error_operand; + else + break; + } + goto no_error_operand; + } else if (hs->opcode2) { + switch (opcode) { + case 0x50: case 0xd7: case 0xf7: + if (pref & (PRE_NONE | PRE_66)) + goto error_operand; + break; + case 0xd6: + if (pref & (PRE_F2 | PRE_F3)) + goto error_operand; + break; + case 0xc5: + goto error_operand; + } + goto no_error_operand; + } else + goto no_error_operand; + + error_operand: + hs->flags |= F_ERROR | F_ERROR_OPERAND; + no_error_operand: + + c = *p++; + if (m_reg <= 1) { + if (opcode == 0xf6) + cflags |= C_IMM8; + else if (opcode == 0xf7) + cflags |= C_IMM_P66; + } + + switch (m_mod) { + case 0: + if (pref & PRE_67) { + if (m_rm == 6) + disp_size = 2; + } else + if (m_rm == 5) + disp_size = 4; + break; + case 1: + disp_size = 1; + break; + case 2: + disp_size = 2; + if (!(pref & PRE_67)) + disp_size <<= 1; + } + + if (m_mod != 3 && m_rm == 4 && !(pref & PRE_67)) { + hs->flags |= F_SIB; + p++; + hs->sib = c; + hs->sib_scale = c >> 6; + hs->sib_index = (c & 0x3f) >> 3; + if ((hs->sib_base = c & 7) == 5 && !(m_mod & 1)) + disp_size = 4; + } + + p--; + switch (disp_size) { + case 1: + hs->flags |= F_DISP8; + hs->disp.disp8 = *p; + break; + case 2: + hs->flags |= F_DISP16; + hs->disp.disp16 = *(uint16_t *)p; + break; + case 4: + hs->flags |= F_DISP32; + hs->disp.disp32 = *(uint32_t *)p; + } + p += disp_size; + } else if (pref & PRE_LOCK) + hs->flags |= F_ERROR | F_ERROR_LOCK; + + if (cflags & C_IMM_P66) { + if (cflags & C_REL32) { + if (pref & PRE_66) { + hs->flags |= F_IMM16 | F_RELATIVE; + hs->imm.imm16 = *(uint16_t *)p; + p += 2; + goto disasm_done; + } + goto rel32_ok; + } + if (pref & PRE_66) { + hs->flags |= F_IMM16; + hs->imm.imm16 = *(uint16_t *)p; + p += 2; + } else { + hs->flags |= F_IMM32; + hs->imm.imm32 = *(uint32_t *)p; + p += 4; + } + } + + if (cflags & C_IMM16) { + if (hs->flags & F_IMM32) { + hs->flags |= F_IMM16; + hs->disp.disp16 = *(uint16_t *)p; + } else if (hs->flags & F_IMM16) { + hs->flags |= F_2IMM16; + hs->disp.disp16 = *(uint16_t *)p; + } else { + hs->flags |= F_IMM16; + hs->imm.imm16 = *(uint16_t *)p; + } + p += 2; + } + if (cflags & C_IMM8) { + hs->flags |= F_IMM8; + hs->imm.imm8 = *p++; + } + + if (cflags & C_REL32) { + rel32_ok: + hs->flags |= F_IMM32 | F_RELATIVE; + hs->imm.imm32 = *(uint32_t *)p; + p += 4; + } else if (cflags & C_REL8) { + hs->flags |= F_IMM8 | F_RELATIVE; + hs->imm.imm8 = *p++; + } + + disasm_done: + + if ((hs->len = (uint8_t)(p-(uint8_t *)code)) > 15) { + hs->flags |= F_ERROR | F_ERROR_LENGTH; + hs->len = 15; + } + + return (unsigned int)hs->len; +} + +#endif // defined(_M_IX86) || defined(__i386__) diff --git a/depthrush/kiero/minhook/src/hde/hde32.h b/depthrush/kiero/minhook/src/hde/hde32.h new file mode 100644 index 0000000..1112450 --- /dev/null +++ b/depthrush/kiero/minhook/src/hde/hde32.h @@ -0,0 +1,105 @@ +/* + * Hacker Disassembler Engine 32 + * Copyright (c) 2006-2009, Vyacheslav Patkov. + * All rights reserved. + * + * hde32.h: C/C++ header file + * + */ + +#ifndef _HDE32_H_ +#define _HDE32_H_ + +/* stdint.h - C99 standard header + * http://en.wikipedia.org/wiki/stdint.h + * + * if your compiler doesn't contain "stdint.h" header (for + * example, Microsoft Visual C++), you can download file: + * http://www.azillionmonkeys.com/qed/pstdint.h + * and change next line to: + * #include "pstdint.h" + */ +#include "pstdint.h" + +#define F_MODRM 0x00000001 +#define F_SIB 0x00000002 +#define F_IMM8 0x00000004 +#define F_IMM16 0x00000008 +#define F_IMM32 0x00000010 +#define F_DISP8 0x00000020 +#define F_DISP16 0x00000040 +#define F_DISP32 0x00000080 +#define F_RELATIVE 0x00000100 +#define F_2IMM16 0x00000800 +#define F_ERROR 0x00001000 +#define F_ERROR_OPCODE 0x00002000 +#define F_ERROR_LENGTH 0x00004000 +#define F_ERROR_LOCK 0x00008000 +#define F_ERROR_OPERAND 0x00010000 +#define F_PREFIX_REPNZ 0x01000000 +#define F_PREFIX_REPX 0x02000000 +#define F_PREFIX_REP 0x03000000 +#define F_PREFIX_66 0x04000000 +#define F_PREFIX_67 0x08000000 +#define F_PREFIX_LOCK 0x10000000 +#define F_PREFIX_SEG 0x20000000 +#define F_PREFIX_ANY 0x3f000000 + +#define PREFIX_SEGMENT_CS 0x2e +#define PREFIX_SEGMENT_SS 0x36 +#define PREFIX_SEGMENT_DS 0x3e +#define PREFIX_SEGMENT_ES 0x26 +#define PREFIX_SEGMENT_FS 0x64 +#define PREFIX_SEGMENT_GS 0x65 +#define PREFIX_LOCK 0xf0 +#define PREFIX_REPNZ 0xf2 +#define PREFIX_REPX 0xf3 +#define PREFIX_OPERAND_SIZE 0x66 +#define PREFIX_ADDRESS_SIZE 0x67 + +#pragma pack(push,1) + +typedef struct { + uint8_t len; + uint8_t p_rep; + uint8_t p_lock; + uint8_t p_seg; + uint8_t p_66; + uint8_t p_67; + uint8_t opcode; + uint8_t opcode2; + uint8_t modrm; + uint8_t modrm_mod; + uint8_t modrm_reg; + uint8_t modrm_rm; + uint8_t sib; + uint8_t sib_scale; + uint8_t sib_index; + uint8_t sib_base; + union { + uint8_t imm8; + uint16_t imm16; + uint32_t imm32; + } imm; + union { + uint8_t disp8; + uint16_t disp16; + uint32_t disp32; + } disp; + uint32_t flags; +} hde32s; + +#pragma pack(pop) + +#ifdef __cplusplus +extern "C" { +#endif + +/* __cdecl */ +unsigned int hde32_disasm(const void *code, hde32s *hs); + +#ifdef __cplusplus +} +#endif + +#endif /* _HDE32_H_ */ diff --git a/depthrush/kiero/minhook/src/hde/hde64.c b/depthrush/kiero/minhook/src/hde/hde64.c new file mode 100644 index 0000000..c23e2fc --- /dev/null +++ b/depthrush/kiero/minhook/src/hde/hde64.c @@ -0,0 +1,337 @@ +/* + * Hacker Disassembler Engine 64 C + * Copyright (c) 2008-2009, Vyacheslav Patkov. + * All rights reserved. + * + */ + +#if defined(_M_X64) || defined(__x86_64__) + +#include "hde64.h" +#include "table64.h" + +unsigned int hde64_disasm(const void *code, hde64s *hs) +{ + uint8_t x, c, *p = (uint8_t *)code, cflags, opcode, pref = 0; + uint8_t *ht = hde64_table, m_mod, m_reg, m_rm, disp_size = 0; + uint8_t op64 = 0; + + // Avoid using memset to reduce the footprint. +#ifndef _MSC_VER + memset((LPBYTE)hs, 0, sizeof(hde64s)); +#else + __stosb((LPBYTE)hs, 0, sizeof(hde64s)); +#endif + + for (x = 16; x; x--) + switch (c = *p++) { + case 0xf3: + hs->p_rep = c; + pref |= PRE_F3; + break; + case 0xf2: + hs->p_rep = c; + pref |= PRE_F2; + break; + case 0xf0: + hs->p_lock = c; + pref |= PRE_LOCK; + break; + case 0x26: case 0x2e: case 0x36: + case 0x3e: case 0x64: case 0x65: + hs->p_seg = c; + pref |= PRE_SEG; + break; + case 0x66: + hs->p_66 = c; + pref |= PRE_66; + break; + case 0x67: + hs->p_67 = c; + pref |= PRE_67; + break; + default: + goto pref_done; + } + pref_done: + + hs->flags = (uint32_t)pref << 23; + + if (!pref) + pref |= PRE_NONE; + + if ((c & 0xf0) == 0x40) { + hs->flags |= F_PREFIX_REX; + if ((hs->rex_w = (c & 0xf) >> 3) && (*p & 0xf8) == 0xb8) + op64++; + hs->rex_r = (c & 7) >> 2; + hs->rex_x = (c & 3) >> 1; + hs->rex_b = c & 1; + if (((c = *p++) & 0xf0) == 0x40) { + opcode = c; + goto error_opcode; + } + } + + if ((hs->opcode = c) == 0x0f) { + hs->opcode2 = c = *p++; + ht += DELTA_OPCODES; + } else if (c >= 0xa0 && c <= 0xa3) { + op64++; + if (pref & PRE_67) + pref |= PRE_66; + else + pref &= ~PRE_66; + } + + opcode = c; + cflags = ht[ht[opcode / 4] + (opcode % 4)]; + + if (cflags == C_ERROR) { + error_opcode: + hs->flags |= F_ERROR | F_ERROR_OPCODE; + cflags = 0; + if ((opcode & -3) == 0x24) + cflags++; + } + + x = 0; + if (cflags & C_GROUP) { + uint16_t t; + t = *(uint16_t *)(ht + (cflags & 0x7f)); + cflags = (uint8_t)t; + x = (uint8_t)(t >> 8); + } + + if (hs->opcode2) { + ht = hde64_table + DELTA_PREFIXES; + if (ht[ht[opcode / 4] + (opcode % 4)] & pref) + hs->flags |= F_ERROR | F_ERROR_OPCODE; + } + + if (cflags & C_MODRM) { + hs->flags |= F_MODRM; + hs->modrm = c = *p++; + hs->modrm_mod = m_mod = c >> 6; + hs->modrm_rm = m_rm = c & 7; + hs->modrm_reg = m_reg = (c & 0x3f) >> 3; + + if (x && ((x << m_reg) & 0x80)) + hs->flags |= F_ERROR | F_ERROR_OPCODE; + + if (!hs->opcode2 && opcode >= 0xd9 && opcode <= 0xdf) { + uint8_t t = opcode - 0xd9; + if (m_mod == 3) { + ht = hde64_table + DELTA_FPU_MODRM + t*8; + t = ht[m_reg] << m_rm; + } else { + ht = hde64_table + DELTA_FPU_REG; + t = ht[t] << m_reg; + } + if (t & 0x80) + hs->flags |= F_ERROR | F_ERROR_OPCODE; + } + + if (pref & PRE_LOCK) { + if (m_mod == 3) { + hs->flags |= F_ERROR | F_ERROR_LOCK; + } else { + uint8_t *table_end, op = opcode; + if (hs->opcode2) { + ht = hde64_table + DELTA_OP2_LOCK_OK; + table_end = ht + DELTA_OP_ONLY_MEM - DELTA_OP2_LOCK_OK; + } else { + ht = hde64_table + DELTA_OP_LOCK_OK; + table_end = ht + DELTA_OP2_LOCK_OK - DELTA_OP_LOCK_OK; + op &= -2; + } + for (; ht != table_end; ht++) + if (*ht++ == op) { + if (!((*ht << m_reg) & 0x80)) + goto no_lock_error; + else + break; + } + hs->flags |= F_ERROR | F_ERROR_LOCK; + no_lock_error: + ; + } + } + + if (hs->opcode2) { + switch (opcode) { + case 0x20: case 0x22: + m_mod = 3; + if (m_reg > 4 || m_reg == 1) + goto error_operand; + else + goto no_error_operand; + case 0x21: case 0x23: + m_mod = 3; + if (m_reg == 4 || m_reg == 5) + goto error_operand; + else + goto no_error_operand; + } + } else { + switch (opcode) { + case 0x8c: + if (m_reg > 5) + goto error_operand; + else + goto no_error_operand; + case 0x8e: + if (m_reg == 1 || m_reg > 5) + goto error_operand; + else + goto no_error_operand; + } + } + + if (m_mod == 3) { + uint8_t *table_end; + if (hs->opcode2) { + ht = hde64_table + DELTA_OP2_ONLY_MEM; + table_end = ht + sizeof(hde64_table) - DELTA_OP2_ONLY_MEM; + } else { + ht = hde64_table + DELTA_OP_ONLY_MEM; + table_end = ht + DELTA_OP2_ONLY_MEM - DELTA_OP_ONLY_MEM; + } + for (; ht != table_end; ht += 2) + if (*ht++ == opcode) { + if (*ht++ & pref && !((*ht << m_reg) & 0x80)) + goto error_operand; + else + break; + } + goto no_error_operand; + } else if (hs->opcode2) { + switch (opcode) { + case 0x50: case 0xd7: case 0xf7: + if (pref & (PRE_NONE | PRE_66)) + goto error_operand; + break; + case 0xd6: + if (pref & (PRE_F2 | PRE_F3)) + goto error_operand; + break; + case 0xc5: + goto error_operand; + } + goto no_error_operand; + } else + goto no_error_operand; + + error_operand: + hs->flags |= F_ERROR | F_ERROR_OPERAND; + no_error_operand: + + c = *p++; + if (m_reg <= 1) { + if (opcode == 0xf6) + cflags |= C_IMM8; + else if (opcode == 0xf7) + cflags |= C_IMM_P66; + } + + switch (m_mod) { + case 0: + if (pref & PRE_67) { + if (m_rm == 6) + disp_size = 2; + } else + if (m_rm == 5) + disp_size = 4; + break; + case 1: + disp_size = 1; + break; + case 2: + disp_size = 2; + if (!(pref & PRE_67)) + disp_size <<= 1; + } + + if (m_mod != 3 && m_rm == 4) { + hs->flags |= F_SIB; + p++; + hs->sib = c; + hs->sib_scale = c >> 6; + hs->sib_index = (c & 0x3f) >> 3; + if ((hs->sib_base = c & 7) == 5 && !(m_mod & 1)) + disp_size = 4; + } + + p--; + switch (disp_size) { + case 1: + hs->flags |= F_DISP8; + hs->disp.disp8 = *p; + break; + case 2: + hs->flags |= F_DISP16; + hs->disp.disp16 = *(uint16_t *)p; + break; + case 4: + hs->flags |= F_DISP32; + hs->disp.disp32 = *(uint32_t *)p; + } + p += disp_size; + } else if (pref & PRE_LOCK) + hs->flags |= F_ERROR | F_ERROR_LOCK; + + if (cflags & C_IMM_P66) { + if (cflags & C_REL32) { + if (pref & PRE_66) { + hs->flags |= F_IMM16 | F_RELATIVE; + hs->imm.imm16 = *(uint16_t *)p; + p += 2; + goto disasm_done; + } + goto rel32_ok; + } + if (op64) { + hs->flags |= F_IMM64; + hs->imm.imm64 = *(uint64_t *)p; + p += 8; + } else if (!(pref & PRE_66)) { + hs->flags |= F_IMM32; + hs->imm.imm32 = *(uint32_t *)p; + p += 4; + } else + goto imm16_ok; + } + + + if (cflags & C_IMM16) { + imm16_ok: + hs->flags |= F_IMM16; + hs->imm.imm16 = *(uint16_t *)p; + p += 2; + } + if (cflags & C_IMM8) { + hs->flags |= F_IMM8; + hs->imm.imm8 = *p++; + } + + if (cflags & C_REL32) { + rel32_ok: + hs->flags |= F_IMM32 | F_RELATIVE; + hs->imm.imm32 = *(uint32_t *)p; + p += 4; + } else if (cflags & C_REL8) { + hs->flags |= F_IMM8 | F_RELATIVE; + hs->imm.imm8 = *p++; + } + + disasm_done: + + if ((hs->len = (uint8_t)(p-(uint8_t *)code)) > 15) { + hs->flags |= F_ERROR | F_ERROR_LENGTH; + hs->len = 15; + } + + return (unsigned int)hs->len; +} + +#endif // defined(_M_X64) || defined(__x86_64__) diff --git a/depthrush/kiero/minhook/src/hde/hde64.h b/depthrush/kiero/minhook/src/hde/hde64.h new file mode 100644 index 0000000..ecbf4df --- /dev/null +++ b/depthrush/kiero/minhook/src/hde/hde64.h @@ -0,0 +1,112 @@ +/* + * Hacker Disassembler Engine 64 + * Copyright (c) 2008-2009, Vyacheslav Patkov. + * All rights reserved. + * + * hde64.h: C/C++ header file + * + */ + +#ifndef _HDE64_H_ +#define _HDE64_H_ + +/* stdint.h - C99 standard header + * http://en.wikipedia.org/wiki/stdint.h + * + * if your compiler doesn't contain "stdint.h" header (for + * example, Microsoft Visual C++), you can download file: + * http://www.azillionmonkeys.com/qed/pstdint.h + * and change next line to: + * #include "pstdint.h" + */ +#include "pstdint.h" + +#define F_MODRM 0x00000001 +#define F_SIB 0x00000002 +#define F_IMM8 0x00000004 +#define F_IMM16 0x00000008 +#define F_IMM32 0x00000010 +#define F_IMM64 0x00000020 +#define F_DISP8 0x00000040 +#define F_DISP16 0x00000080 +#define F_DISP32 0x00000100 +#define F_RELATIVE 0x00000200 +#define F_ERROR 0x00001000 +#define F_ERROR_OPCODE 0x00002000 +#define F_ERROR_LENGTH 0x00004000 +#define F_ERROR_LOCK 0x00008000 +#define F_ERROR_OPERAND 0x00010000 +#define F_PREFIX_REPNZ 0x01000000 +#define F_PREFIX_REPX 0x02000000 +#define F_PREFIX_REP 0x03000000 +#define F_PREFIX_66 0x04000000 +#define F_PREFIX_67 0x08000000 +#define F_PREFIX_LOCK 0x10000000 +#define F_PREFIX_SEG 0x20000000 +#define F_PREFIX_REX 0x40000000 +#define F_PREFIX_ANY 0x7f000000 + +#define PREFIX_SEGMENT_CS 0x2e +#define PREFIX_SEGMENT_SS 0x36 +#define PREFIX_SEGMENT_DS 0x3e +#define PREFIX_SEGMENT_ES 0x26 +#define PREFIX_SEGMENT_FS 0x64 +#define PREFIX_SEGMENT_GS 0x65 +#define PREFIX_LOCK 0xf0 +#define PREFIX_REPNZ 0xf2 +#define PREFIX_REPX 0xf3 +#define PREFIX_OPERAND_SIZE 0x66 +#define PREFIX_ADDRESS_SIZE 0x67 + +#pragma pack(push,1) + +typedef struct { + uint8_t len; + uint8_t p_rep; + uint8_t p_lock; + uint8_t p_seg; + uint8_t p_66; + uint8_t p_67; + uint8_t rex; + uint8_t rex_w; + uint8_t rex_r; + uint8_t rex_x; + uint8_t rex_b; + uint8_t opcode; + uint8_t opcode2; + uint8_t modrm; + uint8_t modrm_mod; + uint8_t modrm_reg; + uint8_t modrm_rm; + uint8_t sib; + uint8_t sib_scale; + uint8_t sib_index; + uint8_t sib_base; + union { + uint8_t imm8; + uint16_t imm16; + uint32_t imm32; + uint64_t imm64; + } imm; + union { + uint8_t disp8; + uint16_t disp16; + uint32_t disp32; + } disp; + uint32_t flags; +} hde64s; + +#pragma pack(pop) + +#ifdef __cplusplus +extern "C" { +#endif + +/* __cdecl */ +unsigned int hde64_disasm(const void *code, hde64s *hs); + +#ifdef __cplusplus +} +#endif + +#endif /* _HDE64_H_ */ diff --git a/depthrush/kiero/minhook/src/hde/pstdint.h b/depthrush/kiero/minhook/src/hde/pstdint.h new file mode 100644 index 0000000..84d82a0 --- /dev/null +++ b/depthrush/kiero/minhook/src/hde/pstdint.h @@ -0,0 +1,39 @@ +/* + * MinHook - The Minimalistic API Hooking Library for x64/x86 + * Copyright (C) 2009-2017 Tsuda Kageyu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include + +// Integer types for HDE. +typedef INT8 int8_t; +typedef INT16 int16_t; +typedef INT32 int32_t; +typedef INT64 int64_t; +typedef UINT8 uint8_t; +typedef UINT16 uint16_t; +typedef UINT32 uint32_t; +typedef UINT64 uint64_t; diff --git a/depthrush/kiero/minhook/src/hde/table32.h b/depthrush/kiero/minhook/src/hde/table32.h new file mode 100644 index 0000000..7b3e12e --- /dev/null +++ b/depthrush/kiero/minhook/src/hde/table32.h @@ -0,0 +1,73 @@ +/* + * Hacker Disassembler Engine 32 C + * Copyright (c) 2008-2009, Vyacheslav Patkov. + * All rights reserved. + * + */ + +#define C_NONE 0x00 +#define C_MODRM 0x01 +#define C_IMM8 0x02 +#define C_IMM16 0x04 +#define C_IMM_P66 0x10 +#define C_REL8 0x20 +#define C_REL32 0x40 +#define C_GROUP 0x80 +#define C_ERROR 0xff + +#define PRE_ANY 0x00 +#define PRE_NONE 0x01 +#define PRE_F2 0x02 +#define PRE_F3 0x04 +#define PRE_66 0x08 +#define PRE_67 0x10 +#define PRE_LOCK 0x20 +#define PRE_SEG 0x40 +#define PRE_ALL 0xff + +#define DELTA_OPCODES 0x4a +#define DELTA_FPU_REG 0xf1 +#define DELTA_FPU_MODRM 0xf8 +#define DELTA_PREFIXES 0x130 +#define DELTA_OP_LOCK_OK 0x1a1 +#define DELTA_OP2_LOCK_OK 0x1b9 +#define DELTA_OP_ONLY_MEM 0x1cb +#define DELTA_OP2_ONLY_MEM 0x1da + +unsigned char hde32_table[] = { + 0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3, + 0xa8,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xac,0xaa,0xb2,0xaa,0x9f,0x9f, + 0x9f,0x9f,0xb5,0xa3,0xa3,0xa4,0xaa,0xaa,0xba,0xaa,0x96,0xaa,0xa8,0xaa,0xc3, + 0xc3,0x96,0x96,0xb7,0xae,0xd6,0xbd,0xa3,0xc5,0xa3,0xa3,0x9f,0xc3,0x9c,0xaa, + 0xaa,0xac,0xaa,0xbf,0x03,0x7f,0x11,0x7f,0x01,0x7f,0x01,0x3f,0x01,0x01,0x90, + 0x82,0x7d,0x97,0x59,0x59,0x59,0x59,0x59,0x7f,0x59,0x59,0x60,0x7d,0x7f,0x7f, + 0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x9a,0x88,0x7d, + 0x59,0x50,0x50,0x50,0x50,0x59,0x59,0x59,0x59,0x61,0x94,0x61,0x9e,0x59,0x59, + 0x85,0x59,0x92,0xa3,0x60,0x60,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59, + 0x59,0x59,0x9f,0x01,0x03,0x01,0x04,0x03,0xd5,0x03,0xcc,0x01,0xbc,0x03,0xf0, + 0x10,0x10,0x10,0x10,0x50,0x50,0x50,0x50,0x14,0x20,0x20,0x20,0x20,0x01,0x01, + 0x01,0x01,0xc4,0x02,0x10,0x00,0x00,0x00,0x00,0x01,0x01,0xc0,0xc2,0x10,0x11, + 0x02,0x03,0x11,0x03,0x03,0x04,0x00,0x00,0x14,0x00,0x02,0x00,0x00,0xc6,0xc8, + 0x02,0x02,0x02,0x02,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xca, + 0x01,0x01,0x01,0x00,0x06,0x00,0x04,0x00,0xc0,0xc2,0x01,0x01,0x03,0x01,0xff, + 0xff,0x01,0x00,0x03,0xc4,0xc4,0xc6,0x03,0x01,0x01,0x01,0xff,0x03,0x03,0x03, + 0xc8,0x40,0x00,0x0a,0x00,0x04,0x00,0x00,0x00,0x00,0x7f,0x00,0x33,0x01,0x00, + 0x00,0x00,0x00,0x00,0x00,0xff,0xbf,0xff,0xff,0x00,0x00,0x00,0x00,0x07,0x00, + 0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0xff,0xff,0x00,0x00,0x00,0xbf,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x7f,0x00,0x00,0xff,0x4a,0x4a,0x4a,0x4a,0x4b,0x52,0x4a,0x4a,0x4a,0x4a,0x4f, + 0x4c,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x55,0x45,0x40,0x4a,0x4a,0x4a, + 0x45,0x59,0x4d,0x46,0x4a,0x5d,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a, + 0x4a,0x4a,0x4a,0x4a,0x4a,0x61,0x63,0x67,0x4e,0x4a,0x4a,0x6b,0x6d,0x4a,0x4a, + 0x45,0x6d,0x4a,0x4a,0x44,0x45,0x4a,0x4a,0x00,0x00,0x00,0x02,0x0d,0x06,0x06, + 0x06,0x06,0x0e,0x00,0x00,0x00,0x00,0x06,0x06,0x06,0x00,0x06,0x06,0x02,0x06, + 0x00,0x0a,0x0a,0x07,0x07,0x06,0x02,0x05,0x05,0x02,0x02,0x00,0x00,0x04,0x04, + 0x04,0x04,0x00,0x00,0x00,0x0e,0x05,0x06,0x06,0x06,0x01,0x06,0x00,0x00,0x08, + 0x00,0x10,0x00,0x18,0x00,0x20,0x00,0x28,0x00,0x30,0x00,0x80,0x01,0x82,0x01, + 0x86,0x00,0xf6,0xcf,0xfe,0x3f,0xab,0x00,0xb0,0x00,0xb1,0x00,0xb3,0x00,0xba, + 0xf8,0xbb,0x00,0xc0,0x00,0xc1,0x00,0xc7,0xbf,0x62,0xff,0x00,0x8d,0xff,0x00, + 0xc4,0xff,0x00,0xc5,0xff,0x00,0xff,0xff,0xeb,0x01,0xff,0x0e,0x12,0x08,0x00, + 0x13,0x09,0x00,0x16,0x08,0x00,0x17,0x09,0x00,0x2b,0x09,0x00,0xae,0xff,0x07, + 0xb2,0xff,0x00,0xb4,0xff,0x00,0xb5,0xff,0x00,0xc3,0x01,0x00,0xc7,0xff,0xbf, + 0xe7,0x08,0x00,0xf0,0x02,0x00 +}; diff --git a/depthrush/kiero/minhook/src/hde/table64.h b/depthrush/kiero/minhook/src/hde/table64.h new file mode 100644 index 0000000..01d4541 --- /dev/null +++ b/depthrush/kiero/minhook/src/hde/table64.h @@ -0,0 +1,74 @@ +/* + * Hacker Disassembler Engine 64 C + * Copyright (c) 2008-2009, Vyacheslav Patkov. + * All rights reserved. + * + */ + +#define C_NONE 0x00 +#define C_MODRM 0x01 +#define C_IMM8 0x02 +#define C_IMM16 0x04 +#define C_IMM_P66 0x10 +#define C_REL8 0x20 +#define C_REL32 0x40 +#define C_GROUP 0x80 +#define C_ERROR 0xff + +#define PRE_ANY 0x00 +#define PRE_NONE 0x01 +#define PRE_F2 0x02 +#define PRE_F3 0x04 +#define PRE_66 0x08 +#define PRE_67 0x10 +#define PRE_LOCK 0x20 +#define PRE_SEG 0x40 +#define PRE_ALL 0xff + +#define DELTA_OPCODES 0x4a +#define DELTA_FPU_REG 0xfd +#define DELTA_FPU_MODRM 0x104 +#define DELTA_PREFIXES 0x13c +#define DELTA_OP_LOCK_OK 0x1ae +#define DELTA_OP2_LOCK_OK 0x1c6 +#define DELTA_OP_ONLY_MEM 0x1d8 +#define DELTA_OP2_ONLY_MEM 0x1e7 + +unsigned char hde64_table[] = { + 0xa5,0xaa,0xa5,0xb8,0xa5,0xaa,0xa5,0xaa,0xa5,0xb8,0xa5,0xb8,0xa5,0xb8,0xa5, + 0xb8,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xac,0xc0,0xcc,0xc0,0xa1,0xa1, + 0xa1,0xa1,0xb1,0xa5,0xa5,0xa6,0xc0,0xc0,0xd7,0xda,0xe0,0xc0,0xe4,0xc0,0xea, + 0xea,0xe0,0xe0,0x98,0xc8,0xee,0xf1,0xa5,0xd3,0xa5,0xa5,0xa1,0xea,0x9e,0xc0, + 0xc0,0xc2,0xc0,0xe6,0x03,0x7f,0x11,0x7f,0x01,0x7f,0x01,0x3f,0x01,0x01,0xab, + 0x8b,0x90,0x64,0x5b,0x5b,0x5b,0x5b,0x5b,0x92,0x5b,0x5b,0x76,0x90,0x92,0x92, + 0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x6a,0x73,0x90, + 0x5b,0x52,0x52,0x52,0x52,0x5b,0x5b,0x5b,0x5b,0x77,0x7c,0x77,0x85,0x5b,0x5b, + 0x70,0x5b,0x7a,0xaf,0x76,0x76,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b, + 0x5b,0x5b,0x86,0x01,0x03,0x01,0x04,0x03,0xd5,0x03,0xd5,0x03,0xcc,0x01,0xbc, + 0x03,0xf0,0x03,0x03,0x04,0x00,0x50,0x50,0x50,0x50,0xff,0x20,0x20,0x20,0x20, + 0x01,0x01,0x01,0x01,0xc4,0x02,0x10,0xff,0xff,0xff,0x01,0x00,0x03,0x11,0xff, + 0x03,0xc4,0xc6,0xc8,0x02,0x10,0x00,0xff,0xcc,0x01,0x01,0x01,0x00,0x00,0x00, + 0x00,0x01,0x01,0x03,0x01,0xff,0xff,0xc0,0xc2,0x10,0x11,0x02,0x03,0x01,0x01, + 0x01,0xff,0xff,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0x10, + 0x10,0x10,0x10,0x02,0x10,0x00,0x00,0xc6,0xc8,0x02,0x02,0x02,0x02,0x06,0x00, + 0x04,0x00,0x02,0xff,0x00,0xc0,0xc2,0x01,0x01,0x03,0x03,0x03,0xca,0x40,0x00, + 0x0a,0x00,0x04,0x00,0x00,0x00,0x00,0x7f,0x00,0x33,0x01,0x00,0x00,0x00,0x00, + 0x00,0x00,0xff,0xbf,0xff,0xff,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0xff,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff, + 0x00,0x00,0x00,0xbf,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7f,0x00,0x00, + 0xff,0x40,0x40,0x40,0x40,0x41,0x49,0x40,0x40,0x40,0x40,0x4c,0x42,0x40,0x40, + 0x40,0x40,0x40,0x40,0x40,0x40,0x4f,0x44,0x53,0x40,0x40,0x40,0x44,0x57,0x43, + 0x5c,0x40,0x60,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40, + 0x40,0x40,0x64,0x66,0x6e,0x6b,0x40,0x40,0x6a,0x46,0x40,0x40,0x44,0x46,0x40, + 0x40,0x5b,0x44,0x40,0x40,0x00,0x00,0x00,0x00,0x06,0x06,0x06,0x06,0x01,0x06, + 0x06,0x02,0x06,0x06,0x00,0x06,0x00,0x0a,0x0a,0x00,0x00,0x00,0x02,0x07,0x07, + 0x06,0x02,0x0d,0x06,0x06,0x06,0x0e,0x05,0x05,0x02,0x02,0x00,0x00,0x04,0x04, + 0x04,0x04,0x05,0x06,0x06,0x06,0x00,0x00,0x00,0x0e,0x00,0x00,0x08,0x00,0x10, + 0x00,0x18,0x00,0x20,0x00,0x28,0x00,0x30,0x00,0x80,0x01,0x82,0x01,0x86,0x00, + 0xf6,0xcf,0xfe,0x3f,0xab,0x00,0xb0,0x00,0xb1,0x00,0xb3,0x00,0xba,0xf8,0xbb, + 0x00,0xc0,0x00,0xc1,0x00,0xc7,0xbf,0x62,0xff,0x00,0x8d,0xff,0x00,0xc4,0xff, + 0x00,0xc5,0xff,0x00,0xff,0xff,0xeb,0x01,0xff,0x0e,0x12,0x08,0x00,0x13,0x09, + 0x00,0x16,0x08,0x00,0x17,0x09,0x00,0x2b,0x09,0x00,0xae,0xff,0x07,0xb2,0xff, + 0x00,0xb4,0xff,0x00,0xb5,0xff,0x00,0xc3,0x01,0x00,0xc7,0xff,0xbf,0xe7,0x08, + 0x00,0xf0,0x02,0x00 +}; diff --git a/depthrush/kiero/minhook/src/hook.c b/depthrush/kiero/minhook/src/hook.c new file mode 100644 index 0000000..ce65e57 --- /dev/null +++ b/depthrush/kiero/minhook/src/hook.c @@ -0,0 +1,889 @@ +/* + * MinHook - The Minimalistic API Hooking Library for x64/x86 + * Copyright (C) 2009-2017 Tsuda Kageyu. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include "../include/MinHook.h" +#include "buffer.h" +#include "trampoline.h" + +#ifndef ARRAYSIZE + #define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) +#endif + +// Initial capacity of the HOOK_ENTRY buffer. +#define INITIAL_HOOK_CAPACITY 32 + +// Initial capacity of the thread IDs buffer. +#define INITIAL_THREAD_CAPACITY 128 + +// Special hook position values. +#define INVALID_HOOK_POS UINT_MAX +#define ALL_HOOKS_POS UINT_MAX + +// Freeze() action argument defines. +#define ACTION_DISABLE 0 +#define ACTION_ENABLE 1 +#define ACTION_APPLY_QUEUED 2 + +// Thread access rights for suspending/resuming threads. +#define THREAD_ACCESS \ + (THREAD_SUSPEND_RESUME | THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION | THREAD_SET_CONTEXT) + +// Hook information. +typedef struct _HOOK_ENTRY +{ + LPVOID pTarget; // Address of the target function. + LPVOID pDetour; // Address of the detour or relay function. + LPVOID pTrampoline; // Address of the trampoline function. + UINT8 backup[8]; // Original prologue of the target function. + + UINT8 patchAbove : 1; // Uses the hot patch area. + UINT8 isEnabled : 1; // Enabled. + UINT8 queueEnable : 1; // Queued for enabling/disabling when != isEnabled. + + UINT nIP : 4; // Count of the instruction boundaries. + UINT8 oldIPs[8]; // Instruction boundaries of the target function. + UINT8 newIPs[8]; // Instruction boundaries of the trampoline function. +} HOOK_ENTRY, *PHOOK_ENTRY; + +// Suspended threads for Freeze()/Unfreeze(). +typedef struct _FROZEN_THREADS +{ + LPDWORD pItems; // Data heap + UINT capacity; // Size of allocated data heap, items + UINT size; // Actual number of data items +} FROZEN_THREADS, *PFROZEN_THREADS; + +//------------------------------------------------------------------------- +// Global Variables: +//------------------------------------------------------------------------- + +// Spin lock flag for EnterSpinLock()/LeaveSpinLock(). +volatile LONG g_isLocked = FALSE; + +// Private heap handle. If not NULL, this library is initialized. +HANDLE g_hHeap = NULL; + +// Hook entries. +struct +{ + PHOOK_ENTRY pItems; // Data heap + UINT capacity; // Size of allocated data heap, items + UINT size; // Actual number of data items +} g_hooks; + +//------------------------------------------------------------------------- +// Returns INVALID_HOOK_POS if not found. +static UINT FindHookEntry(LPVOID pTarget) +{ + UINT i; + for (i = 0; i < g_hooks.size; ++i) + { + if ((ULONG_PTR)pTarget == (ULONG_PTR)g_hooks.pItems[i].pTarget) + return i; + } + + return INVALID_HOOK_POS; +} + +//------------------------------------------------------------------------- +static PHOOK_ENTRY AddHookEntry() +{ + if (g_hooks.pItems == NULL) + { + g_hooks.capacity = INITIAL_HOOK_CAPACITY; + g_hooks.pItems = (PHOOK_ENTRY)HeapAlloc( + g_hHeap, 0, g_hooks.capacity * sizeof(HOOK_ENTRY)); + if (g_hooks.pItems == NULL) + return NULL; + } + else if (g_hooks.size >= g_hooks.capacity) + { + PHOOK_ENTRY p = (PHOOK_ENTRY)HeapReAlloc( + g_hHeap, 0, g_hooks.pItems, (g_hooks.capacity * 2) * sizeof(HOOK_ENTRY)); + if (p == NULL) + return NULL; + + g_hooks.capacity *= 2; + g_hooks.pItems = p; + } + + return &g_hooks.pItems[g_hooks.size++]; +} + +//------------------------------------------------------------------------- +static void DeleteHookEntry(UINT pos) +{ + if (pos < g_hooks.size - 1) + g_hooks.pItems[pos] = g_hooks.pItems[g_hooks.size - 1]; + + g_hooks.size--; + + if (g_hooks.capacity / 2 >= INITIAL_HOOK_CAPACITY && g_hooks.capacity / 2 >= g_hooks.size) + { + PHOOK_ENTRY p = (PHOOK_ENTRY)HeapReAlloc( + g_hHeap, 0, g_hooks.pItems, (g_hooks.capacity / 2) * sizeof(HOOK_ENTRY)); + if (p == NULL) + return; + + g_hooks.capacity /= 2; + g_hooks.pItems = p; + } +} + +//------------------------------------------------------------------------- +static DWORD_PTR FindOldIP(PHOOK_ENTRY pHook, DWORD_PTR ip) +{ + UINT i; + + if (pHook->patchAbove && ip == ((DWORD_PTR)pHook->pTarget - sizeof(JMP_REL))) + return (DWORD_PTR)pHook->pTarget; + + for (i = 0; i < pHook->nIP; ++i) + { + if (ip == ((DWORD_PTR)pHook->pTrampoline + pHook->newIPs[i])) + return (DWORD_PTR)pHook->pTarget + pHook->oldIPs[i]; + } + +#if defined(_M_X64) || defined(__x86_64__) + // Check relay function. + if (ip == (DWORD_PTR)pHook->pDetour) + return (DWORD_PTR)pHook->pTarget; +#endif + + return 0; +} + +//------------------------------------------------------------------------- +static DWORD_PTR FindNewIP(PHOOK_ENTRY pHook, DWORD_PTR ip) +{ + UINT i; + for (i = 0; i < pHook->nIP; ++i) + { + if (ip == ((DWORD_PTR)pHook->pTarget + pHook->oldIPs[i])) + return (DWORD_PTR)pHook->pTrampoline + pHook->newIPs[i]; + } + + return 0; +} + +//------------------------------------------------------------------------- +static void ProcessThreadIPs(HANDLE hThread, UINT pos, UINT action) +{ + // If the thread suspended in the overwritten area, + // move IP to the proper address. + + CONTEXT c; +#if defined(_M_X64) || defined(__x86_64__) + DWORD64 *pIP = &c.Rip; +#else + DWORD *pIP = &c.Eip; +#endif + UINT count; + + c.ContextFlags = CONTEXT_CONTROL; + if (!GetThreadContext(hThread, &c)) + return; + + if (pos == ALL_HOOKS_POS) + { + pos = 0; + count = g_hooks.size; + } + else + { + count = pos + 1; + } + + for (; pos < count; ++pos) + { + PHOOK_ENTRY pHook = &g_hooks.pItems[pos]; + BOOL enable; + DWORD_PTR ip; + + switch (action) + { + case ACTION_DISABLE: + enable = FALSE; + break; + + case ACTION_ENABLE: + enable = TRUE; + break; + + default: // ACTION_APPLY_QUEUED + enable = pHook->queueEnable; + break; + } + if (pHook->isEnabled == enable) + continue; + + if (enable) + ip = FindNewIP(pHook, *pIP); + else + ip = FindOldIP(pHook, *pIP); + + if (ip != 0) + { + *pIP = ip; + SetThreadContext(hThread, &c); + } + } +} + +//------------------------------------------------------------------------- +static VOID EnumerateThreads(PFROZEN_THREADS pThreads) +{ + HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); + if (hSnapshot != INVALID_HANDLE_VALUE) + { + THREADENTRY32 te; + te.dwSize = sizeof(THREADENTRY32); + if (Thread32First(hSnapshot, &te)) + { + do + { + if (te.dwSize >= (FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID) + sizeof(DWORD)) + && te.th32OwnerProcessID == GetCurrentProcessId() + && te.th32ThreadID != GetCurrentThreadId()) + { + if (pThreads->pItems == NULL) + { + pThreads->capacity = INITIAL_THREAD_CAPACITY; + pThreads->pItems + = (LPDWORD)HeapAlloc(g_hHeap, 0, pThreads->capacity * sizeof(DWORD)); + if (pThreads->pItems == NULL) + break; + } + else if (pThreads->size >= pThreads->capacity) + { + LPDWORD p = (LPDWORD)HeapReAlloc( + g_hHeap, 0, pThreads->pItems, (pThreads->capacity * 2) * sizeof(DWORD)); + if (p == NULL) + break; + + pThreads->capacity *= 2; + pThreads->pItems = p; + } + pThreads->pItems[pThreads->size++] = te.th32ThreadID; + } + + te.dwSize = sizeof(THREADENTRY32); + } while (Thread32Next(hSnapshot, &te)); + } + CloseHandle(hSnapshot); + } +} + +//------------------------------------------------------------------------- +static VOID Freeze(PFROZEN_THREADS pThreads, UINT pos, UINT action) +{ + pThreads->pItems = NULL; + pThreads->capacity = 0; + pThreads->size = 0; + EnumerateThreads(pThreads); + + if (pThreads->pItems != NULL) + { + UINT i; + for (i = 0; i < pThreads->size; ++i) + { + HANDLE hThread = OpenThread(THREAD_ACCESS, FALSE, pThreads->pItems[i]); + if (hThread != NULL) + { + SuspendThread(hThread); + ProcessThreadIPs(hThread, pos, action); + CloseHandle(hThread); + } + } + } +} + +//------------------------------------------------------------------------- +static VOID Unfreeze(PFROZEN_THREADS pThreads) +{ + if (pThreads->pItems != NULL) + { + UINT i; + for (i = 0; i < pThreads->size; ++i) + { + HANDLE hThread = OpenThread(THREAD_ACCESS, FALSE, pThreads->pItems[i]); + if (hThread != NULL) + { + ResumeThread(hThread); + CloseHandle(hThread); + } + } + + HeapFree(g_hHeap, 0, pThreads->pItems); + } +} + +//------------------------------------------------------------------------- +static MH_STATUS EnableHookLL(UINT pos, BOOL enable) +{ + PHOOK_ENTRY pHook = &g_hooks.pItems[pos]; + DWORD oldProtect; + SIZE_T patchSize = sizeof(JMP_REL); + LPBYTE pPatchTarget = (LPBYTE)pHook->pTarget; + + if (pHook->patchAbove) + { + pPatchTarget -= sizeof(JMP_REL); + patchSize += sizeof(JMP_REL_SHORT); + } + + if (!VirtualProtect(pPatchTarget, patchSize, PAGE_EXECUTE_READWRITE, &oldProtect)) + return MH_ERROR_MEMORY_PROTECT; + + if (enable) + { + PJMP_REL pJmp = (PJMP_REL)pPatchTarget; + pJmp->opcode = 0xE9; + pJmp->operand = (UINT32)((LPBYTE)pHook->pDetour - (pPatchTarget + sizeof(JMP_REL))); + + if (pHook->patchAbove) + { + PJMP_REL_SHORT pShortJmp = (PJMP_REL_SHORT)pHook->pTarget; + pShortJmp->opcode = 0xEB; + pShortJmp->operand = (UINT8)(0 - (sizeof(JMP_REL_SHORT) + sizeof(JMP_REL))); + } + } + else + { + if (pHook->patchAbove) + memcpy(pPatchTarget, pHook->backup, sizeof(JMP_REL) + sizeof(JMP_REL_SHORT)); + else + memcpy(pPatchTarget, pHook->backup, sizeof(JMP_REL)); + } + + VirtualProtect(pPatchTarget, patchSize, oldProtect, &oldProtect); + + // Just-in-case measure. + FlushInstructionCache(GetCurrentProcess(), pPatchTarget, patchSize); + + pHook->isEnabled = enable; + pHook->queueEnable = enable; + + return MH_OK; +} + +//------------------------------------------------------------------------- +static MH_STATUS EnableAllHooksLL(BOOL enable) +{ + MH_STATUS status = MH_OK; + UINT i, first = INVALID_HOOK_POS; + + for (i = 0; i < g_hooks.size; ++i) + { + if (g_hooks.pItems[i].isEnabled != enable) + { + first = i; + break; + } + } + + if (first != INVALID_HOOK_POS) + { + FROZEN_THREADS threads; + Freeze(&threads, ALL_HOOKS_POS, enable ? ACTION_ENABLE : ACTION_DISABLE); + + for (i = first; i < g_hooks.size; ++i) + { + if (g_hooks.pItems[i].isEnabled != enable) + { + status = EnableHookLL(i, enable); + if (status != MH_OK) + break; + } + } + + Unfreeze(&threads); + } + + return status; +} + +//------------------------------------------------------------------------- +static VOID EnterSpinLock(VOID) +{ + SIZE_T spinCount = 0; + + // Wait until the flag is FALSE. + while (InterlockedCompareExchange(&g_isLocked, TRUE, FALSE) != FALSE) + { + // No need to generate a memory barrier here, since InterlockedCompareExchange() + // generates a full memory barrier itself. + + // Prevent the loop from being too busy. + if (spinCount < 32) + Sleep(0); + else + Sleep(1); + + spinCount++; + } +} + +//------------------------------------------------------------------------- +static VOID LeaveSpinLock(VOID) +{ + // No need to generate a memory barrier here, since InterlockedExchange() + // generates a full memory barrier itself. + + InterlockedExchange(&g_isLocked, FALSE); +} + +//------------------------------------------------------------------------- +MH_STATUS WINAPI MH_Initialize(VOID) +{ + MH_STATUS status = MH_OK; + + EnterSpinLock(); + + if (g_hHeap == NULL) + { + g_hHeap = HeapCreate(0, 0, 0); + if (g_hHeap != NULL) + { + // Initialize the internal function buffer. + InitializeBuffer(); + } + else + { + status = MH_ERROR_MEMORY_ALLOC; + } + } + else + { + status = MH_ERROR_ALREADY_INITIALIZED; + } + + LeaveSpinLock(); + + return status; +} + +//------------------------------------------------------------------------- +MH_STATUS WINAPI MH_Uninitialize(VOID) +{ + MH_STATUS status = MH_OK; + + EnterSpinLock(); + + if (g_hHeap != NULL) + { + status = EnableAllHooksLL(FALSE); + if (status == MH_OK) + { + // Free the internal function buffer. + + // HeapFree is actually not required, but some tools detect a false + // memory leak without HeapFree. + + UninitializeBuffer(); + + HeapFree(g_hHeap, 0, g_hooks.pItems); + HeapDestroy(g_hHeap); + + g_hHeap = NULL; + + g_hooks.pItems = NULL; + g_hooks.capacity = 0; + g_hooks.size = 0; + } + } + else + { + status = MH_ERROR_NOT_INITIALIZED; + } + + LeaveSpinLock(); + + return status; +} + +//------------------------------------------------------------------------- +MH_STATUS WINAPI MH_CreateHook(LPVOID pTarget, LPVOID pDetour, LPVOID *ppOriginal) +{ + MH_STATUS status = MH_OK; + + EnterSpinLock(); + + if (g_hHeap != NULL) + { + if (IsExecutableAddress(pTarget) && IsExecutableAddress(pDetour)) + { + UINT pos = FindHookEntry(pTarget); + if (pos == INVALID_HOOK_POS) + { + LPVOID pBuffer = AllocateBuffer(pTarget); + if (pBuffer != NULL) + { + TRAMPOLINE ct; + + ct.pTarget = pTarget; + ct.pDetour = pDetour; + ct.pTrampoline = pBuffer; + if (CreateTrampolineFunction(&ct)) + { + PHOOK_ENTRY pHook = AddHookEntry(); + if (pHook != NULL) + { + pHook->pTarget = ct.pTarget; +#if defined(_M_X64) || defined(__x86_64__) + pHook->pDetour = ct.pRelay; +#else + pHook->pDetour = ct.pDetour; +#endif + pHook->pTrampoline = ct.pTrampoline; + pHook->patchAbove = ct.patchAbove; + pHook->isEnabled = FALSE; + pHook->queueEnable = FALSE; + pHook->nIP = ct.nIP; + memcpy(pHook->oldIPs, ct.oldIPs, ARRAYSIZE(ct.oldIPs)); + memcpy(pHook->newIPs, ct.newIPs, ARRAYSIZE(ct.newIPs)); + + // Back up the target function. + + if (ct.patchAbove) + { + memcpy( + pHook->backup, + (LPBYTE)pTarget - sizeof(JMP_REL), + sizeof(JMP_REL) + sizeof(JMP_REL_SHORT)); + } + else + { + memcpy(pHook->backup, pTarget, sizeof(JMP_REL)); + } + + if (ppOriginal != NULL) + *ppOriginal = pHook->pTrampoline; + } + else + { + status = MH_ERROR_MEMORY_ALLOC; + } + } + else + { + status = MH_ERROR_UNSUPPORTED_FUNCTION; + } + + if (status != MH_OK) + { + FreeBuffer(pBuffer); + } + } + else + { + status = MH_ERROR_MEMORY_ALLOC; + } + } + else + { + status = MH_ERROR_ALREADY_CREATED; + } + } + else + { + status = MH_ERROR_NOT_EXECUTABLE; + } + } + else + { + status = MH_ERROR_NOT_INITIALIZED; + } + + LeaveSpinLock(); + + return status; +} + +//------------------------------------------------------------------------- +MH_STATUS WINAPI MH_RemoveHook(LPVOID pTarget) +{ + MH_STATUS status = MH_OK; + + EnterSpinLock(); + + if (g_hHeap != NULL) + { + UINT pos = FindHookEntry(pTarget); + if (pos != INVALID_HOOK_POS) + { + if (g_hooks.pItems[pos].isEnabled) + { + FROZEN_THREADS threads; + Freeze(&threads, pos, ACTION_DISABLE); + + status = EnableHookLL(pos, FALSE); + + Unfreeze(&threads); + } + + if (status == MH_OK) + { + FreeBuffer(g_hooks.pItems[pos].pTrampoline); + DeleteHookEntry(pos); + } + } + else + { + status = MH_ERROR_NOT_CREATED; + } + } + else + { + status = MH_ERROR_NOT_INITIALIZED; + } + + LeaveSpinLock(); + + return status; +} + +//------------------------------------------------------------------------- +static MH_STATUS EnableHook(LPVOID pTarget, BOOL enable) +{ + MH_STATUS status = MH_OK; + + EnterSpinLock(); + + if (g_hHeap != NULL) + { + if (pTarget == MH_ALL_HOOKS) + { + status = EnableAllHooksLL(enable); + } + else + { + FROZEN_THREADS threads; + UINT pos = FindHookEntry(pTarget); + if (pos != INVALID_HOOK_POS) + { + if (g_hooks.pItems[pos].isEnabled != enable) + { + Freeze(&threads, pos, ACTION_ENABLE); + + status = EnableHookLL(pos, enable); + + Unfreeze(&threads); + } + else + { + status = enable ? MH_ERROR_ENABLED : MH_ERROR_DISABLED; + } + } + else + { + status = MH_ERROR_NOT_CREATED; + } + } + } + else + { + status = MH_ERROR_NOT_INITIALIZED; + } + + LeaveSpinLock(); + + return status; +} + +//------------------------------------------------------------------------- +MH_STATUS WINAPI MH_EnableHook(LPVOID pTarget) +{ + return EnableHook(pTarget, TRUE); +} + +//------------------------------------------------------------------------- +MH_STATUS WINAPI MH_DisableHook(LPVOID pTarget) +{ + return EnableHook(pTarget, FALSE); +} + +//------------------------------------------------------------------------- +static MH_STATUS QueueHook(LPVOID pTarget, BOOL queueEnable) +{ + MH_STATUS status = MH_OK; + + EnterSpinLock(); + + if (g_hHeap != NULL) + { + if (pTarget == MH_ALL_HOOKS) + { + UINT i; + for (i = 0; i < g_hooks.size; ++i) + g_hooks.pItems[i].queueEnable = queueEnable; + } + else + { + UINT pos = FindHookEntry(pTarget); + if (pos != INVALID_HOOK_POS) + { + g_hooks.pItems[pos].queueEnable = queueEnable; + } + else + { + status = MH_ERROR_NOT_CREATED; + } + } + } + else + { + status = MH_ERROR_NOT_INITIALIZED; + } + + LeaveSpinLock(); + + return status; +} + +//------------------------------------------------------------------------- +MH_STATUS WINAPI MH_QueueEnableHook(LPVOID pTarget) +{ + return QueueHook(pTarget, TRUE); +} + +//------------------------------------------------------------------------- +MH_STATUS WINAPI MH_QueueDisableHook(LPVOID pTarget) +{ + return QueueHook(pTarget, FALSE); +} + +//------------------------------------------------------------------------- +MH_STATUS WINAPI MH_ApplyQueued(VOID) +{ + MH_STATUS status = MH_OK; + UINT i, first = INVALID_HOOK_POS; + + EnterSpinLock(); + + if (g_hHeap != NULL) + { + for (i = 0; i < g_hooks.size; ++i) + { + if (g_hooks.pItems[i].isEnabled != g_hooks.pItems[i].queueEnable) + { + first = i; + break; + } + } + + if (first != INVALID_HOOK_POS) + { + FROZEN_THREADS threads; + Freeze(&threads, ALL_HOOKS_POS, ACTION_APPLY_QUEUED); + + for (i = first; i < g_hooks.size; ++i) + { + PHOOK_ENTRY pHook = &g_hooks.pItems[i]; + if (pHook->isEnabled != pHook->queueEnable) + { + status = EnableHookLL(i, pHook->queueEnable); + if (status != MH_OK) + break; + } + } + + Unfreeze(&threads); + } + } + else + { + status = MH_ERROR_NOT_INITIALIZED; + } + + LeaveSpinLock(); + + return status; +} + +//------------------------------------------------------------------------- +MH_STATUS WINAPI MH_CreateHookApiEx( + LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, + LPVOID *ppOriginal, LPVOID *ppTarget) +{ + HMODULE hModule; + LPVOID pTarget; + + hModule = GetModuleHandleW(pszModule); + if (hModule == NULL) + return MH_ERROR_MODULE_NOT_FOUND; + + pTarget = (LPVOID)GetProcAddress(hModule, pszProcName); + if (pTarget == NULL) + return MH_ERROR_FUNCTION_NOT_FOUND; + + if(ppTarget != NULL) + *ppTarget = pTarget; + + return MH_CreateHook(pTarget, pDetour, ppOriginal); +} + +//------------------------------------------------------------------------- +MH_STATUS WINAPI MH_CreateHookApi( + LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, LPVOID *ppOriginal) +{ + return MH_CreateHookApiEx(pszModule, pszProcName, pDetour, ppOriginal, NULL); +} + +//------------------------------------------------------------------------- +const char * WINAPI MH_StatusToString(MH_STATUS status) +{ +#define MH_ST2STR(x) \ + case x: \ + return #x; + + switch (status) { + MH_ST2STR(MH_UNKNOWN) + MH_ST2STR(MH_OK) + MH_ST2STR(MH_ERROR_ALREADY_INITIALIZED) + MH_ST2STR(MH_ERROR_NOT_INITIALIZED) + MH_ST2STR(MH_ERROR_ALREADY_CREATED) + MH_ST2STR(MH_ERROR_NOT_CREATED) + MH_ST2STR(MH_ERROR_ENABLED) + MH_ST2STR(MH_ERROR_DISABLED) + MH_ST2STR(MH_ERROR_NOT_EXECUTABLE) + MH_ST2STR(MH_ERROR_UNSUPPORTED_FUNCTION) + MH_ST2STR(MH_ERROR_MEMORY_ALLOC) + MH_ST2STR(MH_ERROR_MEMORY_PROTECT) + MH_ST2STR(MH_ERROR_MODULE_NOT_FOUND) + MH_ST2STR(MH_ERROR_FUNCTION_NOT_FOUND) + } + +#undef MH_ST2STR + + return "(unknown)"; +} diff --git a/depthrush/kiero/minhook/src/trampoline.c b/depthrush/kiero/minhook/src/trampoline.c new file mode 100644 index 0000000..ac37f0f --- /dev/null +++ b/depthrush/kiero/minhook/src/trampoline.c @@ -0,0 +1,316 @@ +/* + * MinHook - The Minimalistic API Hooking Library for x64/x86 + * Copyright (C) 2009-2017 Tsuda Kageyu. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#ifndef ARRAYSIZE + #define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) +#endif + +#if defined(_M_X64) || defined(__x86_64__) + #include "./hde/hde64.h" + typedef hde64s HDE; + #define HDE_DISASM(code, hs) hde64_disasm(code, hs) +#else + #include "./hde/hde32.h" + typedef hde32s HDE; + #define HDE_DISASM(code, hs) hde32_disasm(code, hs) +#endif + +#include "trampoline.h" +#include "buffer.h" + +// Maximum size of a trampoline function. +#if defined(_M_X64) || defined(__x86_64__) + #define TRAMPOLINE_MAX_SIZE (MEMORY_SLOT_SIZE - sizeof(JMP_ABS)) +#else + #define TRAMPOLINE_MAX_SIZE MEMORY_SLOT_SIZE +#endif + +//------------------------------------------------------------------------- +static BOOL IsCodePadding(LPBYTE pInst, UINT size) +{ + UINT i; + + if (pInst[0] != 0x00 && pInst[0] != 0x90 && pInst[0] != 0xCC) + return FALSE; + + for (i = 1; i < size; ++i) + { + if (pInst[i] != pInst[0]) + return FALSE; + } + return TRUE; +} + +//------------------------------------------------------------------------- +BOOL CreateTrampolineFunction(PTRAMPOLINE ct) +{ +#if defined(_M_X64) || defined(__x86_64__) + CALL_ABS call = { + 0xFF, 0x15, 0x00000002, // FF15 00000002: CALL [RIP+8] + 0xEB, 0x08, // EB 08: JMP +10 + 0x0000000000000000ULL // Absolute destination address + }; + JMP_ABS jmp = { + 0xFF, 0x25, 0x00000000, // FF25 00000000: JMP [RIP+6] + 0x0000000000000000ULL // Absolute destination address + }; + JCC_ABS jcc = { + 0x70, 0x0E, // 7* 0E: J** +16 + 0xFF, 0x25, 0x00000000, // FF25 00000000: JMP [RIP+6] + 0x0000000000000000ULL // Absolute destination address + }; +#else + CALL_REL call = { + 0xE8, // E8 xxxxxxxx: CALL +5+xxxxxxxx + 0x00000000 // Relative destination address + }; + JMP_REL jmp = { + 0xE9, // E9 xxxxxxxx: JMP +5+xxxxxxxx + 0x00000000 // Relative destination address + }; + JCC_REL jcc = { + 0x0F, 0x80, // 0F8* xxxxxxxx: J** +6+xxxxxxxx + 0x00000000 // Relative destination address + }; +#endif + + UINT8 oldPos = 0; + UINT8 newPos = 0; + ULONG_PTR jmpDest = 0; // Destination address of an internal jump. + BOOL finished = FALSE; // Is the function completed? +#if defined(_M_X64) || defined(__x86_64__) + UINT8 instBuf[16]; +#endif + + ct->patchAbove = FALSE; + ct->nIP = 0; + + do + { + HDE hs; + UINT copySize; + LPVOID pCopySrc; + ULONG_PTR pOldInst = (ULONG_PTR)ct->pTarget + oldPos; + ULONG_PTR pNewInst = (ULONG_PTR)ct->pTrampoline + newPos; + + copySize = HDE_DISASM((LPVOID)pOldInst, &hs); + if (hs.flags & F_ERROR) + return FALSE; + + pCopySrc = (LPVOID)pOldInst; + if (oldPos >= sizeof(JMP_REL)) + { + // The trampoline function is long enough. + // Complete the function with the jump to the target function. +#if defined(_M_X64) || defined(__x86_64__) + jmp.address = pOldInst; +#else + jmp.operand = (UINT32)(pOldInst - (pNewInst + sizeof(jmp))); +#endif + pCopySrc = &jmp; + copySize = sizeof(jmp); + + finished = TRUE; + } +#if defined(_M_X64) || defined(__x86_64__) + else if ((hs.modrm & 0xC7) == 0x05) + { + // Instructions using RIP relative addressing. (ModR/M = 00???101B) + + // Modify the RIP relative address. + PUINT32 pRelAddr; + + // Avoid using memcpy to reduce the footprint. +#ifndef _MSC_VER + memcpy(instBuf, (LPBYTE)pOldInst, copySize); +#else + __movsb(instBuf, (LPBYTE)pOldInst, copySize); +#endif + pCopySrc = instBuf; + + // Relative address is stored at (instruction length - immediate value length - 4). + pRelAddr = (PUINT32)(instBuf + hs.len - ((hs.flags & 0x3C) >> 2) - 4); + *pRelAddr + = (UINT32)((pOldInst + hs.len + (INT32)hs.disp.disp32) - (pNewInst + hs.len)); + + // Complete the function if JMP (FF /4). + if (hs.opcode == 0xFF && hs.modrm_reg == 4) + finished = TRUE; + } +#endif + else if (hs.opcode == 0xE8) + { + // Direct relative CALL + ULONG_PTR dest = pOldInst + hs.len + (INT32)hs.imm.imm32; +#if defined(_M_X64) || defined(__x86_64__) + call.address = dest; +#else + call.operand = (UINT32)(dest - (pNewInst + sizeof(call))); +#endif + pCopySrc = &call; + copySize = sizeof(call); + } + else if ((hs.opcode & 0xFD) == 0xE9) + { + // Direct relative JMP (EB or E9) + ULONG_PTR dest = pOldInst + hs.len; + + if (hs.opcode == 0xEB) // isShort jmp + dest += (INT8)hs.imm.imm8; + else + dest += (INT32)hs.imm.imm32; + + // Simply copy an internal jump. + if ((ULONG_PTR)ct->pTarget <= dest + && dest < ((ULONG_PTR)ct->pTarget + sizeof(JMP_REL))) + { + if (jmpDest < dest) + jmpDest = dest; + } + else + { +#if defined(_M_X64) || defined(__x86_64__) + jmp.address = dest; +#else + jmp.operand = (UINT32)(dest - (pNewInst + sizeof(jmp))); +#endif + pCopySrc = &jmp; + copySize = sizeof(jmp); + + // Exit the function If it is not in the branch + finished = (pOldInst >= jmpDest); + } + } + else if ((hs.opcode & 0xF0) == 0x70 + || (hs.opcode & 0xFC) == 0xE0 + || (hs.opcode2 & 0xF0) == 0x80) + { + // Direct relative Jcc + ULONG_PTR dest = pOldInst + hs.len; + + if ((hs.opcode & 0xF0) == 0x70 // Jcc + || (hs.opcode & 0xFC) == 0xE0) // LOOPNZ/LOOPZ/LOOP/JECXZ + dest += (INT8)hs.imm.imm8; + else + dest += (INT32)hs.imm.imm32; + + // Simply copy an internal jump. + if ((ULONG_PTR)ct->pTarget <= dest + && dest < ((ULONG_PTR)ct->pTarget + sizeof(JMP_REL))) + { + if (jmpDest < dest) + jmpDest = dest; + } + else if ((hs.opcode & 0xFC) == 0xE0) + { + // LOOPNZ/LOOPZ/LOOP/JCXZ/JECXZ to the outside are not supported. + return FALSE; + } + else + { + UINT8 cond = ((hs.opcode != 0x0F ? hs.opcode : hs.opcode2) & 0x0F); +#if defined(_M_X64) || defined(__x86_64__) + // Invert the condition in x64 mode to simplify the conditional jump logic. + jcc.opcode = 0x71 ^ cond; + jcc.address = dest; +#else + jcc.opcode1 = 0x80 | cond; + jcc.operand = (UINT32)(dest - (pNewInst + sizeof(jcc))); +#endif + pCopySrc = &jcc; + copySize = sizeof(jcc); + } + } + else if ((hs.opcode & 0xFE) == 0xC2) + { + // RET (C2 or C3) + + // Complete the function if not in a branch. + finished = (pOldInst >= jmpDest); + } + + // Can't alter the instruction length in a branch. + if (pOldInst < jmpDest && copySize != hs.len) + return FALSE; + + // Trampoline function is too large. + if ((newPos + copySize) > TRAMPOLINE_MAX_SIZE) + return FALSE; + + // Trampoline function has too many instructions. + if (ct->nIP >= ARRAYSIZE(ct->oldIPs)) + return FALSE; + + ct->oldIPs[ct->nIP] = oldPos; + ct->newIPs[ct->nIP] = newPos; + ct->nIP++; + + // Avoid using memcpy to reduce the footprint. +#ifndef _MSC_VER + memcpy((LPBYTE)ct->pTrampoline + newPos, pCopySrc, copySize); +#else + __movsb((LPBYTE)ct->pTrampoline + newPos, pCopySrc, copySize); +#endif + newPos += copySize; + oldPos += hs.len; + } + while (!finished); + + // Is there enough place for a long jump? + if (oldPos < sizeof(JMP_REL) + && !IsCodePadding((LPBYTE)ct->pTarget + oldPos, sizeof(JMP_REL) - oldPos)) + { + // Is there enough place for a short jump? + if (oldPos < sizeof(JMP_REL_SHORT) + && !IsCodePadding((LPBYTE)ct->pTarget + oldPos, sizeof(JMP_REL_SHORT) - oldPos)) + { + return FALSE; + } + + // Can we place the long jump above the function? + if (!IsExecutableAddress((LPBYTE)ct->pTarget - sizeof(JMP_REL))) + return FALSE; + + if (!IsCodePadding((LPBYTE)ct->pTarget - sizeof(JMP_REL), sizeof(JMP_REL))) + return FALSE; + + ct->patchAbove = TRUE; + } + +#if defined(_M_X64) || defined(__x86_64__) + // Create a relay function. + jmp.address = (ULONG_PTR)ct->pDetour; + + ct->pRelay = (LPBYTE)ct->pTrampoline + newPos; + memcpy(ct->pRelay, &jmp, sizeof(jmp)); +#endif + + return TRUE; +} diff --git a/depthrush/kiero/minhook/src/trampoline.h b/depthrush/kiero/minhook/src/trampoline.h new file mode 100644 index 0000000..bdffdac --- /dev/null +++ b/depthrush/kiero/minhook/src/trampoline.h @@ -0,0 +1,105 @@ +/* + * MinHook - The Minimalistic API Hooking Library for x64/x86 + * Copyright (C) 2009-2017 Tsuda Kageyu. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#pragma pack(push, 1) + +// Structs for writing x86/x64 instructions. + +// 8-bit relative jump. +typedef struct _JMP_REL_SHORT +{ + UINT8 opcode; // EB xx: JMP +2+xx + UINT8 operand; +} JMP_REL_SHORT, *PJMP_REL_SHORT; + +// 32-bit direct relative jump/call. +typedef struct _JMP_REL +{ + UINT8 opcode; // E9/E8 xxxxxxxx: JMP/CALL +5+xxxxxxxx + UINT32 operand; // Relative destination address +} JMP_REL, *PJMP_REL, CALL_REL; + +// 64-bit indirect absolute jump. +typedef struct _JMP_ABS +{ + UINT8 opcode0; // FF25 00000000: JMP [+6] + UINT8 opcode1; + UINT32 dummy; + UINT64 address; // Absolute destination address +} JMP_ABS, *PJMP_ABS; + +// 64-bit indirect absolute call. +typedef struct _CALL_ABS +{ + UINT8 opcode0; // FF15 00000002: CALL [+6] + UINT8 opcode1; + UINT32 dummy0; + UINT8 dummy1; // EB 08: JMP +10 + UINT8 dummy2; + UINT64 address; // Absolute destination address +} CALL_ABS; + +// 32-bit direct relative conditional jumps. +typedef struct _JCC_REL +{ + UINT8 opcode0; // 0F8* xxxxxxxx: J** +6+xxxxxxxx + UINT8 opcode1; + UINT32 operand; // Relative destination address +} JCC_REL; + +// 64bit indirect absolute conditional jumps that x64 lacks. +typedef struct _JCC_ABS +{ + UINT8 opcode; // 7* 0E: J** +16 + UINT8 dummy0; + UINT8 dummy1; // FF25 00000000: JMP [+6] + UINT8 dummy2; + UINT32 dummy3; + UINT64 address; // Absolute destination address +} JCC_ABS; + +#pragma pack(pop) + +typedef struct _TRAMPOLINE +{ + LPVOID pTarget; // [In] Address of the target function. + LPVOID pDetour; // [In] Address of the detour function. + LPVOID pTrampoline; // [In] Buffer address for the trampoline and relay function. + +#if defined(_M_X64) || defined(__x86_64__) + LPVOID pRelay; // [Out] Address of the relay function. +#endif + BOOL patchAbove; // [Out] Should use the hot patch area? + UINT nIP; // [Out] Number of the instruction boundaries. + UINT8 oldIPs[8]; // [Out] Instruction boundaries of the target function. + UINT8 newIPs[8]; // [Out] Instruction boundaries of the trampoline function. +} TRAMPOLINE, *PTRAMPOLINE; + +BOOL CreateTrampolineFunction(PTRAMPOLINE ct); diff --git a/depthrush/main.cpp b/depthrush/main.cpp new file mode 100644 index 0000000..7b4eeae --- /dev/null +++ b/depthrush/main.cpp @@ -0,0 +1,53 @@ +#include "includes.h" +#include "drs.h" + +static uintptr_t imageBase; + +inline void safeJMP(injector::memory_pointer_tr at, injector::memory_pointer_raw dest, bool vp = true) +{ + MH_Initialize(); + MH_CreateHook((void*)at.as_int(), (void*)dest.as_int(), nullptr); + MH_EnableHook((void*)at.as_int()); +} + +inline void safeUNJMP(injector::memory_pointer_tr pTarget) { + MH_DisableHook((void*)pTarget.as_int()); + MH_RemoveHook((void*)pTarget.as_int()); +} + +static int returnTrue() { + return 1; +} + +static int returnFalse() { + return 0; +} + + +bool init = false; + +/* +DWORD WINAPI MainThread(LPVOID lpReserved) +{ + bool init_hook = false; + do + { + + } while (!init_hook); + return TRUE; +} +*/ + +BOOL WINAPI DllMain(HMODULE hMod, DWORD dwReason, LPVOID lpReserved) +{ + switch (dwReason) + { + case DLL_PROCESS_ATTACH: + DisableThreadLibraryCalls(hMod); + imageBase = (uintptr_t)GetModuleHandleA(0); + drs::hookDancepad(); + + break; + } + return TRUE; +} \ No newline at end of file