From 2cb4f30b50417a45e48616bea324f9837521d975 Mon Sep 17 00:00:00 2001 From: Dror Smolarsky Date: Wed, 5 Jul 2017 09:03:45 -0400 Subject: [PATCH 1/2] Split project into mhook and mhook-test project. The mhook project creates the mhook library, and creates a directory for public headers. The mhook-test project consumes outputs of mhook, and is used for testing only. The purpose of this change is to create an mhook library that can be consumed as a library. It still leaves an option to use mhook code directly. --- mhook-test.sln | 29 -- mhook-test.vcproj | 445 ------------------ .../mhook-test.vcxproj | 68 ++- mhook-test/mhook-test.vcxproj.filters | 30 ++ .../src/mhook-test.cpp | 2 +- stdafx.cpp => mhook-test/src/stdafx.cpp | 0 stdafx.h => mhook-test/src/stdafx.h | 0 mhook.sln | 41 ++ {mhook-lib => mhook/include/mhook}/mhook.h | 0 mhook/mhook.vcxproj | 185 ++++++++ .../mhook.vcxproj.filters | 70 ++- {disasm-lib => mhook/src/disasm-lib}/cpu.c | 0 {disasm-lib => mhook/src/disasm-lib}/cpu.h | 0 {disasm-lib => mhook/src/disasm-lib}/disasm.c | 0 {disasm-lib => mhook/src/disasm-lib}/disasm.h | 0 .../src/disasm-lib}/disasm_x86.c | 0 .../src/disasm-lib}/disasm_x86.h | 0 .../src/disasm-lib}/disasm_x86_tables.h | 0 {disasm-lib => mhook/src/disasm-lib}/misc.c | 0 {disasm-lib => mhook/src/disasm-lib}/misc.h | 0 {mhook-lib => mhook/src/mhook-lib}/mhook.cpp | 2 +- 21 files changed, 323 insertions(+), 549 deletions(-) delete mode 100644 mhook-test.sln delete mode 100644 mhook-test.vcproj rename mhook-test.vcxproj => mhook-test/mhook-test.vcxproj (79%) create mode 100644 mhook-test/mhook-test.vcxproj.filters rename mhook-test.cpp => mhook-test/src/mhook-test.cpp (99%) rename stdafx.cpp => mhook-test/src/stdafx.cpp (100%) rename stdafx.h => mhook-test/src/stdafx.h (100%) create mode 100644 mhook.sln rename {mhook-lib => mhook/include/mhook}/mhook.h (100%) create mode 100644 mhook/mhook.vcxproj rename mhook-test.vcxproj.filters => mhook/mhook.vcxproj.filters (61%) rename {disasm-lib => mhook/src/disasm-lib}/cpu.c (100%) rename {disasm-lib => mhook/src/disasm-lib}/cpu.h (100%) rename {disasm-lib => mhook/src/disasm-lib}/disasm.c (100%) rename {disasm-lib => mhook/src/disasm-lib}/disasm.h (100%) rename {disasm-lib => mhook/src/disasm-lib}/disasm_x86.c (100%) rename {disasm-lib => mhook/src/disasm-lib}/disasm_x86.h (100%) rename {disasm-lib => mhook/src/disasm-lib}/disasm_x86_tables.h (100%) rename {disasm-lib => mhook/src/disasm-lib}/misc.c (100%) rename {disasm-lib => mhook/src/disasm-lib}/misc.h (100%) rename {mhook-lib => mhook/src/mhook-lib}/mhook.cpp (99%) diff --git a/mhook-test.sln b/mhook-test.sln deleted file mode 100644 index 95fbcce..0000000 --- a/mhook-test.sln +++ /dev/null @@ -1,29 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mhook-test", "mhook-test.vcxproj", "{0E055CAF-C68B-42CB-A302-F775CA5A917F}" -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 - {0E055CAF-C68B-42CB-A302-F775CA5A917F}.Debug|Win32.ActiveCfg = Debug|Win32 - {0E055CAF-C68B-42CB-A302-F775CA5A917F}.Debug|Win32.Build.0 = Debug|Win32 - {0E055CAF-C68B-42CB-A302-F775CA5A917F}.Debug|x64.ActiveCfg = Debug|x64 - {0E055CAF-C68B-42CB-A302-F775CA5A917F}.Debug|x64.Build.0 = Debug|x64 - {0E055CAF-C68B-42CB-A302-F775CA5A917F}.Release|Win32.ActiveCfg = Release|Win32 - {0E055CAF-C68B-42CB-A302-F775CA5A917F}.Release|Win32.Build.0 = Release|Win32 - {0E055CAF-C68B-42CB-A302-F775CA5A917F}.Release|x64.ActiveCfg = Release|x64 - {0E055CAF-C68B-42CB-A302-F775CA5A917F}.Release|x64.Build.0 = Release|x64 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(DPCodeReviewSolutionGUID) = preSolution - DPCodeReviewSolutionGUID = {00000000-0000-0000-0000-000000000000} - EndGlobalSection -EndGlobal diff --git a/mhook-test.vcproj b/mhook-test.vcproj deleted file mode 100644 index ecbd5a9..0000000 --- a/mhook-test.vcproj +++ /dev/null @@ -1,445 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/mhook-test.vcxproj b/mhook-test/mhook-test.vcxproj similarity index 79% rename from mhook-test.vcxproj rename to mhook-test/mhook-test.vcxproj index de107dd..533fc2a 100644 --- a/mhook-test.vcxproj +++ b/mhook-test/mhook-test.vcxproj @@ -1,5 +1,5 @@  - + Debug @@ -18,29 +18,41 @@ x64 + + + + + + + {0E055CAF-C68B-42CB-A302-F775CA5A917F} mhooktest Win32Proj + 8.1 Application Unicode true + v140 Application Unicode + v140 Application Unicode true + v140 Application Unicode + v140 @@ -60,16 +72,16 @@ <_ProjectFileVersion>10.0.40219.1 - $(SolutionDir)$(Platform)\$(Configuration)\ + $(SolutionDir)bin\$(PlatformTarget)\$(Configuration)\ $(SolutionDir)$(Platform)\$(Configuration)\ true - $(SolutionDir)$(Platform)\$(Configuration)\ + $(SolutionDir)bin\$(PlatformTarget)\$(Configuration)\ $(SolutionDir)$(Platform)\$(Configuration)\ true - $(SolutionDir)$(Configuration)\ + $(SolutionDir)bin\$(PlatformTarget)\$(Configuration)\ $(Configuration)\ false - $(SolutionDir)$(Platform)\$(Configuration)\ + $(SolutionDir)bin\$(PlatformTarget)\$(Configuration)\ $(Platform)\$(Configuration)\ false AllRules.ruleset @@ -92,15 +104,17 @@ true EnableFastChecks MultiThreadedDebug - - + NotUsing Level3 EditAndContinue + $(SolutionDir)include;%(AdditionalIncludeDirectories) true Console MachineX86 + mhook.lib;%(AdditionalDependencies) + $(SolutionDir)lib\$(PlatformTarget)\$(Configuration)\ @@ -109,13 +123,12 @@ Disabled - %(AdditionalIncludeDirectories) + $(SolutionDir)include;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) true EnableFastChecks MultiThreadedDebug - - + NotUsing Level3 ProgramDatabase @@ -123,15 +136,18 @@ true Console MachineX64 + mhook.lib;%(AdditionalDependencies) + $(SolutionDir)lib\$(PlatformTarget)\$(Configuration)\ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) MultiThreaded - Use + NotUsing Level3 ProgramDatabase + $(SolutionDir)include;%(AdditionalIncludeDirectories) true @@ -139,6 +155,8 @@ true true MachineX86 + mhook.lib;%(AdditionalDependencies) + $(SolutionDir)lib\$(PlatformTarget)\$(Configuration)\ @@ -148,9 +166,10 @@ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) MultiThreaded - Use + NotUsing Level3 ProgramDatabase + $(SolutionDir)include;%(AdditionalIncludeDirectories) true @@ -158,31 +177,10 @@ true true MachineX64 + mhook.lib;%(AdditionalDependencies) + $(SolutionDir)lib\$(PlatformTarget)\$(Configuration)\ - - - - Create - Create - Create - Create - - - - - - - - - - - - - - - - diff --git a/mhook-test/mhook-test.vcxproj.filters b/mhook-test/mhook-test.vcxproj.filters new file mode 100644 index 0000000..89af4d9 --- /dev/null +++ b/mhook-test/mhook-test.vcxproj.filters @@ -0,0 +1,30 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav + + + + + Source Files + + + Source Files + + + + + Header Files + + + \ No newline at end of file diff --git a/mhook-test.cpp b/mhook-test/src/mhook-test.cpp similarity index 99% rename from mhook-test.cpp rename to mhook-test/src/mhook-test.cpp index 79dc767..d0a76b9 100644 --- a/mhook-test.cpp +++ b/mhook-test/src/mhook-test.cpp @@ -19,7 +19,7 @@ //IN THE SOFTWARE. #include "stdafx.h" -#include "mhook-lib/mhook.h" +#include //========================================================================= // Define _NtOpenProcess so we can dynamically bind to the function diff --git a/stdafx.cpp b/mhook-test/src/stdafx.cpp similarity index 100% rename from stdafx.cpp rename to mhook-test/src/stdafx.cpp diff --git a/stdafx.h b/mhook-test/src/stdafx.h similarity index 100% rename from stdafx.h rename to mhook-test/src/stdafx.h diff --git a/mhook.sln b/mhook.sln new file mode 100644 index 0000000..f03c31a --- /dev/null +++ b/mhook.sln @@ -0,0 +1,41 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mhook", "mhook\mhook.vcxproj", "{DFBE49D1-548A-4CF9-B9AC-705642E0CFF7}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mhook-test", "mhook-test\mhook-test.vcxproj", "{0E055CAF-C68B-42CB-A302-F775CA5A917F}" + ProjectSection(ProjectDependencies) = postProject + {DFBE49D1-548A-4CF9-B9AC-705642E0CFF7} = {DFBE49D1-548A-4CF9-B9AC-705642E0CFF7} + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {DFBE49D1-548A-4CF9-B9AC-705642E0CFF7}.Debug|x64.ActiveCfg = Debug|x64 + {DFBE49D1-548A-4CF9-B9AC-705642E0CFF7}.Debug|x64.Build.0 = Debug|x64 + {DFBE49D1-548A-4CF9-B9AC-705642E0CFF7}.Debug|x86.ActiveCfg = Debug|Win32 + {DFBE49D1-548A-4CF9-B9AC-705642E0CFF7}.Debug|x86.Build.0 = Debug|Win32 + {DFBE49D1-548A-4CF9-B9AC-705642E0CFF7}.Release|x64.ActiveCfg = Release|x64 + {DFBE49D1-548A-4CF9-B9AC-705642E0CFF7}.Release|x64.Build.0 = Release|x64 + {DFBE49D1-548A-4CF9-B9AC-705642E0CFF7}.Release|x86.ActiveCfg = Release|Win32 + {DFBE49D1-548A-4CF9-B9AC-705642E0CFF7}.Release|x86.Build.0 = Release|Win32 + {0E055CAF-C68B-42CB-A302-F775CA5A917F}.Debug|x64.ActiveCfg = Debug|x64 + {0E055CAF-C68B-42CB-A302-F775CA5A917F}.Debug|x64.Build.0 = Debug|x64 + {0E055CAF-C68B-42CB-A302-F775CA5A917F}.Debug|x86.ActiveCfg = Debug|Win32 + {0E055CAF-C68B-42CB-A302-F775CA5A917F}.Debug|x86.Build.0 = Debug|Win32 + {0E055CAF-C68B-42CB-A302-F775CA5A917F}.Release|x64.ActiveCfg = Release|x64 + {0E055CAF-C68B-42CB-A302-F775CA5A917F}.Release|x64.Build.0 = Release|x64 + {0E055CAF-C68B-42CB-A302-F775CA5A917F}.Release|x86.ActiveCfg = Release|Win32 + {0E055CAF-C68B-42CB-A302-F775CA5A917F}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/mhook-lib/mhook.h b/mhook/include/mhook/mhook.h similarity index 100% rename from mhook-lib/mhook.h rename to mhook/include/mhook/mhook.h diff --git a/mhook/mhook.vcxproj b/mhook/mhook.vcxproj new file mode 100644 index 0000000..62b967f --- /dev/null +++ b/mhook/mhook.vcxproj @@ -0,0 +1,185 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {DFBE49D1-548A-4CF9-B9AC-705642E0CFF7} + Win32Proj + mhook + 8.1 + + + + StaticLibrary + true + v140 + Unicode + + + StaticLibrary + false + v140 + true + Unicode + + + StaticLibrary + true + v140 + Unicode + + + StaticLibrary + false + v140 + true + Unicode + + + + + + + + + + + + + + + + + + + + + $(SolutionDir)lib\$(PlatformTarget)\$(Configuration)\ + + + $(SolutionDir)lib\$(PlatformTarget)\$(Configuration)\ + + + $(SolutionDir)lib\$(PlatformTarget)\$(Configuration)\ + + + $(SolutionDir)lib\$(PlatformTarget)\$(Configuration)\ + + + + + + Level3 + Disabled + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + MultiThreadedDebug + $(ProjectDir)\include;%(AdditionalIncludeDirectories) + + + Windows + + + mkdir $(SolutionDir)include +xcopy /s /f /y $(ProjectDir)include $(SolutionDir)include + + + + + + + Level3 + Disabled + _DEBUG;_LIB;%(PreprocessorDefinitions) + MultiThreadedDebug + $(ProjectDir)\include;%(AdditionalIncludeDirectories) + + + Windows + + + mkdir $(SolutionDir)include +xcopy /s /f /y $(ProjectDir)include $(SolutionDir)include + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + MultiThreaded + $(ProjectDir)\include;%(AdditionalIncludeDirectories) + + + Windows + true + true + + + mkdir $(SolutionDir)include +xcopy /s /f /y $(ProjectDir)include $(SolutionDir)include + + + + + Level3 + + + MaxSpeed + true + true + NDEBUG;_LIB;%(PreprocessorDefinitions) + MultiThreaded + $(ProjectDir)\include;%(AdditionalIncludeDirectories) + + + Windows + true + true + + + mkdir $(SolutionDir)include +xcopy /s /f /y $(ProjectDir)include $(SolutionDir)include + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mhook-test.vcxproj.filters b/mhook/mhook.vcxproj.filters similarity index 61% rename from mhook-test.vcxproj.filters rename to mhook/mhook.vcxproj.filters index f267eb7..b29d536 100644 --- a/mhook-test.vcxproj.filters +++ b/mhook/mhook.vcxproj.filters @@ -5,65 +5,59 @@ {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - {8cb5c05a-64f7-4f61-a670-df3f82769fe0} - - - {be9cd60b-52fc-47e7-a645-e6545493fed8} - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hpp;hxx;hm;inl;inc;xsd + h;hh;hpp;hxx;hm;inl;inc;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {b9ea35cb-622a-470b-887b-554631d95c1a} + + + {11af8eb6-ab7f-4127-9f8e-a7a02c3ef372} - - Source Files - - - Source Files - - - Source Files\mhook-lib - - - Source Files\disasm-lib - - - Source Files\disasm-lib - - - Source Files\disasm-lib - - - Source Files\disasm-lib - + - - Source Files\mhook-lib - - + Source Files\disasm-lib - + Source Files\disasm-lib - + Source Files\disasm-lib - + Source Files\disasm-lib - + Source Files\disasm-lib - + Header Files + + + Source Files\mhook-lib + + + Source Files\disasm-lib + + + Source Files\disasm-lib + + + Source Files\disasm-lib + + + Source Files\disasm-lib + + \ No newline at end of file diff --git a/disasm-lib/cpu.c b/mhook/src/disasm-lib/cpu.c similarity index 100% rename from disasm-lib/cpu.c rename to mhook/src/disasm-lib/cpu.c diff --git a/disasm-lib/cpu.h b/mhook/src/disasm-lib/cpu.h similarity index 100% rename from disasm-lib/cpu.h rename to mhook/src/disasm-lib/cpu.h diff --git a/disasm-lib/disasm.c b/mhook/src/disasm-lib/disasm.c similarity index 100% rename from disasm-lib/disasm.c rename to mhook/src/disasm-lib/disasm.c diff --git a/disasm-lib/disasm.h b/mhook/src/disasm-lib/disasm.h similarity index 100% rename from disasm-lib/disasm.h rename to mhook/src/disasm-lib/disasm.h diff --git a/disasm-lib/disasm_x86.c b/mhook/src/disasm-lib/disasm_x86.c similarity index 100% rename from disasm-lib/disasm_x86.c rename to mhook/src/disasm-lib/disasm_x86.c diff --git a/disasm-lib/disasm_x86.h b/mhook/src/disasm-lib/disasm_x86.h similarity index 100% rename from disasm-lib/disasm_x86.h rename to mhook/src/disasm-lib/disasm_x86.h diff --git a/disasm-lib/disasm_x86_tables.h b/mhook/src/disasm-lib/disasm_x86_tables.h similarity index 100% rename from disasm-lib/disasm_x86_tables.h rename to mhook/src/disasm-lib/disasm_x86_tables.h diff --git a/disasm-lib/misc.c b/mhook/src/disasm-lib/misc.c similarity index 100% rename from disasm-lib/misc.c rename to mhook/src/disasm-lib/misc.c diff --git a/disasm-lib/misc.h b/mhook/src/disasm-lib/misc.h similarity index 100% rename from disasm-lib/misc.h rename to mhook/src/disasm-lib/misc.h diff --git a/mhook-lib/mhook.cpp b/mhook/src/mhook-lib/mhook.cpp similarity index 99% rename from mhook-lib/mhook.cpp rename to mhook/src/mhook-lib/mhook.cpp index 3380dce..81774ad 100644 --- a/mhook-lib/mhook.cpp +++ b/mhook/src/mhook-lib/mhook.cpp @@ -21,7 +21,7 @@ #include #include #include -#include "mhook.h" +#include "mhook/mhook.h" #include "../disasm-lib/disasm.h" //========================================================================= From 28c274e9eac3b570547f5870a36f029df91175b3 Mon Sep 17 00:00:00 2001 From: Dror Smolarsky Date: Thu, 20 Jul 2017 13:41:23 -0400 Subject: [PATCH 2/2] mhook-lib. Bring in changes from GPUOpen Mhooks. --- mhook/include/mhook/mhook.h | 11 + mhook/src/mhook-lib/mhook.cpp | 1385 ++++++++++++++++----------------- 2 files changed, 699 insertions(+), 697 deletions(-) diff --git a/mhook/include/mhook/mhook.h b/mhook/include/mhook/mhook.h index 1d7cfff..0040534 100644 --- a/mhook/include/mhook/mhook.h +++ b/mhook/include/mhook/mhook.h @@ -26,3 +26,14 @@ BOOL Mhook_SetHook(PVOID *ppSystemFunction, PVOID pHookFunction); BOOL Mhook_Unhook(PVOID *ppHookedFunction); + +// optimisation - when setting many hooks CreateToolHelp32Snapshot to enumerate threads +// can become a bottleneck, allow apps to suspend threads across multiple hooks +// +// note - it's the responsibility of user code to ensure that the threads don't have +// their instruction pointer near any of the hooks - as this would normally be handled +// on a per-hook basis. +// +// these functions are also not thread safe. +void Mhook_SuspendOtherThreads(); +void Mhook_ResumeOtherThreads(); diff --git a/mhook/src/mhook-lib/mhook.cpp b/mhook/src/mhook-lib/mhook.cpp index 81774ad..c14d2c7 100644 --- a/mhook/src/mhook-lib/mhook.cpp +++ b/mhook/src/mhook-lib/mhook.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include "mhook/mhook.h" #include "../disasm-lib/disasm.h" @@ -42,180 +43,146 @@ //========================================================================= #ifndef ODPRINTF -#ifdef _DEBUG +// uncomment this line to enable debug output +//#define DEBUG_PRINT + +#ifdef DEBUG_PRINT #define ODPRINTF(a) odprintf a -#else -#define ODPRINTF(a) -#endif inline void __cdecl odprintf(PCSTR format, ...) { - va_list args; - va_start(args, format); - int len = _vscprintf(format, args); - if (len > 0) { - len += (1 + 2); - PSTR buf = (PSTR) malloc(len); - if (buf) { - len = vsprintf_s(buf, len, format, args); - if (len > 0) { - while (len && isspace(buf[len-1])) len--; - buf[len++] = '\r'; - buf[len++] = '\n'; - buf[len] = 0; - OutputDebugStringA(buf); - } - free(buf); - } - va_end(args); - } + va_list args; + va_start(args, format); + int len = _vscprintf(format, args); + if (len > 0) { + len += (1 + 2); + PSTR buf = (PSTR)malloc(len); + if (buf) { + len = vsprintf_s(buf, len, format, args); + if (len > 0) { + while (len && isspace(buf[len - 1])) len--; + buf[len++] = '\r'; + buf[len++] = '\n'; + buf[len] = 0; + OutputDebugStringA(buf); + } + free(buf); + } + va_end(args); + } } inline void __cdecl odprintf(PCWSTR format, ...) { - va_list args; - va_start(args, format); - int len = _vscwprintf(format, args); - if (len > 0) { - len += (1 + 2); - PWSTR buf = (PWSTR) malloc(sizeof(WCHAR)*len); - if (buf) { - len = vswprintf_s(buf, len, format, args); - if (len > 0) { - while (len && iswspace(buf[len-1])) len--; - buf[len++] = L'\r'; - buf[len++] = L'\n'; - buf[len] = 0; - OutputDebugStringW(buf); - } - free(buf); - } - va_end(args); - } + va_list args; + va_start(args, format); + int len = _vscwprintf(format, args); + if (len > 0) { + len += (1 + 2); + PWSTR buf = (PWSTR)malloc(sizeof(WCHAR)*len); + if (buf) { + len = vswprintf_s(buf, len, format, args); + if (len > 0) { + while (len && iswspace(buf[len - 1])) len--; + buf[len++] = L'\r'; + buf[len++] = L'\n'; + buf[len] = 0; + OutputDebugStringW(buf); + } + free(buf); + } + va_end(args); + } } +#else +#define ODPRINTF(a) +#endif + #endif //#ifndef ODPRINTF //========================================================================= -#define MHOOKS_MAX_CODE_BYTES 32 -#define MHOOKS_MAX_RIPS 4 +#define MHOOKS_MAX_CODE_BYTES 32 +#define MHOOKS_MAX_RIPS 4 +#define MHOOKS_MAX_SUPPORTED_HOOKS 512 //========================================================================= // The trampoline structure - stores every bit of info about a hook struct MHOOKS_TRAMPOLINE { - PBYTE pSystemFunction; // the original system function - DWORD cbOverwrittenCode; // number of bytes overwritten by the jump - PBYTE pHookFunction; // the hook function that we provide - BYTE codeJumpToHookFunction[MHOOKS_MAX_CODE_BYTES]; // placeholder for code that jumps to the hook function - BYTE codeTrampoline[MHOOKS_MAX_CODE_BYTES]; // placeholder for code that holds the first few - // bytes from the system function and a jump to the remainder - // in the original location - BYTE codeUntouched[MHOOKS_MAX_CODE_BYTES]; // placeholder for unmodified original code - // (we patch IP-relative addressing) - MHOOKS_TRAMPOLINE* pPrevTrampoline; // When in the free list, thess are pointers to the prev and next entry. - MHOOKS_TRAMPOLINE* pNextTrampoline; // When not in the free list, this is a pointer to the prev and next trampoline in use. + PBYTE pSystemFunction; // the original system function + DWORD cbOverwrittenCode; // number of bytes overwritten by the jump + PBYTE pHookFunction; // the hook function that we provide + BYTE codeJumpToHookFunction[MHOOKS_MAX_CODE_BYTES]; // placeholder for code that jumps to the hook function + BYTE codeTrampoline[MHOOKS_MAX_CODE_BYTES]; // placeholder for code that holds the first few + // bytes from the system function and a jump to the remainder + // in the original location + BYTE codeUntouched[MHOOKS_MAX_CODE_BYTES]; // placeholder for unmodified original code + // (we patch IP-relative addressing) }; + //========================================================================= // The patch data structures - store info about rip-relative instructions // during hook placement struct MHOOKS_RIPINFO { - DWORD dwOffset; - S64 nDisplacement; + DWORD dwOffset; + S64 nDisplacement; }; struct MHOOKS_PATCHDATA { - S64 nLimitUp; - S64 nLimitDown; - DWORD nRipCnt; - MHOOKS_RIPINFO rips[MHOOKS_MAX_RIPS]; + S64 nLimitUp; + S64 nLimitDown; + DWORD nRipCnt; + MHOOKS_RIPINFO rips[MHOOKS_MAX_RIPS]; }; //========================================================================= // Global vars static BOOL g_bVarsInitialized = FALSE; static CRITICAL_SECTION g_cs; -static MHOOKS_TRAMPOLINE* g_pHooks = NULL; -static MHOOKS_TRAMPOLINE* g_pFreeList = NULL; +static MHOOKS_TRAMPOLINE* g_pHooks[MHOOKS_MAX_SUPPORTED_HOOKS]; static DWORD g_nHooksInUse = 0; +static BOOL g_bThreadsSuspended = FALSE; static HANDLE* g_hThreadHandles = NULL; static DWORD g_nThreadHandles = 0; #define MHOOK_JMPSIZE 5 -#define MHOOK_MINALLOCSIZE 4096 //========================================================================= // Toolhelp defintions so the functions can be dynamically bound to typedef HANDLE (WINAPI * _CreateToolhelp32Snapshot)( - DWORD dwFlags, - DWORD th32ProcessID - ); + DWORD dwFlags, DWORD th32ProcessID); typedef BOOL (WINAPI * _Thread32First)( - HANDLE hSnapshot, - LPTHREADENTRY32 lpte - ); + HANDLE hSnapshot, LPTHREADENTRY32 lpte); typedef BOOL (WINAPI * _Thread32Next)( - HANDLE hSnapshot, - LPTHREADENTRY32 lpte - ); + HANDLE hSnapshot, LPTHREADENTRY32 lpte); //========================================================================= // Bring in the toolhelp functions from kernel32 +#ifdef UNICODE _CreateToolhelp32Snapshot fnCreateToolhelp32Snapshot = (_CreateToolhelp32Snapshot) GetProcAddress(GetModuleHandle(L"kernel32"), "CreateToolhelp32Snapshot"); _Thread32First fnThread32First = (_Thread32First) GetProcAddress(GetModuleHandle(L"kernel32"), "Thread32First"); _Thread32Next fnThread32Next = (_Thread32Next) GetProcAddress(GetModuleHandle(L"kernel32"), "Thread32Next"); - -//========================================================================= -// Internal function: -// -// Remove the trampoline from the specified list, updating the head pointer -// if necessary. -//========================================================================= -static VOID ListRemove(MHOOKS_TRAMPOLINE** pListHead, MHOOKS_TRAMPOLINE* pNode) { - if (pNode->pPrevTrampoline) { - pNode->pPrevTrampoline->pNextTrampoline = pNode->pNextTrampoline; - } - - if (pNode->pNextTrampoline) { - pNode->pNextTrampoline->pPrevTrampoline = pNode->pPrevTrampoline; - } - - if ((*pListHead) == pNode) { - (*pListHead) = pNode->pNextTrampoline; - assert((*pListHead)->pPrevTrampoline == NULL); - } - - pNode->pPrevTrampoline = NULL; - pNode->pNextTrampoline = NULL; -} - -//========================================================================= -// Internal function: -// -// Prepend the trampoline from the specified list and update the head pointer. -//========================================================================= -static VOID ListPrepend(MHOOKS_TRAMPOLINE** pListHead, MHOOKS_TRAMPOLINE* pNode) { - pNode->pPrevTrampoline = NULL; - pNode->pNextTrampoline = (*pListHead); - if ((*pListHead)) { - (*pListHead)->pPrevTrampoline = pNode; - } - (*pListHead) = pNode; -} +#else +_CreateToolhelp32Snapshot fnCreateToolhelp32Snapshot = (_CreateToolhelp32Snapshot) GetProcAddress(GetModuleHandle("kernel32"), "CreateToolhelp32Snapshot"); +_Thread32First fnThread32First = (_Thread32First) GetProcAddress(GetModuleHandle("kernel32"), "Thread32First"); +_Thread32Next fnThread32Next = (_Thread32Next) GetProcAddress(GetModuleHandle("kernel32"), "Thread32Next"); +#endif //========================================================================= static VOID EnterCritSec() { - if (!g_bVarsInitialized) { - InitializeCriticalSection(&g_cs); - g_bVarsInitialized = TRUE; - } - EnterCriticalSection(&g_cs); + if (!g_bVarsInitialized) { + InitializeCriticalSection(&g_cs); + ZeroMemory(g_pHooks, sizeof(g_pHooks)); + g_bVarsInitialized = TRUE; + } + EnterCriticalSection(&g_cs); } //========================================================================= static VOID LeaveCritSec() { - LeaveCriticalSection(&g_cs); + LeaveCriticalSection(&g_cs); } //========================================================================= @@ -225,46 +192,37 @@ static VOID LeaveCritSec() { // jump tables, etc. //========================================================================= static PBYTE SkipJumps(PBYTE pbCode) { - PBYTE pbOrgCode = pbCode; #ifdef _M_IX86_X64 + if (pbCode[0] == 0xff && pbCode[1] == 0x25) { #ifdef _M_IX86 - //mov edi,edi: hot patch point - if (pbCode[0] == 0x8b && pbCode[1] == 0xff) - pbCode += 2; - // push ebp; mov ebp, esp; pop ebp; - // "collapsed" stackframe generated by MSVC - if (pbCode[0] == 0x55 && pbCode[1] == 0x8b && pbCode[2] == 0xec && pbCode[3] == 0x5d) - pbCode += 4; -#endif - if (pbCode[0] == 0xff && pbCode[1] == 0x25) { -#ifdef _M_IX86 - // on x86 we have an absolute pointer... - PBYTE pbTarget = *(PBYTE *)&pbCode[2]; - // ... that shows us an absolute pointer. - return SkipJumps(*(PBYTE *)pbTarget); + // on x86 we have an absolute pointer... + PBYTE pbTarget = *(PBYTE *)&pbCode[2]; + // ... that shows us an absolute pointer. + return SkipJumps(*(PBYTE *)pbTarget); #elif defined _M_X64 - // on x64 we have a 32-bit offset... - INT32 lOffset = *(INT32 *)&pbCode[2]; - // ... that shows us an absolute pointer - return SkipJumps(*(PBYTE*)(pbCode + 6 + lOffset)); - } else if (pbCode[0] == 0x48 && pbCode[1] == 0xff && pbCode[2] == 0x25) { - // or we can have the same with a REX prefix - INT32 lOffset = *(INT32 *)&pbCode[3]; - // ... that shows us an absolute pointer - return SkipJumps(*(PBYTE*)(pbCode + 7 + lOffset)); + // on x64 we have a 32-bit offset... + INT32 lOffset = *(INT32 *)&pbCode[2]; + // ... that shows us an absolute pointer + return SkipJumps(*(PBYTE*)(pbCode + 6 + lOffset)); + // AMD: on 64-bit, reinstate the indirect jump check and skip it if necessary + } else if (pbCode[0] == 0x48 && pbCode[1] == 0xff && pbCode[2] == 0x25) { + // or we can have the same with a REX prefix + INT32 lOffset = *(INT32 *)&pbCode[3]; + // ... that shows us an absolute pointer + return SkipJumps(*(PBYTE*)(pbCode + 7 + lOffset)); #endif - } else if (pbCode[0] == 0xe9) { - // here the behavior is identical, we have... - // ...a 32-bit offset to the destination. - return SkipJumps(pbCode + 5 + *(INT32 *)&pbCode[1]); - } else if (pbCode[0] == 0xeb) { - // and finally an 8-bit offset to the destination - return SkipJumps(pbCode + 2 + *(CHAR *)&pbCode[1]); - } + } else if (pbCode[0] == 0xe9) { + // here the behavior is identical, we have... + // ...a 32-bit offset to the destination. + return SkipJumps(pbCode + 5 + *(INT32 *)&pbCode[1]); + } else if (pbCode[0] == 0xeb) { + // and finally an 8-bit offset to the destination + return SkipJumps(pbCode + 2 + *(CHAR *)&pbCode[1]); + } #else #error unsupported platform #endif - return pbOrgCode; + return pbCode; } //========================================================================= @@ -276,122 +234,33 @@ static PBYTE SkipJumps(PBYTE pbCode) { //========================================================================= static PBYTE EmitJump(PBYTE pbCode, PBYTE pbJumpTo) { #ifdef _M_IX86_X64 - PBYTE pbJumpFrom = pbCode + 5; - SIZE_T cbDiff = pbJumpFrom > pbJumpTo ? pbJumpFrom - pbJumpTo : pbJumpTo - pbJumpFrom; - ODPRINTF((L"mhooks: EmitJump: Jumping from %p to %p, diff is %p", pbJumpFrom, pbJumpTo, cbDiff)); - if (cbDiff <= 0x7fff0000) { - pbCode[0] = 0xe9; - pbCode += 1; - *((PDWORD)pbCode) = (DWORD)(DWORD_PTR)(pbJumpTo - pbJumpFrom); - pbCode += sizeof(DWORD); - } else { - pbCode[0] = 0xff; - pbCode[1] = 0x25; - pbCode += 2; + PBYTE pbJumpFrom = pbCode + 5; + SIZE_T cbDiff = pbJumpFrom > pbJumpTo ? pbJumpFrom - pbJumpTo : pbJumpTo - pbJumpFrom; + ODPRINTF((L"mhooks: EmitJump: Jumping from %p to %p, diff is %p", pbJumpFrom, pbJumpTo, cbDiff)); + if (cbDiff <= 0x7fff0000) { + pbCode[0] = 0xe9; + pbCode += 1; + *((PDWORD)pbCode) = (DWORD)(DWORD_PTR)(pbJumpTo - pbJumpFrom); + pbCode += sizeof(DWORD); + } else { + pbCode[0] = 0xff; + pbCode[1] = 0x25; + pbCode += 2; #ifdef _M_IX86 - // on x86 we write an absolute address (just behind the instruction) - *((PDWORD)pbCode) = (DWORD)(DWORD_PTR)(pbCode + sizeof(DWORD)); + // on x86 we write an absolute address (just behind the instruction) + *((PDWORD)pbCode) = (DWORD)(DWORD_PTR)(pbCode + sizeof(DWORD)); #elif defined _M_X64 - // on x64 we write the relative address of the same location - *((PDWORD)pbCode) = (DWORD)0; + // on x64 we write the relative address of the same location + *((PDWORD)pbCode) = (DWORD)0; #endif - pbCode += sizeof(DWORD); - *((PDWORD_PTR)pbCode) = (DWORD_PTR)(pbJumpTo); - pbCode += sizeof(DWORD_PTR); - } + pbCode += sizeof(DWORD); + *((PDWORD_PTR)pbCode) = (DWORD_PTR)(pbJumpTo); + pbCode += sizeof(DWORD_PTR); + } #else #error unsupported platform #endif - return pbCode; -} - - -//========================================================================= -// Internal function: -// -// Round down to the next multiple of rndDown -//========================================================================= -static size_t RoundDown(size_t addr, size_t rndDown) -{ - return (addr / rndDown) * rndDown; -} - -//========================================================================= -// Internal function: -// -// Will attempt allocate a block of memory within the specified range, as -// near as possible to the specified function. -//========================================================================= -static MHOOKS_TRAMPOLINE* BlockAlloc(PBYTE pSystemFunction, PBYTE pbLower, PBYTE pbUpper) { - SYSTEM_INFO sSysInfo = {0}; - ::GetSystemInfo(&sSysInfo); - - // Always allocate in bulk, in case the system actually has a smaller allocation granularity than MINALLOCSIZE. - const ptrdiff_t cAllocSize = max(sSysInfo.dwAllocationGranularity, MHOOK_MINALLOCSIZE); - - MHOOKS_TRAMPOLINE* pRetVal = NULL; - PBYTE pModuleGuess = (PBYTE) RoundDown((size_t)pSystemFunction, cAllocSize); - int loopCount = 0; - for (PBYTE pbAlloc = pModuleGuess; pbLower < pbAlloc && pbAlloc < pbUpper; ++loopCount) { - // determine current state - MEMORY_BASIC_INFORMATION mbi; - ODPRINTF((L"mhooks: BlockAlloc: Looking at address %p", pbAlloc)); - if (!VirtualQuery(pbAlloc, &mbi, sizeof(mbi))) - break; - // free & large enough? - if (mbi.State == MEM_FREE && mbi.RegionSize >= (unsigned)cAllocSize) { - // and then try to allocate it - pRetVal = (MHOOKS_TRAMPOLINE*) VirtualAlloc(pbAlloc, cAllocSize, MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE); - if (pRetVal) { - size_t trampolineCount = cAllocSize / sizeof(MHOOKS_TRAMPOLINE); - ODPRINTF((L"mhooks: BlockAlloc: Allocated block at %p as %d trampolines", pRetVal, trampolineCount)); - - pRetVal[0].pPrevTrampoline = NULL; - pRetVal[0].pNextTrampoline = &pRetVal[1]; - - // prepare them by having them point down the line at the next entry. - for (size_t s = 1; s < trampolineCount; ++s) { - pRetVal[s].pPrevTrampoline = &pRetVal[s - 1]; - pRetVal[s].pNextTrampoline = &pRetVal[s + 1]; - } - - // last entry points to the current head of the free list - pRetVal[trampolineCount - 1].pNextTrampoline = g_pFreeList; - break; - } - } - - // This is a spiral, should be -1, 1, -2, 2, -3, 3, etc. (* cAllocSize) - ptrdiff_t bytesToOffset = (cAllocSize * (loopCount + 1) * ((loopCount % 2 == 0) ? -1 : 1)); - pbAlloc = pbAlloc + bytesToOffset; - } - - return pRetVal; -} - -//========================================================================= -// Internal function: -// -// Will try to allocate a big block of memory inside the required range. -//========================================================================= -static MHOOKS_TRAMPOLINE* FindTrampolineInRange(PBYTE pLower, PBYTE pUpper) { - if (!g_pFreeList) { - return NULL; - } - - // This is a standard free list, except we're doubly linked to deal with soem return shenanigans. - MHOOKS_TRAMPOLINE* curEntry = g_pFreeList; - while (curEntry) { - if ((MHOOKS_TRAMPOLINE*) pLower < curEntry && curEntry < (MHOOKS_TRAMPOLINE*) pUpper) { - ListRemove(&g_pFreeList, curEntry); - - return curEntry; - } - - curEntry = curEntry->pNextTrampoline; - } - - return NULL; + return pbCode; } //========================================================================= @@ -402,34 +271,61 @@ static MHOOKS_TRAMPOLINE* FindTrampolineInRange(PBYTE pLower, PBYTE pUpper) { //========================================================================= static MHOOKS_TRAMPOLINE* TrampolineAlloc(PBYTE pSystemFunction, S64 nLimitUp, S64 nLimitDown) { - MHOOKS_TRAMPOLINE* pTrampoline = NULL; - - // determine lower and upper bounds for the allocation locations. - // in the basic scenario this is +/- 2GB but IP-relative instructions - // found in the original code may require a smaller window. - PBYTE pLower = pSystemFunction + nLimitUp; - pLower = pLower < (PBYTE)(DWORD_PTR)0x0000000080000000 ? - (PBYTE)(0x1) : (PBYTE)(pLower - (PBYTE)0x7fff0000); - PBYTE pUpper = pSystemFunction + nLimitDown; - pUpper = pUpper < (PBYTE)(DWORD_PTR)0xffffffff80000000 ? - (PBYTE)(pUpper + (DWORD_PTR)0x7ff80000) : (PBYTE)(DWORD_PTR)0xfffffffffff80000; - ODPRINTF((L"mhooks: TrampolineAlloc: Allocating for %p between %p and %p", pSystemFunction, pLower, pUpper)); - - // try to find a trampoline in the specified range - pTrampoline = FindTrampolineInRange(pLower, pUpper); - if (!pTrampoline) { - // if it we can't find it, then we need to allocate a new block and - // try again. Just fail if that doesn't work - g_pFreeList = BlockAlloc(pSystemFunction, pLower, pUpper); - pTrampoline = FindTrampolineInRange(pLower, pUpper); - } - - // found and allocated a trampoline? - if (pTrampoline) { - ListPrepend(&g_pHooks, pTrampoline); - } - - return pTrampoline; + MHOOKS_TRAMPOLINE* pTrampoline = NULL; + + // do we have room to store this guy? + if (g_nHooksInUse < MHOOKS_MAX_SUPPORTED_HOOKS) { + + // determine lower and upper bounds for the allocation locations. + // in the basic scenario this is +/- 2GB but IP-relative instructions + // found in the original code may require a smaller window. + PBYTE pLower = pSystemFunction + nLimitUp; + pLower = pLower < (PBYTE)(DWORD_PTR)0x0000000080000000 ? + (PBYTE)(0x1) : (PBYTE)(pLower - (PBYTE)0x7fff0000); + PBYTE pUpper = pSystemFunction + nLimitDown; + pUpper = pUpper < (PBYTE)(DWORD_PTR)0xffffffff80000000 ? + (PBYTE)(pUpper + (DWORD_PTR)0x7ff80000) : (PBYTE)(DWORD_PTR)0xfffffffffff80000; + ODPRINTF((L"mhooks: TrampolineAlloc: Allocating for %p between %p and %p", pSystemFunction, pLower, pUpper)); + + SYSTEM_INFO sSysInfo = {0}; + ::GetSystemInfo(&sSysInfo); + + // go through the available memory blocks and try to allocate a chunk for us + for (PBYTE pbAlloc = pLower; pbAlloc < pUpper;) { + // determine current state + MEMORY_BASIC_INFORMATION mbi; + ODPRINTF((L"mhooks: TrampolineAlloc: Looking at address %p", pbAlloc)); + if (!VirtualQuery(pbAlloc, &mbi, sizeof(mbi))) + break; + // free & large enough? + if (mbi.State == MEM_FREE && mbi.RegionSize >= sizeof(MHOOKS_TRAMPOLINE) && mbi.RegionSize >= sSysInfo.dwAllocationGranularity) { + // yes, align the pointer to the 64K boundary first + pbAlloc = (PBYTE)(ULONG_PTR((ULONG_PTR(pbAlloc) + (sSysInfo.dwAllocationGranularity-1)) / sSysInfo.dwAllocationGranularity) * sSysInfo.dwAllocationGranularity); + // and then try to allocate it + pTrampoline = (MHOOKS_TRAMPOLINE*)VirtualAlloc(pbAlloc, sizeof(MHOOKS_TRAMPOLINE), MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READ); + if (pTrampoline) { + ODPRINTF((L"mhooks: TrampolineAlloc: Allocated block at %p as the trampoline", pTrampoline)); + break; + } + } + // continue the search + pbAlloc = (PBYTE)mbi.BaseAddress + mbi.RegionSize; + } + + // found and allocated a trampoline? + if (pTrampoline) { + // put it into our list so we know we'll have to free it + for (DWORD i=0; ipHookFunction == pHookedFunction) { - return pCurrent; - } - - pCurrent = pCurrent->pNextTrampoline; - } - - return NULL; + for (DWORD i=0; icodeTrampoline == pHookedFunction) + return g_pHooks[i]; + } + } + return NULL; } //========================================================================= @@ -457,17 +349,20 @@ static MHOOKS_TRAMPOLINE* TrampolineGet(PBYTE pHookedFunction) { // Free a trampoline structure. //========================================================================= static VOID TrampolineFree(MHOOKS_TRAMPOLINE* pTrampoline, BOOL bNeverUsed) { - ListRemove(&g_pHooks, pTrampoline); - - // If a thread could feasinbly have some of our trampoline code - // on its stack and we yank the region from underneath it then it will - // surely crash upon returning. So instead of freeing the - // memory we just let it leak. Ugly, but safe. - if (bNeverUsed) { - ListPrepend(&g_pFreeList, pTrampoline); - } - - g_nHooksInUse--; + for (DWORD i=0; i= pbCode && pIp < (pbCode + cbBytes)) { - if (nTries < 3) { - // oops - we should try to get the instruction pointer out of here. - ODPRINTF((L"mhooks: SuspendOneThread: suspended thread %d - IP is at %p - IS COLLIDING WITH CODE", dwThreadId, pIp)); - ResumeThread(hThread); - Sleep(100); - SuspendThread(hThread); - nTries++; - } else { - // we gave it all we could. (this will probably never - // happen - unless the thread has already been suspended - // to begin with) - ODPRINTF((L"mhooks: SuspendOneThread: suspended thread %d - IP is at %p - IS COLLIDING WITH CODE - CAN'T FIX", dwThreadId, pIp)); - ResumeThread(hThread); - CloseHandle(hThread); - hThread = NULL; - break; - } - } else { - // success, the IP is not conflicting - ODPRINTF((L"mhooks: SuspendOneThread: Successfully suspended thread %d - IP is at %p", dwThreadId, pIp)); - break; - } - } - } else { - // couldn't suspend - CloseHandle(hThread); - hThread = NULL; - } - } - return hThread; + if (pIp >= pbCode && pIp < (pbCode + cbBytes)) { + if (nTries < 3) { + // oops - we should try to get the instruction pointer out of here. + ODPRINTF((L"mhooks: SuspendOneThread: suspended thread %d - IP is at %p - IS COLLIDING WITH CODE", dwThreadId, pIp)); + ResumeThread(hThread); + Sleep(100); + SuspendThread(hThread); + nTries++; + } else { + // we gave it all we could. (this will probably never + // happen - unless the thread has already been suspended + // to begin with) + ODPRINTF((L"mhooks: SuspendOneThread: suspended thread %d - IP is at %p - IS COLLIDING WITH CODE - CAN'T FIX", dwThreadId, pIp)); + ResumeThread(hThread); + CloseHandle(hThread); + hThread = NULL; + break; + } + } else { + // success, the IP is not conflicting + ODPRINTF((L"mhooks: SuspendOneThread: Successfully suspended thread %d - IP is at %p", dwThreadId, pIp)); + break; + } + } + } else { + // couldn't suspend + CloseHandle(hThread); + hThread = NULL; + } + } + return hThread; } //========================================================================= @@ -532,20 +427,22 @@ static HANDLE SuspendOneThread(DWORD dwThreadId, PBYTE pbCode, DWORD cbBytes) { // Resumes all previously suspended threads in the current process. //========================================================================= static VOID ResumeOtherThreads() { - // make sure things go as fast as possible - INT nOriginalPriority = GetThreadPriority(GetCurrentThread()); - SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); - // go through our list - for (DWORD i=0; inRipCnt; i++) { - DWORD dwNewDisplacement = (DWORD)(pdata->rips[i].nDisplacement - diff); - ODPRINTF((L"mhooks: fixing up RIP instruction operand for code at 0x%p: " - L"old displacement: 0x%8.8x, new displacement: 0x%8.8x", - pbNew + pdata->rips[i].dwOffset, - (DWORD)pdata->rips[i].nDisplacement, - dwNewDisplacement)); - *(PDWORD)(pbNew + pdata->rips[i].dwOffset) = dwNewDisplacement; - } -#endif + S64 diff = pbNew - pbOriginal; + for (DWORD i = 0; i < pdata->nRipCnt; i++) { + DWORD dwNewDisplacement = (DWORD)(pdata->rips[i].nDisplacement - diff); + ODPRINTF((L"mhooks: fixing up RIP instruction operand for code at 0x%p: " + L"old displacement: 0x%8.8x, new displacement: 0x%8.8x", + pbNew + pdata->rips[i].dwOffset, + (DWORD)pdata->rips[i].nDisplacement, + dwNewDisplacement)); + *(PDWORD)(pbNew + pdata->rips[i].dwOffset) = dwNewDisplacement; + } } //========================================================================= @@ -657,262 +555,355 @@ static void FixupIPRelativeAddressing(PBYTE pbNew, PBYTE pbOriginal, MHOOKS_PATC // Finally, detect and collect information on IP-relative instructions // that we can patch. static DWORD DisassembleAndSkip(PVOID pFunction, DWORD dwMinLen, MHOOKS_PATCHDATA* pdata) { - DWORD dwRet = 0; - pdata->nLimitDown = 0; - pdata->nLimitUp = 0; - pdata->nRipCnt = 0; + DWORD dwRet = 0; + pdata->nLimitDown = 0; + pdata->nLimitUp = 0; + pdata->nRipCnt = 0; #ifdef _M_IX86 - ARCHITECTURE_TYPE arch = ARCH_X86; + ARCHITECTURE_TYPE arch = ARCH_X86; #elif defined _M_X64 - ARCHITECTURE_TYPE arch = ARCH_X64; + ARCHITECTURE_TYPE arch = ARCH_X64; #else - #error unsupported platform + #error unsupported platform +#endif + DISASSEMBLER dis; + if (InitDisassembler(&dis, arch)) { + INSTRUCTION* pins = NULL; + U8* pLoc = (U8*)pFunction; + DWORD dwFlags = DISASM_DECODE | DISASM_DISASSEMBLE | DISASM_ALIGNOUTPUT; +#ifndef _DEBUG + // suppress errors in release build + dwFlags |= DISASM_SUPPRESSERRORS; +#endif + + ODPRINTF((L"mhooks: DisassembleAndSkip: Disassembling %p", pLoc)); + while ( (dwRet < dwMinLen) && ((pins = GetInstruction(&dis, (ULONG_PTR)pLoc, pLoc, dwFlags)) != NULL ) ) + { + ODPRINTF(("mhooks: DisassembleAndSkip: %p: %s", pLoc, pins->String)); + BOOL bProcessRip = FALSE; + DWORD displacementOffset = 3; // number of bytes from the start of the instruction where offset is located + + if (pins->Type == ITYPE_RET) + break; + else if (pins->Type == ITYPE_BRANCH) + { + if (pLoc[0] == 0xe9) + { + if (pins->OperandCount == 1 && (pins->Operands[0].Flags & OP_IPREL) && pins->X86.Relative && (pins->X86.OperandSize == 4 || pins->X86.OperandSize == 8)) + { + // jmp [rip + ilen + offset] (0xE9, [offset]) + displacementOffset = 1; + bProcessRip = TRUE; + } + } +#if defined _M_X64 + else if (pLoc[0] == 0x65 && pLoc[1] == 0xff && pLoc[2] == 0x24 && pLoc[3] == 0x25) + { + // jmp qword_ptr gs:[offset] (0x65, 0xFF, 0x24, 0x25, [offset]) + // Nothing to do here - no relocation needed; offset is relative to gs + } #endif - DISASSEMBLER dis; - if (InitDisassembler(&dis, arch)) { - INSTRUCTION* pins = NULL; - U8* pLoc = (U8*)pFunction; - DWORD dwFlags = DISASM_DECODE | DISASM_DISASSEMBLE | DISASM_ALIGNOUTPUT; - - ODPRINTF((L"mhooks: DisassembleAndSkip: Disassembling %p", pLoc)); - while ( (dwRet < dwMinLen) && (pins = GetInstruction(&dis, (ULONG_PTR)pLoc, pLoc, dwFlags)) ) { - ODPRINTF(("mhooks: DisassembleAndSkip: %p:(0x%2.2x) %s", pLoc, pins->Length, pins->String)); - if (pins->Type == ITYPE_RET ) break; - if (pins->Type == ITYPE_BRANCH ) break; - if (pins->Type == ITYPE_BRANCHCC) break; - if (pins->Type == ITYPE_CALL ) break; - if (pins->Type == ITYPE_CALLCC ) break; - - #if defined _M_X64 - BOOL bProcessRip = FALSE; - // mov or lea to register from rip+imm32 - if ((pins->Type == ITYPE_MOV || pins->Type == ITYPE_LEA) && (pins->X86.Relative) && - (pins->X86.OperandSize == 8) && (pins->OperandCount == 2) && - (pins->Operands[1].Flags & OP_IPREL) && (pins->Operands[1].Register == AMD64_REG_RIP)) - { - // rip-addressing "mov reg, [rip+imm32]" - ODPRINTF((L"mhooks: DisassembleAndSkip: found OP_IPREL on operand %d with displacement 0x%x (in memory: 0x%x)", 1, pins->X86.Displacement, *(PDWORD)(pLoc+3))); - bProcessRip = TRUE; - } - // mov or lea to rip+imm32 from register - else if ((pins->Type == ITYPE_MOV || pins->Type == ITYPE_LEA) && (pins->X86.Relative) && - (pins->X86.OperandSize == 8) && (pins->OperandCount == 2) && - (pins->Operands[0].Flags & OP_IPREL) && (pins->Operands[0].Register == AMD64_REG_RIP)) - { - // rip-addressing "mov [rip+imm32], reg" - ODPRINTF((L"mhooks: DisassembleAndSkip: found OP_IPREL on operand %d with displacement 0x%x (in memory: 0x%x)", 0, pins->X86.Displacement, *(PDWORD)(pLoc+3))); - bProcessRip = TRUE; - } - else if ( (pins->OperandCount >= 1) && (pins->Operands[0].Flags & OP_IPREL) ) - { - // unsupported rip-addressing - ODPRINTF((L"mhooks: DisassembleAndSkip: found unsupported OP_IPREL on operand %d", 0)); - // dump instruction bytes to the debug output - for (DWORD i=0; iLength; i++) { - ODPRINTF((L"mhooks: DisassembleAndSkip: instr byte %2.2d: 0x%2.2x", i, pLoc[i])); - } - break; - } - else if ( (pins->OperandCount >= 2) && (pins->Operands[1].Flags & OP_IPREL) ) - { - // unsupported rip-addressing - ODPRINTF((L"mhooks: DisassembleAndSkip: found unsupported OP_IPREL on operand %d", 1)); - // dump instruction bytes to the debug output - for (DWORD i=0; iLength; i++) { - ODPRINTF((L"mhooks: DisassembleAndSkip: instr byte %2.2d: 0x%2.2x", i, pLoc[i])); - } - break; - } - else if ( (pins->OperandCount >= 3) && (pins->Operands[2].Flags & OP_IPREL) ) - { - // unsupported rip-addressing - ODPRINTF((L"mhooks: DisassembleAndSkip: found unsupported OP_IPREL on operand %d", 2)); - // dump instruction bytes to the debug output - for (DWORD i=0; iLength; i++) { - ODPRINTF((L"mhooks: DisassembleAndSkip: instr byte %2.2d: 0x%2.2x", i, pLoc[i])); - } - break; - } - // follow through with RIP-processing if needed - if (bProcessRip) { - // calculate displacement relative to function start - S64 nAdjustedDisplacement = pins->X86.Displacement + (pLoc - (U8*)pFunction); - // store displacement values furthest from zero (both positive and negative) - if (nAdjustedDisplacement < pdata->nLimitDown) - pdata->nLimitDown = nAdjustedDisplacement; - if (nAdjustedDisplacement > pdata->nLimitUp) - pdata->nLimitUp = nAdjustedDisplacement; - // store patch info - if (pdata->nRipCnt < MHOOKS_MAX_RIPS) { - pdata->rips[pdata->nRipCnt].dwOffset = dwRet + 3; - pdata->rips[pdata->nRipCnt].nDisplacement = pins->X86.Displacement; - pdata->nRipCnt++; - } else { - // no room for patch info, stop disassembly - break; - } - } - #endif - - dwRet += pins->Length; - pLoc += pins->Length; - } - - CloseDisassembler(&dis); - } - - return dwRet; + else + { + break; + } + } + else if (pins->Type == ITYPE_BRANCHCC) + break; + else if (pins->Type == ITYPE_CALL) + { + if (pLoc[0] == 0xe8) + { + if (pins->OperandCount == 1 && (pins->Operands[0].Flags & OP_IPREL) && pins->X86.Relative && (pins->X86.OperandSize == 4 || pins->X86.OperandSize == 8)) + { + // call [rip + ilen + offset] (0xE8, [offset]) + displacementOffset = 1; + bProcessRip = TRUE; + } + } + else if (pLoc[0] == 0xff && pLoc[1] == 0x15) + { + if (pins->OperandCount == 1 && (pins->Operands[0].Flags & OP_IPREL) && pins->X86.Relative && (pins->X86.OperandSize == 4 || pins->X86.OperandSize == 8)) + { + // call dword_ptr [offset] (0xff, 0x15, [offset]) + displacementOffset = 2; + bProcessRip = TRUE; + } + } + else + { + break; + } + } + else if (pins->Type == ITYPE_CALLCC ) + break; + + #if defined _M_X64 + // mov or lea to register from rip+imm32 + else if ((pins->Type == ITYPE_MOV || pins->Type == ITYPE_LEA) && (pins->X86.Relative) && + (pins->X86.OperandSize == 8) && (pins->OperandCount == 2) && + (pins->Operands[1].Flags & OP_IPREL) && (pins->Operands[1].Register == AMD64_REG_RIP)) + { + // rip-addressing "mov reg, [rip+imm32]" + ODPRINTF((L"mhooks: DisassembleAndSkip: found OP_IPREL on operand %d with displacement 0x%x (in memory: 0x%x)", 1, pins->X86.Displacement, *(PDWORD)(pLoc+3))); + bProcessRip = TRUE; + } + // mov or lea to register from eip+imm32 + else if ((pins->Type == ITYPE_MOV || pins->Type == ITYPE_LEA) && (pins->X86.Relative) && + (pins->X86.OperandSize == 4) && (pins->OperandCount == 2) && + (pins->Operands[1].Flags & OP_IPREL) && (pins->Operands[1].Register == X86_REG_EIP)) + { + // rip-addressing "mov reg, [rip+imm32]" + ODPRINTF((L"mhooks: DisassembleAndSkip: found OP_IPREL on operand %d with displacement 0x%x (in memory: 0x%x)", 1, pins->X86.Displacement, *(PDWORD)(pLoc+2))); + displacementOffset = 2; + bProcessRip = TRUE; + } + // mov or lea to rip+imm32 from register + else if ((pins->Type == ITYPE_MOV || pins->Type == ITYPE_LEA) && (pins->X86.Relative) && + (pins->X86.OperandSize == 8) && (pins->OperandCount == 2) && + (pins->Operands[0].Flags & OP_IPREL) && (pins->Operands[0].Register == AMD64_REG_RIP)) + { + // rip-addressing "mov [rip+imm32], reg" + ODPRINTF((L"mhooks: DisassembleAndSkip: found OP_IPREL on operand %d with displacement 0x%x (in memory: 0x%x)", 0, pins->X86.Displacement, *(PDWORD)(pLoc+3))); + bProcessRip = TRUE; + } + // cmp from eip+imm32, value + else if (pins->Type == ITYPE_CMP && (pins->X86.Relative) && + (pins->X86.OperandSize == 4) && (pins->OperandCount == 2) && + (pins->Operands[0].Flags & OP_IPREL) && (pins->Operands[0].Register == X86_REG_EIP)) + { + // rip-addressing "mov reg, [rip+imm32]" + ODPRINTF((L"mhooks: DisassembleAndSkip: found OP_IPREL on operand %d with displacement 0x%x (in memory: 0x%x)", 1, pins->X86.Displacement, *(PDWORD)(pLoc + 2))); + displacementOffset = 2; + bProcessRip = TRUE; + } + else if ((pins->OperandCount >= 1) && (pins->Operands[0].Flags & OP_IPREL)) + { + // unsupported rip-addressing + ODPRINTF((L"mhooks: DisassembleAndSkip: found unsupported OP_IPREL on operand %d", 0)); + // dump instruction bytes to the debug output + for (DWORD i=0; iLength; i++) { + ODPRINTF((L"mhooks: DisassembleAndSkip: instr byte %2.2d: 0x%2.2x", i, pLoc[i])); + } + break; + } + else if ( (pins->OperandCount >= 2) && (pins->Operands[1].Flags & OP_IPREL) ) + { + // unsupported rip-addressing + ODPRINTF((L"mhooks: DisassembleAndSkip: found unsupported OP_IPREL on operand %d", 1)); + // dump instruction bytes to the debug output + for (DWORD i=0; iLength; i++) { + ODPRINTF((L"mhooks: DisassembleAndSkip: instr byte %2.2d: 0x%2.2x", i, pLoc[i])); + } + break; + } + else if ( (pins->OperandCount >= 3) && (pins->Operands[2].Flags & OP_IPREL) ) + { + // unsupported rip-addressing + ODPRINTF((L"mhooks: DisassembleAndSkip: found unsupported OP_IPREL on operand %d", 2)); + // dump instruction bytes to the debug output + for (DWORD i=0; iLength; i++) { + ODPRINTF((L"mhooks: DisassembleAndSkip: instr byte %2.2d: 0x%2.2x", i, pLoc[i])); + } + break; + } + #endif + // follow through with RIP-processing if needed + if (bProcessRip) { + // calculate displacement relative to function start + S64 nAdjustedDisplacement = pins->X86.Displacement + (pLoc - (U8*)pFunction); + // store displacement values furthest from zero (both positive and negative) + if (nAdjustedDisplacement < pdata->nLimitDown) + pdata->nLimitDown = nAdjustedDisplacement; + if (nAdjustedDisplacement > pdata->nLimitUp) + pdata->nLimitUp = nAdjustedDisplacement; + // store patch info + if (pdata->nRipCnt < MHOOKS_MAX_RIPS) { + pdata->rips[pdata->nRipCnt].dwOffset = dwRet + displacementOffset; + pdata->rips[pdata->nRipCnt].nDisplacement = pins->X86.Displacement; + pdata->nRipCnt++; + } else { + // no room for patch info, stop disassembly + break; + } + } + + dwRet += pins->Length; + pLoc += pins->Length; + } + + CloseDisassembler(&dis); + } + + return dwRet; +} + +//========================================================================= +void Mhook_SuspendOtherThreads() { + SuspendOtherThreads(NULL, 0); + g_bThreadsSuspended = TRUE; +} + +//========================================================================= +void Mhook_ResumeOtherThreads() { + ResumeOtherThreads(); + g_bThreadsSuspended = FALSE; } //========================================================================= BOOL Mhook_SetHook(PVOID *ppSystemFunction, PVOID pHookFunction) { - MHOOKS_TRAMPOLINE* pTrampoline = NULL; - PVOID pSystemFunction = *ppSystemFunction; - // ensure thread-safety - EnterCritSec(); - ODPRINTF((L"mhooks: Mhook_SetHook: Started on the job: %p / %p", pSystemFunction, pHookFunction)); - // find the real functions (jump over jump tables, if any) - pSystemFunction = SkipJumps((PBYTE)pSystemFunction); - pHookFunction = SkipJumps((PBYTE)pHookFunction); - ODPRINTF((L"mhooks: Mhook_SetHook: Started on the job: %p / %p", pSystemFunction, pHookFunction)); - // figure out the length of the overwrite zone - MHOOKS_PATCHDATA patchdata = {0}; - DWORD dwInstructionLength = DisassembleAndSkip(pSystemFunction, MHOOK_JMPSIZE, &patchdata); - if (dwInstructionLength >= MHOOK_JMPSIZE) { - ODPRINTF((L"mhooks: Mhook_SetHook: disassembly signals %d bytes", dwInstructionLength)); - // suspend every other thread in this process, and make sure their IP - // is not in the code we're about to overwrite. - SuspendOtherThreads((PBYTE)pSystemFunction, dwInstructionLength); - // allocate a trampoline structure (TODO: it is pretty wasteful to get - // VirtualAlloc to grab chunks of memory smaller than 100 bytes) - pTrampoline = TrampolineAlloc((PBYTE)pSystemFunction, patchdata.nLimitUp, patchdata.nLimitDown); - if (pTrampoline) { - ODPRINTF((L"mhooks: Mhook_SetHook: allocated structure at %p", pTrampoline)); - DWORD dwOldProtectSystemFunction = 0; - DWORD dwOldProtectTrampolineFunction = 0; - // set the system function to PAGE_EXECUTE_READWRITE - if (VirtualProtect(pSystemFunction, dwInstructionLength, PAGE_EXECUTE_READWRITE, &dwOldProtectSystemFunction)) { - ODPRINTF((L"mhooks: Mhook_SetHook: readwrite set on system function")); - // mark our trampoline buffer to PAGE_EXECUTE_READWRITE - if (VirtualProtect(pTrampoline, sizeof(MHOOKS_TRAMPOLINE), PAGE_EXECUTE_READWRITE, &dwOldProtectTrampolineFunction)) { - ODPRINTF((L"mhooks: Mhook_SetHook: readwrite set on trampoline structure")); - - // create our trampoline function - PBYTE pbCode = pTrampoline->codeTrampoline; - // save original code.. - for (DWORD i = 0; icodeUntouched[i] = pbCode[i] = ((PBYTE)pSystemFunction)[i]; - } - pbCode += dwInstructionLength; - // plus a jump to the continuation in the original location - pbCode = EmitJump(pbCode, ((PBYTE)pSystemFunction) + dwInstructionLength); - ODPRINTF((L"mhooks: Mhook_SetHook: updated the trampoline")); - - // fix up any IP-relative addressing in the code - FixupIPRelativeAddressing(pTrampoline->codeTrampoline, (PBYTE)pSystemFunction, &patchdata); - - DWORD_PTR dwDistance = (PBYTE)pHookFunction < (PBYTE)pSystemFunction ? - (PBYTE)pSystemFunction - (PBYTE)pHookFunction : (PBYTE)pHookFunction - (PBYTE)pSystemFunction; - if (dwDistance > 0x7fff0000) { - // create a stub that jumps to the replacement function. - // we need this because jumping from the API to the hook directly - // will be a long jump, which is 14 bytes on x64, and we want to - // avoid that - the API may or may not have room for such stuff. - // (remember, we only have 5 bytes guaranteed in the API.) - // on the other hand we do have room, and the trampoline will always be - // within +/- 2GB of the API, so we do the long jump in there. - // the API will jump to the "reverse trampoline" which - // will jump to the user's hook code. - pbCode = pTrampoline->codeJumpToHookFunction; - pbCode = EmitJump(pbCode, (PBYTE)pHookFunction); - ODPRINTF((L"mhooks: Mhook_SetHook: created reverse trampoline")); - FlushInstructionCache(GetCurrentProcess(), pTrampoline->codeJumpToHookFunction, - pbCode - pTrampoline->codeJumpToHookFunction); - - // update the API itself - pbCode = (PBYTE)pSystemFunction; - pbCode = EmitJump(pbCode, pTrampoline->codeJumpToHookFunction); - } else { - // the jump will be at most 5 bytes so we can do it directly - // update the API itself - pbCode = (PBYTE)pSystemFunction; - pbCode = EmitJump(pbCode, (PBYTE)pHookFunction); - } - - // update data members - pTrampoline->cbOverwrittenCode = dwInstructionLength; - pTrampoline->pSystemFunction = (PBYTE)pSystemFunction; - pTrampoline->pHookFunction = (PBYTE)pHookFunction; - - // flush instruction cache and restore original protection - FlushInstructionCache(GetCurrentProcess(), pTrampoline->codeTrampoline, dwInstructionLength); - VirtualProtect(pTrampoline, sizeof(MHOOKS_TRAMPOLINE), dwOldProtectTrampolineFunction, &dwOldProtectTrampolineFunction); - } else { - ODPRINTF((L"mhooks: Mhook_SetHook: failed VirtualProtect 2: %d", gle())); - } - // flush instruction cache and restore original protection - FlushInstructionCache(GetCurrentProcess(), pSystemFunction, dwInstructionLength); - VirtualProtect(pSystemFunction, dwInstructionLength, dwOldProtectSystemFunction, &dwOldProtectSystemFunction); - } else { - ODPRINTF((L"mhooks: Mhook_SetHook: failed VirtualProtect 1: %d", gle())); - } - if (pTrampoline->pSystemFunction) { - // this is what the application will use as the entry point - // to the "original" unhooked function. - *ppSystemFunction = pTrampoline->codeTrampoline; - ODPRINTF((L"mhooks: Mhook_SetHook: Hooked the function!")); - } else { - // if we failed discard the trampoline (forcing VirtualFree) - TrampolineFree(pTrampoline, TRUE); - pTrampoline = NULL; - } - } - // resume everybody else - ResumeOtherThreads(); - } else { - ODPRINTF((L"mhooks: disassembly signals %d bytes (unacceptable)", dwInstructionLength)); - } - LeaveCritSec(); - return (pTrampoline != NULL); + MHOOKS_TRAMPOLINE* pTrampoline = NULL; + PVOID pSystemFunction = *ppSystemFunction; + // ensure thread-safety + EnterCritSec(); + ODPRINTF((L"mhooks: Mhook_SetHook: Started on the job: %p / %p", pSystemFunction, pHookFunction)); + // find the real functions (jump over jump tables, if any) + pSystemFunction = SkipJumps((PBYTE)pSystemFunction); + pHookFunction = SkipJumps((PBYTE)pHookFunction); + ODPRINTF((L"mhooks: Mhook_SetHook: Started on the job: %p / %p", pSystemFunction, pHookFunction)); + // figure out the length of the overwrite zone + MHOOKS_PATCHDATA patchdata = {0}; + + DWORD dwInstructionLength = DisassembleAndSkip(pSystemFunction, MHOOK_JMPSIZE, &patchdata); + if (dwInstructionLength >= MHOOK_JMPSIZE) { + ODPRINTF((L"mhooks: Mhook_SetHook: disassembly signals %d bytes", dwInstructionLength)); + // suspend every other thread in this process, and make sure their IP + // is not in the code we're about to overwrite. + SuspendOtherThreads((PBYTE)pSystemFunction, dwInstructionLength); + // allocate a trampoline structure (TODO: it is pretty wasteful to get + // VirtualAlloc to grab chunks of memory smaller than 100 bytes) + pTrampoline = TrampolineAlloc((PBYTE)pSystemFunction, patchdata.nLimitUp, patchdata.nLimitDown); + if (pTrampoline) { + ODPRINTF((L"mhooks: Mhook_SetHook: allocated structure at %p", pTrampoline)); + // open ourselves so we can VirtualProtectEx + HANDLE hProc = GetCurrentProcess(); + DWORD dwOldProtectSystemFunction = 0; + DWORD dwOldProtectTrampolineFunction = 0; + // set the system function to PAGE_EXECUTE_READWRITE + if (VirtualProtectEx(hProc, pSystemFunction, dwInstructionLength, PAGE_EXECUTE_READWRITE, &dwOldProtectSystemFunction)) { + ODPRINTF((L"mhooks: Mhook_SetHook: readwrite set on system function")); + // mark our trampoline buffer to PAGE_EXECUTE_READWRITE + if (VirtualProtectEx(hProc, pTrampoline, sizeof(MHOOKS_TRAMPOLINE), PAGE_EXECUTE_READWRITE, &dwOldProtectTrampolineFunction)) { + ODPRINTF((L"mhooks: Mhook_SetHook: readwrite set on trampoline structure")); + + // create our trampoline function + PBYTE pbCode = pTrampoline->codeTrampoline; + // save original code.. + for (DWORD i = 0; icodeUntouched[i] = pbCode[i] = ((PBYTE)pSystemFunction)[i]; + } + pbCode += dwInstructionLength; + // plus a jump to the continuation in the original location + pbCode = EmitJump(pbCode, ((PBYTE)pSystemFunction) + dwInstructionLength); + ODPRINTF((L"mhooks: Mhook_SetHook: updated the trampoline")); + + // fix up any IP-relative addressing in the code + FixupIPRelativeAddressing(pTrampoline->codeTrampoline, (PBYTE)pSystemFunction, &patchdata); + + DWORD_PTR dwDistance = (PBYTE)pHookFunction < (PBYTE)pSystemFunction ? + (PBYTE)pSystemFunction - (PBYTE)pHookFunction : (PBYTE)pHookFunction - (PBYTE)pSystemFunction; + if (dwDistance > 0x7fff0000) { + // create a stub that jumps to the replacement function. + // we need this because jumping from the API to the hook directly + // will be a long jump, which is 14 bytes on x64, and we want to + // avoid that - the API may or may not have room for such stuff. + // (remember, we only have 5 bytes guaranteed in the API.) + // on the other hand we do have room, and the trampoline will always be + // within +/- 2GB of the API, so we do the long jump in there. + // the API will jump to the "reverse trampoline" which + // will jump to the user's hook code. + pbCode = pTrampoline->codeJumpToHookFunction; + pbCode = EmitJump(pbCode, (PBYTE)pHookFunction); + ODPRINTF((L"mhooks: Mhook_SetHook: created reverse trampoline")); + FlushInstructionCache(hProc, pTrampoline->codeJumpToHookFunction, + pbCode - pTrampoline->codeJumpToHookFunction); + + // update the API itself + pbCode = (PBYTE)pSystemFunction; + pbCode = EmitJump(pbCode, pTrampoline->codeJumpToHookFunction); + ODPRINTF((L"mhooks: Mhook_SetHook: Hooked the function!")); + } else { + // the jump will be at most 5 bytes so we can do it directly + ODPRINTF((L"mhooks: Mhook_SetHook: no need for reverse trampoline")); + // update the API itself + pbCode = (PBYTE)pSystemFunction; + pbCode = EmitJump(pbCode, (PBYTE)pHookFunction); + ODPRINTF((L"mhooks: Mhook_SetHook: Hooked the function!")); + } + + // update data members + pTrampoline->cbOverwrittenCode = dwInstructionLength; + pTrampoline->pSystemFunction = (PBYTE)pSystemFunction; + pTrampoline->pHookFunction = (PBYTE)pHookFunction; + + // flush instruction cache and restore original protection + FlushInstructionCache(hProc, pTrampoline->codeTrampoline, dwInstructionLength); + VirtualProtectEx(hProc, pTrampoline, sizeof(MHOOKS_TRAMPOLINE), dwOldProtectTrampolineFunction, &dwOldProtectTrampolineFunction); + } else { + ODPRINTF((L"mhooks: Mhook_SetHook: failed VirtualProtectEx 2: %d", gle())); + } + // flush instruction cache and restore original protection + FlushInstructionCache(hProc, pSystemFunction, dwInstructionLength); + VirtualProtectEx(hProc, pSystemFunction, dwInstructionLength, dwOldProtectSystemFunction, &dwOldProtectSystemFunction); + } else { + ODPRINTF((L"mhooks: Mhook_SetHook: failed VirtualProtectEx 1: %d", gle())); + } + if (pTrampoline->pSystemFunction) { + // this is what the application will use as the entry point + // to the "original" unhooked function. + *ppSystemFunction = pTrampoline->codeTrampoline; + ODPRINTF((L"mhooks: Mhook_SetHook: Hooked the function!")); + } + else { + // if we failed discard the trampoline (forcing VirtualFree) + TrampolineFree(pTrampoline, TRUE); + pTrampoline = NULL; + } + } + } else { + ODPRINTF((L"mhooks: disassembly signals %d bytes (unacceptable)", dwInstructionLength)); + } + LeaveCritSec(); + return (pTrampoline != NULL); } //========================================================================= BOOL Mhook_Unhook(PVOID *ppHookedFunction) { - ODPRINTF((L"mhooks: Mhook_Unhook: %p", *ppHookedFunction)); - BOOL bRet = FALSE; - EnterCritSec(); - // get the trampoline structure that corresponds to our function - MHOOKS_TRAMPOLINE* pTrampoline = TrampolineGet((PBYTE)*ppHookedFunction); - if (pTrampoline) { - // make sure nobody's executing code where we're about to overwrite a few bytes - SuspendOtherThreads(pTrampoline->pSystemFunction, pTrampoline->cbOverwrittenCode); - ODPRINTF((L"mhooks: Mhook_Unhook: found struct at %p", pTrampoline)); - DWORD dwOldProtectSystemFunction = 0; - // make memory writable - if (VirtualProtect(pTrampoline->pSystemFunction, pTrampoline->cbOverwrittenCode, PAGE_EXECUTE_READWRITE, &dwOldProtectSystemFunction)) { - ODPRINTF((L"mhooks: Mhook_Unhook: readwrite set on system function")); - PBYTE pbCode = (PBYTE)pTrampoline->pSystemFunction; - for (DWORD i = 0; icbOverwrittenCode; i++) { - pbCode[i] = pTrampoline->codeUntouched[i]; - } - // flush instruction cache and make memory unwritable - FlushInstructionCache(GetCurrentProcess(), pTrampoline->pSystemFunction, pTrampoline->cbOverwrittenCode); - VirtualProtect(pTrampoline->pSystemFunction, pTrampoline->cbOverwrittenCode, dwOldProtectSystemFunction, &dwOldProtectSystemFunction); - // return the original function pointer - *ppHookedFunction = pTrampoline->pSystemFunction; - bRet = TRUE; - ODPRINTF((L"mhooks: Mhook_Unhook: sysfunc: %p", *ppHookedFunction)); - // free the trampoline while not really discarding it from memory - TrampolineFree(pTrampoline, FALSE); - ODPRINTF((L"mhooks: Mhook_Unhook: unhook successful")); - } else { - ODPRINTF((L"mhooks: Mhook_Unhook: failed VirtualProtect 1: %d", gle())); - } - // make the other guys runnable - ResumeOtherThreads(); - } - LeaveCritSec(); - return bRet; + ODPRINTF((L"mhooks: Mhook_Unhook: %p", *ppHookedFunction)); + BOOL bRet = FALSE; + EnterCritSec(); + // get the trampoline structure that corresponds to our function + MHOOKS_TRAMPOLINE* pTrampoline = TrampolineGet((PBYTE)*ppHookedFunction); + if (pTrampoline) { + // make sure nobody's executing code where we're about to overwrite a few bytes + SuspendOtherThreads(pTrampoline->pSystemFunction, pTrampoline->cbOverwrittenCode); + ODPRINTF((L"mhooks: Mhook_Unhook: found struct at %p", pTrampoline)); + // open ourselves so we can VirtualProtectEx + HANDLE hProc = GetCurrentProcess(); + DWORD dwOldProtectSystemFunction = 0; + // make memory writable + if (VirtualProtectEx(hProc, pTrampoline->pSystemFunction, pTrampoline->cbOverwrittenCode, PAGE_EXECUTE_READWRITE, &dwOldProtectSystemFunction)) { + ODPRINTF((L"mhooks: Mhook_Unhook: readwrite set on system function")); + PBYTE pbCode = (PBYTE)pTrampoline->pSystemFunction; + for (DWORD i = 0; icbOverwrittenCode; i++) { + pbCode[i] = pTrampoline->codeUntouched[i]; + } + // flush instruction cache and make memory unwritable + FlushInstructionCache(hProc, pTrampoline->pSystemFunction, pTrampoline->cbOverwrittenCode); + VirtualProtectEx(hProc, pTrampoline->pSystemFunction, pTrampoline->cbOverwrittenCode, dwOldProtectSystemFunction, &dwOldProtectSystemFunction); + // return the original function pointer + *ppHookedFunction = pTrampoline->pSystemFunction; + bRet = TRUE; + ODPRINTF((L"mhooks: Mhook_Unhook: sysfunc: %p", *ppHookedFunction)); + // free the trampoline while not really discarding it from memory + TrampolineFree(pTrampoline, FALSE); + ODPRINTF((L"mhooks: Mhook_Unhook: unhook successful")); + } else { + ODPRINTF((L"mhooks: Mhook_Unhook: failed VirtualProtectEx 1: %d", gle())); + } + } + LeaveCritSec(); + return bRet; } //=========================================================================