From 0d8925151ddc8e334b908e61c1bd9592f6f75dde Mon Sep 17 00:00:00 2001 From: gho tik Date: Wed, 1 Jul 2015 12:40:14 -0400 Subject: [PATCH] v2_03_07_src Former-commit-id: 0546aa60f9848b6f3f8db5f6b0714456b37774f3 --- Include/MinHook.h | 146 ++++ Include/dxwnd.h | 4 + MinHook_13_src/AUTHORS.txt | 8 + MinHook_13_src/LICENSE.txt | 81 ++ MinHook_13_src/build/VC10/MinHook.vcxproj | 189 +++++ MinHook_13_src/build/VC10/MinHookVC10.sln | 39 + MinHook_13_src/build/VC10/libMinHook.vcxproj | 172 ++++ .../build/VC10/libMinHook.vcxproj.filters | 55 ++ MinHook_13_src/build/VC11/MinHook.vcxproj | 189 +++++ MinHook_13_src/build/VC11/MinHookVC11.sln | 39 + MinHook_13_src/build/VC11/libMinHook.vcxproj | 172 ++++ .../build/VC11/libMinHook.vcxproj.filters | 55 ++ MinHook_13_src/build/VC12/MinHook.vcxproj | 189 +++++ MinHook_13_src/build/VC12/MinHookVC12.sln | 41 + MinHook_13_src/build/VC12/libMinHook.vcxproj | 174 ++++ .../build/VC12/libMinHook.vcxproj.filters | 55 ++ MinHook_13_src/build/VC9/MinHook.vcproj | 343 ++++++++ MinHook_13_src/build/VC9/MinHookVC9.sln | 39 + MinHook_13_src/build/VC9/MinHookVC9.suo | Bin 0 -> 9728 bytes MinHook_13_src/build/VC9/libMinHook.vcproj | 410 ++++++++++ MinHook_13_src/dll_resources/MinHook.def | 11 + MinHook_13_src/dll_resources/MinHook.rc | Bin 0 -> 1760 bytes MinHook_13_src/include/MinHook.h | 146 ++++ MinHook_13_src/src/HDE/hde32.c | 319 ++++++++ MinHook_13_src/src/HDE/hde32.h | 105 +++ MinHook_13_src/src/HDE/hde64.c | 330 ++++++++ MinHook_13_src/src/HDE/hde64.h | 112 +++ MinHook_13_src/src/HDE/pstdint.h | 39 + MinHook_13_src/src/HDE/table32.h | 73 ++ MinHook_13_src/src/HDE/table64.h | 74 ++ MinHook_13_src/src/buffer.c | 229 ++++++ MinHook_13_src/src/buffer.h | 42 + MinHook_13_src/src/hook.c | 752 ++++++++++++++++++ MinHook_13_src/src/trampoline.c | 303 +++++++ MinHook_13_src/src/trampoline.h | 105 +++ Release/.gitattributes | 1 + Release/dxwnd.dll | 3 + build/dxwnd.dll | 4 +- build/dxwnd.exe | 4 +- build/exports/Amerzone.dxw | 7 +- build/exports/Celtic Kings Rage of War.dxw | 5 +- .../Close Combat 5 Invasion Normandy.dxw | 3 + build/exports/Daikatana.dxw | 4 +- build/exports/Kingpin Life Of Crime.dxw | 5 +- build/exports/MDK2.dxw | 11 +- .../Sid Meier's Alpha Centauri (GOG).dxw | 29 + build/exports/Take no Prisoners.dxw | 11 +- build/exports/Theme Hospital.dxw | 2 + .../Thief the Dark Project GOLD (GOG).dxw | 7 +- build/exports/Thief the Dark Project GOLD.dxw | 9 +- .../exports/Tiger Woods PGA TOUR 08 Demo.dxw | 29 + build/exports/Unreal Tournament.dxw | 9 +- build/exports/dxwnd.ini | 5 - build/readme-relnotes.txt | 25 + build/registry/dxwnd.Die Hard Trilogy.REG | 69 ++ dll/Inject.cpp | 105 +++ dll/advapi.cpp | 52 +- dll/d3dtexture.cpp | 10 + dll/ddraw.cpp | 48 +- dll/ddtexture.cpp | 43 +- dll/dxhook.cpp | 39 +- dll/dxwnd.cpp | 96 ++- dll/dxwnd.vs2008.suo | Bin 280576 -> 49152 bytes dll/dxwnd.vs2008.vcproj | 12 +- dll/gdi32.cpp | 26 - dll/hd3d.cpp | 147 +++- dll/hotpatch.cpp | 36 + dll/kernel32.cpp | 162 +++- dll/libMinHook.x86.lib | Bin 0 -> 28026 bytes dll/syslibs.h | 4 + dll/user32.cpp | 102 +-- host/Inject.cpp | 57 +- host/Resource.h | Bin 31298 -> 32152 bytes host/TabNotes.cpp | 55 ++ host/TabNotes.h | 45 ++ host/TabSysLibs.cpp | 3 + host/TabWindow.cpp | 2 +- host/TargetDlg.cpp | 5 +- host/TargetDlg.h | 4 +- host/dxTabCtrl.cpp | 3 + host/dxwndhost.aps | Bin 126128 -> 153360 bytes host/dxwndhost.clw | 156 ---- host/dxwndhost.h | 3 + host/dxwndhost.opt | Bin 52736 -> 0 bytes host/dxwndhost.plg | 16 - host/dxwndhost.rc | Bin 91912 -> 93882 bytes host/dxwndhost.vs2008.suo | Bin 26624 -> 140800 bytes host/dxwndhost.vs2008.vcproj | 8 + host/dxwndhostView.cpp | 195 ++++- host/host.aps | Bin 47496 -> 0 bytes host/resource | Bin 31424 -> 32278 bytes 91 files changed, 6242 insertions(+), 474 deletions(-) create mode 100644 Include/MinHook.h create mode 100644 MinHook_13_src/AUTHORS.txt create mode 100644 MinHook_13_src/LICENSE.txt create mode 100644 MinHook_13_src/build/VC10/MinHook.vcxproj create mode 100644 MinHook_13_src/build/VC10/MinHookVC10.sln create mode 100644 MinHook_13_src/build/VC10/libMinHook.vcxproj create mode 100644 MinHook_13_src/build/VC10/libMinHook.vcxproj.filters create mode 100644 MinHook_13_src/build/VC11/MinHook.vcxproj create mode 100644 MinHook_13_src/build/VC11/MinHookVC11.sln create mode 100644 MinHook_13_src/build/VC11/libMinHook.vcxproj create mode 100644 MinHook_13_src/build/VC11/libMinHook.vcxproj.filters create mode 100644 MinHook_13_src/build/VC12/MinHook.vcxproj create mode 100644 MinHook_13_src/build/VC12/MinHookVC12.sln create mode 100644 MinHook_13_src/build/VC12/libMinHook.vcxproj create mode 100644 MinHook_13_src/build/VC12/libMinHook.vcxproj.filters create mode 100644 MinHook_13_src/build/VC9/MinHook.vcproj create mode 100644 MinHook_13_src/build/VC9/MinHookVC9.sln create mode 100644 MinHook_13_src/build/VC9/MinHookVC9.suo create mode 100644 MinHook_13_src/build/VC9/libMinHook.vcproj create mode 100644 MinHook_13_src/dll_resources/MinHook.def create mode 100644 MinHook_13_src/dll_resources/MinHook.rc create mode 100644 MinHook_13_src/include/MinHook.h create mode 100644 MinHook_13_src/src/HDE/hde32.c create mode 100644 MinHook_13_src/src/HDE/hde32.h create mode 100644 MinHook_13_src/src/HDE/hde64.c create mode 100644 MinHook_13_src/src/HDE/hde64.h create mode 100644 MinHook_13_src/src/HDE/pstdint.h create mode 100644 MinHook_13_src/src/HDE/table32.h create mode 100644 MinHook_13_src/src/HDE/table64.h create mode 100644 MinHook_13_src/src/buffer.c create mode 100644 MinHook_13_src/src/buffer.h create mode 100644 MinHook_13_src/src/hook.c create mode 100644 MinHook_13_src/src/trampoline.c create mode 100644 MinHook_13_src/src/trampoline.h create mode 100644 Release/.gitattributes create mode 100644 Release/dxwnd.dll create mode 100644 build/exports/Sid Meier's Alpha Centauri (GOG).dxw create mode 100644 build/exports/Tiger Woods PGA TOUR 08 Demo.dxw delete mode 100644 build/exports/dxwnd.ini create mode 100644 build/registry/dxwnd.Die Hard Trilogy.REG create mode 100644 dll/Inject.cpp create mode 100644 dll/libMinHook.x86.lib create mode 100644 host/TabNotes.cpp create mode 100644 host/TabNotes.h delete mode 100644 host/dxwndhost.clw delete mode 100644 host/dxwndhost.opt delete mode 100644 host/dxwndhost.plg delete mode 100644 host/host.aps diff --git a/Include/MinHook.h b/Include/MinHook.h new file mode 100644 index 0000000..8760ea0 --- /dev/null +++ b/Include/MinHook.h @@ -0,0 +1,146 @@ +/* + * MinHook - The Minimalistic API Hooking Library for x64/x86 + * Copyright (C) 2009-2014 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) + #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 +} +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); + + // 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); + +#ifdef __cplusplus +} +#endif + diff --git a/Include/dxwnd.h b/Include/dxwnd.h index ddeacf0..f6ff34a 100644 --- a/Include/dxwnd.h +++ b/Include/dxwnd.h @@ -166,6 +166,8 @@ #define NORMALIZEPERFCOUNT 0x00020000 // Normalize Performance Counter to a Performance Frequency of 1MHz #define HYBRIDMODE 0x00040000 // ????? #define GDICOLORCONV 0x00080000 // do color conversion using GDI +#define INJECTSON 0x00100000 // when starting a son process, inject dxwnd.dll at the beginning of execution +#define ENABLESONHOOK 0x00200000 // forward hooking capability to son processes // logging Tflags DWORD: #define OUTTRACE 0x00000001 // enables tracing to dxwnd.log in general @@ -232,6 +234,7 @@ typedef struct int TimeShift; short CursorX, CursorY; PALETTEENTRY Palette[256]; + BOOL AllowMultiTask; } DXWNDSTATUS; extern DXWNDSTATUS DxWndStatus; @@ -244,6 +247,7 @@ int GetHookStatus(DXWNDSTATUS *); DXWNDSTATUS *GetHookInfo(); void HookInit(TARGETMAP *, HWND); +char *GetDxWndPath(); void *SetHook(void *, void *); void SetHook(void *, void *, void **, char *); void OutTrace(const char *, ...); diff --git a/MinHook_13_src/AUTHORS.txt b/MinHook_13_src/AUTHORS.txt new file mode 100644 index 0000000..ebef1a6 --- /dev/null +++ b/MinHook_13_src/AUTHORS.txt @@ -0,0 +1,8 @@ +Tsuda Kageyu + Creator, maintainer + +Michael Maltsev + Added "Queue" functions. A lot of bug fixes. + +Andrey Unis + Rewrote the hook engine in plain C. diff --git a/MinHook_13_src/LICENSE.txt b/MinHook_13_src/LICENSE.txt new file mode 100644 index 0000000..fa1acc1 --- /dev/null +++ b/MinHook_13_src/LICENSE.txt @@ -0,0 +1,81 @@ +MinHook - The Minimalistic API Hooking Library for x64/x86 +Copyright (C) 2009-2014 Tsuda Kageyu. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +================================================================================ +Portions of this software are Copyright (c) 2008-2009, Vyacheslav Patkov. +================================================================================ +Hacker Disassembler Engine 32 C +Copyright (c) 2008-2009, Vyacheslav Patkov. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +------------------------------------------------------------------------------- +Hacker Disassembler Engine 64 C +Copyright (c) 2008-2009, Vyacheslav Patkov. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/MinHook_13_src/build/VC10/MinHook.vcxproj b/MinHook_13_src/build/VC10/MinHook.vcxproj new file mode 100644 index 0000000..3944d80 --- /dev/null +++ b/MinHook_13_src/build/VC10/MinHook.vcxproj @@ -0,0 +1,189 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {027FAC75-3FDB-4044-8DD0-BC297BD4C461} + MinHook + Win32Proj + + + + DynamicLibrary + Unicode + true + + + DynamicLibrary + Unicode + + + DynamicLibrary + Unicode + true + + + DynamicLibrary + Unicode + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.40219.1 + $(SolutionDir)bin\$(Configuration)\ + $(Platform)\$(Configuration)\$(ProjectName)\ + true + $(SolutionDir)bin\$(Configuration)\ + $(Platform)\$(Configuration)\$(ProjectName)\ + true + $(SolutionDir)bin\$(Configuration)\ + $(Platform)\$(Configuration)\$(ProjectName)\ + false + $(SolutionDir)bin\$(Configuration)\ + $(Platform)\$(Configuration)\$(ProjectName)\ + false + $(ProjectName).x86 + $(ProjectName).x86 + $(ProjectName).x64 + $(ProjectName).x64 + + + + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;MINHOOK_EXPORTS;%(PreprocessorDefinitions) + false + EnableFastChecks + MultiThreadedDebug + + + Level3 + + + + + $(SolutionDir)..\..\dll_resources\MinHook.def + false + Windows + MachineX86 + $(SolutionDir)lib\$(Configuration)\libMinHook.x86.lib;%(AdditionalDependencies) + + + + + X64 + + + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;MINHOOK_EXPORTS;%(PreprocessorDefinitions) + false + EnableFastChecks + MultiThreadedDebug + + + Level3 + + + + + $(SolutionDir)..\..\dll_resources\MinHook.def + false + Windows + MachineX64 + $(SolutionDir)lib\$(Configuration)\libMinHook.x64.lib;%(AdditionalDependencies) + + + + + MinSpace + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;MINHOOK_EXPORTS;%(PreprocessorDefinitions) + MultiThreaded + true + + + Level3 + + + false + + + $(SolutionDir)..\..\dll_resources\MinHook.def + false + Windows + true + true + MachineX86 + $(SolutionDir)lib\$(Configuration)\libMinHook.x86.lib;%(AdditionalDependencies) + true + .CRT=.text + + + + + X64 + + + MinSpace + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;MINHOOK_EXPORTS;%(PreprocessorDefinitions) + MultiThreaded + true + + + Level3 + + + false + + + $(SolutionDir)..\..\dll_resources\MinHook.def + false + Windows + true + true + MachineX64 + $(SolutionDir)lib\$(Configuration)\libMinHook.x64.lib;%(AdditionalDependencies) + true + .CRT=.text + + + + + + + + + + + + \ No newline at end of file diff --git a/MinHook_13_src/build/VC10/MinHookVC10.sln b/MinHook_13_src/build/VC10/MinHookVC10.sln new file mode 100644 index 0000000..dcc1d5c --- /dev/null +++ b/MinHook_13_src/build/VC10/MinHookVC10.sln @@ -0,0 +1,39 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libMinHook", "libMinHook.vcxproj", "{F142A341-5EE0-442D-A15F-98AE9B48DBAE}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MinHook", "MinHook.vcxproj", "{027FAC75-3FDB-4044-8DD0-BC297BD4C461}" + ProjectSection(ProjectDependencies) = postProject + {F142A341-5EE0-442D-A15F-98AE9B48DBAE} = {F142A341-5EE0-442D-A15F-98AE9B48DBAE} + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Debug|Win32.ActiveCfg = Debug|Win32 + {F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Debug|Win32.Build.0 = Debug|Win32 + {F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Debug|x64.ActiveCfg = Debug|x64 + {F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Debug|x64.Build.0 = Debug|x64 + {F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Release|Win32.ActiveCfg = Release|Win32 + {F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Release|Win32.Build.0 = Release|Win32 + {F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Release|x64.ActiveCfg = Release|x64 + {F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Release|x64.Build.0 = Release|x64 + {027FAC75-3FDB-4044-8DD0-BC297BD4C461}.Debug|Win32.ActiveCfg = Debug|Win32 + {027FAC75-3FDB-4044-8DD0-BC297BD4C461}.Debug|Win32.Build.0 = Debug|Win32 + {027FAC75-3FDB-4044-8DD0-BC297BD4C461}.Debug|x64.ActiveCfg = Debug|x64 + {027FAC75-3FDB-4044-8DD0-BC297BD4C461}.Debug|x64.Build.0 = Debug|x64 + {027FAC75-3FDB-4044-8DD0-BC297BD4C461}.Release|Win32.ActiveCfg = Release|Win32 + {027FAC75-3FDB-4044-8DD0-BC297BD4C461}.Release|Win32.Build.0 = Release|Win32 + {027FAC75-3FDB-4044-8DD0-BC297BD4C461}.Release|x64.ActiveCfg = Release|x64 + {027FAC75-3FDB-4044-8DD0-BC297BD4C461}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/MinHook_13_src/build/VC10/libMinHook.vcxproj b/MinHook_13_src/build/VC10/libMinHook.vcxproj new file mode 100644 index 0000000..eb7cb9a --- /dev/null +++ b/MinHook_13_src/build/VC10/libMinHook.vcxproj @@ -0,0 +1,172 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {F142A341-5EE0-442D-A15F-98AE9B48DBAE} + libMinHook + Win32Proj + + + + StaticLibrary + Unicode + true + + + StaticLibrary + Unicode + + + StaticLibrary + Unicode + true + + + StaticLibrary + Unicode + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.40219.1 + $(SolutionDir)lib\$(Configuration)\ + $(Platform)\$(Configuration)\$(ProjectName)\ + $(SolutionDir)lib\$(Configuration)\ + $(Platform)\$(Configuration)\$(ProjectName)\ + $(SolutionDir)lib\$(Configuration)\ + $(Platform)\$(Configuration)\$(ProjectName)\ + $(SolutionDir)lib\$(Configuration)\ + $(Platform)\$(Configuration)\$(ProjectName)\ + $(ProjectName).x86 + $(ProjectName).x86 + $(ProjectName).x64 + $(ProjectName).x64 + + + + Disabled + $(SolutionDir)..\..\include\;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + false + EnableFastChecks + MultiThreadedDebug + Level3 + + + false + + + + + + X64 + + + Disabled + $(SolutionDir)..\..\include\;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + false + EnableFastChecks + MultiThreadedDebug + Level3 + + + false + + + + + + MinSpace + true + $(SolutionDir)..\..\include\;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + false + MultiThreaded + true + Level3 + + + true + AnySuitable + + + + + + X64 + + + MinSpace + true + $(SolutionDir)..\..\include\;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + false + MultiThreaded + true + Level3 + + + true + AnySuitable + + + + + + + true + true + + + true + true + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/MinHook_13_src/build/VC10/libMinHook.vcxproj.filters b/MinHook_13_src/build/VC10/libMinHook.vcxproj.filters new file mode 100644 index 0000000..f2d1d97 --- /dev/null +++ b/MinHook_13_src/build/VC10/libMinHook.vcxproj.filters @@ -0,0 +1,55 @@ + + + + + Source Files + + + Source Files + + + Source Files + + + HDE + + + HDE + + + + + Header Files + + + Header Files + + + + HDE + + + HDE + + + HDE + + + HDE + + + HDE + + + + + {9d24b740-be2e-4cfd-b9a4-340a50946ee9} + + + {76381bc7-2863-4cc5-aede-926ec2c506e4} + + + {56ddb326-6179-430d-ae19-e13bfd767bfa} + + + \ No newline at end of file diff --git a/MinHook_13_src/build/VC11/MinHook.vcxproj b/MinHook_13_src/build/VC11/MinHook.vcxproj new file mode 100644 index 0000000..4c0e212 --- /dev/null +++ b/MinHook_13_src/build/VC11/MinHook.vcxproj @@ -0,0 +1,189 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {027FAC75-3FDB-4044-8DD0-BC297BD4C461} + MinHook + Win32Proj + + + + DynamicLibrary + Unicode + true + v110_xp + + + DynamicLibrary + Unicode + v110_xp + + + DynamicLibrary + Unicode + true + v110_xp + + + DynamicLibrary + Unicode + v110_xp + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.40219.1 + $(SolutionDir)bin\$(Configuration)\ + $(Platform)\$(Configuration)\$(ProjectName)\ + true + $(SolutionDir)bin\$(Configuration)\ + $(Platform)\$(Configuration)\$(ProjectName)\ + true + $(SolutionDir)bin\$(Configuration)\ + $(Platform)\$(Configuration)\$(ProjectName)\ + false + $(SolutionDir)bin\$(Configuration)\ + $(Platform)\$(Configuration)\$(ProjectName)\ + false + $(ProjectName).x86 + $(ProjectName).x86 + $(ProjectName).x64 + $(ProjectName).x64 + + + + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;MINHOOK_EXPORTS;%(PreprocessorDefinitions) + false + EnableFastChecks + MultiThreadedDebug + + + Level3 + None + + + $(SolutionDir)..\..\dll_resources\MinHook.def + false + Windows + MachineX86 + $(SolutionDir)lib\$(Configuration)\libMinHook.x86.lib;%(AdditionalDependencies) + + + + + X64 + + + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;MINHOOK_EXPORTS;%(PreprocessorDefinitions) + false + EnableFastChecks + MultiThreadedDebug + + + Level3 + None + + + $(SolutionDir)..\..\dll_resources\MinHook.def + false + Windows + MachineX64 + $(SolutionDir)lib\$(Configuration)\libMinHook.x64.lib;%(AdditionalDependencies) + + + + + MinSpace + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;MINHOOK_EXPORTS;%(PreprocessorDefinitions) + MultiThreaded + true + + + Level3 + None + false + + + $(SolutionDir)..\..\dll_resources\MinHook.def + false + Windows + true + true + MachineX86 + $(SolutionDir)lib\$(Configuration)\libMinHook.x86.lib;%(AdditionalDependencies) + true + .CRT=.text + + + + + X64 + + + MinSpace + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;MINHOOK_EXPORTS;%(PreprocessorDefinitions) + MultiThreaded + true + + + Level3 + None + false + + + $(SolutionDir)..\..\dll_resources\MinHook.def + false + Windows + true + true + MachineX64 + $(SolutionDir)lib\$(Configuration)\libMinHook.x64.lib;%(AdditionalDependencies) + true + .CRT=.text + + + + + + + + + + + + \ No newline at end of file diff --git a/MinHook_13_src/build/VC11/MinHookVC11.sln b/MinHook_13_src/build/VC11/MinHookVC11.sln new file mode 100644 index 0000000..5b56553 --- /dev/null +++ b/MinHook_13_src/build/VC11/MinHookVC11.sln @@ -0,0 +1,39 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2012 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libMinHook", "libMinHook.vcxproj", "{F142A341-5EE0-442D-A15F-98AE9B48DBAE}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MinHook", "MinHook.vcxproj", "{027FAC75-3FDB-4044-8DD0-BC297BD4C461}" + ProjectSection(ProjectDependencies) = postProject + {F142A341-5EE0-442D-A15F-98AE9B48DBAE} = {F142A341-5EE0-442D-A15F-98AE9B48DBAE} + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Debug|Win32.ActiveCfg = Debug|Win32 + {F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Debug|Win32.Build.0 = Debug|Win32 + {F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Debug|x64.ActiveCfg = Debug|x64 + {F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Debug|x64.Build.0 = Debug|x64 + {F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Release|Win32.ActiveCfg = Release|Win32 + {F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Release|Win32.Build.0 = Release|Win32 + {F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Release|x64.ActiveCfg = Release|x64 + {F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Release|x64.Build.0 = Release|x64 + {027FAC75-3FDB-4044-8DD0-BC297BD4C461}.Debug|Win32.ActiveCfg = Debug|Win32 + {027FAC75-3FDB-4044-8DD0-BC297BD4C461}.Debug|Win32.Build.0 = Debug|Win32 + {027FAC75-3FDB-4044-8DD0-BC297BD4C461}.Debug|x64.ActiveCfg = Debug|x64 + {027FAC75-3FDB-4044-8DD0-BC297BD4C461}.Debug|x64.Build.0 = Debug|x64 + {027FAC75-3FDB-4044-8DD0-BC297BD4C461}.Release|Win32.ActiveCfg = Release|Win32 + {027FAC75-3FDB-4044-8DD0-BC297BD4C461}.Release|Win32.Build.0 = Release|Win32 + {027FAC75-3FDB-4044-8DD0-BC297BD4C461}.Release|x64.ActiveCfg = Release|x64 + {027FAC75-3FDB-4044-8DD0-BC297BD4C461}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/MinHook_13_src/build/VC11/libMinHook.vcxproj b/MinHook_13_src/build/VC11/libMinHook.vcxproj new file mode 100644 index 0000000..018886f --- /dev/null +++ b/MinHook_13_src/build/VC11/libMinHook.vcxproj @@ -0,0 +1,172 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {F142A341-5EE0-442D-A15F-98AE9B48DBAE} + libMinHook + Win32Proj + + + + StaticLibrary + Unicode + true + v110_xp + + + StaticLibrary + Unicode + v110_xp + + + StaticLibrary + Unicode + true + v110_xp + + + StaticLibrary + Unicode + v110_xp + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.40219.1 + $(SolutionDir)lib\$(Configuration)\ + $(Platform)\$(Configuration)\$(ProjectName)\ + $(SolutionDir)lib\$(Configuration)\ + $(Platform)\$(Configuration)\$(ProjectName)\ + $(SolutionDir)lib\$(Configuration)\ + $(Platform)\$(Configuration)\$(ProjectName)\ + $(SolutionDir)lib\$(Configuration)\ + $(Platform)\$(Configuration)\$(ProjectName)\ + $(ProjectName).x86 + $(ProjectName).x86 + $(ProjectName).x64 + $(ProjectName).x64 + + + + Disabled + $(SolutionDir)..\..\include\;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + false + EnableFastChecks + MultiThreadedDebug + Level3 + None + NoExtensions + + + + + + X64 + + + Disabled + $(SolutionDir)..\..\include\;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + false + EnableFastChecks + MultiThreadedDebug + Level3 + None + + + + + + MinSpace + true + $(SolutionDir)..\..\include\;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + false + MultiThreaded + true + Level3 + None + true + AnySuitable + NoExtensions + + + + + + X64 + + + MinSpace + true + $(SolutionDir)..\..\include\;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + false + MultiThreaded + true + Level3 + None + true + AnySuitable + + + + + + + true + true + + + true + true + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/MinHook_13_src/build/VC11/libMinHook.vcxproj.filters b/MinHook_13_src/build/VC11/libMinHook.vcxproj.filters new file mode 100644 index 0000000..f2d1d97 --- /dev/null +++ b/MinHook_13_src/build/VC11/libMinHook.vcxproj.filters @@ -0,0 +1,55 @@ + + + + + Source Files + + + Source Files + + + Source Files + + + HDE + + + HDE + + + + + Header Files + + + Header Files + + + + HDE + + + HDE + + + HDE + + + HDE + + + HDE + + + + + {9d24b740-be2e-4cfd-b9a4-340a50946ee9} + + + {76381bc7-2863-4cc5-aede-926ec2c506e4} + + + {56ddb326-6179-430d-ae19-e13bfd767bfa} + + + \ No newline at end of file diff --git a/MinHook_13_src/build/VC12/MinHook.vcxproj b/MinHook_13_src/build/VC12/MinHook.vcxproj new file mode 100644 index 0000000..40ec836 --- /dev/null +++ b/MinHook_13_src/build/VC12/MinHook.vcxproj @@ -0,0 +1,189 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {027FAC75-3FDB-4044-8DD0-BC297BD4C461} + MinHook + Win32Proj + + + + DynamicLibrary + Unicode + true + v120_xp + + + DynamicLibrary + Unicode + v120_xp + + + DynamicLibrary + Unicode + true + v120_xp + + + DynamicLibrary + Unicode + v120_xp + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.40219.1 + $(SolutionDir)bin\$(Configuration)\ + $(Platform)\$(Configuration)\$(ProjectName)\ + true + $(SolutionDir)bin\$(Configuration)\ + $(Platform)\$(Configuration)\$(ProjectName)\ + true + $(SolutionDir)bin\$(Configuration)\ + $(Platform)\$(Configuration)\$(ProjectName)\ + false + $(SolutionDir)bin\$(Configuration)\ + $(Platform)\$(Configuration)\$(ProjectName)\ + false + $(ProjectName).x86 + $(ProjectName).x86 + $(ProjectName).x64 + $(ProjectName).x64 + + + + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;MINHOOK_EXPORTS;%(PreprocessorDefinitions) + false + EnableFastChecks + MultiThreadedDebug + + + Level3 + None + + + $(SolutionDir)..\..\dll_resources\MinHook.def + false + Windows + MachineX86 + $(SolutionDir)lib\$(Configuration)\libMinHook.x86.lib;%(AdditionalDependencies) + + + + + X64 + + + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;MINHOOK_EXPORTS;%(PreprocessorDefinitions) + false + EnableFastChecks + MultiThreadedDebug + + + Level3 + None + + + $(SolutionDir)..\..\dll_resources\MinHook.def + false + Windows + MachineX64 + $(SolutionDir)lib\$(Configuration)\libMinHook.x64.lib;%(AdditionalDependencies) + + + + + MinSpace + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;MINHOOK_EXPORTS;%(PreprocessorDefinitions) + MultiThreaded + true + + + Level3 + None + false + + + $(SolutionDir)..\..\dll_resources\MinHook.def + false + Windows + true + true + MachineX86 + $(SolutionDir)lib\$(Configuration)\libMinHook.x86.lib;%(AdditionalDependencies) + true + .CRT=.text + + + + + X64 + + + MinSpace + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;MINHOOK_EXPORTS;%(PreprocessorDefinitions) + MultiThreaded + true + + + Level3 + None + false + + + $(SolutionDir)..\..\dll_resources\MinHook.def + false + Windows + true + true + MachineX64 + $(SolutionDir)lib\$(Configuration)\libMinHook.x64.lib;%(AdditionalDependencies) + true + .CRT=.text + + + + + + + + + + + + \ No newline at end of file diff --git a/MinHook_13_src/build/VC12/MinHookVC12.sln b/MinHook_13_src/build/VC12/MinHookVC12.sln new file mode 100644 index 0000000..cfd928b --- /dev/null +++ b/MinHook_13_src/build/VC12/MinHookVC12.sln @@ -0,0 +1,41 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.30501.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libMinHook", "libMinHook.vcxproj", "{F142A341-5EE0-442D-A15F-98AE9B48DBAE}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MinHook", "MinHook.vcxproj", "{027FAC75-3FDB-4044-8DD0-BC297BD4C461}" + ProjectSection(ProjectDependencies) = postProject + {F142A341-5EE0-442D-A15F-98AE9B48DBAE} = {F142A341-5EE0-442D-A15F-98AE9B48DBAE} + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Debug|Win32.ActiveCfg = Debug|Win32 + {F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Debug|Win32.Build.0 = Debug|Win32 + {F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Debug|x64.ActiveCfg = Debug|x64 + {F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Debug|x64.Build.0 = Debug|x64 + {F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Release|Win32.ActiveCfg = Release|Win32 + {F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Release|Win32.Build.0 = Release|Win32 + {F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Release|x64.ActiveCfg = Release|x64 + {F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Release|x64.Build.0 = Release|x64 + {027FAC75-3FDB-4044-8DD0-BC297BD4C461}.Debug|Win32.ActiveCfg = Debug|Win32 + {027FAC75-3FDB-4044-8DD0-BC297BD4C461}.Debug|Win32.Build.0 = Debug|Win32 + {027FAC75-3FDB-4044-8DD0-BC297BD4C461}.Debug|x64.ActiveCfg = Debug|x64 + {027FAC75-3FDB-4044-8DD0-BC297BD4C461}.Debug|x64.Build.0 = Debug|x64 + {027FAC75-3FDB-4044-8DD0-BC297BD4C461}.Release|Win32.ActiveCfg = Release|Win32 + {027FAC75-3FDB-4044-8DD0-BC297BD4C461}.Release|Win32.Build.0 = Release|Win32 + {027FAC75-3FDB-4044-8DD0-BC297BD4C461}.Release|x64.ActiveCfg = Release|x64 + {027FAC75-3FDB-4044-8DD0-BC297BD4C461}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/MinHook_13_src/build/VC12/libMinHook.vcxproj b/MinHook_13_src/build/VC12/libMinHook.vcxproj new file mode 100644 index 0000000..7aa3e70 --- /dev/null +++ b/MinHook_13_src/build/VC12/libMinHook.vcxproj @@ -0,0 +1,174 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {F142A341-5EE0-442D-A15F-98AE9B48DBAE} + libMinHook + Win32Proj + + + + StaticLibrary + Unicode + true + v120_xp + + + StaticLibrary + Unicode + v120_xp + + + StaticLibrary + Unicode + true + v120_xp + + + StaticLibrary + Unicode + v120_xp + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.40219.1 + $(SolutionDir)lib\$(Configuration)\ + $(Platform)\$(Configuration)\$(ProjectName)\ + $(SolutionDir)lib\$(Configuration)\ + $(Platform)\$(Configuration)\$(ProjectName)\ + $(SolutionDir)lib\$(Configuration)\ + $(Platform)\$(Configuration)\$(ProjectName)\ + $(SolutionDir)lib\$(Configuration)\ + $(Platform)\$(Configuration)\$(ProjectName)\ + $(ProjectName).x86 + $(ProjectName).x86 + $(ProjectName).x64 + $(ProjectName).x64 + + + + Disabled + $(SolutionDir)..\..\include\;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + false + EnableFastChecks + MultiThreadedDebug + Level3 + None + NoExtensions + + + + + + X64 + + + Disabled + $(SolutionDir)..\..\include\;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + false + EnableFastChecks + MultiThreadedDebug + Level3 + None + NotSet + + + + + + MinSpace + true + $(SolutionDir)..\..\include\;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + false + MultiThreaded + true + Level3 + None + AnySuitable + CompileAsC + true + NoExtensions + + + + + + X64 + + + MinSpace + true + $(SolutionDir)..\..\include\;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + false + MultiThreaded + true + Level3 + None + true + AnySuitable + + + + + + + true + true + + + true + true + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/MinHook_13_src/build/VC12/libMinHook.vcxproj.filters b/MinHook_13_src/build/VC12/libMinHook.vcxproj.filters new file mode 100644 index 0000000..f2d1d97 --- /dev/null +++ b/MinHook_13_src/build/VC12/libMinHook.vcxproj.filters @@ -0,0 +1,55 @@ + + + + + Source Files + + + Source Files + + + Source Files + + + HDE + + + HDE + + + + + Header Files + + + Header Files + + + + HDE + + + HDE + + + HDE + + + HDE + + + HDE + + + + + {9d24b740-be2e-4cfd-b9a4-340a50946ee9} + + + {76381bc7-2863-4cc5-aede-926ec2c506e4} + + + {56ddb326-6179-430d-ae19-e13bfd767bfa} + + + \ No newline at end of file diff --git a/MinHook_13_src/build/VC9/MinHook.vcproj b/MinHook_13_src/build/VC9/MinHook.vcproj new file mode 100644 index 0000000..4bad257 --- /dev/null +++ b/MinHook_13_src/build/VC9/MinHook.vcproj @@ -0,0 +1,343 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MinHook_13_src/build/VC9/MinHookVC9.sln b/MinHook_13_src/build/VC9/MinHookVC9.sln new file mode 100644 index 0000000..869f5b6 --- /dev/null +++ b/MinHook_13_src/build/VC9/MinHookVC9.sln @@ -0,0 +1,39 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libMinHook", "libMinHook.vcproj", "{F142A341-5EE0-442D-A15F-98AE9B48DBAE}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MinHook", "MinHook.vcproj", "{027FAC75-3FDB-4044-8DD0-BC297BD4C461}" + ProjectSection(ProjectDependencies) = postProject + {F142A341-5EE0-442D-A15F-98AE9B48DBAE} = {F142A341-5EE0-442D-A15F-98AE9B48DBAE} + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Debug|Win32.ActiveCfg = Debug|Win32 + {F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Debug|Win32.Build.0 = Debug|Win32 + {F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Debug|x64.ActiveCfg = Debug|x64 + {F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Debug|x64.Build.0 = Debug|x64 + {F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Release|Win32.ActiveCfg = Release|Win32 + {F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Release|Win32.Build.0 = Release|Win32 + {F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Release|x64.ActiveCfg = Release|x64 + {F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Release|x64.Build.0 = Release|x64 + {027FAC75-3FDB-4044-8DD0-BC297BD4C461}.Debug|Win32.ActiveCfg = Debug|Win32 + {027FAC75-3FDB-4044-8DD0-BC297BD4C461}.Debug|Win32.Build.0 = Debug|Win32 + {027FAC75-3FDB-4044-8DD0-BC297BD4C461}.Debug|x64.ActiveCfg = Debug|x64 + {027FAC75-3FDB-4044-8DD0-BC297BD4C461}.Debug|x64.Build.0 = Debug|x64 + {027FAC75-3FDB-4044-8DD0-BC297BD4C461}.Release|Win32.ActiveCfg = Release|Win32 + {027FAC75-3FDB-4044-8DD0-BC297BD4C461}.Release|Win32.Build.0 = Release|Win32 + {027FAC75-3FDB-4044-8DD0-BC297BD4C461}.Release|x64.ActiveCfg = Release|x64 + {027FAC75-3FDB-4044-8DD0-BC297BD4C461}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/MinHook_13_src/build/VC9/MinHookVC9.suo b/MinHook_13_src/build/VC9/MinHookVC9.suo new file mode 100644 index 0000000000000000000000000000000000000000..bf204d70322cdc2449ffc108739bb7d37d716542 GIT binary patch literal 9728 zcmeI1S!|S56vuDt23i-ipd#W(%cit;gSLW5+i5`sLCWGnB2cD?3@vui8bOUdxRPKr zQR5Od7!wH+VxrNw#3B#G7>tiT$pg_C6(2M)MybF5{qCK9GwIBHrDI&;nVh-zJ9jzf z-g~zDeP0}z@b#+)CLc8!u+W@hP7IAVr-$f8)P)w#Gp3k|o=*%74FyDFsQwTCq8<1H zSxAgX>tpDYvxM(7+UWTk4Lk;qovM<3J z_^q;MkTe@92TZ%=e>d`UP|F9?SikI7>yZrk`m0@jTVn+B-C>ju@Xui%;O$?&Akzn& z@5Yo;YA+^%7lIdoey&TXPXR9lr-GM(G4OJ58dwIFgVVto;7o89I2*hIyb_!PR)Ce@ zRbUlZ4c36-Q%5=1)$1u6z((+DM_b@ZFViBPo4{tU1&o8OU>mp?TmoJPUJu>?YTSPf zp1Y+ZdEPT6iywZVynb+MiMC->@KcMT#NP+9rLlmE;E5~b75C9Zq6oGrePSzYFA}JE~ za|TJC+M)F$kM<&lE>~g^>*&+Mzee^^dd)ZHNoE|a298An>1q-U??jeiIsEfKMV>%P zd4a!of23I*?3d0n!G0AAoGPVFHx^pjM0p-gH$_Es`BpxK6?9+cxt6= zrnLdZZ??}n%?>ts3vM+%P*f69I&dsO!n=b*C{)Pt26zWXs*qF(pB?<}L#k$0)4-|% ztibWtu%a5|ujUue?tkvZu9T5_PwBTRy~O|6JEQW(sQyoRl%FM&|1val8(eh{S<5EM z7j~O_vU=e;_&?>T%1@*6*PV5h6F~mH51u`6lE)j4l>TOtU^DyhcZvn-nI4g@J_-Mu zzW#3J?`O@y%;7b!fM+jzkSh{ZvHvBkMfdcm>(|w*TUH~Go;UETw|ZX1zkY3&G8bwi zdR=37s%K5DGVFA~{;UQ5fWiOFoRew(d~eT!A6yS z48@;-G1u6f!I=3NnnA4#XR4Lx*F84M4f9=YqkovI-gt~GTc924JI>x)3Mt!sW-at; zSE6Ui1a<38;3DGaST_Chci}j_buJ@8CHk*Bp>$6$QlCuP#So@AqVgO?B(LLvopqx1 zo1rRy^(~pR1kc!iig#50($Nz4vSK+t?oKpNxpJnBufYiNf$rbB|4NUe@~@@uLvG?0 zRNH0W?WX*!Kbo%G^~sV0 zU%h$cg4frcyVRU>cyjXh@ny?*4?Xhhk-8)p{9&a4o_|Z$8KH{dBV!>bg_9@q8#OGMbgSX@Nm3b>04zeBP zySYK}N7<3?dr{-hJo=Wp{Bt$=w771-^CUnoPmovTdh-*=Mf@H0`;Tz^TSBI>$@1=E z%}Jzbvl)q`=^-lT1M%1UZmnIAufN*E{FmF@M<>)}adT+r<4}6?OvwJHct_na~C)TMN!_n^5h#9oFx3(jbk-mT~S3MKVOSLs}ml@mjQOZPT(xQENP-m$K+ zm#AvuX%%rb7u~M?ul1fsccN7eUeF0bd3M`#?|I zZz;yS?`V2Y_z^fA{MgZS+Z_b8?t_k|w_l$++95}4q5J?8{054LBH!1*&m8SL zN7H&Iasc`wJBd=wTJMF_WyKdc+Dhu*yRnUqrghAxF6eMH=}N-Uo(T2Z u+R>hMv<1+fceHmMtrpsQj`pFWX|FzZw1bYOGd$#IKT)2HwJ^nh4E_Z4aa$As literal 0 HcmV?d00001 diff --git a/MinHook_13_src/build/VC9/libMinHook.vcproj b/MinHook_13_src/build/VC9/libMinHook.vcproj new file mode 100644 index 0000000..ec1c9f7 --- /dev/null +++ b/MinHook_13_src/build/VC9/libMinHook.vcproj @@ -0,0 +1,410 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MinHook_13_src/dll_resources/MinHook.def b/MinHook_13_src/dll_resources/MinHook.def new file mode 100644 index 0000000..9bd1cd8 --- /dev/null +++ b/MinHook_13_src/dll_resources/MinHook.def @@ -0,0 +1,11 @@ +EXPORTS + MH_Initialize + MH_Uninitialize + + MH_CreateHook + MH_RemoveHook + MH_EnableHook + MH_DisableHook + MH_QueueEnableHook + MH_QueueDisableHook + MH_ApplyQueued diff --git a/MinHook_13_src/dll_resources/MinHook.rc b/MinHook_13_src/dll_resources/MinHook.rc new file mode 100644 index 0000000000000000000000000000000000000000..c82d9a34fc7f2b05fdf9d06cf9a9bedda80716e9 GIT binary patch literal 1760 zcmbW2Ur*vd6vfZ8$$p0hUv?8ks$_S41o^YZDrD)B_++Y9wh;>f+{l;L{mqn82iT1e zGBa)O+;i@nd*|}@>dRAKP5o3)=j!N+`ARLGK4}kIi}wJ#yT_BS1D!A`c4T|IpmfyG zFP$m;kVlT*f}^H3FUwmDFT<`b_7*CIx>lFSGW+qGy+_1#w0XZPXRPDbw(4hm9w;8W z12jFeij-iUX+lh?24p7TL@^#IF^*JP#F_hTLd$Hcs1;RF`;<}DEwhVM`1}ts-_<97 z0jD$?l!$wzCyf;m5}QvYW9O~BR#@)$O!ctACHPNdL~2`p=Sb$@xH@SBbVo_g7LJH9$6|D!u)gRk z+8wce=V_Pt`@}MV+l|{-nOjySCuVGeYpP;qF;jc4Rqb6x8e3rp;0`RR$?q5?Oid2Z zbGB4`-Qwvp_3#C?jya2|w?q5nqnydc=2|FYtC5!>Yr!(LZzkhLwLi@FD71|}#Th~n zBHLL^1x%=q>GBl+ifS9CE|??mpQ)+Y+8uE7;L+Cte!JuEUPn!0YdAe*{fpa;`c|*} l2JX^_+-{L&B1Qip$nG6B{{~r?GQGoJ)X@7*Swver{RMe2-VXo( literal 0 HcmV?d00001 diff --git a/MinHook_13_src/include/MinHook.h b/MinHook_13_src/include/MinHook.h new file mode 100644 index 0000000..8760ea0 --- /dev/null +++ b/MinHook_13_src/include/MinHook.h @@ -0,0 +1,146 @@ +/* + * MinHook - The Minimalistic API Hooking Library for x64/x86 + * Copyright (C) 2009-2014 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) + #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 +} +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); + + // 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); + +#ifdef __cplusplus +} +#endif + diff --git a/MinHook_13_src/src/HDE/hde32.c b/MinHook_13_src/src/HDE/hde32.c new file mode 100644 index 0000000..391bd79 --- /dev/null +++ b/MinHook_13_src/src/HDE/hde32.c @@ -0,0 +1,319 @@ +/* + * Hacker Disassembler Engine 32 C + * Copyright (c) 2008-2009, Vyacheslav Patkov. + * All rights reserved. + * + */ + +#include +#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. + __stosb((LPBYTE)hs, 0, sizeof(hde32s)); + + for (x = 16; x; x--) + switch (c = *p++) { + case 0xf3: + hs->p_rep = c; + pref |= PRE_F3; + break; + case 0xf2: + hs->p_rep = c; + pref |= PRE_F2; + break; + case 0xf0: + hs->p_lock = c; + pref |= PRE_LOCK; + break; + case 0x26: case 0x2e: case 0x36: + case 0x3e: case 0x64: case 0x65: + hs->p_seg = c; + pref |= PRE_SEG; + break; + case 0x66: + hs->p_66 = c; + pref |= PRE_66; + break; + case 0x67: + hs->p_67 = c; + pref |= PRE_67; + break; + default: + goto pref_done; + } + pref_done: + + hs->flags = (uint32_t)pref << 23; + + if (!pref) + pref |= PRE_NONE; + + if ((hs->opcode = c) == 0x0f) { + hs->opcode2 = c = *p++; + ht += DELTA_OPCODES; + } else if (c >= 0xa0 && c <= 0xa3) { + if (pref & PRE_67) + pref |= PRE_66; + else + pref &= ~PRE_66; + } + + opcode = c; + cflags = ht[ht[opcode / 4] + (opcode % 4)]; + + if (cflags == C_ERROR) { + hs->flags |= F_ERROR | F_ERROR_OPCODE; + cflags = 0; + if ((opcode & -3) == 0x24) + cflags++; + } + + x = 0; + if (cflags & C_GROUP) { + uint16_t t; + t = *(uint16_t *)(ht + (cflags & 0x7f)); + cflags = (uint8_t)t; + x = (uint8_t)(t >> 8); + } + + if (hs->opcode2) { + ht = hde32_table + DELTA_PREFIXES; + if (ht[ht[opcode / 4] + (opcode % 4)] & pref) + hs->flags |= F_ERROR | F_ERROR_OPCODE; + } + + if (cflags & C_MODRM) { + hs->flags |= F_MODRM; + hs->modrm = c = *p++; + hs->modrm_mod = m_mod = c >> 6; + hs->modrm_rm = m_rm = c & 7; + hs->modrm_reg = m_reg = (c & 0x3f) >> 3; + + if (x && ((x << m_reg) & 0x80)) + hs->flags |= F_ERROR | F_ERROR_OPCODE; + + if (!hs->opcode2 && opcode >= 0xd9 && opcode <= 0xdf) { + uint8_t t = opcode - 0xd9; + if (m_mod == 3) { + ht = hde32_table + DELTA_FPU_MODRM + t*8; + t = ht[m_reg] << m_rm; + } else { + ht = hde32_table + DELTA_FPU_REG; + t = ht[t] << m_reg; + } + if (t & 0x80) + hs->flags |= F_ERROR | F_ERROR_OPCODE; + } + + if (pref & PRE_LOCK) { + if (m_mod == 3) { + hs->flags |= F_ERROR | F_ERROR_LOCK; + } else { + uint8_t *table_end, op = opcode; + if (hs->opcode2) { + ht = hde32_table + DELTA_OP2_LOCK_OK; + table_end = ht + DELTA_OP_ONLY_MEM - DELTA_OP2_LOCK_OK; + } else { + ht = hde32_table + DELTA_OP_LOCK_OK; + table_end = ht + DELTA_OP2_LOCK_OK - DELTA_OP_LOCK_OK; + op &= -2; + } + for (; ht != table_end; ht++) + if (*ht++ == op) { + if (!((*ht << m_reg) & 0x80)) + goto no_lock_error; + else + break; + } + hs->flags |= F_ERROR | F_ERROR_LOCK; + no_lock_error: + ; + } + } + + if (hs->opcode2) { + switch (opcode) { + case 0x20: case 0x22: + m_mod = 3; + if (m_reg > 4 || m_reg == 1) + goto error_operand; + else + goto no_error_operand; + case 0x21: case 0x23: + m_mod = 3; + if (m_reg == 4 || m_reg == 5) + goto error_operand; + else + goto no_error_operand; + } + } else { + switch (opcode) { + case 0x8c: + if (m_reg > 5) + goto error_operand; + else + goto no_error_operand; + case 0x8e: + if (m_reg == 1 || m_reg > 5) + goto error_operand; + else + goto no_error_operand; + } + } + + if (m_mod == 3) { + uint8_t *table_end; + if (hs->opcode2) { + ht = hde32_table + DELTA_OP2_ONLY_MEM; + table_end = ht + sizeof(hde32_table) - DELTA_OP2_ONLY_MEM; + } else { + ht = hde32_table + DELTA_OP_ONLY_MEM; + table_end = ht + DELTA_OP2_ONLY_MEM - DELTA_OP_ONLY_MEM; + } + for (; ht != table_end; ht += 2) + if (*ht++ == opcode) { + if (*ht++ & pref && !((*ht << m_reg) & 0x80)) + goto error_operand; + else + break; + } + goto no_error_operand; + } else if (hs->opcode2) { + switch (opcode) { + case 0x50: case 0xd7: case 0xf7: + if (pref & (PRE_NONE | PRE_66)) + goto error_operand; + break; + case 0xd6: + if (pref & (PRE_F2 | PRE_F3)) + goto error_operand; + break; + case 0xc5: + goto error_operand; + } + goto no_error_operand; + } else + goto no_error_operand; + + error_operand: + hs->flags |= F_ERROR | F_ERROR_OPERAND; + no_error_operand: + + c = *p++; + if (m_reg <= 1) { + if (opcode == 0xf6) + cflags |= C_IMM8; + else if (opcode == 0xf7) + cflags |= C_IMM_P66; + } + + switch (m_mod) { + case 0: + if (pref & PRE_67) { + if (m_rm == 6) + disp_size = 2; + } else + if (m_rm == 5) + disp_size = 4; + break; + case 1: + disp_size = 1; + break; + case 2: + disp_size = 2; + if (!(pref & PRE_67)) + disp_size <<= 1; + } + + 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; +} diff --git a/MinHook_13_src/src/HDE/hde32.h b/MinHook_13_src/src/HDE/hde32.h new file mode 100644 index 0000000..1112450 --- /dev/null +++ b/MinHook_13_src/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/MinHook_13_src/src/HDE/hde64.c b/MinHook_13_src/src/HDE/hde64.c new file mode 100644 index 0000000..68a5611 --- /dev/null +++ b/MinHook_13_src/src/HDE/hde64.c @@ -0,0 +1,330 @@ +/* + * Hacker Disassembler Engine 64 C + * Copyright (c) 2008-2009, Vyacheslav Patkov. + * All rights reserved. + * + */ + +#include +#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. + __stosb((LPBYTE)hs, 0, sizeof(hde64s)); + + for (x = 16; x; x--) + switch (c = *p++) { + case 0xf3: + hs->p_rep = c; + pref |= PRE_F3; + break; + case 0xf2: + hs->p_rep = c; + pref |= PRE_F2; + break; + case 0xf0: + hs->p_lock = c; + pref |= PRE_LOCK; + break; + case 0x26: case 0x2e: case 0x36: + case 0x3e: case 0x64: case 0x65: + hs->p_seg = c; + pref |= PRE_SEG; + break; + case 0x66: + hs->p_66 = c; + pref |= PRE_66; + break; + case 0x67: + hs->p_67 = c; + pref |= PRE_67; + break; + default: + goto pref_done; + } + pref_done: + + hs->flags = (uint32_t)pref << 23; + + if (!pref) + pref |= PRE_NONE; + + if ((c & 0xf0) == 0x40) { + hs->flags |= F_PREFIX_REX; + if ((hs->rex_w = (c & 0xf) >> 3) && (*p & 0xf8) == 0xb8) + op64++; + hs->rex_r = (c & 7) >> 2; + hs->rex_x = (c & 3) >> 1; + hs->rex_b = c & 1; + if (((c = *p++) & 0xf0) == 0x40) { + opcode = c; + goto error_opcode; + } + } + + if ((hs->opcode = c) == 0x0f) { + hs->opcode2 = c = *p++; + ht += DELTA_OPCODES; + } else if (c >= 0xa0 && c <= 0xa3) { + op64++; + if (pref & PRE_67) + pref |= PRE_66; + else + pref &= ~PRE_66; + } + + opcode = c; + cflags = ht[ht[opcode / 4] + (opcode % 4)]; + + if (cflags == C_ERROR) { + error_opcode: + hs->flags |= F_ERROR | F_ERROR_OPCODE; + cflags = 0; + if ((opcode & -3) == 0x24) + cflags++; + } + + x = 0; + if (cflags & C_GROUP) { + uint16_t t; + t = *(uint16_t *)(ht + (cflags & 0x7f)); + cflags = (uint8_t)t; + x = (uint8_t)(t >> 8); + } + + if (hs->opcode2) { + ht = hde64_table + DELTA_PREFIXES; + if (ht[ht[opcode / 4] + (opcode % 4)] & pref) + hs->flags |= F_ERROR | F_ERROR_OPCODE; + } + + if (cflags & C_MODRM) { + hs->flags |= F_MODRM; + hs->modrm = c = *p++; + hs->modrm_mod = m_mod = c >> 6; + hs->modrm_rm = m_rm = c & 7; + hs->modrm_reg = m_reg = (c & 0x3f) >> 3; + + if (x && ((x << m_reg) & 0x80)) + hs->flags |= F_ERROR | F_ERROR_OPCODE; + + if (!hs->opcode2 && opcode >= 0xd9 && opcode <= 0xdf) { + uint8_t t = opcode - 0xd9; + if (m_mod == 3) { + ht = hde64_table + DELTA_FPU_MODRM + t*8; + t = ht[m_reg] << m_rm; + } else { + ht = hde64_table + DELTA_FPU_REG; + t = ht[t] << m_reg; + } + if (t & 0x80) + hs->flags |= F_ERROR | F_ERROR_OPCODE; + } + + if (pref & PRE_LOCK) { + if (m_mod == 3) { + hs->flags |= F_ERROR | F_ERROR_LOCK; + } else { + uint8_t *table_end, op = opcode; + if (hs->opcode2) { + ht = hde64_table + DELTA_OP2_LOCK_OK; + table_end = ht + DELTA_OP_ONLY_MEM - DELTA_OP2_LOCK_OK; + } else { + ht = hde64_table + DELTA_OP_LOCK_OK; + table_end = ht + DELTA_OP2_LOCK_OK - DELTA_OP_LOCK_OK; + op &= -2; + } + for (; ht != table_end; ht++) + if (*ht++ == op) { + if (!((*ht << m_reg) & 0x80)) + goto no_lock_error; + else + break; + } + hs->flags |= F_ERROR | F_ERROR_LOCK; + no_lock_error: + ; + } + } + + if (hs->opcode2) { + switch (opcode) { + case 0x20: case 0x22: + m_mod = 3; + if (m_reg > 4 || m_reg == 1) + goto error_operand; + else + goto no_error_operand; + case 0x21: case 0x23: + m_mod = 3; + if (m_reg == 4 || m_reg == 5) + goto error_operand; + else + goto no_error_operand; + } + } else { + switch (opcode) { + case 0x8c: + if (m_reg > 5) + goto error_operand; + else + goto no_error_operand; + case 0x8e: + if (m_reg == 1 || m_reg > 5) + goto error_operand; + else + goto no_error_operand; + } + } + + if (m_mod == 3) { + uint8_t *table_end; + if (hs->opcode2) { + ht = hde64_table + DELTA_OP2_ONLY_MEM; + table_end = ht + sizeof(hde64_table) - DELTA_OP2_ONLY_MEM; + } else { + ht = hde64_table + DELTA_OP_ONLY_MEM; + table_end = ht + DELTA_OP2_ONLY_MEM - DELTA_OP_ONLY_MEM; + } + for (; ht != table_end; ht += 2) + if (*ht++ == opcode) { + if (*ht++ & pref && !((*ht << m_reg) & 0x80)) + goto error_operand; + else + break; + } + goto no_error_operand; + } else if (hs->opcode2) { + switch (opcode) { + case 0x50: case 0xd7: case 0xf7: + if (pref & (PRE_NONE | PRE_66)) + goto error_operand; + break; + case 0xd6: + if (pref & (PRE_F2 | PRE_F3)) + goto error_operand; + break; + case 0xc5: + goto error_operand; + } + goto no_error_operand; + } else + goto no_error_operand; + + error_operand: + hs->flags |= F_ERROR | F_ERROR_OPERAND; + no_error_operand: + + c = *p++; + if (m_reg <= 1) { + if (opcode == 0xf6) + cflags |= C_IMM8; + else if (opcode == 0xf7) + cflags |= C_IMM_P66; + } + + switch (m_mod) { + case 0: + if (pref & PRE_67) { + if (m_rm == 6) + disp_size = 2; + } else + if (m_rm == 5) + disp_size = 4; + break; + case 1: + disp_size = 1; + break; + case 2: + disp_size = 2; + if (!(pref & PRE_67)) + disp_size <<= 1; + } + + 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; +} diff --git a/MinHook_13_src/src/HDE/hde64.h b/MinHook_13_src/src/HDE/hde64.h new file mode 100644 index 0000000..ecbf4df --- /dev/null +++ b/MinHook_13_src/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/MinHook_13_src/src/HDE/pstdint.h b/MinHook_13_src/src/HDE/pstdint.h new file mode 100644 index 0000000..46af329 --- /dev/null +++ b/MinHook_13_src/src/HDE/pstdint.h @@ -0,0 +1,39 @@ +/* + * MinHook - The Minimalistic API Hooking Library for x64/x86 + * Copyright (C) 2009-2014 Tsuda Kageyu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include + +// Integer types for HDE. +typedef INT8 int8_t; +typedef INT16 int16_t; +typedef INT32 int32_t; +typedef INT64 int64_t; +typedef UINT8 uint8_t; +typedef UINT16 uint16_t; +typedef UINT32 uint32_t; +typedef UINT64 uint64_t; diff --git a/MinHook_13_src/src/HDE/table32.h b/MinHook_13_src/src/HDE/table32.h new file mode 100644 index 0000000..7b3e12e --- /dev/null +++ b/MinHook_13_src/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/MinHook_13_src/src/HDE/table64.h b/MinHook_13_src/src/HDE/table64.h new file mode 100644 index 0000000..01d4541 --- /dev/null +++ b/MinHook_13_src/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/MinHook_13_src/src/buffer.c b/MinHook_13_src/src/buffer.c new file mode 100644 index 0000000..d4cf2e5 --- /dev/null +++ b/MinHook_13_src/src/buffer.c @@ -0,0 +1,229 @@ +/* + * MinHook - The Minimalistic API Hooking Library for x64/x86 + * Copyright (C) 2009-2014 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. (= 32MB) +#define MAX_MEMORY_RANGE 0x02000000 + +// 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; + } +} + +//------------------------------------------------------------------------- +static PMEMORY_BLOCK GetMemoryBlock(LPVOID pOrigin) +{ + ULONG_PTR minAddr; + ULONG_PTR maxAddr; + PMEMORY_BLOCK pBlock; + + SYSTEM_INFO si; + GetSystemInfo(&si); + minAddr = (ULONG_PTR)si.lpMinimumApplicationAddress; + maxAddr = (ULONG_PTR)si.lpMaximumApplicationAddress; + +#ifdef _M_X64 + // pOrigin ± 16MB + if ((ULONG_PTR)pOrigin > MAX_MEMORY_RANGE) + minAddr = max(minAddr, (ULONG_PTR)pOrigin - MAX_MEMORY_RANGE); + + maxAddr = min(maxAddr, (ULONG_PTR)pOrigin + MAX_MEMORY_RANGE); +#endif + + // Look the registered blocks for a reachable one. + for (pBlock = g_pMemoryBlocks; pBlock != NULL; pBlock = pBlock->pNext) + { +#ifdef _M_X64 + // 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; + } + + // Alloc a new block if not found. + { + ULONG_PTR pStart = ((ULONG_PTR)pOrigin / MEMORY_BLOCK_SIZE) * MEMORY_BLOCK_SIZE; + ULONG_PTR pAlloc; + for (pAlloc = pStart - MEMORY_BLOCK_SIZE; pAlloc >= minAddr; pAlloc -= MEMORY_BLOCK_SIZE) + { + pBlock = (PMEMORY_BLOCK)VirtualAlloc( + (LPVOID)pAlloc, MEMORY_BLOCK_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); + if (pBlock != NULL) + break; + } + if (pBlock == NULL) + { + for (pAlloc = pStart + MEMORY_BLOCK_SIZE; pAlloc < maxAddr; pAlloc += MEMORY_BLOCK_SIZE) + { + pBlock = (PMEMORY_BLOCK)VirtualAlloc( + (LPVOID)pAlloc, MEMORY_BLOCK_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); + if (pBlock != NULL) + break; + } + } + } + + if (pBlock != NULL) + { + // Build a linked list of all the slots. + PMEMORY_SLOT pSlot = (PMEMORY_SLOT)pBlock + 1; + pBlock->pFree = NULL; + pBlock->usedCount = 0; + do + { + pSlot->pNext = pBlock->pFree; + pBlock->pFree = pSlot; + pSlot++; + } while ((ULONG_PTR)pSlot - (ULONG_PTR)pBlock <= MEMORY_BLOCK_SIZE - MEMORY_SLOT_SIZE); + + pBlock->pNext = g_pMemoryBlocks; + g_pMemoryBlocks = pBlock; + } + + return pBlock; +} + +//------------------------------------------------------------------------- +LPVOID AllocateBuffer(LPVOID pOrigin) +{ + PMEMORY_SLOT pSlot; + PMEMORY_BLOCK pBlock = GetMemoryBlock(pOrigin); + if (pBlock == NULL) + return NULL; + + // Remove an unused slot from the list. + pSlot = pBlock->pFree; + pBlock->pFree = pSlot->pNext; + pBlock->usedCount++; +#ifdef _DEBUG + // Fill the slot with INT3 for debugging. + memset(pSlot, 0xCC, sizeof(MEMORY_SLOT)); +#endif + return pSlot; +} + +//------------------------------------------------------------------------- +VOID FreeBuffer(LPVOID pBuffer) +{ + PMEMORY_BLOCK pBlock = g_pMemoryBlocks; + PMEMORY_BLOCK pPrev = NULL; + ULONG_PTR pTargetBlock = ((ULONG_PTR)pBuffer / MEMORY_BLOCK_SIZE) * MEMORY_BLOCK_SIZE; + + while (pBlock != NULL) + { + if ((ULONG_PTR)pBlock == pTargetBlock) + { + PMEMORY_SLOT pSlot = (PMEMORY_SLOT)pBuffer; +#ifdef _DEBUG + // Clear the released slot for debugging. + memset(pSlot, 0x00, sizeof(MEMORY_SLOT)); +#endif + // Restore the released slot to the list. + pSlot->pNext = pBlock->pFree; + pBlock->pFree = pSlot; + pBlock->usedCount--; + + // Free if unused. + if (pBlock->usedCount == 0) + { + if (pPrev) + pPrev->pNext = pBlock->pNext; + else + g_pMemoryBlocks = pBlock->pNext; + + VirtualFree(pBlock, 0, MEM_RELEASE); + } + + break; + } + + pPrev = pBlock; + pBlock = pBlock->pNext; + } +} + +//------------------------------------------------------------------------- +BOOL IsExecutableAddress(LPVOID pAddress) +{ + MEMORY_BASIC_INFORMATION mi; + VirtualQuery(pAddress, &mi, sizeof(MEMORY_BASIC_INFORMATION)); + + return (mi.State == MEM_COMMIT && (mi.Protect & PAGE_EXECUTE_FLAGS)); +} diff --git a/MinHook_13_src/src/buffer.h b/MinHook_13_src/src/buffer.h new file mode 100644 index 0000000..03edcd5 --- /dev/null +++ b/MinHook_13_src/src/buffer.h @@ -0,0 +1,42 @@ +/* + * MinHook - The Minimalistic API Hooking Library for x64/x86 + * Copyright (C) 2009-2014 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. +#ifdef _M_X64 + #define MEMORY_SLOT_SIZE 64 +#else + #define MEMORY_SLOT_SIZE 32 +#endif + +VOID InitializeBuffer(VOID); +VOID UninitializeBuffer(VOID); +LPVOID AllocateBuffer(LPVOID pOrigin); +VOID FreeBuffer(LPVOID pBuffer); +BOOL IsExecutableAddress(LPVOID pAddress); diff --git a/MinHook_13_src/src/hook.c b/MinHook_13_src/src/hook.c new file mode 100644 index 0000000..265a8e8 --- /dev/null +++ b/MinHook_13_src/src/hook.c @@ -0,0 +1,752 @@ +/* + * MinHook - The Minimalistic API Hooking Library for x64/x86 + * Copyright (C) 2009-2014 Tsuda Kageyu. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include "MinHook.h" +#include "buffer.h" +#include "trampoline.h" + +// 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. + + BOOL patchAbove : 1; // Uses the hot patch area. + BOOL isEnabled : 1; // Enabled. + BOOL queueEnable : 1; // Queued for enabling/disabling when != isEnabled. + + UINT nIP : 3; // 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 NewHookEntry() +{ + 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 DelHookEntry(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]; + } + + 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; +#ifdef _M_X64 + 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; + + case 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); + + pHook->isEnabled = enable; + pHook->queueEnable = enable; + + return MH_OK; +} + +//------------------------------------------------------------------------- +static MH_STATUS EnableAllHooksLL(BOOL enable) +{ + UINT i; + for (i = 0; i < g_hooks.size; ++i) + { + if (g_hooks.pItems[i].isEnabled != enable) + { + FROZEN_THREADS threads; + Freeze(&threads, ALL_HOOKS_POS, enable ? ACTION_ENABLE : ACTION_DISABLE); + __try + { + for (; i < g_hooks.size; ++i) + { + if (g_hooks.pItems[i].isEnabled != enable) + { + MH_STATUS status = EnableHookLL(i, enable); + if (status != MH_OK) + return status; + } + } + } + __finally + { + Unfreeze(&threads); + } + } + } + + return MH_OK; +} + +//------------------------------------------------------------------------- +static VOID EnterSpinLock(VOID) +{ + // Wait until the flag is FALSE. + while (_InterlockedCompareExchange(&g_isLocked, TRUE, FALSE) != FALSE) + { + // Prevent the loop from being too busy. + Sleep(1); + } +} + +//------------------------------------------------------------------------- +static VOID LeaveSpinLock(VOID) +{ + _InterlockedExchange(&g_isLocked, FALSE); +} + +//------------------------------------------------------------------------- +MH_STATUS WINAPI MH_Initialize(VOID) +{ + EnterSpinLock(); + __try + { + if (g_hHeap != NULL) + return MH_ERROR_ALREADY_INITIALIZED; + + g_hHeap = HeapCreate(0, 0, 0); + if (g_hHeap == NULL) + return MH_ERROR_MEMORY_ALLOC; + + // Initialize the internal function buffer. + InitializeBuffer(); + + return MH_OK; + } + __finally + { + LeaveSpinLock(); + } +} + +//------------------------------------------------------------------------- +MH_STATUS WINAPI MH_Uninitialize(VOID) +{ + EnterSpinLock(); + __try + { + MH_STATUS status; + + if (g_hHeap == NULL) + return MH_ERROR_NOT_INITIALIZED; + + status = EnableAllHooksLL(FALSE); + if (status != MH_OK) + return status; + + // Free the internal function buffer. + UninitializeBuffer(); + HeapDestroy(g_hHeap); + + g_hHeap = NULL; + + g_hooks.pItems = NULL; + g_hooks.capacity = 0; + g_hooks.size = 0; + + return MH_OK; + } + __finally + { + LeaveSpinLock(); + } +} + +//------------------------------------------------------------------------- +MH_STATUS WINAPI MH_CreateHook(LPVOID pTarget, LPVOID pDetour, LPVOID *ppOriginal) +{ + EnterSpinLock(); + __try + { + UINT pos; + LPVOID pBuffer; + TRAMPOLINE ct; + PHOOK_ENTRY pHook; + + if (g_hHeap == NULL) + return MH_ERROR_NOT_INITIALIZED; + + if (!IsExecutableAddress(pTarget) || !IsExecutableAddress(pDetour)) + return MH_ERROR_NOT_EXECUTABLE; + + pos = FindHookEntry(pTarget); + if (pos != INVALID_HOOK_POS) + return MH_ERROR_ALREADY_CREATED; + + pBuffer = AllocateBuffer(pTarget); + if (pBuffer == NULL) + return MH_ERROR_MEMORY_ALLOC; + + ct.pTarget = pTarget; + ct.pDetour = pDetour; + ct.pTrampoline = pBuffer; + if (!CreateTrampolineFunction(&ct)) + { + FreeBuffer(pBuffer); + return MH_ERROR_UNSUPPORTED_FUNCTION; + } + + pHook = NewHookEntry(); + if (pHook == NULL) + { + FreeBuffer(pBuffer); + return MH_ERROR_MEMORY_ALLOC; + } + + pHook->pTarget = ct.pTarget; +#ifdef _M_X64 + 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; + + return MH_OK; + } + __finally + { + LeaveSpinLock(); + } +} + +//------------------------------------------------------------------------- +MH_STATUS WINAPI MH_RemoveHook(LPVOID pTarget) +{ + EnterSpinLock(); + __try + { + UINT pos; + + if (g_hHeap == NULL) + return MH_ERROR_NOT_INITIALIZED; + + pos = FindHookEntry(pTarget); + if (pos == INVALID_HOOK_POS) + return MH_ERROR_NOT_CREATED; + + if (g_hooks.pItems[pos].isEnabled) + { + FROZEN_THREADS threads; + Freeze(&threads, pos, ACTION_DISABLE); + __try + { + MH_STATUS status = EnableHookLL(pos, FALSE); + if (status != MH_OK) + return status; + } + __finally + { + Unfreeze(&threads); + } + } + + FreeBuffer(g_hooks.pItems[pos].pTrampoline); + DelHookEntry(pos); + + return MH_OK; + } + __finally + { + LeaveSpinLock(); + } +} + +//------------------------------------------------------------------------- +static MH_STATUS EnableHook(LPVOID pTarget, BOOL enable) +{ + EnterSpinLock(); + __try + { + if (g_hHeap == NULL) + return MH_ERROR_NOT_INITIALIZED; + + if (pTarget == MH_ALL_HOOKS) + { + return EnableAllHooksLL(enable); + } + else + { + FROZEN_THREADS threads; + UINT pos = FindHookEntry(pTarget); + if (pos == INVALID_HOOK_POS) + return MH_ERROR_NOT_CREATED; + + if (g_hooks.pItems[pos].isEnabled == enable) + return enable ? MH_ERROR_ENABLED : MH_ERROR_DISABLED; + + Freeze(&threads, pos, ACTION_ENABLE); + __try + { + return EnableHookLL(pos, enable); + } + __finally + { + Unfreeze(&threads); + } + } + } + __finally + { + LeaveSpinLock(); + } +} + +//------------------------------------------------------------------------- +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) +{ + EnterSpinLock(); + __try + { + if (g_hHeap == NULL) + return MH_ERROR_NOT_INITIALIZED; + + 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) + return MH_ERROR_NOT_CREATED; + + g_hooks.pItems[pos].queueEnable = queueEnable; + } + + return MH_OK; + } + __finally + { + LeaveSpinLock(); + } +} + +//------------------------------------------------------------------------- +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) +{ + EnterSpinLock(); + __try + { + UINT i; + + if (g_hHeap == NULL) + return MH_ERROR_NOT_INITIALIZED; + + for (i = 0; i < g_hooks.size; ++i) + { + if (g_hooks.pItems[i].isEnabled != g_hooks.pItems[i].queueEnable) + { + FROZEN_THREADS threads; + Freeze(&threads, ALL_HOOKS_POS, ACTION_APPLY_QUEUED); + __try + { + for (; i < g_hooks.size; ++i) + { + PHOOK_ENTRY pHook = &g_hooks.pItems[i]; + if (pHook->isEnabled != pHook->queueEnable) + { + MH_STATUS status = EnableHookLL(i, pHook->queueEnable); + if (status != MH_OK) + return status; + } + } + } + __finally + { + Unfreeze(&threads); + } + } + } + + return MH_OK; + } + __finally + { + LeaveSpinLock(); + } +} diff --git a/MinHook_13_src/src/trampoline.c b/MinHook_13_src/src/trampoline.c new file mode 100644 index 0000000..c82ca02 --- /dev/null +++ b/MinHook_13_src/src/trampoline.c @@ -0,0 +1,303 @@ +/* + * MinHook - The Minimalistic API Hooking Library for x64/x86 + * Copyright (C) 2009-2014 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 + +#ifdef _M_X64 + #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. +#ifdef _M_X64 + #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) +{ +#ifdef _M_X64 + 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? +#ifdef _M_X64 + 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. +#ifdef _M_X64 + jmp.address = pOldInst; +#else + jmp.operand = (UINT32)(pOldInst - (pNewInst + sizeof(jmp))); +#endif + pCopySrc = &jmp; + copySize = sizeof(jmp); + + finished = TRUE; + } +#ifdef _M_X64 + 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. + __movsb(instBuf, (LPBYTE)pOldInst, copySize); + 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; +#ifdef _M_X64 + 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 + { +#ifdef _M_X64 + 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); +#ifdef _M_X64 + // Invert the condition. + 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; + + if ((newPos + copySize) > TRAMPOLINE_MAX_SIZE) + return FALSE; + + 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. + __movsb((LPBYTE)ct->pTrampoline + newPos, pCopySrc, copySize); + 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; + } + +#ifdef _M_X64 + // Create a relay function. + jmp.address = (ULONG_PTR)ct->pDetour; + + ct->pRelay = (LPBYTE)ct->pTrampoline + newPos; + memcpy(ct->pRelay, &jmp, sizeof(jmp)); +#endif + + return TRUE; +} diff --git a/MinHook_13_src/src/trampoline.h b/MinHook_13_src/src/trampoline.h new file mode 100644 index 0000000..d2badef --- /dev/null +++ b/MinHook_13_src/src/trampoline.h @@ -0,0 +1,105 @@ +/* + * MinHook - The Minimalistic API Hooking Library for x64/x86 + * Copyright (C) 2009-2014 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. + +#ifdef _M_X64 + 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/Release/.gitattributes b/Release/.gitattributes new file mode 100644 index 0000000..b110a24 --- /dev/null +++ b/Release/.gitattributes @@ -0,0 +1 @@ +*.{dll,exe} filter=lfs diff=lfs merge=lfs -text \ No newline at end of file diff --git a/Release/dxwnd.dll b/Release/dxwnd.dll new file mode 100644 index 0000000..7cf3888 --- /dev/null +++ b/Release/dxwnd.dll @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1bcc2c21f162e19a96bbc07c40a6ce54c339debc559162518e694347e0fbf956 +size 557568 diff --git a/build/dxwnd.dll b/build/dxwnd.dll index 28e192b..25edaa3 100644 --- a/build/dxwnd.dll +++ b/build/dxwnd.dll @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dbb593dd74acbc13c129c32d28ba3808deabc13a62e2163696e371d06a5cdee5 -size 537600 +oid sha256:83be0cc19a6e76c301cfbbd1b9e261a0f875bf019d891fc17a781dd715875777 +size 557568 diff --git a/build/dxwnd.exe b/build/dxwnd.exe index 08c7dac..033e67a 100644 --- a/build/dxwnd.exe +++ b/build/dxwnd.exe @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8fb3df5a4f77d45fa4e102e1b37ab739fdee0eefc74edd8f3574f58c43166ee2 -size 532992 +oid sha256:6ff7d449b4f5a4658ffbe9f95b623d54bcdd46b33c3e7142b4e731c6f7bff61c +size 536064 diff --git a/build/exports/Amerzone.dxw b/build/exports/Amerzone.dxw index c9fc281..fb02f95 100644 --- a/build/exports/Amerzone.dxw +++ b/build/exports/Amerzone.dxw @@ -8,7 +8,7 @@ coord0=0 flag0=134234114 flagg0=1207959568 flagh0=20 -flagi0=4 +flagi0=4194308 tflag0=0 initx0=0 inity0=0 @@ -22,3 +22,8 @@ sizx0=800 sizy0=600 maxfps0=0 initts0=0 +launchpath0= +notes0= +flagj0=128 +winver0=0 +maxres0=0 diff --git a/build/exports/Celtic Kings Rage of War.dxw b/build/exports/Celtic Kings Rage of War.dxw index aebc866..4b9a33b 100644 --- a/build/exports/Celtic Kings Rage of War.dxw +++ b/build/exports/Celtic Kings Rage of War.dxw @@ -6,12 +6,12 @@ module0= opengllib0= ver0=0 coord0=0 -flag0=671088674 +flag0=673185826 flagg0=1744830466 flagh0=65556 flagi0=138412036 flagj0=128 -tflag0=0 +tflag0=6403 initx0=0 inity0=0 minx0=0 @@ -26,3 +26,4 @@ maxfps0=0 initts0=0 winver0=0 maxres0=-1 +notes0= diff --git a/build/exports/Close Combat 5 Invasion Normandy.dxw b/build/exports/Close Combat 5 Invasion Normandy.dxw index a34522b..9de7a68 100644 --- a/build/exports/Close Combat 5 Invasion Normandy.dxw +++ b/build/exports/Close Combat 5 Invasion Normandy.dxw @@ -24,3 +24,6 @@ maxfps0=0 initts0=0 winver0=0 maxres0=-1 +launchpath0= +notes0= +flagj0=128 diff --git a/build/exports/Daikatana.dxw b/build/exports/Daikatana.dxw index ddc3f57..9bec6a3 100644 --- a/build/exports/Daikatana.dxw +++ b/build/exports/Daikatana.dxw @@ -9,7 +9,7 @@ flag0=269492772 flagg0=671220225 flagh0=20 flagi0=4194304 -tflag0=2 +tflag0=0 initx0=0 inity0=0 minx0=0 @@ -25,3 +25,5 @@ initts0=0 launchpath0= winver0=0 maxres0=0 +notes0= +flagj0=0 diff --git a/build/exports/Kingpin Life Of Crime.dxw b/build/exports/Kingpin Life Of Crime.dxw index f9579ac..b435d53 100644 --- a/build/exports/Kingpin Life Of Crime.dxw +++ b/build/exports/Kingpin Life Of Crime.dxw @@ -6,12 +6,12 @@ module0= opengllib0=3dfxgl.dll ver0=9 coord0=0 -flag0=134234400 +flag0=134234146 flagg0=1744830464 flagh0=21 flagi0=-2009071610 flagj0=128 -tflag0=6147 +tflag0=0 initx0=0 inity0=0 minx0=0 @@ -26,3 +26,4 @@ maxfps0=0 initts0=0 winver0=0 maxres0=-1 +notes0= diff --git a/build/exports/MDK2.dxw b/build/exports/MDK2.dxw index 8a3a852..265338b 100644 --- a/build/exports/MDK2.dxw +++ b/build/exports/MDK2.dxw @@ -5,11 +5,11 @@ module0= opengllib0= ver0=0 coord0=0 -flag0=134217760 +flag0=134234146 flagg0=1744961536 -flagh0=20 -flagi0=4 -tflag0=64 +flagh0=65556 +flagi0=4194308 +tflag0=0 initx0=0 inity0=0 minx0=0 @@ -24,3 +24,6 @@ maxfps0=0 initts0=0 winver0=0 maxres0=-1 +launchpath0= +notes0= +flagj0=4096 diff --git a/build/exports/Sid Meier's Alpha Centauri (GOG).dxw b/build/exports/Sid Meier's Alpha Centauri (GOG).dxw new file mode 100644 index 0000000..29366b7 --- /dev/null +++ b/build/exports/Sid Meier's Alpha Centauri (GOG).dxw @@ -0,0 +1,29 @@ +[target] +title0=Sid Meier's Alpha Centauri (GOG) +path0=D:\Games\Sid Meier's Alpha Centauri\terran.exe +launchpath0= +module0= +opengllib0= +notes0= +ver0=0 +coord0=0 +flag0=134217762 +flagg0=1744830464 +flagh0=20 +flagi0=138412036 +flagj0=4224 +tflag0=0 +initx0=0 +inity0=0 +minx0=0 +miny0=0 +maxx0=0 +maxy0=0 +posx0=50 +posy0=50 +sizx0=800 +sizy0=600 +maxfps0=0 +initts0=0 +winver0=0 +maxres0=-1 diff --git a/build/exports/Take no Prisoners.dxw b/build/exports/Take no Prisoners.dxw index f43be41..4087dda 100644 --- a/build/exports/Take no Prisoners.dxw +++ b/build/exports/Take no Prisoners.dxw @@ -6,10 +6,10 @@ opengllib0= ver0=0 coord0=0 flag0=134217762 -flagg0=134217860 +flagg0=1207959684 flagh0=20 -flagi0=512 -tflag0=259 +flagi0=4194816 +tflag0=0 initx0=0 inity0=0 minx0=0 @@ -22,3 +22,8 @@ sizx0=800 sizy0=600 maxfps0=0 initts0=0 +launchpath0= +notes0= +flagj0=128 +winver0=0 +maxres0=0 diff --git a/build/exports/Theme Hospital.dxw b/build/exports/Theme Hospital.dxw index acf4ae7..ea92844 100644 --- a/build/exports/Theme Hospital.dxw +++ b/build/exports/Theme Hospital.dxw @@ -25,3 +25,5 @@ maxfps0=0 initts0=0 winver0=0 maxres0=-1 +notes0= +flagj0=128 diff --git a/build/exports/Thief the Dark Project GOLD (GOG).dxw b/build/exports/Thief the Dark Project GOLD (GOG).dxw index a20eb17..74e5545 100644 --- a/build/exports/Thief the Dark Project GOLD (GOG).dxw +++ b/build/exports/Thief the Dark Project GOLD (GOG).dxw @@ -5,10 +5,10 @@ module0= opengllib0= ver0=0 coord0=0 -flag0=256 +flag0=2 flagg0=1207959568 flagh0=20 -flagi0=4 +flagi0=4194308 tflag0=0 initx0=0 inity0=0 @@ -24,3 +24,6 @@ maxfps0=0 initts0=0 winver0=0 maxres0=-1 +launchpath0= +notes0= +flagj0=128 diff --git a/build/exports/Thief the Dark Project GOLD.dxw b/build/exports/Thief the Dark Project GOLD.dxw index 08bcc64..b10d8f9 100644 --- a/build/exports/Thief the Dark Project GOLD.dxw +++ b/build/exports/Thief the Dark Project GOLD.dxw @@ -5,10 +5,10 @@ module0= opengllib0= ver0=7 coord0=0 -flag0=272 -flagg0=440401936 +flag0=18 +flagg0=1514143760 flagh0=131612 -flagi0=4 +flagi0=4194308 tflag0=9 initx0=0 inity0=0 @@ -24,3 +24,6 @@ maxfps0=0 initts0=0 winver0=1 maxres0=-1 +launchpath0= +notes0= +flagj0=128 diff --git a/build/exports/Tiger Woods PGA TOUR 08 Demo.dxw b/build/exports/Tiger Woods PGA TOUR 08 Demo.dxw new file mode 100644 index 0000000..07a7424 --- /dev/null +++ b/build/exports/Tiger Woods PGA TOUR 08 Demo.dxw @@ -0,0 +1,29 @@ +[target] +title0=Tiger Woods PGA TOUR 08 Demo +path0=D:\Games\Tiger Woods PGA TOUR 08 Demo\bin\TW2008Demo.exe +launchpath0= +module0= +opengllib0= +notes0= +ver0=0 +coord0=0 +flag0=134217762 +flagg0=1476395008 +flagh0=65556 +flagi0=138412036 +flagj0=4224 +tflag0=6403 +initx0=0 +inity0=0 +minx0=0 +miny0=0 +maxx0=0 +maxy0=0 +posx0=50 +posy0=50 +sizx0=800 +sizy0=600 +maxfps0=0 +initts0=0 +winver0=0 +maxres0=-1 diff --git a/build/exports/Unreal Tournament.dxw b/build/exports/Unreal Tournament.dxw index fe92270..f57b635 100644 --- a/build/exports/Unreal Tournament.dxw +++ b/build/exports/Unreal Tournament.dxw @@ -8,8 +8,8 @@ coord0=0 flag0=-2013265886 flagg0=1209073680 flagh0=20 -flagi0=2052 -tflag0=4097 +flagi0=4196356 +tflag0=0 initx0=0 inity0=0 minx0=0 @@ -22,3 +22,8 @@ sizx0=1200 sizy0=900 maxfps0=0 initts0=0 +launchpath0= +notes0= +flagj0=128 +winver0=0 +maxres0=0 diff --git a/build/exports/dxwnd.ini b/build/exports/dxwnd.ini deleted file mode 100644 index 06104c1..0000000 --- a/build/exports/dxwnd.ini +++ /dev/null @@ -1,5 +0,0 @@ -[window] -posx=1189 -posy=594 -sizx=497 -sizy=464 diff --git a/build/readme-relnotes.txt b/build/readme-relnotes.txt index 1faa15b..7a93f41 100644 --- a/build/readme-relnotes.txt +++ b/build/readme-relnotes.txt @@ -681,3 +681,28 @@ fix: catched several sporadic errors before they could crash the application fix: GetAttachedSurface() now retrieves a backbuffer from the list, instead of referencing the last one - this fixes "Tomb Raider III" GOG release in non emulated mode. add: "Normalize performance counter" flag to fix an improper use of QueryPerformanceCounter() made by "Cyber Gladiators" add: "GDI Color conversion" debug flag + +v2.03.07 +fix: key matching for virtual registry now made case insensitive (needed for "Die Hard Trilogy") +fix: handling of null values passed to extRegQueryValueEx as lpType and lpData arguments (needed for "Die Hard Trilogy") +fix: DirectDrawSurface::GetPalette returns the virtual palette when applied to virtual primary / backup surfaces (needed for "Die Hard Trilogy") +fix: fixed dump for 8BPP palettized textures (needed for "Die Hard Trilogy") +fix: handling (with no operation) of D3DFMT_Q8W8V8U8 texture type and other bumpmap formats (used by "Tiger Woods PGA Tour 08") +fix: handling of LIMITRESOURCES flag for DirectDraw::GetCaps method when memory exceeds 0x70000000 bytes +fix: handling of LIMITRESOURCES flag for Direct3DDevice::GetAvailableTextureMem method when memory exceeds 1GB +fix: don't change screen resolution in SetDisplayMode when wrong (negative) values are passed. Fixes a problem in binkplayer.exe +fix: fixed OutTrace to avoid possible infinite recursion when loading C runtime libraries and logging LoadLibrary activity +fix: eliminated critical races when using DLL injection, thank to Luigi Auriemma's suggestion (inject an endless loop in the main thread and remove it at the end of injection) +fix: implemented DLL injection according to Luigi Auriemma's schema in CreateProcess hooking routine (needed for "Die Hard Trilogy") +fix: using MinHook library to acquire compatibility with all APIs +fix: hooked GetExitCodeProcess to handle "SUPPRESSCHILD" special case +fix: using hot patching for SystemParametersInfo APIs +fix: in SystemParametersInfo suppressed invalid operations in window mode: SPI_SETKEYBOARDDELAY SPI_SETKEYBOARDSPEED +add: son process handling with 4 different cases: 2 old cases (default case and "SUPPRESSCHILD") plus "INJECTSON" and "ENABLESONHOOK" to hook the son process without/with DLL injection +add: debug color conversion mode through GDI routines +add: multi-hooking for multiple processes contemporarily, adding the line "multiprocesshook=1" in [window] section of dxwnd.ini. Use at your own risk! +add: partial logging of Direct3DDevice::GetDeviceCaps output (to be completed) +add: handling of notes in the DxWnd GUI (configuration notes tab) +mod: when log is not possible on program's folder, it is no longer written in %TEMP% dir, is now written in DxWnd local dir. + + diff --git a/build/registry/dxwnd.Die Hard Trilogy.REG b/build/registry/dxwnd.Die Hard Trilogy.REG new file mode 100644 index 0000000..3b1826b --- /dev/null +++ b/build/registry/dxwnd.Die Hard Trilogy.REG @@ -0,0 +1,69 @@ + +[HKEY_LOCAL_MACHINE] +[HKEY_LOCAL_MACHINE\SOFTWARE] +[HKEY_LOCAL_MACHINE\SOFTWARE\Die Hard] +"Install Directory"="d:\\games\\Die Hard Trilogy" +"FMV Installed"=dword:00000000 +"Data Installed"=dword:00000001 +"Abnormal Termination"=dword:00000000 +"StartupFolder"="Fox Interactive" +"Version"="1.00" + +[HKEY_LOCAL_MACHINE\SOFTWARE\Die Hard\Configuration] +"complexity"=dword:00000000 +"cutoff"=dword:00000000 +"perspective"=dword:00000000 +"Quality"=dword:00000000 +"Use Hardware"=dword:00000000 +"Preferred Height"=dword:00000258 +"Preferred Width"=dword:00000320 +"lighting"=dword:00000001 +"texture quality"=dword:00000000 +"Language"=dword:00000000 + +[HKEY_LOCAL_MACHINE\SOFTWARE\Die Hard\d1] +"Input"=dword:00000000 +[HKEY_LOCAL_MACHINE\SOFTWARE\Die Hard\d1\Keyboard] +"keyboard"=dword:00000001 +"speed"=dword:00000000 +"sensitivity"=dword:00000000 +[HKEY_LOCAL_MACHINE\SOFTWARE\Die Hard\d1\Joystick] +"joystick"=dword:00000001 +"speed"=dword:00000000 +"sensitivity"=dword:00000000 +[HKEY_LOCAL_MACHINE\SOFTWARE\Die Hard\d1\Mouse] +"mouse"=dword:00000001 +"speed"=dword:00000000 +"sensitivity"=dword:00000000 +[HKEY_LOCAL_MACHINE\SOFTWARE\Die Hard\d2] +"Input"=dword:00000000 +[HKEY_LOCAL_MACHINE\SOFTWARE\Die Hard\d2\Keyboard] +[HKEY_LOCAL_MACHINE\SOFTWARE\Die Hard\d2\Keyboard\speed] +[HKEY_LOCAL_MACHINE\SOFTWARE\Die Hard\d2\Keyboard\sensitivity] +[HKEY_LOCAL_MACHINE\SOFTWARE\Die Hard\d2\Joystick] +[HKEY_LOCAL_MACHINE\SOFTWARE\Die Hard\d2\Joystick\speed] +[HKEY_LOCAL_MACHINE\SOFTWARE\Die Hard\d2\Joystick\sensitivity] +[HKEY_LOCAL_MACHINE\SOFTWARE\Die Hard\d2\Mouse] +[HKEY_LOCAL_MACHINE\SOFTWARE\Die Hard\d2\Mouse\speed] +[HKEY_LOCAL_MACHINE\SOFTWARE\Die Hard\d2\Mouse\sensitivity] +[HKEY_LOCAL_MACHINE\SOFTWARE\Die Hard\d3] +"Input"=dword:00000000 +[HKEY_LOCAL_MACHINE\SOFTWARE\Die Hard\d3\Keyboard] +[HKEY_LOCAL_MACHINE\SOFTWARE\Die Hard\d3\Keyboard\speed] +[HKEY_LOCAL_MACHINE\SOFTWARE\Die Hard\d3\Keyboard\sensitivity] +[HKEY_LOCAL_MACHINE\SOFTWARE\Die Hard\d3\Joystick] +[HKEY_LOCAL_MACHINE\SOFTWARE\Die Hard\d3\Joystick\speed] +[HKEY_LOCAL_MACHINE\SOFTWARE\Die Hard\d3\Joystick\sensitivity] +[HKEY_LOCAL_MACHINE\SOFTWARE\Die Hard\d3\Mouse] +[HKEY_LOCAL_MACHINE\SOFTWARE\Die Hard\d3\Mouse\speed] +[HKEY_LOCAL_MACHINE\SOFTWARE\Die Hard\d3\Mouse\sensitivity] + +[HKEY_LOCAL_MACHINE\SOFTWARE\Die Hard\Sound] +"Music Volume"=dword:000000ff +"Music Mute"=dword:00000000 +"SFX Volume"=dword:000000ff +"SFX Mute"=dword:00000000 + +[HKEY_LOCAL_MACHINE\SOFTWARE\Fox Interactive] +[HKEY_LOCAL_MACHINE\SOFTWARE\Fox Interactive\Die Hard Trilogy] +[HKEY_LOCAL_MACHINE\SOFTWARE\Fox Interactive\Die Hard Trilogy\1.0] diff --git a/dll/Inject.cpp b/dll/Inject.cpp new file mode 100644 index 0000000..3d018c5 --- /dev/null +++ b/dll/Inject.cpp @@ -0,0 +1,105 @@ +#define _CRT_SECURE_NO_WARNINGS + +#include +#include +#include +#include +#include + +#define WIN32_LEAN_AND_MEAN + +#define true 1 +#define false 0 + +#include "Winternl.h" + +BOOL Inject(DWORD pID, const char * DLL_NAME) +{ + HANDLE Proc; + char buf[50] = {0}; + LPVOID RemoteString, LoadLibAddy; + if(!pID) return false; + //Proc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pID); // not working on Win XP + Proc = OpenProcess(PROCESS_CREATE_THREAD|PROCESS_QUERY_INFORMATION|PROCESS_VM_OPERATION|PROCESS_VM_READ|PROCESS_VM_WRITE, FALSE, pID); + if(!Proc) + { + sprintf(buf, "OpenProcess() failed: pid=%x err=%d", pID, GetLastError()); + MessageBox(NULL, buf, "Loader", MB_OK); + printf(buf); + return false; + } + LoadLibAddy = (LPVOID)GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA"); + // Allocate space in the process for our DLL + RemoteString = (LPVOID)VirtualAllocEx(Proc, NULL, strlen(DLL_NAME), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); + // Write the string name of our DLL in the memory allocated + WriteProcessMemory(Proc, (LPVOID)RemoteString, DLL_NAME, strlen(DLL_NAME), NULL); + // Load our DLL + if(!CreateRemoteThread(Proc, NULL, 0, (LPTHREAD_START_ROUTINE)LoadLibAddy, (LPVOID)RemoteString, 0, NULL)){ + sprintf(buf, "CreateRemoteThread() failed: pid=%x err=%d", pID, GetLastError()); + MessageBox(NULL, buf, "Loader", MB_OK); + printf(buf); + return false; + } + CloseHandle(Proc); + return true; +} + +#if 0 +DWORD GetTargetThreadIDFromProcName(const char * ProcName) +{ + PROCESSENTRY32 pe; + HANDLE thSnapShot; + BOOL retval, ProcFound = false; + thSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if(thSnapShot == INVALID_HANDLE_VALUE) + { + MessageBox(NULL, "Error: Unable to create toolhelp snapshot!", "2MLoader", MB_OK); + //printf("Error: Unable to create toolhelp snapshot!"); + return false; + } + pe.dwSize = sizeof(PROCESSENTRY32); + retval = Process32First(thSnapShot, &pe); + while(retval) + { + if(StrStrI(pe.szExeFile, ProcName)) + { + return pe.th32ProcessID; + } + retval = Process32Next(thSnapShot, &pe); + } + return 0; +} +#endif + +#define STATUS_SUCCESS ((NTSTATUS)0x000 00000L) +#define ThreadQuerySetWin32StartAddress 9 + +LPVOID GetThreadStartAddress(HANDLE hThread) +{ + NTSTATUS ntStatus; + HANDLE hDupHandle; + HMODULE hLibNTHandle; + LPVOID dwStartAddress; + + typedef NTSTATUS (WINAPI *NtQueryInformationThread_Type)(HANDLE, THREADINFOCLASS, PVOID, ULONG, PULONG); + hLibNTHandle = GetModuleHandle("ntdll.dll"); + if(!hLibNTHandle) return 0; + + NtQueryInformationThread_Type NtQueryInformationThread = + (NtQueryInformationThread_Type)GetProcAddress(hLibNTHandle, "NtQueryInformationThread"); + + if(NtQueryInformationThread == NULL) return 0; + + HANDLE hCurrentProcess = GetCurrentProcess(); + if(!DuplicateHandle(hCurrentProcess, hThread, hCurrentProcess, &hDupHandle, THREAD_QUERY_INFORMATION, FALSE, 0)){ + SetLastError(ERROR_ACCESS_DENIED); + return 0; + } + + ntStatus = NtQueryInformationThread(hDupHandle, (THREADINFOCLASS)ThreadQuerySetWin32StartAddress, &dwStartAddress, sizeof(DWORD), NULL); + CloseHandle(hDupHandle); + CloseHandle(hLibNTHandle); + //if(ntStatus != STATUS_SUCCESS) return 0; + + return dwStartAddress; +} \ No newline at end of file diff --git a/dll/advapi.cpp b/dll/advapi.cpp index 3bd31bc..729f179 100644 --- a/dll/advapi.cpp +++ b/dll/advapi.cpp @@ -112,7 +112,8 @@ static LONG myRegOpenKeyEx( fgets(RegBuf, 256, regf); while (!feof(regf)){ if(RegBuf[0]=='['){ - if((!strncmp(&RegBuf[1],sKey,strlen(sKey))) && (RegBuf[strlen(sKey)+1]==']')){ + // beware: registry keys are case insensitive. Must use _strnicmp instead of strncmp + if((!_strnicmp(&RegBuf[1],sKey,strlen(sKey))) && (RegBuf[strlen(sKey)+1]==']')){ OutTrace("RegOpenKeyEx: found fake Key=\"%s\" hkResult=%x\n", sKey, phkResult ? *phkResult : 0); fclose(regf); return ERROR_SUCCESS; @@ -158,8 +159,8 @@ LONG WINAPI extRegQueryValueEx( HKEY hKey, LPCTSTR lpValueName, LPDWORD lpReserved, - LPDWORD lpType, - LPBYTE lpData, + LPDWORD lpType, // beware: could be NULL + LPBYTE lpData, // beware: could be NULL LPDWORD lpcbData) { LONG res; @@ -171,7 +172,7 @@ LONG WINAPI extRegQueryValueEx( if (res==ERROR_SUCCESS){ OutTrace("RegQueryValueEx: size=%d type=%x(%s) ", lpcbData?*lpcbData:0, lpType?*lpType:0, lpType?ExplainRegType(*lpType):"none"); - if(lpType) switch(*lpType){ + if(lpType && lpData) switch(*lpType){ case REG_SZ: OutTrace("Data=\"%s\"\n", lpData); break; case REG_DWORD: OutTrace("Data=0x%x\n", *(DWORD *)lpData); break; case REG_BINARY: @@ -213,23 +214,25 @@ LONG WINAPI extRegQueryValueEx( //OutTrace("loop: \"%s\"\n", RegBuf); if((RegBuf[0]=='"') && - !strncmp(lpValueName, &RegBuf[1], strlen(lpValueName)) && + !_strnicmp(lpValueName, &RegBuf[1], strlen(lpValueName)) && (RegBuf[strlen(lpValueName)+1]=='"') && (RegBuf[strlen(lpValueName)+2]=='=')) { res=ERROR_FILE_NOT_FOUND; pData=&RegBuf[strlen(lpValueName)+3]; if(*pData=='"'){ // string value - LPBYTE lpb; - lpb = lpData; - *lpcbData=0; - pData++; - while(*pData && (*pData != '"')){ - if(*pData=='\\') pData++; - *lpb++=*pData++; - *lpcbData++; + if(lpData){ + LPBYTE lpb; + lpb = lpData; + *lpcbData=0; + pData++; + while(*pData && (*pData != '"')){ + if(*pData=='\\') pData++; + *lpb++=*pData++; + *lpcbData++; + } + *lpb = 0; // string terminator } - *lpb = 0; // string terminator if(lpType) *lpType=REG_SZ; // OutTraceR("RegQueryValueEx: Data=\"%s\" type=REG_SZ\n", lpData); @@ -239,7 +242,7 @@ LONG WINAPI extRegQueryValueEx( DWORD val; pData+=strlen("dword:"); sscanf(pData, "%x", &val); - memcpy(lpData, &val, sizeof(DWORD)); + if(lpData) memcpy(lpData, &val, sizeof(DWORD)); if(lpType) *lpType=REG_DWORD; *lpcbData=sizeof(DWORD); OutTraceR("RegQueryValueEx: Data=0x%x type=REG_DWORD\n", val); @@ -247,16 +250,18 @@ LONG WINAPI extRegQueryValueEx( } if(!strncmp(pData,"hex:",strlen("hex:"))){ //dword value pData+=strlen("hex:"); - lpData[strlen((char *)lpData)-1]=0; // eliminates \n + pData[strlen(pData)-1]=0; // eliminates \n if(lpType) *lpType=REG_BINARY; - *lpcbData=0; OutTraceDW("RegQueryValueEx: Data="); - while(strlen(pData)>1){ - sscanf(pData, "%x,", (char *)lpData); - OutTraceDW("%02.2x,", *(unsigned char *)lpData); - pData+=3; - lpData++; - (*lpcbData)++; + if(lpData){ + *lpcbData=0; + while(strlen(pData)>1){ + sscanf(pData, "%x,", (char *)lpData); + OutTraceDW("%02.2x,", *(unsigned char *)lpData); + pData+=3; + lpData++; + (*lpcbData)++; + } } OutTraceR(" type=REG_BINARY cbData=%d\n", *lpcbData); res=ERROR_SUCCESS; @@ -279,7 +284,6 @@ LONG WINAPI extRegCloseKey(HKEY hKey) return (*pRegCloseKey)(hKey); } - LONG WINAPI extRegSetValueEx(HKEY hKey, LPCTSTR lpValueName, DWORD Reserved, DWORD dwType, const BYTE *lpData, DWORD cbData) { if (IsTraceR){ diff --git a/dll/d3dtexture.cpp b/dll/d3dtexture.cpp index c41c305..1186cc8 100644 --- a/dll/d3dtexture.cpp +++ b/dll/d3dtexture.cpp @@ -217,6 +217,16 @@ void D3DTextureDump(D3DSURFACE_DESC Desc, D3DLOCKED_RECT LockedRect) case D3DFMT_DXT5: hash = HashSurface((BYTE *)LockedRect.pBits, LockedRect.Pitch / 4, Desc.Width / 4, Desc.Height); break; + case D3DFMT_V8U8: + case D3DFMT_Q8W8V8U8: // Tiger Woods PGA Tour 08 + case D3DFMT_V16U16: + case D3DFMT_Q16W16V16U16: + case D3DFMT_CxV8U8: + case D3DFMT_L6V5U5: + case D3DFMT_X8L8V8U8: + case D3DFMT_A2W10V10U10: + // Bumpmap surfaces, dump is meaningless ..... + break; default: char sMsg[80+1]; static BOOL DoOnce = TRUE; diff --git a/dll/ddraw.cpp b/dll/ddraw.cpp index 8e12d72..0dc8e61 100644 --- a/dll/ddraw.cpp +++ b/dll/ddraw.cpp @@ -1507,6 +1507,14 @@ HRESULT WINAPI extGetCapsD(LPDIRECTDRAW lpdd, LPDDCAPS c1, LPDDCAPS c2) c2->dwCKeyCaps, ExplainDDCKeyCaps(c2->dwCKeyCaps)); } + if(dxw.dwFlags2 & LIMITRESOURCES){ // check for memory value overflow + const DWORD dwMaxMem = 0x70000000; + if(c1->dwVidMemTotal > dwMaxMem) c1->dwVidMemTotal = dwMaxMem; + if(c1->dwVidMemFree > dwMaxMem) c1->dwVidMemFree = dwMaxMem; + if(c2->dwVidMemTotal > dwMaxMem) c2->dwVidMemTotal = dwMaxMem; + if(c2->dwVidMemFree > dwMaxMem) c2->dwVidMemFree = dwMaxMem; + } + if((dxw.dwFlags3 & FORCESHEL) && c1) { DDCAPS_DX7 swcaps; // DDCAPS_DX7 because it is the bigger in size int size; @@ -1517,7 +1525,16 @@ HRESULT WINAPI extGetCapsD(LPDIRECTDRAW lpdd, LPDDCAPS c1, LPDDCAPS c2) c2=&swcaps; res=(*pGetCapsD)(lpdd, NULL, c2); } + DWORD dwVidMemTotal=c1->dwVidMemTotal; + DWORD dwVidMemFree=c1->dwVidMemFree; memcpy((void *)c1, (void *)c2, size); +#if 0 + if(c1->dwVidMemTotal == 0) c1->dwVidMemTotal=0x40000000; // about 1GB - aqrit's suggestion + if(c1->dwVidMemFree == 0) c1->dwVidMemFree =0x40000000; // about 1GB - aqrit's suggestion +#else + if(c1->dwVidMemTotal == 0) c1->dwVidMemTotal=dwVidMemTotal; + if(c1->dwVidMemFree == 0) c1->dwVidMemFree =dwVidMemFree; +#endif } if(dxw.dwFlags3 & CAPMASK) MaskCapsD(c1, c2); @@ -1902,7 +1919,8 @@ HRESULT WINAPI extQueryInterfaceS(void *lpdds, REFIID riid, LPVOID *obp) } if(dwLocalTexVersion) { - if(dxw.dwFlags5 & (TEXTUREHIGHLIGHT|TEXTUREDUMP|TEXTUREHACK)) TextureHandling((LPDIRECTDRAWSURFACE)lpdds); + // Texture Handling on QueryInterface + if(dxw.dwFlags5 & TEXTUREMASK) TextureHandling((LPDIRECTDRAWSURFACE)lpdds); HookTexture(obp, dwLocalTexVersion); } @@ -1924,6 +1942,10 @@ HRESULT WINAPI extSetDisplayMode(int version, LPDIRECTDRAW lpdd, else OutTrace("\n"); } + // binkplayer fix + if((int)dwwidth < 0) dwwidth = dxw.GetScreenWidth(); + if((int)dwheight < 0) dwheight = dxw.GetScreenHeight(); + dxw.SetScreenSize(dwwidth, dwheight); GetHookInfo()->Height=(short)dxw.GetScreenHeight(); GetHookInfo()->Width=(short)dxw.GetScreenWidth(); @@ -3265,7 +3287,10 @@ HRESULT WINAPI sBlt(char *api, LPDIRECTDRAWSURFACE lpdds, LPRECT lpdestrect, if (lpddssrc) DescribeSurface(lpddssrc, 0, "[SRC]" , __LINE__); // lpddssrc could be NULL!!! } if(dxw.dwFlags1 & SUPPRESSDXERRORS) res=0; - if(dxw.dwFlags5 & (TEXTUREHIGHLIGHT|TEXTUREDUMP|TEXTUREHACK)) TextureHandling(lpdds); + if(dxw.dwFlags5 & TEXTUREMASK) { + // Texture Handling on Blt + TextureHandling(lpdds); + } return res; } @@ -3681,10 +3706,22 @@ HRESULT WINAPI extCreatePalette(LPDIRECTDRAW lpdd, DWORD dwflags, LPPALETTEENTRY HRESULT WINAPI extGetPalette(LPDIRECTDRAWSURFACE lpdds, LPDIRECTDRAWPALETTE *lplpddp) { HRESULT res; + BOOL isPrim, isBack; - OutTraceDDRAW("GetPalette: lpdds=%x\n", lpdds); + isPrim=dxw.IsAPrimarySurface(lpdds); + isBack=dxw.IsABackBufferSurface(lpdds); + OutTraceDDRAW("GetPalette: lpdds=%x%s%s\n", lpdds, isPrim?"(PRIM)":"", isBack?"(BACK)":""); res=(*pGetPalette)(lpdds, lplpddp); + + // v2.03.07: in "Die Hard Trilogy" the backbuffer surface is queryed for the palette + if((dxw.dwFlags1 & EMULATESURFACE) && (res == DDERR_NOPALETTEATTACHED) && (isPrim||isBack)){ + OutTraceDW("GetPalette: retrieve PRIMARY palette for emulated surface lpDDP=%x\n", lpDDP); + *lplpddp = lpDDP; + lpDDP->AddRef(); + res=DD_OK; + } + if (res) OutTraceE("GetPalette: ERROR res=%x(%s)\n", res, ExplainDDError(res)); else OutTraceDDRAW("GetPalette: OK\n"); return res; @@ -3938,7 +3975,10 @@ HRESULT WINAPI extUnlock(int dxversion, Unlock4_Type pUnlock, LPDIRECTDRAWSURFAC if(dxw.dwFlags1 & SUPPRESSDXERRORS) res=DD_OK; - if((dxw.dwFlags5 & (TEXTUREHIGHLIGHT|TEXTUREDUMP|TEXTUREHACK)) && (!IsPrim)) TextureHandling(lpdds); + if((dxw.dwFlags5 & TEXTUREMASK) && (!IsPrim)) { + // Texture Handling on Unlock + TextureHandling(lpdds); + } return res; } diff --git a/dll/ddtexture.cpp b/dll/ddtexture.cpp index 0872c59..375b4a5 100644 --- a/dll/ddtexture.cpp +++ b/dll/ddtexture.cpp @@ -21,26 +21,6 @@ extern int Set_dwSize_From_Surface(LPDIRECTDRAWSURFACE); #define GRIDSIZE 16 -char *GetDxWndPath() -{ - static BOOL DoOnce = TRUE; - static char sFolderPath[MAX_PATH]; - - if(DoOnce){ // first time through, build the texture dir if not done yet - DWORD dwAttrib; - dwAttrib = GetFileAttributes("dxwnd.dll"); - if (dwAttrib != INVALID_FILE_ATTRIBUTES && !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)) { - MessageBox(0, "DXWND: ERROR can't locate itself", "ERROR", MB_OK | MB_ICONEXCLAMATION); - exit(0); - } - GetModuleFileName(GetModuleHandle("dxwnd"), sFolderPath, MAX_PATH); - sFolderPath[strlen(sFolderPath)-strlen("dxwnd.dll")] = 0; // terminate the string just before "dxwnd.dll" - DoOnce = FALSE; - } - - return sFolderPath; -} - /* RS Hash Function */ static unsigned int Hash(BYTE *buf, int len) @@ -76,6 +56,9 @@ static char *SurfaceType(DDPIXELFORMAT ddpfPixelFormat) char sColorType[21]; DWORD mask; int i, count; + + if(ddpfPixelFormat.dwRGBBitCount == 8) return "RGB8"; + strcpy(sSurfaceType, ""); // red mask=ddpfPixelFormat.dwRBitMask; @@ -251,9 +234,11 @@ static void TextureDump(LPDIRECTDRAWSURFACE s) pbi.bV4Height = - pbi.bV4Height; pbi.bV4Planes = 1; pbi.bV4V4Compression = BI_BITFIELDS; + if(pbi.bV4BitCount == 8) pbi.bV4V4Compression = BI_RGB; pbi.bV4XPelsPerMeter = 1; pbi.bV4YPelsPerMeter = 1; pbi.bV4ClrUsed = 0; + if(pbi.bV4BitCount == 8) pbi.bV4ClrUsed = 256; pbi.bV4ClrImportant = 0; pbi.bV4RedMask = ddsd.ddpfPixelFormat.dwRBitMask; pbi.bV4GreenMask = ddsd.ddpfPixelFormat.dwGBitMask; @@ -288,8 +273,22 @@ static void TextureDump(LPDIRECTDRAWSURFACE s) // Copy the BITMAPFILEHEADER into the .BMP file. fwrite((LPVOID)&hdr, sizeof(BITMAPFILEHEADER), 1, hf); - // Copy the BITMAPINFOHEADER and RGBQUAD array into the file. - fwrite((LPVOID)&pbi, sizeof(BITMAPV4HEADER) + pbi.bV4ClrUsed * sizeof (RGBQUAD), 1, hf); + // Copy the BITMAPINFOHEADER array into the file. + fwrite((LPVOID)&pbi, sizeof(BITMAPV4HEADER), 1, hf); + + // Copy the RGBQUAD array into the file. + if(pbi.bV4ClrUsed){ + //DWORD PaletteEntries[256]; + //LPDIRECTDRAWSURFACE lpDDS=NULL; + //LPDIRECTDRAWPALETTE lpDDP=NULL; + //lpDDS=dxw.GetPrimarySurface(); + //if(lpDDS) lpDDS->GetPalette(&lpDDP); + //if(lpDDP) lpDDP->GetEntries(0, 0, 256, (LPPALETTEENTRY)PaletteEntries); + //for(int i=0; i<256; i++) PaletteEntries[i]=0xff0000; + extern DWORD PaletteEntries[256]; + fwrite((LPVOID)PaletteEntries, pbi.bV4ClrUsed * sizeof (RGBQUAD), 1, hf); + //fwrite((LPBYTE)PaletteEntries, pbi.bV4ClrUsed * sizeof (RGBQUAD), 1, hf); + } // Copy the array of color indices into the .BMP file. for(int y=0; y<(int)ddsd.dwHeight; y++) diff --git a/dll/dxhook.cpp b/dll/dxhook.cpp index c2e45de..ee275b7 100644 --- a/dll/dxhook.cpp +++ b/dll/dxhook.cpp @@ -20,6 +20,7 @@ #include "Winnls32.h" #include "Mmsystem.h" #include "disasm.h" +#include "MinHook.h" #define SKIPIMEWINDOW TRUE @@ -41,8 +42,6 @@ extern void *IATPatch(HMODULE, char *, void *, const char *, void *); void HookModule(HMODULE, int); static void RecoverScreenMode(); static void LockScreenMode(DWORD, DWORD, DWORD); -DEVMODE SetDevMode; -DEVMODE *pSetDevMode=NULL; extern HANDLE hTraceMutex; @@ -97,8 +96,8 @@ static char *Flag5Names[32]={ "NOBLT", "NOSYSTEMEMULATED", "DOFASTBLT", "AEROBOOST", "QUARTERBLT", "NOIMAGEHLP", "BILINEARFILTER", "REPLACEPRIVOPS", "REMAPMCI", "TEXTUREHIGHLIGHT", "TEXTUREDUMP", "TEXTUREHACK", - "TEXTURETRANSP", "", "", "", - "", "", "", "", + "TEXTURETRANSP", "NORMALIZEPERFCOUNT", "HYBRIDMODE", "GDICOLORCONV", + "INJECTSON", "ENABLESONHOOK", "", "", "", "", "", "", "", "", "", "", }; @@ -114,6 +113,26 @@ static char *TFlagNames[32]={ "", "", "", "", }; +char *GetDxWndPath() +{ + static BOOL DoOnce = TRUE; + static char sFolderPath[MAX_PATH]; + + if(DoOnce){ + DWORD dwAttrib; + dwAttrib = GetFileAttributes("dxwnd.dll"); + if (dwAttrib != INVALID_FILE_ATTRIBUTES && !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)) { + MessageBox(0, "DXWND: ERROR can't locate itself", "ERROR", MB_OK | MB_ICONEXCLAMATION); + exit(0); + } + GetModuleFileName(GetModuleHandle("dxwnd"), sFolderPath, MAX_PATH); + sFolderPath[strlen(sFolderPath)-strlen("dxwnd.dll")] = 0; // terminate the string just before "dxwnd.dll" + DoOnce = FALSE; + } + + return sFolderPath; +} + static void OutTraceHeader(FILE *fp) { SYSTEMTIME Time; @@ -139,9 +158,12 @@ void OutTrace(const char *format, ...) va_list al; static char path[MAX_PATH]; static FILE *fp=NULL; // GHO: thread safe??? + DWORD tFlags; // check global log flag if(!(dxw.dwTFlags & OUTTRACE)) return; + tFlags = dxw.dwTFlags; + dxw.dwTFlags = 0x0; // to avoid possible log recursion while loading C runtime libraries!!! WaitForSingleObject(hTraceMutex, INFINITE); if (fp == NULL){ @@ -149,14 +171,12 @@ void OutTrace(const char *format, ...) strcat(path, "\\dxwnd.log"); fp = fopen(path, "a+"); if (fp==NULL){ // in case of error (e.g. current dir on unwritable CD unit)... - strcpy(path, getenv("TEMP")); + strcpy(path, GetDxWndPath()); strcat(path, "\\dxwnd.log"); fp = fopen(path, "a+"); } - if (fp==NULL){ // last chance: do not log... - dxw.dwTFlags &= ~OUTTRACE; // turn flag OFF - return; - } + if (fp==NULL) + return; // last chance: do not log... else OutTraceHeader(fp); } @@ -166,6 +186,7 @@ void OutTrace(const char *format, ...) ReleaseMutex(hTraceMutex); fflush(fp); + dxw.dwTFlags = tFlags; // restore settings } #ifdef CHECKFORCOMPATIBILITYFLAGS diff --git a/dll/dxwnd.cpp b/dll/dxwnd.cpp index 0b0fcc9..dda3968 100644 --- a/dll/dxwnd.cpp +++ b/dll/dxwnd.cpp @@ -16,6 +16,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ +#define _CRT_SECURE_NO_WARNINGS #include #include @@ -24,10 +25,12 @@ along with this program. If not, see . #include "dxwnd.h" #include "dxwcore.hpp" -#define VERSION "2.03.06" -#define DXWACTIVATESINGLETASK 1 // comment to allow multiple task activations +#include "TlHelp32.h" + +#define VERSION "2.03.07" #define DDTHREADLOCK 1 +//#define LOCKTHREADS LRESULT CALLBACK HookProc(int ncode, WPARAM wparam, LPARAM lparam); @@ -60,12 +63,46 @@ BOOL APIENTRY DllMain( HANDLE hmodule, if(dwreason != DLL_PROCESS_ATTACH) return TRUE; + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST); // trick to reduce concurrency problems at program startup + +#ifdef LOCKTHREADS + DWORD currentPID = GetCurrentProcessId(); + DWORD currentTID = GetCurrentThreadId(); + if(currentTID && currentPID){ + int ThreadCount=0; + HANDLE hThreadSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, NULL); + if(hThreadSnapshot != INVALID_HANDLE_VALUE){ + DWORD result = 0; + THREADENTRY32 tEntry; + tEntry.dwSize = sizeof(THREADENTRY32); + for (BOOL success = Thread32First(hThreadSnapshot, &tEntry); + !result && success && GetLastError() != ERROR_NO_MORE_FILES; + success = Thread32Next(hThreadSnapshot, &tEntry)){ + if ((tEntry.th32ThreadID != currentTID) && (tEntry.th32OwnerProcessID == currentPID)){ + HANDLE th; + th=OpenThread(THREAD_SUSPEND_RESUME, FALSE, tEntry.th32ThreadID); + ThreadCount++; + SuspendThread(th); + CloseHandle(th); + } + } + CloseHandle(hThreadSnapshot); + //char sMsg[81]; + //sprintf(sMsg,"suspended threads=%d", ThreadCount); + //MessageBox(0, sMsg, "info", MB_OK | MB_ICONEXCLAMATION); + } + } +#endif + hInst = (HINSTANCE)hmodule; // optimization: disables DLL_THREAD_ATTACH and DLL_THREAD_DETACH notifications for the specified DLL DisableThreadLibraryCalls((HMODULE)hmodule); hMapping = CreateFileMapping((HANDLE)0xffffffff, NULL, PAGE_READWRITE, 0, sizeof(DxWndStatus)+sizeof(TARGETMAP)*MAXTARGETS, "UniWind_TargetList"); - if(!hMapping) return false; + if(!hMapping) { + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL); + return false; + } // v2.0.2.75: beware: some tasks (namely, Flash player) get dxwnd.dll loaded, but can't create the file mapping // this situation has to be intercepted, or it can cause the dll to cause faults that may crash the program. pStatus = (DXWNDSTATUS *)MapViewOfFile(hMapping, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(DXWNDSTATUS)+sizeof(TARGETMAP)*MAXTARGETS); @@ -82,9 +119,46 @@ BOOL APIENTRY DllMain( HANDLE hmodule, if(!hDDLockMutex) hDDLockMutex = CreateMutex(0, FALSE, "DDLock_Mutex"); } InjectHook(); + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL); + +#ifdef LOCKTHREADS + if(currentTID && currentPID){ + int ThreadCount=0; + HANDLE hThreadSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, NULL); + if(hThreadSnapshot != INVALID_HANDLE_VALUE){ + DWORD result = 0; + THREADENTRY32 tEntry; + tEntry.dwSize = sizeof(THREADENTRY32); + for (BOOL success = Thread32First(hThreadSnapshot, &tEntry); + !result && success && GetLastError() != ERROR_NO_MORE_FILES; + success = Thread32Next(hThreadSnapshot, &tEntry)){ + if ((tEntry.th32ThreadID != currentTID) && (tEntry.th32OwnerProcessID == currentPID)){ + HANDLE th; + th=OpenThread(THREAD_SUSPEND_RESUME, FALSE, tEntry.th32ThreadID); + ThreadCount++; + ResumeThread(th); + CloseHandle(th); + } + } + CloseHandle(hThreadSnapshot); + //char sMsg[81]; + //sprintf(sMsg,"resumed threads=%d", ThreadCount); + //MessageBox(0, sMsg, "info", MB_OK | MB_ICONEXCLAMATION); + } + } +#endif + return true; } +static BOOL GetMultiTaskEnabling(){ + char inipath[MAX_PATH]; + GetModuleFileName(GetModuleHandle("dxwnd"), inipath, MAX_PATH); + inipath[strlen(inipath)-strlen("dxwnd.dll")] = 0; // terminate the string just before "dxwnd.dll" + strcat(inipath, "dxwnd.ini"); + return GetPrivateProfileInt("window", "multiprocesshook", 0, inipath); +} + int SetTarget(TARGETMAP *targets){ int i, j; @@ -97,6 +171,7 @@ int SetTarget(TARGETMAP *targets){ memset((void *)&(pStatus->pfd), 0, sizeof(DDPIXELFORMAT)); pStatus->Height = pStatus->Width = 0; pStatus->DXVersion = 0; + pStatus->AllowMultiTask=GetMultiTaskEnabling(); for(i = 0; targets[i].path[0]; i ++){ //OutTraceDW("SetTarget entry %s\n",pMapping[i].path); pMapping[i] = targets[i]; @@ -193,14 +268,15 @@ LRESULT CALLBACK HookProc(int ncode, WPARAM wparam, LPARAM lparam) // no good trying to insert fancy dialog boxes: the window // isn't ready yet, and the operation fails. -#ifdef DXWACTIVATESINGLETASK - if(WaitForSingleObject(hLockMutex, 0)==WAIT_TIMEOUT){ - ReleaseMutex(hMutex); - exit(0); + // V2.03.07: allow multiple process hooking depending on config + if(!(pStatus->AllowMultiTask)){ + if(WaitForSingleObject(hLockMutex, 0)==WAIT_TIMEOUT){ + ReleaseMutex(hMutex); + exit(0); + } } -#else - WaitForSingleObject(hLockMutex, 0); -#endif + else + WaitForSingleObject(hLockMutex, 0); pStatus->Status=DXW_RUNNING; pStatus->TaskIdx=i; diff --git a/dll/dxwnd.vs2008.suo b/dll/dxwnd.vs2008.suo index b65a310e9a82a14b138f7def0ddeb1cf12fb5243..d6328666845a9f18112677ec81478b39a2f47392 100644 GIT binary patch literal 49152 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

);d*2nk$1* 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 Rat*iP(*C{8QvDmT{|~%+{M`Tm literal 280576 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 LOtW5Q{WAU&VPh>! diff --git a/dll/dxwnd.vs2008.vcproj b/dll/dxwnd.vs2008.vcproj index d862672..f5f4e48 100644 --- a/dll/dxwnd.vs2008.vcproj +++ b/dll/dxwnd.vs2008.vcproj @@ -50,7 +50,7 @@ Name="VCCLCompilerTool" AdditionalOptions="/IInclude" Optimization="0" - AdditionalIncludeDirectories="../Include" + AdditionalIncludeDirectories=".;../Include" PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;DXWND_EXPORTS" MinimalRebuild="true" ExceptionHandling="2" @@ -77,10 +77,13 @@ /> + + diff --git a/dll/gdi32.cpp b/dll/gdi32.cpp index ca635d4..06bce5e 100644 --- a/dll/gdi32.cpp +++ b/dll/gdi32.cpp @@ -206,7 +206,6 @@ FARPROC Remap_GDI32_ProcAddress(LPCSTR proc, HMODULE hModule) // //-------------------------------------------------------------------------------------------- -extern DEVMODE *pSetDevMode; extern DWORD PaletteEntries[256]; extern Unlock4_Type pUnlockMethod(LPDIRECTDRAWSURFACE); extern HRESULT WINAPI sBlt(char *, LPDIRECTDRAWSURFACE, LPRECT, LPDIRECTDRAWSURFACE, LPRECT, DWORD, LPDDBLTFX, BOOL); @@ -317,31 +316,6 @@ int WINAPI extGetDeviceCaps(HDC hdc, int nindex) } } - // if you have a bypassed setting, use it first! - if(pSetDevMode){ - switch(nindex){ - case BITSPIXEL: - case COLORRES: - res = pSetDevMode->dmBitsPerPel; - OutTraceDW("GetDeviceCaps: fix(1) BITSPIXEL/COLORRES cap=%x\n",res); - return res; - case HORZRES: - if(dxw.Windowize){ - res = pSetDevMode->dmPelsWidth; - OutTraceDW("GetDeviceCaps: fix(1) HORZRES cap=%d\n", res); - return res; - } - break; - case VERTRES: - if(dxw.Windowize){ - res = pSetDevMode->dmPelsHeight; - OutTraceDW("GetDeviceCaps: fix(1) VERTRES cap=%d\n", res); - return res; - } - break; - } - } - switch(nindex){ case VERTRES: if(dxw.Windowize){ diff --git a/dll/hd3d.cpp b/dll/hd3d.cpp index 8985c54..a8b4811 100644 --- a/dll/hd3d.cpp +++ b/dll/hd3d.cpp @@ -91,6 +91,7 @@ DisableD3DSpy_Type pDisableD3DSpy = 0; // IDirect3DDevice8/9 methods +typedef UINT (WINAPI *GetAvailableTextureMem_Type)(void *); typedef HRESULT (WINAPI *TestCooperativeLevel_Type)(void *); typedef HRESULT (WINAPI *GetDirect3D8_Type)(void *, void **); typedef HRESULT (WINAPI *GetDirect3D9_Type)(void *, void **); @@ -110,6 +111,7 @@ typedef HRESULT (WINAPI *CreateTexture8_Type)(void *, UINT, UINT, UINT, DWORD, D typedef HRESULT (WINAPI *CreateTexture9_Type)(void *, UINT, UINT, UINT, DWORD, D3DFORMAT, D3DPOOL, void **, HANDLE *); typedef HRESULT (WINAPI *CopyRects_Type)(void *, void *, CONST RECT *, UINT, void *, CONST POINT *); +UINT WINAPI extGetAvailableTextureMem(void *); HRESULT WINAPI extTestCooperativeLevel(void *); HRESULT WINAPI extGetDirect3D8(void *, void **); HRESULT WINAPI extGetDirect3D9(void *, void **); @@ -130,6 +132,7 @@ HRESULT WINAPI extCreateTexture8(void *, UINT, UINT, UINT, DWORD, D3DFORMAT, D3D HRESULT WINAPI extCreateTexture9(void *, UINT, UINT, UINT, DWORD, D3DFORMAT, D3DPOOL, void **, HANDLE *); HRESULT WINAPI extCopyRects(void *, void *, CONST RECT *, UINT, void *, CONST POINT *); +GetAvailableTextureMem_Type pGetAvailableTextureMem = 0; TestCooperativeLevel_Type pTestCooperativeLevel = 0; GetDirect3D8_Type pGetDirect3D8 = 0; GetDirect3D9_Type pGetDirect3D9 = 0; @@ -196,7 +199,6 @@ HRESULT WINAPI extQueryInterfaceDev8(void *, REFIID, void** ); HRESULT WINAPI extQueryInterfaceD3D9(void *, REFIID, void** ); HRESULT WINAPI extQueryInterfaceDev9(void *, REFIID, void** ); - HRESULT WINAPI extEnumAdapterModes8(void *, UINT, UINT , D3DDISPLAYMODE *); HRESULT WINAPI extEnumAdapterModes9(void *, UINT, D3DFORMAT, UINT , D3DDISPLAYMODE *); HRESULT WINAPI extGetAdapterDisplayMode8(void *, UINT, D3DDISPLAYMODE *); @@ -361,7 +363,7 @@ int HookDirect3D(HMODULE module, int version){ ID3D11Device *lpd3d11; HRESULT res; - OutTrace("HookDirect3D: module=%x version=%d\n", module, version); + OutTraceDW("HookDirect3D: module=%x version=%d\n", module, version); switch(version){ case 0: HookLibrary(module, d3d8Hooks, "d3d8.dll"); @@ -434,6 +436,8 @@ void HookD3DDevice8(void** ppD3Ddev8) { OutTraceD3D("Device hook for IID_IDirect3DDevice8 interface\n"); SetHook((void *)(**(DWORD **)ppD3Ddev8 + 0), extQueryInterfaceDev8, (void **)&pQueryInterfaceDev8, "QueryInterface(D8)"); + SetHook((void *)(**(DWORD **)ppD3Ddev8 + 12), extTestCooperativeLevel, (void **)&pTestCooperativeLevel, "TestCooperativeLevel(D8)"); + SetHook((void *)(**(DWORD **)ppD3Ddev8 + 16), extGetAvailableTextureMem, (void **)&pGetAvailableTextureMem, "GetAvailableTextureMem(D8)"); SetHook((void *)(**(DWORD **)ppD3Ddev8 + 24), extGetDirect3D8, (void **)&pGetDirect3D8, "GetDirect3D(D8)"); SetHook((void *)(**(DWORD **)ppD3Ddev8 + 32), extGetDisplayMode8, (void **)&pGetDisplayMode8, "GetDisplayMode(D8)"); SetHook((void *)(**(DWORD **)ppD3Ddev8 + 44), extSetCursorPosition8, (void **)&pSetCursorPosition8, "SetCursorPosition(D8)"); @@ -472,6 +476,8 @@ void HookD3DDevice9(void** ppD3Ddev9) { OutTraceD3D("Device hook for IID_IDirect3DDevice9 interface\n"); SetHook((void *)(**(DWORD **)ppD3Ddev9 + 0), extQueryInterfaceDev9, (void **)&pQueryInterfaceDev9, "QueryInterface(D9)"); + SetHook((void *)(**(DWORD **)ppD3Ddev9 + 12), extTestCooperativeLevel, (void **)&pTestCooperativeLevel, "TestCooperativeLevel(D9)"); + SetHook((void *)(**(DWORD **)ppD3Ddev9 + 16), extGetAvailableTextureMem, (void **)&pGetAvailableTextureMem, "GetAvailableTextureMem(D9)"); SetHook((void *)(**(DWORD **)ppD3Ddev9 + 24), extGetDirect3D9, (void **)&pGetDirect3D9, "GetDirect3D(D9)"); SetHook((void *)(**(DWORD **)ppD3Ddev9 + 32), extGetDisplayMode9, (void **)&pGetDisplayMode9, "GetDisplayMode(D9)"); SetHook((void *)(**(DWORD **)ppD3Ddev9 + 44), extSetCursorPosition9, (void **)&pSetCursorPosition9, "SetCursorPosition(D9)"); @@ -508,9 +514,6 @@ void HookD3DDevice9(void** ppD3Ddev9) if (!(dxw.dwTFlags & OUTPROXYTRACE)) return; SetHook((void *)(**(DWORD **)ppD3Ddev9 + 4), extAddRef9, (void **)&pAddRef9, "AddRef(D9)"); SetHook((void *)(**(DWORD **)ppD3Ddev9 + 8), extRelease9, (void **)&pRelease9, "Release(D9)"); - SetHook((void *)(**(DWORD **)ppD3Ddev9 + 12), extTestCooperativeLevel, (void **)&pTestCooperativeLevel, "TestCooperativeLevel(D9)"); - - } // WIP @@ -1708,70 +1711,170 @@ HRESULT WINAPI voidDirect3DShaderValidatorCreate9(void) HRESULT WINAPI extRegisterSoftwareDevice(void *lpd3d, void *pInitializeFunction) { - OutTrace("RegisterSoftwareDevice: d3d=%x\n", lpd3d); + OutTraceD3D("RegisterSoftwareDevice: d3d=%x\n", lpd3d); return (*pRegisterSoftwareDevice)(lpd3d, pInitializeFunction); } UINT WINAPI extGetAdapterModeCount(void *lpd3d, UINT Adapter, D3DFORMAT Format) { - OutTrace("GetAdapterModeCount: d3d=%x adapter=%d\n", lpd3d, Adapter); + OutTraceD3D("GetAdapterModeCount: d3d=%x adapter=%d\n", lpd3d, Adapter); return (*pGetAdapterModeCount)(lpd3d, Adapter, Format); } HRESULT WINAPI extCheckDeviceType(void *lpd3d, UINT Adapter, D3DDEVTYPE DevType, D3DFORMAT AdapterFormat, D3DFORMAT BackBufferFormat, BOOL bWindowed) { - OutTrace("CheckDeviceType: d3d=%x adapter=%d windowed=%x\n", lpd3d, Adapter, bWindowed); + OutTraceD3D("CheckDeviceType: d3d=%x adapter=%d windowed=%x\n", lpd3d, Adapter, bWindowed); return (*pCheckDeviceType)(lpd3d, Adapter, DevType, AdapterFormat, BackBufferFormat, bWindowed); } HRESULT WINAPI extCheckDeviceFormat(void *lpd3d, UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT AdapterFormat, DWORD Usage, D3DRESOURCETYPE RType, D3DFORMAT CheckFormat) { - OutTrace("CheckDeviceFormat: d3d=%x adapter=%d\n", lpd3d, Adapter); + OutTraceD3D("CheckDeviceFormat: d3d=%x adapter=%d\n", lpd3d, Adapter); return (*pCheckDeviceFormat)(lpd3d, Adapter, DeviceType, AdapterFormat, Usage, RType, CheckFormat); } HRESULT WINAPI extCheckDeviceMultiSampleType(void *lpd3d, UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT SurfaceFormat, BOOL Windowed, D3DMULTISAMPLE_TYPE MultiSampleType, DWORD *pQualityLevels) { - OutTrace("CheckDeviceMultiSampleType: d3d=%x adapter=%d windowed=%x\n", lpd3d, Adapter, Windowed); + OutTraceD3D("CheckDeviceMultiSampleType: d3d=%x adapter=%d windowed=%x\n", lpd3d, Adapter, Windowed); return (*pCheckDeviceMultiSampleType)(lpd3d, Adapter, DeviceType, SurfaceFormat, Windowed, MultiSampleType, pQualityLevels); } HRESULT WINAPI extCheckDepthStencilMatch(void *lpd3d, UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT AdapterFormat, D3DFORMAT RenderTargetFormat, D3DFORMAT DepthStencilFormat) { - OutTrace("CheckDepthStencilMatch: d3d=%x adapter=%d\n", lpd3d, Adapter); + OutTraceD3D("CheckDepthStencilMatch: d3d=%x adapter=%d\n", lpd3d, Adapter); return (*pCheckDepthStencilMatch)(lpd3d, Adapter, DeviceType, AdapterFormat, RenderTargetFormat, DepthStencilFormat); } HRESULT WINAPI extCheckDeviceFormatConversion(void *lpd3d, UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT SourceFormat, D3DFORMAT TargetFormat) { - OutTrace("CheckDeviceFormatConversion: d3d=%x adapter=%d\n", lpd3d, Adapter); + OutTraceD3D("CheckDeviceFormatConversion: d3d=%x adapter=%d\n", lpd3d, Adapter); return (*pCheckDeviceFormatConversion)(lpd3d, Adapter, DeviceType, SourceFormat, TargetFormat); } +static char *ExplainD3D9DeviceType(D3DDEVTYPE DeviceType) +{ + char *s; + switch(DeviceType){ + case D3DDEVTYPE_HAL: s="HAL"; break; + case D3DDEVTYPE_NULLREF: s="NULLREF"; break; + case D3DDEVTYPE_REF: s="REF"; break; + case D3DDEVTYPE_SW: s="SW"; break; + default: s="unknown"; break; + } + return s; +} + HRESULT WINAPI extD3DGetDeviceCaps(void *lpd3d, UINT Adapter, D3DDEVTYPE DeviceType, D3DCAPS9* pCaps) { - OutTrace("GetDeviceCaps: d3d=%x adapter=%d\n", lpd3d, Adapter); - return (*pD3DGetDeviceCaps)(lpd3d, Adapter, DeviceType, pCaps); + HRESULT res; + OutTraceD3D("GetDeviceCaps: d3d=%x adapter=%d devtype=%x(%s)\n", lpd3d, Adapter, DeviceType, ExplainD3D9DeviceType(DeviceType)); + res=(*pD3DGetDeviceCaps)(lpd3d, Adapter, DeviceType, pCaps); + if(res){ + OutTraceE("GetDeviceCaps: ERROR: err=%x\n", res); + } + else{ + if(IsDebug){ + OutTrace("GetDeviceCaps: DeviceType=%x(%s) Caps=%x Caps2=%x Caps3=%x PresentationIntervals=%x CursorCaps=%x DevCaps=%x\n", + pCaps->DeviceType, ExplainD3D9DeviceType(pCaps->DeviceType), pCaps->Caps, pCaps->Caps2, pCaps->Caps3, pCaps->PresentationIntervals, + pCaps->CursorCaps, pCaps->DevCaps); + OutTrace("GetDeviceCaps: PrimitiveMiscCaps=%x RasterCaps=%x ZCmpCaps=%x SrcBlendCaps=%x DestBlendCaps=%x AlphaCmpCaps=%x\n", + pCaps->PrimitiveMiscCaps, pCaps->RasterCaps, pCaps->ZCmpCaps, pCaps->SrcBlendCaps, pCaps->DestBlendCaps, pCaps->AlphaCmpCaps); + OutTrace("GetDeviceCaps: AlphaCmpCaps=%x ShadeCaps=%x TextureCaps=%x TextureFilterCaps=%x CubeTextureFilterCaps=%x VolumeTextureFilterCaps=%x\n", + pCaps->AlphaCmpCaps, pCaps->ShadeCaps, pCaps->TextureCaps, pCaps->TextureFilterCaps, pCaps->CubeTextureFilterCaps, pCaps->VolumeTextureFilterCaps); + OutTrace("GetDeviceCaps: TextureAddressCaps=%x VolumeTextureAddressCaps=%x LineCaps=%x StencilCaps=%x FVFCaps=%x TextureOpCaps=%x VertexProcessingCaps=%x\n", + pCaps->TextureAddressCaps, pCaps->VolumeTextureAddressCaps, pCaps->LineCaps, pCaps->StencilCaps, pCaps->FVFCaps, pCaps->TextureOpCaps, pCaps->VertexProcessingCaps); + OutTrace("GetDeviceCaps: MaxTexture(Width x Height)=(%dx%d) MaxVolumeExtent=%d MaxTextureRepeat=%d MaxTextureAspectRatio=%d MaxAnisotropy=%d\n", + pCaps->MaxTextureWidth, pCaps->MaxTextureHeight, pCaps->MaxVolumeExtent, pCaps->MaxTextureRepeat, pCaps->MaxTextureAspectRatio, pCaps->MaxAnisotropy); + OutTrace("GetDeviceCaps: MaxActiveLights=%d MaxUserClipPlanes=%x MaxUserClipPlanes=%x\n", + pCaps->MaxActiveLights, pCaps->MaxUserClipPlanes, pCaps->MaxUserClipPlanes); +/* + float MaxVertexW; + + float GuardBandLeft; + float GuardBandTop; + float GuardBandRight; + float GuardBandBottom; + + float ExtentsAdjust; + + DWORD MaxTextureBlendStages; + DWORD MaxSimultaneousTextures; + + DWORD ; + DWORD ; + DWORD ; + DWORD MaxVertexBlendMatrices; + DWORD MaxVertexBlendMatrixIndex; + + float MaxPointSize; + + DWORD MaxPrimitiveCount; // max number of primitives per DrawPrimitive call + DWORD MaxVertexIndex; + DWORD MaxStreams; + DWORD MaxStreamStride; // max stride for SetStreamSource + + DWORD VertexShaderVersion; + DWORD MaxVertexShaderConst; // number of vertex shader constant registers + + DWORD PixelShaderVersion; + float PixelShader1xMaxValue; // max value storable in registers of ps.1.x shaders + + // Here are the DX9 specific ones + DWORD DevCaps2; + + float MaxNpatchTessellationLevel; + DWORD Reserved5; + + UINT MasterAdapterOrdinal; // ordinal of master adaptor for adapter group + UINT AdapterOrdinalInGroup; // ordinal inside the adapter group + UINT NumberOfAdaptersInGroup; // number of adapters in this adapter group (only if master) + DWORD DeclTypes; // Data types, supported in vertex declarations + DWORD NumSimultaneousRTs; // Will be at least 1 + DWORD StretchRectFilterCaps; // Filter caps supported by StretchRect + D3DVSHADERCAPS2_0 VS20Caps; + D3DPSHADERCAPS2_0 PS20Caps; + DWORD VertexTextureFilterCaps; // D3DPTFILTERCAPS for IDirect3DTexture9's for texture, used in vertex shaders + DWORD MaxVShaderInstructionsExecuted; // maximum number of vertex shader instructions that can be executed + DWORD MaxPShaderInstructionsExecuted; // maximum number of pixel shader instructions that can be executed + DWORD MaxVertexShader30InstructionSlots; + DWORD MaxPixelShader30InstructionSlots; + */ + } + } + return res; } HMONITOR WINAPI extGetAdapterMonitor(void *lpd3d, UINT Adapter) { - OutTrace("GetAdapterMonitor: d3d=%x adapter=%d\n", lpd3d, Adapter); + OutTraceD3D("GetAdapterMonitor: d3d=%x adapter=%d\n", lpd3d, Adapter); return (*pGetAdapterMonitor)(lpd3d, Adapter); } +UINT WINAPI extGetAvailableTextureMem(void *lpd3dd) +{ + const UINT TextureMemoryLimit = 1024 * 1024 * 1024; // 1GB + // const DWORD dwMaxMem = 0x70000000; = 1.8G + UINT AvailableTextureMem = (*pGetAvailableTextureMem)(lpd3dd); + OutTraceD3D("GetAvailableTextureMem: lpd3dd=%x AvailableTextureMem=%u(%dMB)\n", lpd3dd, AvailableTextureMem, AvailableTextureMem>>20); + if((dxw.dwFlags2 & LIMITRESOURCES) && (AvailableTextureMem > TextureMemoryLimit)){ + OutTraceDW("GetAvailableTextureMem: LIMIT AvailableTextureMem=%u->%u\n", AvailableTextureMem, TextureMemoryLimit); + AvailableTextureMem = TextureMemoryLimit; + } + return AvailableTextureMem; +} + HRESULT WINAPI extTestCooperativeLevel(void *lpd3dd) { HRESULT res; res = (*pTestCooperativeLevel)(lpd3dd); - OutTrace("TestCooperativeLevel: d3dd=%x res=%x\n", lpd3dd, res); + OutTraceB("TestCooperativeLevel: d3dd=%x res=%x\n", lpd3dd, res); return res; } HRESULT WINAPI extGetSwapChain(void *lpd3dd, UINT iSwapChain, IDirect3DSwapChain9** pSwapChain) { HRESULT res; - OutTrace("GetSwapChain: d3dd=%x SwapChain=%d\n", lpd3dd, iSwapChain); + OutTraceD3D("GetSwapChain: d3dd=%x SwapChain=%d\n", lpd3dd, iSwapChain); res = (*pGetSwapChain)(lpd3dd, iSwapChain, pSwapChain); return res; } @@ -1780,14 +1883,14 @@ UINT WINAPI extGetNumberOfSwapChains(void *lpd3dd) { UINT res; res = (*pGetNumberOfSwapChains)(lpd3dd); - OutTrace("GetNumberOfSwapChains: d3dd=%x res=%d\n", lpd3dd, res); + OutTraceD3D("GetNumberOfSwapChains: d3dd=%x res=%d\n", lpd3dd, res); return res; } HRESULT WINAPI extBeginStateBlock8(void *lpd3dd) { HRESULT res; - OutTrace("BeginStateBlock(8): d3dd=%x\n", lpd3dd); + OutTraceD3D("BeginStateBlock(8): d3dd=%x\n", lpd3dd); res = (*pBeginStateBlock8)(lpd3dd); HookD3DDevice8(&lpd3dd); return res; @@ -1799,7 +1902,7 @@ HRESULT WINAPI extBeginStateBlock9(void *lpd3dd) // you need to hook the device object again. This operation fixes the switch to fullscreen mode // in "Freedom Force vs. the Third Reich". HRESULT res; - OutTrace("BeginStateBlock(9): d3dd=%x\n", lpd3dd); + OutTraceD3D("BeginStateBlock(9): d3dd=%x\n", lpd3dd); res = (*pBeginStateBlock9)(lpd3dd); HookD3DDevice9(&lpd3dd); return res; @@ -1808,7 +1911,7 @@ HRESULT WINAPI extBeginStateBlock9(void *lpd3dd) HRESULT WINAPI extEndStateBlock8(void *lpd3dd, DWORD *pToken) { HRESULT res; - OutTrace("EndStateBlock(8): d3dd=%x\n", lpd3dd); + OutTraceD3D("EndStateBlock(8): d3dd=%x\n", lpd3dd); res = (*pEndStateBlock8)(lpd3dd, pToken); return res; } @@ -1816,7 +1919,7 @@ HRESULT WINAPI extEndStateBlock8(void *lpd3dd, DWORD *pToken) HRESULT WINAPI extEndStateBlock9(void *lpd3dd, IDirect3DStateBlock9** ppSB) { HRESULT res; - OutTrace("EndStateBlock(9): d3dd=%x\n", lpd3dd); + OutTraceD3D("EndStateBlock(9): d3dd=%x\n", lpd3dd); res = (*pEndStateBlock9)(lpd3dd, ppSB); return res; } diff --git a/dll/hotpatch.cpp b/dll/hotpatch.cpp index b2d5ce9..496b6e2 100644 --- a/dll/hotpatch.cpp +++ b/dll/hotpatch.cpp @@ -5,12 +5,47 @@ // 1 = already patched // addr = address of the original function +#define USEMINHOOK + #include #include "dxwnd.h" #include "dxwcore.hpp" +#ifdef USEMINHOOK +#include "MinHook.h" +#endif void *HotPatch(void *apiproc, const char *apiname, void *hookproc) { +#ifdef USEMINHOOK + void *pProc; + static BOOL DoOnce = TRUE; + + if(DoOnce){ + if (MH_Initialize() != MH_OK) { + OutTraceE("HotPatch: MH_Initialize FAILED\n"); + // What to do here? No recovery action ... + return 0; + } + DoOnce = FALSE; + } + + OutTraceH("HotPatch: api=%s addr=%x hook=%x\n", apiname, apiproc, hookproc); + + if(!strcmp(apiname, "GetProcAddress")) return 0; // do not mess with this one! + + if (MH_CreateHook(apiproc, hookproc, reinterpret_cast(&pProc)) != MH_OK){ + OutTraceH("HotPatch: MH_CreateHook FAILED\n"); + return 0; + } + + if (MH_EnableHook(apiproc) != MH_OK){ + OutTraceH("HotPatch: MH_EnableHook FAILED\n"); + return 0; + } + + OutTrace("HotPatch: api=%s addr=%x->%x hook=%x\n", apiname, apiproc, pProc, hookproc); + return pProc; +#else DWORD dwPrevProtect; BYTE* patch_address; void *orig_address; @@ -62,4 +97,5 @@ void *HotPatch(void *apiproc, const char *apiname, void *hookproc) VirtualProtect( patch_address, 12, dwPrevProtect, &dwPrevProtect ); // restore protection OutTrace("HotPatch: api=%s addr=%x->%x hook=%x\n", apiname, apiproc, orig_address, hookproc); return orig_address; +#endif } \ No newline at end of file diff --git a/dll/kernel32.cpp b/dll/kernel32.cpp index e1b9ed7..12ddec9 100644 --- a/dll/kernel32.cpp +++ b/dll/kernel32.cpp @@ -9,6 +9,7 @@ //#undef IsTraceDW //#define IsTraceDW TRUE +#define LOCKINJECTIONTHREADS BOOL WINAPI extCheckRemoteDebuggerPresent(HANDLE, PBOOL); @@ -735,42 +736,145 @@ BOOL WINAPI extCreateProcessA( ) { BOOL res; +#ifdef LOCKINJECTIONTHREADS + DWORD StartingCode; + LPVOID StartAddress = 0; + extern LPVOID GetThreadStartAddress(HANDLE); +#endif OutTraceDW("CreateProcess: ApplicationName=\"%s\" CommandLine=\"%s\"\n", lpApplicationName, lpCommandLine); if(dxw.dwFlags4 & SUPPRESSCHILD) { OutTraceDW("CreateProcess: SUPPRESS\n"); - return TRUE; + return TRUE; + } + + if(dxw.dwFlags5 & (INJECTSON|ENABLESONHOOK)) { + extern HANDLE hLockMutex; + ReleaseMutex(hLockMutex); + } + + if(dxw.dwFlags5 & INJECTSON) { + DEBUG_EVENT debug_event ={0}; + char path[MAX_PATH]; + extern char *GetFileNameFromHandle(HANDLE); + DWORD dwContinueStatus = DBG_CONTINUE; + extern BOOL Inject(DWORD, const char *); + + //dwCreationFlags |= DEBUG_ONLY_THIS_PROCESS; + dwCreationFlags |= (DEBUG_ONLY_THIS_PROCESS|DEBUG_PROCESS); + + res=(*pCreateProcessA)( + lpApplicationName, lpCommandLine, + lpProcessAttributes, lpThreadAttributes, bInheritHandles, + dwCreationFlags, lpEnvironment, + lpCurrentDirectory, lpStartupInfo, lpProcessInformation + ); + OutTrace("CreateProcess res=%x\n", res); + BOOL bContinueDebugging = TRUE; + while(bContinueDebugging) + { + if (!WaitForDebugEvent(&debug_event, INFINITE)) break; + switch(debug_event.dwDebugEventCode){ + case EXIT_PROCESS_DEBUG_EVENT: + bContinueDebugging=false; + OutTrace("CreateProcess: process terminated\n", res); + break; + case CREATE_PROCESS_DEBUG_EVENT: + GetModuleFileName(GetModuleHandle("dxwnd"), path, MAX_PATH); + OutTrace("CreateProcess: injecting path=%s\n", path); + if(!Inject(lpProcessInformation->dwProcessId, path)){ + OutTrace("CreateProcess: Injection ERROR pid=%x dll=%s\n", lpProcessInformation->dwProcessId, path); + } +#ifdef LOCKINJECTIONTHREADS + extern LPVOID GetThreadStartAddress(HANDLE); + DWORD TargetHandle; + DWORD EndlessLoop; + EndlessLoop=0x9090FEEB; + SIZE_T BytesCount; + TargetHandle = (DWORD)OpenProcess( + PROCESS_QUERY_INFORMATION|PROCESS_VM_OPERATION|PROCESS_VM_READ|PROCESS_VM_WRITE, + FALSE, + lpProcessInformation->dwProcessId); + if(TargetHandle){ + StartAddress = GetThreadStartAddress(lpProcessInformation->hThread); + OutTrace("CreateProcess: StartAddress=%x\n", StartAddress); + if(StartAddress){ + if(!ReadProcessMemory(lpProcessInformation->hProcess, StartAddress, &StartingCode, 4, &BytesCount)){ + OutTrace("CreateProcess: ReadProcessMemory error=%d\n", GetLastError()); + } + OutTrace("CreateProcess: StartCode=%x\n", StartingCode); + if(!WriteProcessMemory(lpProcessInformation->hProcess, StartAddress, &EndlessLoop, 4, &BytesCount)){ + OutTrace("CreateProcess: WriteProcessMemory error=%d\n", GetLastError()); + } + } + } +#endif OutTrace("CreateProcess: injection terminated\n", res); + break; + case EXIT_THREAD_DEBUG_EVENT: +#ifdef LOCKINJECTIONTHREADS + if(TargetHandle && StartAddress){ + if(!WriteProcessMemory(lpProcessInformation->hProcess, StartAddress, &StartingCode, 4, &BytesCount)){ + OutTrace("CreateProcess: WriteProcessMemory error=%d\n", GetLastError()); + } + CloseHandle((HANDLE)TargetHandle); + } +#endif + bContinueDebugging=false; + default: + break; + } + if(bContinueDebugging){ + ContinueDebugEvent(debug_event.dwProcessId, + debug_event.dwThreadId, + dwContinueStatus); + } + else{ + DebugSetProcessKillOnExit(FALSE); + ContinueDebugEvent(debug_event.dwProcessId, debug_event.dwThreadId, DBG_CONTINUE); + DebugActiveProcessStop(debug_event.dwProcessId); + } + } + OutTrace("CreateProcess: detached\n", res); + } + else{ + res=(*pCreateProcessA)( + lpApplicationName, + lpCommandLine, + lpProcessAttributes, + lpThreadAttributes, + bInheritHandles, + dwCreationFlags, + lpEnvironment, + lpCurrentDirectory, + lpStartupInfo, + lpProcessInformation + ); + } + + if(!res) OutTraceE("CreateProcess: ERROR err=%d\n", GetLastError()); + return res; } -#if 0 - // useless: DxWnd should hook to two processes contemporarily .... - // problem: binkplay seems to detect the screen actual size - if(TRUE){ - // get rid of /R option from binkplay argument line - if(!strncmp(lpCommandLine, "binkplay.exe ", strlen("binkplay.exe "))){ - char *rCommand; - rCommand = strstr(lpCommandLine, "/R "); - if(rCommand) memset(rCommand, ' ', strlen("/R ")); - OutTraceDW("CreateProcess: ApplicationName=\"%s\" CommandLine=\"%s\"\n", lpApplicationName, lpCommandLine); +BOOL WINAPI extGetExitCodeProcess(HANDLE hProcess, LPDWORD lpExitCode) +{ + BOOL res; + + OutTraceDW("GetExitCodeProcess: hProcess=%x\n", hProcess); + + if(dxw.dwFlags4 & SUPPRESSCHILD) { + OutTraceDW("GetExitCodeProcess: FAKE exit code=0\n"); + lpExitCode = 0; + return TRUE; + } + + res=(*pGetExitCodeProcess)(hProcess, lpExitCode); + if(dxw.dwFlags5 & (INJECTSON|ENABLESONHOOK)) { + if(*lpExitCode != STILL_ACTIVE){ + OutTraceDW("GetExitCodeProcess: locking mutex\n"); + extern HANDLE hLockMutex; + WaitForSingleObject(hLockMutex, 0); } } -#endif - -//#define RELEASEHOOKTOSONPROCESS TRUE -// extern void UnhookProc(); -// if(RELEASEHOOKTOSONPROCESS) UnhookProc(); - res=(*pCreateProcessA)( - lpApplicationName, - lpCommandLine, - lpProcessAttributes, - lpThreadAttributes, - bInheritHandles, - dwCreationFlags, - lpEnvironment, - lpCurrentDirectory, - lpStartupInfo, - lpProcessInformation - ); - if(!res) OutTraceE("CreateProcess: ERROR err=%d\n", GetLastError()); + OutTraceDW("GetExitCodeProcess: hProcess=%x ExitCode=%x res=%x\n", hProcess, *lpExitCode, res); return res; } diff --git a/dll/libMinHook.x86.lib b/dll/libMinHook.x86.lib new file mode 100644 index 0000000000000000000000000000000000000000..9239facb31eabd42c703ee75cb7010890276623e GIT binary patch literal 28026 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 J@>R6T`hRAHsj&b6 literal 0 HcmV?d00001 diff --git a/dll/syslibs.h b/dll/syslibs.h index 07cd16c..a699a50 100644 --- a/dll/syslibs.h +++ b/dll/syslibs.h @@ -130,6 +130,7 @@ typedef BOOL (WINAPI *CloseHandle_Type)(HANDLE); typedef BOOL (WINAPI *QueryPerformanceFrequency_Type)(LARGE_INTEGER *); typedef BOOL (WINAPI *QueryPerformanceCounter_Type)(LARGE_INTEGER *); typedef BOOL (WINAPI *QueryPerformanceFrequency_Type)(LARGE_INTEGER *); +typedef BOOL (WINAPI *GetExitCodeProcess_Type)(HANDLE, LPDWORD); // ole32.dll: typedef HRESULT (STDAPICALLTYPE *CoCreateInstance_Type)(REFCLSID, LPUNKNOWN, DWORD, REFIID, LPVOID FAR*); @@ -342,6 +343,8 @@ DXWEXTERN SetFilePointer_Type pSetFilePointer DXWINITIALIZED; DXWEXTERN CloseHandle_Type pCloseHandle DXWINITIALIZED; DXWEXTERN QueryPerformanceFrequency_Type pQueryPerformanceFrequency DXWINITIALIZED; DXWEXTERN QueryPerformanceCounter_Type pQueryPerformanceCounter DXWINITIALIZED; +DXWEXTERN GetExitCodeProcess_Type pGetExitCodeProcess DXWINITIALIZED; + // ole32.dll: DXWEXTERN CoCreateInstance_Type pCoCreateInstance DXWINITIALIZED; @@ -553,6 +556,7 @@ extern HANDLE WINAPI extCreateFile(LPCTSTR, DWORD, DWORD, LPSECURITY_ATTRIBUTES, extern DWORD WINAPI extSetFilePointer(HANDLE, LONG, PLONG, DWORD); extern BOOL WINAPI extCloseHandle(HANDLE); extern BOOL WINAPI extCreateProcessA(LPCTSTR, LPTSTR, LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES, BOOL, DWORD, LPVOID, LPCTSTR, LPSTARTUPINFO, LPPROCESS_INFORMATION); +extern BOOL WINAPI extGetExitCodeProcess(HANDLE, LPDWORD); extern BOOL WINAPI extQueryPerformanceFrequency(LARGE_INTEGER *); extern BOOL WINAPI extQueryPerformanceCounter(LARGE_INTEGER *); diff --git a/dll/user32.cpp b/dll/user32.cpp index 62c1bb4..fe0d6c5 100644 --- a/dll/user32.cpp +++ b/dll/user32.cpp @@ -54,8 +54,9 @@ static HookEntry_Type Hooks[]={ {HOOK_HOT_CANDIDATE, "SetWindowLongW", (FARPROC)SetWindowLongW, (FARPROC *)&pSetWindowLongW, (FARPROC)extSetWindowLongW}, {HOOK_HOT_CANDIDATE, "GetWindowLongW", (FARPROC)GetWindowLongW, (FARPROC *)&pGetWindowLongW, (FARPROC)extGetWindowLongW}, {HOOK_IAT_CANDIDATE, "IsWindowVisible", (FARPROC)NULL, (FARPROC *)&pIsWindowVisible, (FARPROC)extIsWindowVisible}, - {HOOK_IAT_CANDIDATE, "SystemParametersInfoA", (FARPROC)SystemParametersInfoA, (FARPROC *)&pSystemParametersInfoA, (FARPROC)extSystemParametersInfoA}, - {HOOK_IAT_CANDIDATE, "SystemParametersInfoW", (FARPROC)SystemParametersInfoW, (FARPROC *)&pSystemParametersInfoW, (FARPROC)extSystemParametersInfoW}, + // hot by MinHook since v2.03.07 + {HOOK_HOT_CANDIDATE, "SystemParametersInfoA", (FARPROC)SystemParametersInfoA, (FARPROC *)&pSystemParametersInfoA, (FARPROC)extSystemParametersInfoA}, + {HOOK_HOT_CANDIDATE, "SystemParametersInfoW", (FARPROC)SystemParametersInfoW, (FARPROC *)&pSystemParametersInfoW, (FARPROC)extSystemParametersInfoW}, //{HOOK_HOT_CANDIDATE, "GetActiveWindow", (FARPROC)NULL, (FARPROC *)&pGetActiveWindow, (FARPROC)extGetActiveWindow}, //{HOOK_HOT_CANDIDATE, "GetForegroundWindow", (FARPROC)NULL, (FARPROC *)&pGetForegroundWindow, (FARPROC)extGetForegroundWindow}, //{HOOK_IAT_CANDIDATE, "GetWindowTextA", (FARPROC)GetWindowTextA, (FARPROC *)&pGetWindowTextA, (FARPROC)extGetWindowTextA}, @@ -245,7 +246,6 @@ int LastCurPosX, LastCurPosY; extern GetDC_Type pGetDC; extern ReleaseDC_Type pReleaseDC; -extern DEVMODE *pSetDevMode; //extern void FixWindowFrame(HWND); extern HRESULT WINAPI sBlt(char *, LPDIRECTDRAWSURFACE, LPRECT, LPDIRECTDRAWSURFACE, LPRECT, DWORD, LPDDBLTFX, BOOL); @@ -411,18 +411,8 @@ void dxwFixMinMaxInfo(char *ApiName, HWND hwnd, LPARAM lParam) lpmmi->ptMaxPosition.x, lpmmi->ptMaxPosition.y, lpmmi->ptMaxSize.x, lpmmi->ptMaxSize.y); lpmmi->ptMaxPosition.x=0; lpmmi->ptMaxPosition.y=0; - if(pSetDevMode){ - lpmmi->ptMaxSize.x = pSetDevMode->dmPelsWidth; - lpmmi->ptMaxSize.y = pSetDevMode->dmPelsHeight; - } - else{ - lpmmi->ptMaxSize.x = dxw.GetScreenWidth(); - lpmmi->ptMaxSize.y = dxw.GetScreenHeight(); - } - - // allow for initial dimensions .... - //if(lpmmi->ptMaxSize.x < dxw.iSizX) lpmmi->ptMaxSize.x = dxw.iSizX; - //if(lpmmi->ptMaxSize.y < dxw.iSizY) lpmmi->ptMaxSize.y = dxw.iSizY; + lpmmi->ptMaxSize.x = dxw.GetScreenWidth(); + lpmmi->ptMaxSize.y = dxw.GetScreenHeight(); OutTraceDW("%s: SET PREVENTMAXIMIZE MaxPosition=(%d,%d) MaxSize=(%d,%d)\n", ApiName, lpmmi->ptMaxPosition.x, lpmmi->ptMaxPosition.y, lpmmi->ptMaxSize.x, lpmmi->ptMaxSize.y); @@ -1114,24 +1104,6 @@ int WINAPI extGetSystemMetrics(int nindex) return res; } - // if you have a bypassed setting, use it first! - if(pSetDevMode){ - switch(nindex){ - case SM_CXFULLSCREEN: - case SM_CXSCREEN: - case SM_CXVIRTUALSCREEN: // v2.02.31 - res = pSetDevMode->dmPelsWidth; - OutTraceDW("GetDeviceCaps: fix HORZRES cap=%d\n", res); - return res; - case SM_CYFULLSCREEN: - case SM_CYSCREEN: - case SM_CYVIRTUALSCREEN: // v2.02.31 - res = pSetDevMode->dmPelsHeight; - OutTraceDW("GetDeviceCaps: fix VERTRES cap=%d\n", res); - return res; - } - } - switch(nindex){ case SM_CXFULLSCREEN: case SM_CXSCREEN: @@ -1698,32 +1670,26 @@ LONG WINAPI extEnumDisplaySettings(LPCTSTR lpszDeviceName, DWORD iModeNum, DEVMO { LONG res; OutTraceDW("EnumDisplaySettings: Devicename=%s ModeNum=%x\n", lpszDeviceName, iModeNum); - if(pSetDevMode && iModeNum==ENUM_CURRENT_SETTINGS){ - lpDevMode=pSetDevMode; - return 1; - } - else{ - res=(*pEnumDisplaySettings)(lpszDeviceName, iModeNum, lpDevMode); - if(dxw.dwFlags4 & LIMITSCREENRES){ - #define HUGE 100000 - DWORD maxw, maxh; - maxw = maxh = HUGE; - switch(dxw.MaxScreenRes){ - case DXW_NO_LIMIT: maxw=HUGE; maxh=HUGE; break; - case DXW_LIMIT_320x200: maxw=320; maxh=200; break; - case DXW_LIMIT_640x480: maxw=640; maxh=480; break; - case DXW_LIMIT_800x600: maxw=800; maxh=600; break; - case DXW_LIMIT_1024x768: maxw=1024; maxh=768; break; - case DXW_LIMIT_1280x960: maxw=1280; maxh=960; break; - } - if((lpDevMode->dmPelsWidth > maxw) || (lpDevMode->dmPelsHeight > maxh)){ - OutTraceDW("EnumDisplaySettings: limit device size=(%d,%d)\n", maxw, maxh); - lpDevMode->dmPelsWidth = maxw; - lpDevMode->dmPelsHeight = maxh; - } + res=(*pEnumDisplaySettings)(lpszDeviceName, iModeNum, lpDevMode); + if(dxw.dwFlags4 & LIMITSCREENRES){ + #define HUGE 100000 + DWORD maxw, maxh; + maxw = maxh = HUGE; + switch(dxw.MaxScreenRes){ + case DXW_NO_LIMIT: maxw=HUGE; maxh=HUGE; break; + case DXW_LIMIT_320x200: maxw=320; maxh=200; break; + case DXW_LIMIT_640x480: maxw=640; maxh=480; break; + case DXW_LIMIT_800x600: maxw=800; maxh=600; break; + case DXW_LIMIT_1024x768: maxw=1024; maxh=768; break; + case DXW_LIMIT_1280x960: maxw=1280; maxh=960; break; + } + if((lpDevMode->dmPelsWidth > maxw) || (lpDevMode->dmPelsHeight > maxh)){ + OutTraceDW("EnumDisplaySettings: limit device size=(%d,%d)\n", maxw, maxh); + lpDevMode->dmPelsWidth = maxw; + lpDevMode->dmPelsHeight = maxh; } - return res; } + return res; } LONG WINAPI extChangeDisplaySettingsA(DEVMODEA *lpDevMode, DWORD dwflags) @@ -2614,7 +2580,14 @@ BOOL WINAPI extIsWindowVisible(HWND hwnd) BOOL WINAPI extSystemParametersInfoA(UINT uiAction, UINT uiParam, PVOID pvParam, UINT fWinIni) { BOOL ret; - OutTraceDW("SystemParametersInfo: Action=%x Param=%x WinIni=%x\n", uiAction, uiParam, fWinIni); + OutTraceDW("SystemParametersInfoA: Action=%x Param=%x WinIni=%x\n", uiAction, uiParam, fWinIni); + switch(uiAction){ + case SPI_SETKEYBOARDDELAY: + case SPI_SETKEYBOARDSPEED: + OutTraceDW("SystemParametersInfoA: bypass action=%x\n", uiAction); + return TRUE; + break; + } ret=(*pSystemParametersInfoA)(uiAction, uiParam, pvParam, fWinIni); if(uiAction==SPI_GETWORKAREA){ LPRECT cli = (LPRECT)pvParam; @@ -2622,7 +2595,7 @@ BOOL WINAPI extSystemParametersInfoA(UINT uiAction, UINT uiParam, PVOID pvParam, cli->left = 0; cli->bottom = dxw.GetScreenHeight(); cli->right = dxw.GetScreenWidth(); - OutTraceDW("SystemParametersInfo: resized client workarea rect=(%d,%d)-(%d,%d)\n", cli->left, cli->top, cli->right, cli->bottom); + OutTraceDW("SystemParametersInfoA: resized client workarea rect=(%d,%d)-(%d,%d)\n", cli->left, cli->top, cli->right, cli->bottom); } return ret; } @@ -2630,7 +2603,14 @@ BOOL WINAPI extSystemParametersInfoA(UINT uiAction, UINT uiParam, PVOID pvParam, BOOL WINAPI extSystemParametersInfoW(UINT uiAction, UINT uiParam, PVOID pvParam, UINT fWinIni) { BOOL ret; - OutTraceDW("SystemParametersInfo: Action=%x Param=%x WinIni=%x\n", uiAction, uiParam, fWinIni); + OutTraceDW("SystemParametersInfoW: Action=%x Param=%x WinIni=%x\n", uiAction, uiParam, fWinIni); + switch(uiAction){ + case SPI_SETKEYBOARDDELAY: + case SPI_SETKEYBOARDSPEED: + OutTraceDW("SystemParametersInfoW: bypass action=%x\n", uiAction); + return TRUE; + break; + } ret=(*pSystemParametersInfoW)(uiAction, uiParam, pvParam, fWinIni); if(uiAction==SPI_GETWORKAREA){ LPRECT cli = (LPRECT)pvParam; @@ -2638,7 +2618,7 @@ BOOL WINAPI extSystemParametersInfoW(UINT uiAction, UINT uiParam, PVOID pvParam, cli->left = 0; cli->bottom = dxw.GetScreenHeight(); cli->right = dxw.GetScreenWidth(); - OutTraceDW("SystemParametersInfo: resized client workarea rect=(%d,%d)-(%d,%d)\n", cli->left, cli->top, cli->right, cli->bottom); + OutTraceDW("SystemParametersInfoW: resized client workarea rect=(%d,%d)-(%d,%d)\n", cli->left, cli->top, cli->right, cli->bottom); } return ret; } diff --git a/host/Inject.cpp b/host/Inject.cpp index 9ae6c69..cca0596 100644 --- a/host/Inject.cpp +++ b/host/Inject.cpp @@ -5,6 +5,8 @@ #include #include +#include + #define WIN32_LEAN_AND_MEAN #define true 1 @@ -36,28 +38,35 @@ BOOL Inject(DWORD pID, const char * DLL_NAME) return true; } -DWORD GetTargetThreadIDFromProcName(const char * ProcName) -{ - PROCESSENTRY32 pe; - HANDLE thSnapShot; - BOOL retval, ProcFound = false; - thSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); - if(thSnapShot == INVALID_HANDLE_VALUE) - { - MessageBox(NULL, "Error: Unable to create toolhelp snapshot!", "2MLoader", MB_OK); - //printf("Error: Unable to create toolhelp snapshot!"); - return false; - } - pe.dwSize = sizeof(PROCESSENTRY32); - retval = Process32First(thSnapShot, &pe); - while(retval) - { - if(StrStrI(pe.szExeFile, ProcName)) - { - return pe.th32ProcessID; - } - retval = Process32Next(thSnapShot, &pe); - } - return 0; -} +#define STATUS_SUCCESS ((NTSTATUS)0x000 00000L) +#define ThreadQuerySetWin32StartAddress 9 +LPVOID GetThreadStartAddress(HANDLE hThread) +{ + NTSTATUS ntStatus; + HANDLE hDupHandle; + HMODULE hLibNTHandle; + LPVOID dwStartAddress; + + typedef NTSTATUS (WINAPI *NtQueryInformationThread_Type)(HANDLE, THREADINFOCLASS, PVOID, ULONG, PULONG); + hLibNTHandle = GetModuleHandle("ntdll.dll"); + if(!hLibNTHandle) return 0; + + NtQueryInformationThread_Type NtQueryInformationThread = + (NtQueryInformationThread_Type)GetProcAddress(hLibNTHandle, "NtQueryInformationThread"); + + if(NtQueryInformationThread == NULL) return 0; + + HANDLE hCurrentProcess = GetCurrentProcess(); + if(!DuplicateHandle(hCurrentProcess, hThread, hCurrentProcess, &hDupHandle, THREAD_QUERY_INFORMATION, FALSE, 0)){ + SetLastError(ERROR_ACCESS_DENIED); + return 0; + } + + ntStatus = NtQueryInformationThread(hDupHandle, (THREADINFOCLASS)ThreadQuerySetWin32StartAddress, &dwStartAddress, sizeof(DWORD), NULL); + CloseHandle(hDupHandle); + CloseHandle(hLibNTHandle); + //if(ntStatus != STATUS_SUCCESS) return 0; + + return dwStartAddress; +} \ No newline at end of file diff --git a/host/Resource.h b/host/Resource.h index 7945a0e131425471f43dfe98e19c18db75e7fdf7..9c7785535da9591572a271fe5edfadf21a06e870 100644 GIT binary patch delta 280 zcmX@~g>lAj#tlINlLI(;Sp69M8A2vs^w4BgU@&GdoE*q4J^2G4%jPfv8~4e(Y-J`3 zFmg=3z{oOrLAVS^0-<5@gHS$3xCVAt1{VfTpizpfhG6xdOci*8fFjNe9t?0xCQp!K zVKkcjP()EM7-+E{P>(Bv8-pW5D1*=BMs7`JBcKx$`Q(KmvcW*{03ZwkstTTLXrvC* z%a|++atAj=g$K|S|H+AV`aqdPb494AC(vZC$?@*mQ2PSKWH;{#KOjH(mz==l1;qkL Qp5QCzVl>@cUVfSh085!mWdHyG delta 42 tcmbR7oAJ;W#tlINlMAdlHg6HIaOdP?fWpcA5sI6)L>-Xde5vXf6979o4pIOB diff --git a/host/TabNotes.cpp b/host/TabNotes.cpp new file mode 100644 index 0000000..0990fb8 --- /dev/null +++ b/host/TabNotes.cpp @@ -0,0 +1,55 @@ +// TabNotes.cpp : implementation file +// + +#include "stdafx.h" +#include "TargetDlg.h" +#include "TabNotes.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CTabNotes dialog + +CTabNotes::CTabNotes(CWnd* pParent /*=NULL*/) +// : CTargetDlg(pParent) + : CDialog(CTabNotes::IDD, pParent) +{ + //{{AFX_DATA_INIT(CTabNotes) + // NOTE: the ClassWizard will add member initialization here + //}}AFX_DATA_INIT +} + +void CTabNotes::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + CTargetDlg *cTarget = ((CTargetDlg *)(this->GetParent()->GetParent())); + DDX_Text(pDX, IDC_NOTES, cTarget->m_Notes); +} + +BEGIN_MESSAGE_MAP(CTabNotes, CDialog) + //{{AFX_MSG_MAP(CTabNotes) + // NOTE: the ClassWizard will add message map macros here + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CTabNotes message handlers + + +//BOOL CTabNotes::OnInitDialog() +//{ +// AfxEnableControlContainer(); +// CListBox *List; +// CTargetDlg *cTarget = ((CTargetDlg *)(this->GetParent()->GetParent())); +// int i; +// List=(CListBox *)this->GetDlgItem(IDC_LISTFAKE); +// List->ResetContent(); +// for(i=0; i<9; i++) List->AddString(WinVersions[i].sName); +// List->SetCurSel(cTarget->m_FakeVersion); +// CDialog::OnInitDialog(); +// return TRUE; +//} \ No newline at end of file diff --git a/host/TabNotes.h b/host/TabNotes.h new file mode 100644 index 0000000..9da054b --- /dev/null +++ b/host/TabNotes.h @@ -0,0 +1,45 @@ +#if !defined(AFX_TABNOTES_H__798A9124_C906_446C_822D_322B5AB6C4C4__INCLUDED_) +#define AFX_TABNOTES_H__798A9124_C906_446C_822D_322B5AB6C4C4__INCLUDED_ + +#include "resource.h" +#include "TargetDlg.h" + +///////////////////////////////////////////////////////////////////////////// +// CTabNotes dialog + +//class CTabNotes : public CTargetDlg +class CTabNotes : public CDialog +{ +// Construction +public: + CTabNotes(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(CTabDirectX) + enum { IDD = IDD_TAB_NOTES }; + // NOTE: the ClassWizard will add data members here + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CTabDirectX) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + //BOOL OnInitDialog(); + + // Generated message map functions + //{{AFX_MSG(CTabDirectX) + // NOTE: the ClassWizard will add member functions here + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_TABTHREE_H__798A9124_C906_446C_822D_322B5AB6C4C4__INCLUDED_) diff --git a/host/TabSysLibs.cpp b/host/TabSysLibs.cpp index 4c72038..3dcfe92 100644 --- a/host/TabSysLibs.cpp +++ b/host/TabSysLibs.cpp @@ -40,6 +40,9 @@ void CTabSysLibs::DoDataExchange(CDataExchange* pDX) // MCI DDX_Check(pDX, IDC_REMAPMCI, cTarget->m_RemapMCI); + + // Kernel32 + DDX_Radio(pDX, IDC_SONDEFAULT, cTarget->m_SonProcessMode); } BEGIN_MESSAGE_MAP(CTabSysLibs, CDialog) diff --git a/host/TabWindow.cpp b/host/TabWindow.cpp index 7307df5..df40232 100644 --- a/host/TabWindow.cpp +++ b/host/TabWindow.cpp @@ -43,7 +43,7 @@ void CTabWindow::DoDataExchange(CDataExchange* pDX) DDX_Check(pDX, IDC_REFRESHONRESIZE, cTarget->m_RefreshOnResize); DDX_Check(pDX, IDC_FIXD3DFRAME, cTarget->m_FixD3DFrame); DDX_Check(pDX, IDC_NOWINDOWMOVE, cTarget->m_NoWindowMove); - DDX_Check(pDX, IDC_SUPPRESSCHILD, cTarget->m_SuppressChild); + //DDX_Check(pDX, IDC_SUPPRESSCHILD, cTarget->m_SuppressChild); DDX_Check(pDX, IDC_HIDEDESKTOP, cTarget->m_HideDesktop); // color management diff --git a/host/TargetDlg.cpp b/host/TargetDlg.cpp index 1b2a563..9b0c06e 100644 --- a/host/TargetDlg.cpp +++ b/host/TargetDlg.cpp @@ -52,7 +52,7 @@ CTargetDlg::CTargetDlg(CWnd* pParent /*=NULL*/) m_FixRefCounter = TRUE; // default true !! m_ReturnNullRef = FALSE; m_NoD3DReset = FALSE; - m_SuppressChild = FALSE; + //m_SuppressChild = FALSE; m_HideDesktop = FALSE; m_LockSysColors = FALSE; m_ForceYUVtoRGB = FALSE; @@ -193,6 +193,7 @@ BOOL CTargetDlg::OnInitDialog() m_tabdxTabCtrl.InsertItem(i++, _T("Log")); m_tabdxTabCtrl.InsertItem(i++, _T("Libs")); m_tabdxTabCtrl.InsertItem(i++, _T("Compat")); + m_tabdxTabCtrl.InsertItem(i++, _T("Notes")); if (gbDebug) m_tabdxTabCtrl.InsertItem(i++, _T("Debug")); #else char sCaption[48+1]; @@ -214,6 +215,8 @@ BOOL CTargetDlg::OnInitDialog() m_tabdxTabCtrl.InsertItem(i++, _T(sCaption)); LoadString(AfxGetResourceHandle(), DXW_TAB_COMPAT, sCaption, sizeof(sCaption)); m_tabdxTabCtrl.InsertItem(i++, _T(sCaption)); + LoadString(AfxGetResourceHandle(), DXW_TAB_NOTES, sCaption, sizeof(sCaption)); + m_tabdxTabCtrl.InsertItem(i++, _T(sCaption)); LoadString(AfxGetResourceHandle(), DXW_TAB_DEBUG, sCaption, sizeof(sCaption)); if (gbDebug) m_tabdxTabCtrl.InsertItem(i++, _T(sCaption)); #endif diff --git a/host/TargetDlg.h b/host/TargetDlg.h index 58bd0ae..9ffe3ab 100644 --- a/host/TargetDlg.h +++ b/host/TargetDlg.h @@ -31,6 +31,7 @@ public: int m_DCEmulationMode; int m_MouseVisibility; int m_TextureHandling; + int m_SonProcessMode; BOOL m_HookDI; BOOL m_ModifyMouse; BOOL m_OutProxyTrace; @@ -66,6 +67,7 @@ public: CString m_Module; CString m_Title; CString m_OpenGLLib; + CString m_Notes; BOOL m_SaveLoad; BOOL m_SlowDown; BOOL m_BlitFromBackBuffer; @@ -164,7 +166,7 @@ public: BOOL m_FixRefCounter; BOOL m_ReturnNullRef; BOOL m_NoD3DReset; - BOOL m_SuppressChild; + //BOOL m_SuppressChild; BOOL m_HideDesktop; BOOL m_LockSysColors; BOOL m_SingleProcAffinity; diff --git a/host/dxTabCtrl.cpp b/host/dxTabCtrl.cpp index 9086e0b..89699c8 100644 --- a/host/dxTabCtrl.cpp +++ b/host/dxTabCtrl.cpp @@ -31,6 +31,7 @@ #include "TabOpenGL.h" #include "TabCompat.h" #include "TabColor.h" +#include "TabNotes.h" #include "TabSysLibs.h" #include "TabDebug.h" @@ -57,6 +58,7 @@ CDXTabCtrl::CDXTabCtrl() m_tabPages[i++]=new CTabLogs; m_tabPages[i++]=new CTabSysLibs; m_tabPages[i++]=new CTabCompat; + m_tabPages[i++]=new CTabNotes; if (gbDebug) m_tabPages[i++]=new CTabDebug; m_nNumberOfPages=i; @@ -83,6 +85,7 @@ void CDXTabCtrl::Init() m_tabPages[i++]->Create(IDD_TAB_LOG, this); m_tabPages[i++]->Create(IDD_TAB_SYSLIBS, this); m_tabPages[i++]->Create(IDD_TAB_COMPAT, this); + m_tabPages[i++]->Create(IDD_TAB_NOTES, this); if (gbDebug) m_tabPages[i++]->Create(IDD_TAB_DEBUG, this); for(int nCount=0; nCount < m_nNumberOfPages; nCount++){ diff --git a/host/dxwndhost.aps b/host/dxwndhost.aps index a7c5aefbdd92cc70ce51ccf5db784ef613f6191d..ed57fe9f7dc1ccf1b6d87859f06db3b9e6392403 100644 GIT binary patch delta 25206 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 I1)Yfef8qq-LI3~& delta 6676 zcmZu$34B!5)j#(oWFwFSCYd!0NEMY2X3H!p%gfB0WX#OGVHT1AHlV1eRO3=bQ5Ngh zDuSX1`>H5{MFmloMjEQoVr$(|{OsrcDOT)f)k<8df?|5^y)P5;`!)WCcklmy&bjBF zd+xdKKG*Fi-}P4Mx@fKILk5dZn;%`;aq*)0S9-hMzHWB_UtMJFJ^0mvQOemx*m7iQ zc?IylPhk;WePmwQIH-X^`onjRv?%mwI_hSAoPG3!qTfL&?i{q^x})PbvE``XKR$XM z@1?*0DSzzxelCAh{vdiwe(3C7f6P+F`unz3vYU(S-xL8EUkMW-3q5cyoC6oYLRbP5 zjhZRU+P9#ZO}4RqR-(rOE?E#?b(q%Es~m zW*UowY!a(6)&|*3dhC~vF(IZ?@2n8ZvMOVDh|Ml<6Ja6mjZ4dz(>O8Awz4+kl`#7= zJ@7O(wQtNB?EJF6CXGF$>;4Wa-3+zV1&M07a2<&NN zVH+Di{p%6phM*=X$D8<#z;Yw7y99QFv74KCbOg0QP=`if_Xx~FOEc`*Mu9~Q7AF%| zjG#6PYRd@hK7svp1om5j6&th3M6JL0cil{K1aGf56lMKx53FW_(4gnk_G&JgHy=? zy6$$Q^HL)h&xu^}g<+04L_+T%ZND2jBoIl&(*=k89_W-Xk8O@m+3$re0Av?g8mQxg zxsq>FQ_+N`CUP-97xKeA++A81n=je4p5-C+2jHv%z(eW}0__Q&IobGJArXS^0w5&9 zumEt@Jy!cdi6^yeR&CYN8J#Ctvj@(`>1Flo9Gq9??4nx7jkTzec1mA0ms7<)X>O*v zEE6=F)iOL>%>g)1LVRYSmUNnfK{$UH^aM;a1dH*NGN%DbFKc~g1>Zu#$4*K9qoK<04 zah2&-JQ>f)v<)`HQVC|;<7pEMz||&3S!ZHFxJF_;QK>BC;z^ytC6YB5f@P8W9ef#t)ptdfz{6aEGHPhabi1Sqq1>pt>CgNF{ub~k9_b?Pw+j*2j zVfZEP8f{U21$f(LOWlo9BBi&esgx*sVK3Z-1EcHgt0mPD&(JnjlUzIOhnp$N)mPPG z)qSJb8a%DiVOuNZVtOW`4P{O^2)E#pN~iBuNk+A7drnVBv{X(G5{7k>iRgMJ8c$K# zr9DO^7jDDHDjm~qH;v{KiHsJ>wIpbgR3-o=E&`v{c%o4Ofc4n(TZ^5}alQxIDENgm zq|}CAqkD|Am5y@Gnx4eSFxL$N9p@Yu78A7CRE{W2aHoV@611Z_+TuByKFuHQ!Vk7I z)X|wfG!kDq4^{lTDK3{@ZNZ-VN3nbG;1~zni0|I-V)vr!0UO(dp0W1Y%~Fk4JEf<% zK`OoXVfWZtE1lfjpm_jAX@h&H=x)KA$2#ZTFFEdUHj>da(oIl-@OXXjfJy18TuWD4 z&GHaZ4SrC1Wq%H21FQ%Gfs zP~F~3HF}#(`2%3%HamL^H&)x5A2-K{cTzG(^t?<%kG~oEB}02SpGl=ey2ZHh06c-8 zRok6UN`9_wZd#(TPO$xwFeGb{?wIe=0ChU$lUG z;m^Z(M*;T1(-Mw$ayufwoBi+?+*RXX&)|zScI&gldi18!O1?CQ;5opTAF6XaFF7?D z6<*Mx%om61^b2_EIEU>;NvCvr`4ZcTlU{fU&l+cS?w6!l&iHo72xULKjJJ++`d=AR z<|l8S&ULOFfCCav@ER-A)1-p%D&VWz9m@XzezColy@u7}t@gi~qvX@P_P0foeES4F z@V_`~eC?U94;kZL@Byflzag=-n$U7NEutrM;aAWPZ%QT_&+;QArnk1nQ#>A2;*={$MZ!rsA`$Jf`rE2Z1?Tv`?N+C%mF|BzYi>^*c^9JcqR0413(N~3GM;!sIG zh)Ijp^?|8P1tJp7=t(V^&hbqc^26V7rNwIh&~!!?I{7B*2?gK~?y^{wj{qxoIF!Ew zPT66f`TtTjre@8;7IwqO5;MaulR4~xe@K|>v#5YnoIe3vx5F~+(;*_Bq?dxW1m6)< zo+3{5~@_+CJ? z0Yu*iKZw58nWnUnGJZHFuuMKBWPqzLWdOpb8Yq3QX#^xMdAeG zBD9`mc#ExWO1a>8vF*;%F)FwKR0xy?>E?q8Fbt#dfUVA2DJYtBfeXEPR{2GR!_7b!K$^1fs#VYFRH(XWVR+<1bavL@0=vyVPRMW{$UA-y z6Koj9<8Ai3I*VYlbZrq=K2oGxQ!PGWch=PjD%#nRiqaIgmd6KH{M>G{+62X8H59Or zCj@q^cUbKX!3m$@;uVr>(xt_D`{VYLT!Ku~l2CmSgcs;m%h%0}e25Ot23+f~DUA#- ze0&0H!WyR)4>~H@1krl&@k%z4S|_j*L_gsi9hk&`{G|D1sK#{j(~Z}f0Nr0b@RJdo z*!rCC!il)nX;)5Sxav78n~eLMF6Cr~e|*lVoWk%RJx*oFo_8oy7*2S;-uYi6>}Z|z zVn=p7ZkWn&;qy*54R3V0m>chRH8y!h$chyci^&!I;AQxa=NlCtnO|)!<5&Rv3_UN@ zH#Cn>)k&23`41V>tMNQ#qYs(~C~!42J59 zPBtCg4K6kVlMVH3CSFAU&cd4-T#f38k+e)kY-m0upBq{jmcQg;5&WQ`fkkg`taWQr zoUQ|u3sFrMf_{h%!|_y_E___S8Cn@`e91bkZJ4F1Ew~`Whe0~V%ysgG;7o=CFIiYS zZfdM`B!-3P1)Gipu1s$aNrtob*E&*?HrG2&H$1`n=pOoGW4$fSpj%C6yM57a+6}pk zo}eNLa~M8yM+57?Q~R4(C$4UCjiSQV#qgiEJK5acXPaiuR1Oqj#RA7TMSd7DKX45W zRu|)@HBBslPpvt*f`1I;e*}KA=I)W*)mVMYxZWuX+zM0ith1ds>lPPwtf(ec{*|%+ zY>OhAdnJII&wb5otvT;gvvuqF*JH!tpU58Dj`J6{n(aNw8X?h^_baOVy1w@p zR|HK-c31CfKd)nB>BWE!-)y8Uo`}NI-Vd)V2t0+prKgx?pXFvly*rnlH>CbIoO;dM zG^*6EW2w+z=9punNzd5l;f@fV#D57Jc-r<2XC#d`H!D(*(o@0dCatX z2p?N+dcr=!W6Mu6@xSBr6+<2#R&dpd6HRvq6nv`C`9eXq(hR=xrGm3oPBO*5QgC^p z^Rj-(q9g6 zqs`b+Nkk73MT;haah0yrE1#WcjCB&ai;KJ5#*9f^ybNH8-?(Km5d)lPn{HHis8gM$ z&erM1h14l|fV?^*V*D5&B27e5aVr=dX{EksQi`loL)n)yUXBobfar-!bUx*3`Y{nE zcOh5+>x%($8RL^yHluX11)!`0jMHb6h4e*6Y9Vu$&{)5@9E@cgJ9wq>-V)|0B{}%` zY7)#U_R_7D0IqAmc$_4P4*e30cV;X0l8Gepuwgv3mi*-s1BUTAwM(~F1MJ-bhBIRd zt=wZQ-)<65?;)F<-~+6A##r6YwMk>&GsctD76ae-28~4n3!?uG#FsjmaeB@q9;VLr8k4zV3Nz*n zFrQp42Tf znbE-;J=D0QS+N`I^0cf2)H}?3yLhitWkw$tx}k*`Z}7$zjTzta#{3vLbp{E#=aAr= z)Cc6Jbsm5qMS=u11WA#Ct`#LwqC`Si+@uA0cV`#e6|skR7XT$ufd{)yV8g}C zW_FeYti+d?q{@*MCr(_c{K@gdsmf&~PAal1j3i5TE-7)PDv2&jj%BNC#Yt5vNyXM- z=6l^evxAF-WY|zDJ*bD*{rXM!>({S)X5RMk)xY2M_22rVu7A;%Tz6}mv>#mDrfmt( zTM#DTm#!iG;zbI3n8F~b>;I4j-iE9pwA&^?8(=el+WZQ@R=_quJ77D2+PMRGC*Vo| zOhDTO=mhKr>;dcr5Z^xF{eY_hU4Ux<*8;8sTo1SbKzu3S1Av$uUO*q9A3%JAz(asxz)`?4!21Aq0Y(6K1BfpI90kMx1|SYd z07e1F0Z9Py5u8AH43Gw709il|@Bu&`Pyi6$IPe5u5^xf5FW^4F{eV+|2LL|-m;!td zU;?HAB|sU#0F=*<>&k03{#-4Dnp?n*wg3s%eq9BN@nmhi%x$5ce)aWzKlOAA)z4#{ zMCp!pMyqQz=%R%BhoDOrVVCQ|<68}fv{r7TWVpwU)nwijn0f(CQV`m9+3~uAL~K`YV-9*IM$pikM{iEoeWIeOr0{oK>E+ z>+TKlW2bWY?D11Rr5Wlj*K5Qb>uB$2zyCP1m}6E$DeJWTKu7xobLth_8VPr_kC}DX zsG9Zqh!LH(8*aQhBZx&dSF4&X8!_S)vucByVdq9GRhF_!)kc|xM{cT!Q0PG2Et{w3 zyJrt9W$Fzu`9g9&DmQ1&u#p}TkV|0L4a*%Fl(8@h_9_~!)g-kyK#kiaN$m?zCo1fm zB=?UPX|rODI_4asQu3^BH=Gg^1@$!v;gM84J{65+$BVJ-$tJxQ^!!vhn#zpkqv?dq zb|9!KF8SbyQ8b+yCbZ`j)5miyM)SuLMcxX=SmD^BN}(x=3pK8TBs0s*A^na76xGq5 zLMyO2X1S(|7F&{DBE5j-GG`>SLJ`o=Wr_et_w1!hA(qP(L`uI*NyKuwqBu{mETf}6 zX4aWB(6LJ070o+QaomPkl~tdZ1RohxcKRn-$%Wdd(%E=w^j=a4te@-Mw}h4D6v=a| zNa&>oWkZ`TE;E|T2^$i{HjDN;*2*O}o15pp<_3>1Dc_iyNaPEtEDRx_Gq3Y5)>xS| z2F=9Bjvot}z9%6DhGm0-VOy?aS4HRP2ZM-H-!Y~N#b_~Q9Ilg@mJWye`g_AeL%qZ3 zW0H-!k}qM9udipge;9*+l^AD9zM!zmClT# zR)LPYyjs3BP>yqRrd6hTOi>diPNvYbl1gIx#w2MZ;#52_mMGGQAnqo4rlN7Qi6jho z+3``uCkyQja51CC*t-;SG&Po($|NTJY`K`6n$0C(06|GPxe%RLB5Wd_Do%}O;#r@Y zi_FQ!cvAy|R1rbV=Fp>L)Fm}@DVOFlVFuZ}D z!M^Z7z%qrQQ!@muv$wx@I82W=k&m$Q=y)+3WFi|Mq^>8J$fk^`cq*STiYLQCM&7qz z>ynWg&SxCD1S7Sj&p3PuMrvK3@#rNOsksf9Q6^i&cpGHoHEzmC24GAj(&J;%Vj>qE zy96t@3S-KM=8EH}`*^;P&HL>ALxcT;Jp=uLHW7`IOvTs0D_SR+&7O#-{5&yQEs>Vn zlOa2PIy+uST#8vV6kZ;R`KWP8PHL&e!xk?%P#NAt(TtH;uA!#+ghl(vK{q%E(kLkg zLH^M5P1>_|477na4L^C6$6hm?Kblr}tjH(u@@EnC5xi(FdQQuyj$t~?)eMK>?XqB3 zE9D^JxMMDe_;Kd)K+djfg+Mr2DKo)Qn6=M|P~0(RqNNh6x%7s_`BNp^(vSzoxPP)@ zmBEg##pc~4tJYFwN?wUPUMoYixapc&)*@DkQdXJG%kU_&V~&iB*DKbHhO9Vqp4IGn zg}<&<&6&E^jK@r;uF2f_OSSC4b~+TE1bW?Xp=Nv+bHjEETK4I7LfXI?hg;jcSvDXg+t=lH)sSma*N+=>-I$ zCAV@G8VLmPMjnV2?b;YS%c>w5rd489$VlAAY^7Qb+-+3tI;V?PNHeQJTy||iDq9!@ z(OkS}R3UNJinBW2J4lX%WlmS=erTb<>huZ2BhXx`ED{{NEym8wSY;$Y3+alLHs|T9 z2XWr$B1lG&2=bn&RI8(QsZj?XiDk^1R5k?*tcsUPir8nV@EkrVD4K&ATduF94>Xvw zKz7ItE^G1lB0)I?A5l58%3Q8ZPRZ67RRitk2|#u&O#$@Ehp5yd44o<^=;#xW(3&B$ zTS{ZZmMI0)o+%k-wInk_g!eqol&rW#sF#PwV*F()K3z4cXBJWHb0lDZ=`*GD zGJcQZ@yoc!Z#U|&Vy?qX$!B9C?sCa zL?n(ctY*>)wh$NjgAWm&IrwWPjGb#W&d!nZC9C7zzZ-p zB3?QS*uSG#nQP{S#->vIM6_zks`ya^gys7ZCkv&|wzjmkYSzSa=ik!4{Wxp2npN=R zU)qFTTvRmJd|p#|jqSw+XRzVY)NBD`;&IuGJUltec+bzX8GN5Hhhc=mEk1ck^Db`O zVADb=a+rd-LtCT*XvLMogcr=&wFV!2>Sv7UQQMg_-3&hP!7TJ=OaoWQV&%wTR4cV< zTXY|E&~w2!^onm9SbUR(TV{}uXEfADX@H=@XowIb5l57|R`8CA<+*axa#+e`b6hf5 zt{;I+l8zw@4j4m>o@r**8~!b16GdC{O`yg7NIIShv`>N_B&0I_^uSm-FEl8l1$Ut; z?U0+T9~DNAnuq{eSOg5S=0XagTe=1Pq5>ZZ4L^*YG=&BpI+OevQi_(#juy9RG*8Tz z1WOEdA)aT{Ihg+l6S17bijHa3$<>s`m~J9dJaOixD;Qk;$C4cN9J_=>l$EZ}X!1Em zIFO8y(an^+XGWf#roo#(hvY~tN%ZMXxu626c%@!5-O{W!>ezG5SgHz1>I4~|EK(a%KxYQf6D(C@Cz#cADr+_C!F&C zDgU4H|0(~U^8YFSAHFd&CFTF)enQWGru=`(|0n$WL2sV&|0)09d%*v<6*GU+WB1V1 zR^|Vr>5t0)r~H3>>Z6+f=knP#YW|;?`l9Cl$sfM?tT;9QPtE_MNksUaTlxQ#|4;e< zl>blp|CIkv`Tx}XKg*x&spkLrQv}ugKbljl=KpE$vH5>1t-qeciewAxl4)J^<*ox* ze@uG?h}FkyTYr2bWh&Pnr?84Xh1Kw7Z62jjsk11zjup{syROChZ?EjNu7AE`h4R-{ z+H$V;cTxU8pnOBCVy$}}e-qF;gAPL2JK!DZ{~g!={eki~Vf}Y=?e{jmK9g3%ZoVs# z&EFmT-{CJQ4L$bH7evZRv>o0`LzbRDcrAHsT@a!v(UI^Xli+Ag>P&o9=D=+@} z%fI{B(}Cii`JF$0en){X^A7QK0z1TdClOhy3tHH`I_OU z*sOq>bCqRgexq`ppi^6||6aTyKu|Z!*W4-puk!z@_20pD;cEV;n*X_It+?|4D*vzY z|0@5l^8YITuk!yY|F2sA?JWma>%ZUQ>%Twvx|sNRb^PRHCZ5a|ilHN+$yBDVHEjc-KW^0wte~eY@m9a*YTx-nxm7I64IV#iqe6RcwtSQvkQ91-Gfg_B(3?Dj zfVOY>y>T|(n7RC&Tej{g6k>TyWZkaGtrKZygVlw(RB@ChGhF2PYr%H7Tmi=&yHS%I zo44+X-!r+$DR>FKot}qOxDPMExv9*s-{v08h%@ZxQf5|ZfhOHxBMRooqPI;$t8TsA zJ==m!c>|2(r(7i7x@IP7EdDRsVp8q@C^y~oc6eNQGfXcv=zmlDKL)nMQ~N)v{U7-@ znrihy{TyHNW-s{J3;{*V5?lZ&?q z^mm9<`#-AvA2IDn?f;my)c%kE|Mq|M{C_QhS%C0-wX8Ikl4f_d;8ZB(|04&e^8ej4 zsr-N5NkA)ah3|Rzl>d(oBvR+U$Q9HyUr3$*qRxMjAL{D-mnBEfsPkXo)l=ucET4<6 z&VQlf`qcR^>iie~pc-}li#q>>ADV$ZC)N2cVy8)U{tFINQRly?^ItB1{)>11^TmsN z)|^qTk5n*XQf{{@ad_k5$u|EK(aQUkPS zpz{AI|DQZ&UHSi(9~Q0rf6D)-&VN?tKdbYf#R<~Fwd0=<&FAeQrp|wshvq8(pKZy- z*UJB={D12F=QW-G9LJu6(^^BD!5`Bc&(*hP-1#o{KfH%>m-|1)vD;$_<2Nc6ij7!^|^Y3f60XbT6&Ia548Ql&;jxYGuZn!vXAc9|79z z37z(Wtk**m6&r`Odm}7f+{9=f`<_@X=aA#Gd64yD& z-Jf%CBU+D@S8Bbt!Ln1-cIU`m!uOg2dgJ*IQy`989)69fQmhJ3ZATVJTJSS$e5H_w{}0O3AV7 z_Gvd{+;PWp%@S`L+5G2!?{iOvCGM5DPvU+}Gf}0s7OEFESRf}^G?$x-#qdNQrDF>^DWKbm6LzlLu+K4OTy zz&qMkeHN+tpKAW6Wz1I8{7*Iiv&Q$DRQ_M4{J&Xi@#22v|5g59<^Nri|91;KaaY0p z@WiIQ+OLt5!oQ3=;mFXA!W9t#TmU@hIp*>Whl5{2z^9aLdut^W@6%8{haL8lQ}j;J0P%I}twAeX8jZ zb4u;vTO`yxY$tU8r{DO@gAc#{z`+Om`iBPxj}G@A=|7r?!TyZVBeBu`L9)MSPfx!Q z?oISw#{SUfsNZRS0^8dGF?JywULr!^m?X%NpGrqlnbCYSomgK{&ZB4o65}HfhdSV$ zOGJ_9Foh-xQ_>$e1zED|E67(+Fu`l+44;B9)DLc7A_$!m^`lhAucJ(Y*Pw_m0(K&N z$Eq@no-8I#7VpiibBjERLJ8i20AB@kY;f;91X&UQx;!jmn${Um4$b>suRcnHH^Eui zM|W(n4z7nRWRTQRJ&Yi~ws)WMcY5##s2}F(@gO9}ZGC6Nd{sa_IgVyP7Wa;=rN7Vf&$Dd%! zXCMRpw!L)x2|SwkFGI2H$@LwGy+QC%2=E9j;ff8m1ZwsD^bePZMGRKzza6a4n~|3< z^#ba8TQ<1vRNql4l!rYT@nYzWJL}a4V#u4odoSL+!PUPRvRp0e>tPwyQ2pjB+xoWt zJ!pCQENI){>eCRk8_*HJUiEhcsxQt#T$g9h(ocg;8>|AVF1?_)2Qbm-(gP~si_X?n z1K*HCKoqtRF_T#Me z92ziT(d@7yrtOVYrnTeDrC`cBZD&s(Z&b=ep}AqETbfO!;z0&}GNwns&4yW;Wq~*a z^9@Wun-f3zY9BVT({z-KX4CXLoCh7U*tuq;j6-8whBMO(%n{SNAQ+-2DpomrTJxxU zCYIc7%}(ty%{(ty%{(ty%{(ty%{(!lB(_^j5_)(S84ZQr=B^|3G9+p7OUMBfaD zum15!XS>8Z2|pC++)ntDTGtLq*Evphwo2Sa_#tio6@-6Y+rNeI!`l81iML9;jqn>u zeTUW-*59CffuGa%>jHOma2)BpQeb^2@UG{z+lE_i4ex5zZW{vDTeYqM!Y^rEgM?pB z>UXwLJfe3KzL@On5%JEb#4*BOi*)u&JV^M3NauizA0_-!r1O}h4-tMb(s{SUBb=^v z^%DM?))gj<@(mduCj6q-^?t(7Yh8Vk9wGdK)^!)*m$k0<5!PDw{~PeGTOy(V*^Ky; zN&O9pzc2CkB;F>mPWYju{(ZXtp@`m3@!KQ%0O3PPy^ZjfBKix2561LYi4XXjgs+e3 z&k%MZ`X3TL5YhjRr$;)!DD(L);hQ7+zY@MBqW?SL!x8;u!Kc3>`JW`*6Vd;Y@Qo4u zD-u6V`0?M+e0`+z3&?ldp-AZWDgLEM=bsY3F{%Hj;M4zs%Kf>d z{@)b;*`)qo68{Hbl=}>&A57|72&3M63164g_Yl4*sb3-Ky9pnN>FtDXi0FSy_^F8g zXA(av@m~|ZDWV@D{PBqX8sYOv{RYA}B=wNQHxh>4|B3KZN&QWd2lyh#5&d~ikLdp> z>75jRI-=jiaZt96zaDtk(=q*RiQkm?2NJ&ptbaM8f0OX}i2esu j9^#+pIH_O9)06slWc*r+qg`L+IH~^y(a$9H3&8&iMe7!b diff --git a/host/dxwndhost.plg b/host/dxwndhost.plg deleted file mode 100644 index fbcb14c..0000000 --- a/host/dxwndhost.plg +++ /dev/null @@ -1,16 +0,0 @@ - - -

-

ނ۸

-

---------------------\: dxwndhost - Win32 Release-------------------- -

-

ײ

- - - -

-dxwnd.exe - װ 0Ax 0 -
- - diff --git a/host/dxwndhost.rc b/host/dxwndhost.rc index 8a8492112c54330a2c47ba9e264fbec97590ab0d..5861eaf4d2db92b7f5d9a5ccc86a800760af3012 100644 GIT binary patch delta 973 zcmZWoTS$~a6h7m+UNAQ=wt(J-rn2N4tY3izwJs3hl2*v7o z_$z$KAP|CD+GwE%(ekNat(P8($R4t!kSHnWq55XU3}pD{%$b?*JLi0J{wpEvFYX%z`g3nnzhqy+X9 zKG#f}^PNpJL?hHi9l+OOK207P#ZESLQ$KlOk5Uhvrz&{;bPj$O^-?bz`qFvK93@KF zpp@0t0nr0&56;W6_G0CsJ`Bmb#Uy#oV!xAyao!Iv8_`E$JAsgX4_)M!H}qWCr<3d& zh0Nej4y-7br#R%>AGo*Vjpg2@EN-YZ2HHbzy>RgT55Z|)B>R0)Jg~YTm$r|uq|tqP z&A!%1jguTa{w1A%HyG7hb`it2HH#?b@lu7q`XU1}YgrK*JK0$*D}nXTzXTiy58kv! zdy$eST&#hweam8BsZrY~6Xn^b$s93Y+HHr#3OO)L1X;Q5yFITGm~Iq40-=VnVLEAPYx$|QaBXKh?6`{aG1!-$A+EkpG?TyiEX52YQQ*7E`Ik$ zqSH`mEZ#l*+BSY)=PYp11qAjW`7S8D^>RDOs*p}S&;B^DNfQ-qCM)9G+3>&__a6!W ztlAeZGq1*1gX}AKacm2O*s^#BI?wPZFQ8j@r1GIl39)XFH{ot03yXox9Xg?)H5DG{ znB@7&+?;>s06S&Xo9zk(B%N$t8?1{JZzeyf$hEgXF*g`dIp$_qH_lt2X&ZJA@n?N^anShgfT{AB}FaQi&m}t zl@xV6MHB_+c8fDf>Po7}*se*P(uoQ6MY8A;HeO6I1k;SdTdK~riq!2(oM_%FgKH36 Z9V`=ib*59K1s|Rg-O=*bRp+}!&0lHf{Nn%s delta 349 zcmdmWm$l;@>joL7>21c0OE&LgiqhaTWzbG2S!dq1`D8s zA%pqki3a+UFIe(S{-iU5(PVPBZY#4ngT-cHz0=H_wTxRPPJZymL^+uugCUb4ham-M zQ~^T~Lq04qa+}v3 zna0U!!C(Tm(rEI-{pOon&iRRRn*#-m7|ejA+2oBS=95L9l`)!6?tXR>EMv-G0FFKdBrGy=JN^PZPVOhAc^6O1SC^WoSm_2v^JSgvrp@@B42 zW)hPHv^H!v<7HIhU^Zf~n69YA7|U$IU^soD5Tp3?2~vy_+uw*XW(ZAQr^L0rMU%0} fX8H^pM!x9**^E-#GtwB-*e73MSu(vKkI@SNf$wh8 diff --git a/host/dxwndhost.vs2008.suo b/host/dxwndhost.vs2008.suo index 34016bb07bdf5e6c1ad0f641fa1e450d54f52917..79bb7dec4e0cd7432d1532a81f16bda4f43885e3 100644 GIT binary patch literal 140800 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 + + @@ -538,6 +542,10 @@ RelativePath=".\TabLogs.h" > + + diff --git a/host/dxwndhostView.cpp b/host/dxwndhostView.cpp index e8dc9ce..9cc3fcb 100644 --- a/host/dxwndhostView.cpp +++ b/host/dxwndhostView.cpp @@ -33,6 +33,48 @@ extern int KillProcByName(char *, BOOL); PRIVATEMAP *pTitles; // global ptr: get rid of it!! TARGETMAP *pTargets; // idem. +#define LOCKINJECTIONTHREADS + + +static char *Escape(char *s) +{ + static char tmp[1024]; + char *t = tmp; + for(; *s; s++){ + if(*s=='\n'){ + *t++ = '\\'; + *t++ = 'n'; + } + else{ + if(*s == '\r'){ + } + else{ + *t++ = *s; + } + } + } + *t=0; + return tmp; +} + +static char *Unescape(char *s) +{ + static char tmp[1024]; + char *t = tmp; + for(; *s; s++){ + if((*s=='\\') && (*(s+1)=='n')){ + *t++ = '\r'; + *t++ = '\n'; + s++; + } + else{ + *t++ = *s; + } + } + *t=0; + return tmp; +} + ///////////////////////////////////////////////////////////////////////////// // CDxwndhostView @@ -167,6 +209,13 @@ static void SetTargetFromDlg(TARGETMAP *t, CTargetDlg *dlg) case 3: t->flags5 |= TEXTUREHACK; break; } + switch(dlg->m_SonProcessMode){ + case 0: break; + case 1: t->flags4 |= SUPPRESSCHILD; break; + case 2: t->flags5 |= ENABLESONHOOK; break; + case 3: t->flags5 |= INJECTSON; break; + } + if(dlg->m_HookDI) t->flags |= HOOKDI; if(dlg->m_ModifyMouse) t->flags |= MODIFYMOUSE; if(dlg->m_OutProxyTrace) t->tflags |= OUTPROXYTRACE; @@ -209,7 +258,7 @@ static void SetTargetFromDlg(TARGETMAP *t, CTargetDlg *dlg) if(dlg->m_FixRefCounter) t->flags4 |= FIXREFCOUNTER; if(dlg->m_ReturnNullRef) t->flags4 |= RETURNNULLREF; if(dlg->m_NoD3DReset) t->flags4 |= NOD3DRESET; - if(dlg->m_SuppressChild) t->flags4 |= SUPPRESSCHILD; + //if(dlg->m_SuppressChild) t->flags4 |= SUPPRESSCHILD; if(dlg->m_HideDesktop) t->flags4 |= HIDEDESKTOP; if(dlg->m_LockSysColors) t->flags3 |= LOCKSYSCOLORS; if(dlg->m_ForceYUVtoRGB) t->flags3 |= YUV2RGB; @@ -362,6 +411,11 @@ static void SetDlgFromTarget(TARGETMAP *t, CTargetDlg *dlg) if(t->flags5 & TEXTUREDUMP) dlg->m_TextureHandling = 2; if(t->flags5 & TEXTUREHACK) dlg->m_TextureHandling = 3; + dlg->m_SonProcessMode = 0; + if(t->flags4 & SUPPRESSCHILD) dlg->m_SonProcessMode = 1; + if(t->flags5 & ENABLESONHOOK) dlg->m_SonProcessMode = 2; + if(t->flags5 & INJECTSON) dlg->m_SonProcessMode = 3; + dlg->m_HookDI = t->flags & HOOKDI ? 1 : 0; dlg->m_ModifyMouse = t->flags & MODIFYMOUSE ? 1 : 0; dlg->m_OutProxyTrace = t->tflags & OUTPROXYTRACE ? 1 : 0; @@ -392,7 +446,7 @@ static void SetDlgFromTarget(TARGETMAP *t, CTargetDlg *dlg) dlg->m_FixRefCounter = t->flags4 & FIXREFCOUNTER ? 1 : 0; dlg->m_ReturnNullRef = t->flags4 & RETURNNULLREF ? 1 : 0; dlg->m_NoD3DReset = t->flags4 & NOD3DRESET ? 1 : 0; - dlg->m_SuppressChild = t->flags4 & SUPPRESSCHILD ? 1 : 0; + //dlg->m_SuppressChild = t->flags4 & SUPPRESSCHILD ? 1 : 0; dlg->m_HideDesktop = t->flags4 & HIDEDESKTOP ? 1 : 0; dlg->m_LockSysColors = t->flags3 & LOCKSYSCOLORS ? 1 : 0; dlg->m_ForceRGBtoYUV = t->flags3 & RGB2YUV ? 1 : 0; @@ -517,6 +571,8 @@ static void SaveConfigItem(TARGETMAP *TargetMap, PRIVATEMAP *PrivateMap, int i, WritePrivateProfileString("target", key, TargetMap->module, InitPath); sprintf_s(key, sizeof(key), "opengllib%i", i); WritePrivateProfileString("target", key, TargetMap->OpenGLLib, InitPath); + sprintf_s(key, sizeof(key), "notes%i", i); + WritePrivateProfileString("target", key, Escape(PrivateMap->notes), InitPath); sprintf_s(key, sizeof(key), "ver%i", i); sprintf_s(val, sizeof(val), "%i", TargetMap->dxversion); WritePrivateProfileString("target", key, val, InitPath); @@ -638,12 +694,13 @@ static void ClearTarget(int i, char *InitPath) WritePrivateProfileString("target", key, 0, InitPath); sprintf_s(key, sizeof(key), "maxres%i", i); WritePrivateProfileString("target", key, 0, InitPath); + sprintf_s(key, sizeof(key), "notes%i", i); + WritePrivateProfileString("target", key, 0, InitPath); } static int LoadConfigItem(TARGETMAP *TargetMap, PRIVATEMAP *PrivateMap, int i, char *InitPath) { char key[32]; - DWORD flags; extern BOOL gbDebug; sprintf_s(key, sizeof(key), "path%i", i); GetPrivateProfileString("target", key, "", TargetMap->path, MAX_PATH, InitPath); @@ -656,22 +713,15 @@ static int LoadConfigItem(TARGETMAP *TargetMap, PRIVATEMAP *PrivateMap, int i, c GetPrivateProfileString("target", key, "", TargetMap->module, sizeof(TargetMap->module)-1, InitPath); sprintf_s(key, sizeof(key), "opengllib%i", i); GetPrivateProfileString("target", key, "", TargetMap->OpenGLLib, sizeof(TargetMap->OpenGLLib)-1, InitPath); + sprintf_s(key, sizeof(key), "notes%i", i); + GetPrivateProfileString("target", key, "", PrivateMap->notes, MAX_NOTES, InitPath); + strcpy(PrivateMap->notes, Unescape(PrivateMap->notes)); sprintf_s(key, sizeof(key), "ver%i", i); TargetMap->dxversion = GetPrivateProfileInt("target", key, 0, InitPath); sprintf_s(key, sizeof(key), "coord%i", i); TargetMap->coordinates = GetPrivateProfileInt("target", key, 0, InitPath); - sprintf_s(key, sizeof(key), "flag%i", i); TargetMap->flags = GetPrivateProfileInt("target", key, 0, InitPath); - //// be sure just one of the emulation flags is set - //flags = TargetMap->flags; - //TargetMap->flags &= ~EMULATEFLAGS; - //do{ - // if(flags & EMULATESURFACE) {TargetMap->flags |= EMULATESURFACE; break;} - // if(flags & EMULATEBUFFER) {TargetMap->flags |= EMULATEBUFFER; break;} - // if(flags & LOCKEDSURFACE) {TargetMap->flags |= LOCKEDSURFACE; break;} - //} while (0); - sprintf_s(key, sizeof(key), "flagg%i", i); TargetMap->flags2 = GetPrivateProfileInt("target", key, 0, InitPath); sprintf_s(key, sizeof(key), "flagh%i", i); @@ -980,10 +1030,12 @@ void CDxwndhostView::OnModify() pos = listctrl.GetFirstSelectedItemPosition(); i = listctrl.GetNextSelectedItem(pos); dlg.m_Title = TitleMaps[i].title; + dlg.m_Notes = TitleMaps[i].notes; dlg.m_LaunchPath = TitleMaps[i].launchpath; SetDlgFromTarget(&TargetMaps[i], &dlg); if(dlg.DoModal() == IDOK && dlg.m_FilePath.GetLength()){ strncpy(TitleMaps[i].title, dlg.m_Title, 40); + strncpy(TitleMaps[i].notes, dlg.m_Notes, MAX_NOTES); strncpy(TitleMaps[i].launchpath, dlg.m_LaunchPath, MAX_PATH); SetTargetFromDlg(&TargetMaps[i], &dlg); CListCtrl& listctrl = GetListCtrl(); @@ -1283,6 +1335,7 @@ void CDxwndhostView::OnAdd() memset(&TargetMaps[i],0,sizeof(TARGETMAP)); // clean up, just in case.... if(dlg.DoModal() == IDOK && dlg.m_FilePath.GetLength()){ strncpy(TitleMaps[i].title, dlg.m_Title, 40); + strncpy(TitleMaps[i].notes, dlg.m_Notes, MAX_NOTES); strncpy(TitleMaps[i].launchpath, dlg.m_LaunchPath, MAX_PATH); SetTargetFromDlg(&TargetMaps[i], &dlg); CListCtrl& listctrl = GetListCtrl(); @@ -1623,38 +1676,52 @@ DWORD WINAPI StartDebug(void *p) { ThreadInfo_Type *ThInfo; STARTUPINFO sinfo; - PROCESS_INFORMATION pinfo, *pi; + PROCESS_INFORMATION pinfo; + char path[MAX_PATH]; +#ifdef DXWDEBUGSTEPPING + PROCESS_INFORMATION *pi; CREATE_THREAD_DEBUG_INFO *ti; LOAD_DLL_DEBUG_INFO *li; UNLOAD_DLL_DEBUG_INFO *ui; EXCEPTION_DEBUG_INFO *ei; EXIT_PROCESS_DEBUG_INFO *xpi; EXIT_THREAD_DEBUG_INFO *xti; - char path[MAX_PATH]; - BOOL step=FALSE; // initialize to TRUE to enable + int res; + BOOL step=TRUE; // initialize to TRUE to enable BOOL stepdll=FALSE; // initialize to TRUE to enable +#endif +#ifdef LOCKINJECTIONTHREADS + DWORD StartingCode; + LPVOID StartAddress = 0; + DWORD TargetHandle = NULL; +#endif extern char *GetFileNameFromHandle(HANDLE); + bool bContinueDebugging; + char DebugMessage[256+1]; ThInfo = (ThreadInfo_Type *)p; ZeroMemory(&sinfo, sizeof(sinfo)); sinfo.cb = sizeof(sinfo); strcpy_s(path, sizeof(path), ThInfo->TM->path); PathRemoveFileSpec(path); - CreateProcess(NULL, + if(!CreateProcess(NULL, (strlen(ThInfo->PM->launchpath)>0) ? ThInfo->PM->launchpath : ThInfo->TM->path, - 0, 0, false, DEBUG_ONLY_THIS_PROCESS, NULL, path, &sinfo, &pinfo); + 0, 0, false, DEBUG_PROCESS|DEBUG_ONLY_THIS_PROCESS, NULL, path, &sinfo, &pinfo)){ + sprintf(DebugMessage, "CREATE PROCESS error=%d", GetLastError()); + MessageBoxEx(0, DebugMessage, "ERROR", MB_YESNO | MB_ICONQUESTION, NULL); + } + CString strEventMessage; DEBUG_EVENT debug_event ={0}; - bool bContinueDebugging = true; + bContinueDebugging = true; DWORD dwContinueStatus = DBG_CONTINUE; while(bContinueDebugging) { - int res; - char DebugMessage[256+1]; dwContinueStatus = DBG_EXCEPTION_NOT_HANDLED; if (!WaitForDebugEvent(&debug_event, INFINITE)) return TRUE; switch(debug_event.dwDebugEventCode){ case EXIT_PROCESS_DEBUG_EVENT: +#ifdef DXWDEBUGSTEPPING if(step){ // DXW_STRING_STEPPING xpi=(EXIT_PROCESS_DEBUG_INFO *)&debug_event.u; @@ -1662,30 +1729,54 @@ DWORD WINAPI StartDebug(void *p) res=MessageBoxEx(0, DebugMessage, "Continue stepping?", MB_YESNO | MB_ICONQUESTION, NULL); if(res!=IDYES) step=FALSE; } +#endif bContinueDebugging=false; break; case CREATE_PROCESS_DEBUG_EVENT: - // wait for process to stabilize .... - // ref: problems in setting default exception handler in Tomb Raider IV demo - if(ThInfo->TM->flags & HANDLEEXCEPTIONS) { - Sleep(500); - MessageBoxLang(DXW_STRING_EXCEPTION, DXW_STRING_WAIT, MB_OK); - } +#ifdef DXWDEBUGSTEPPING if(step){ pi=(PROCESS_INFORMATION *)&debug_event.u; - sprintf(DebugMessage, "CREATE PROCESS hProcess=%x dwProcessId=%x path=%s", - pi->hProcess, pi->dwProcessId, GetFileNameFromHandle(pi->hProcess)); + sprintf(DebugMessage, "CREATE PROCESS hProcess=%x hThread=%x dwProcessId=%x dwThreadId=%x path=%s", + pi->hProcess, pi->hThread, pi->dwProcessId, pi->dwThreadId, GetFileNameFromHandle(pi->hProcess)); res=MessageBoxEx(0, DebugMessage, "Continue stepping?", MB_YESNO | MB_ICONQUESTION, NULL); if(res!=IDYES) step=FALSE; } +#endif GetFullPathName("dxwnd.dll", MAX_PATH, path, NULL); if(!Inject(pinfo.dwProcessId, path)){ // DXW_STRING_INJECTION sprintf(DebugMessage,"Injection error: pid=%x dll=%s", pinfo.dwProcessId, path); MessageBoxEx(0, DebugMessage, "Injection", MB_ICONEXCLAMATION, NULL); } +#ifdef LOCKINJECTIONTHREADS + extern LPVOID GetThreadStartAddress(HANDLE); + DWORD EndlessLoop; + EndlessLoop=0x9090FEEB; // careful: it's BIG ENDIAN: EB FE 90 90 + SIZE_T BytesCount; + TargetHandle = (DWORD)OpenProcess(PROCESS_QUERY_INFORMATION|PROCESS_VM_OPERATION|PROCESS_VM_READ|PROCESS_VM_WRITE, FALSE, pinfo.dwProcessId); + if(TargetHandle){ + //sprintf(DebugMessage,"OpenProcess returns=%x", TargetHandle); + //MessageBoxEx(0, DebugMessage, "Injection", MB_ICONEXCLAMATION, NULL); + StartAddress = GetThreadStartAddress(pinfo.hThread); + //sprintf(DebugMessage,"GetThreadStartAddress returns=%x", StartAddress); + //MessageBoxEx(0, DebugMessage, "Injection", MB_ICONEXCLAMATION, NULL); + if(StartAddress){ + if(!ReadProcessMemory(pinfo.hProcess, StartAddress, &StartingCode, 4, &BytesCount)){ + sprintf(DebugMessage,"ReadProcessMemory error=%d", GetLastError()); + MessageBoxEx(0, DebugMessage, "Injection", MB_ICONEXCLAMATION, NULL); + } + //sprintf(DebugMessage,"ReadProcessMemory got=%x", StartingCode); + //MessageBoxEx(0, DebugMessage, "Injection", MB_ICONEXCLAMATION, NULL); + if(!WriteProcessMemory(pinfo.hProcess, StartAddress, &EndlessLoop, 4, &BytesCount)){ + sprintf(DebugMessage,"WriteProcessMemory error=%d", GetLastError()); + MessageBoxEx(0, DebugMessage, "Injection", MB_ICONEXCLAMATION, NULL); + } + } + } +#endif break; case CREATE_THREAD_DEBUG_EVENT: +#ifdef DXWDEBUGSTEPPING if(step){ ti=(CREATE_THREAD_DEBUG_INFO *)&debug_event.u; sprintf(DebugMessage, "CREATE THREAD hThread=%x lpThreadLocalBase=%x lpStartAddress=%x", @@ -1693,37 +1784,59 @@ DWORD WINAPI StartDebug(void *p) res=MessageBoxEx(0, DebugMessage, "Continue stepping?", MB_YESNO | MB_ICONQUESTION, NULL); if(res!=IDYES) step=FALSE; } +#endif break; case EXIT_THREAD_DEBUG_EVENT: +#ifdef DXWDEBUGSTEPPING if(step){ xti=(EXIT_THREAD_DEBUG_INFO *)&debug_event.u; sprintf(DebugMessage, "EXIT THREAD RetCode=%x", xti->dwExitCode); res=MessageBoxEx(0, DebugMessage, "Continue stepping?", MB_YESNO | MB_ICONQUESTION, NULL); if(res!=IDYES) step=FALSE; } +#endif +#ifdef LOCKINJECTIONTHREADS + if(TargetHandle && StartAddress){ + //sprintf(DebugMessage,"OpenProcess returns=%x", TargetHandle); + //MessageBoxEx(0, DebugMessage, "Injection", MB_ICONEXCLAMATION, NULL); + if(!WriteProcessMemory(pinfo.hProcess, StartAddress, &StartingCode, 4, &BytesCount)){ + sprintf(DebugMessage,"WriteProcessMemory error=%d", GetLastError()); + MessageBoxEx(0, DebugMessage, "Injection", MB_ICONEXCLAMATION, NULL); + } + //sprintf(DebugMessage,"WriteProcessMemory recovered=%x", StartingCode); + //MessageBoxEx(0, DebugMessage, "Injection", MB_ICONEXCLAMATION, NULL); + CloseHandle((HANDLE)TargetHandle); + } +#endif + bContinueDebugging=false; break; case LOAD_DLL_DEBUG_EVENT: +#ifdef DXWDEBUGSTEPPING if(stepdll){ li=(LOAD_DLL_DEBUG_INFO *)&debug_event.u; sprintf(DebugMessage, "LOAD DLL hFile=%x path=%s", li->hFile, GetFileNameFromHandle(li->hFile)); res=MessageBoxEx(0, DebugMessage, "Continue stepping?", MB_YESNO | MB_ICONQUESTION, NULL); - if(res!=IDYES) stepdll=FALSE; + if(res!=IDYES) stepdll=FALSE; } +#endif break; case UNLOAD_DLL_DEBUG_EVENT: +#ifdef DXWDEBUGSTEPPING if(stepdll){ ui=(UNLOAD_DLL_DEBUG_INFO *)&debug_event.u; sprintf(DebugMessage, "UNLOAD DLL Base=%x", ui->lpBaseOfDll); res=MessageBoxEx(0, DebugMessage, "Continue stepping?", MB_YESNO | MB_ICONQUESTION, NULL); - if(res!=IDYES) stepdll=FALSE; + if(res!=IDYES) stepdll=FALSE; } +#endif break; case OUTPUT_DEBUG_STRING_EVENT: break; case EXCEPTION_DEBUG_EVENT: - ei=(EXCEPTION_DEBUG_INFO *)&debug_event.u; +#ifdef DXWDEBUGSTEPPING if(step){ + ei=(EXCEPTION_DEBUG_INFO *)&debug_event.u; sprintf(DebugMessage, "EXCEPTION code=%x flags=%x addr=%x firstchance=%x", ei->ExceptionRecord.ExceptionCode, ei->ExceptionRecord.ExceptionFlags, @@ -1732,15 +1845,23 @@ DWORD WINAPI StartDebug(void *p) res=MessageBoxEx(0, DebugMessage, "Continue stepping?", MB_YESNO | MB_ICONQUESTION, NULL); if(res!=IDYES) step=FALSE; } - //if(ei->ExceptionRecord.ExceptionCode==EXCEPTION_BREAKPOINT) - // dwContinueStatus = DBG_CONTINUE; // skip initial breakpoint +#endif break; default: break; } - ContinueDebugEvent(debug_event.dwProcessId, - debug_event.dwThreadId, - dwContinueStatus); + if(bContinueDebugging){ + ContinueDebugEvent(debug_event.dwProcessId, + debug_event.dwThreadId, + dwContinueStatus); + } + else{ + DebugSetProcessKillOnExit(FALSE); + ContinueDebugEvent(debug_event.dwProcessId,debug_event.dwThreadId, DBG_CONTINUE); + DebugActiveProcessStop(debug_event.dwProcessId); + if (pinfo.hProcess) CloseHandle(pinfo.hProcess); + if (pinfo.hThread) CloseHandle(pinfo.hThread); + } } return TRUE; } diff --git a/host/host.aps b/host/host.aps deleted file mode 100644 index d9b7ec6369b0bbb0175befe4c7e5260f2d85115c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 47496 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 delta 54 zcmbRChw;Ey#tjhylRxmWY~Cg?!=00p0SYG