From 4e00ce0447e066ffcb61e0945abb57ae16656dfb Mon Sep 17 00:00:00 2001 From: narzoul Date: Sun, 28 Mar 2021 20:03:55 +0200 Subject: [PATCH] Removed dependency on Detours --- DDrawCompat/Common/Hook.cpp | 253 ++++++++++++++++++------- DDrawCompat/Common/Hook.h | 2 +- DDrawCompat/Common/Log.h | 47 +++-- DDrawCompat/DDrawCompat.vcxproj | 20 +- DDrawCompat/Dll/Dll.cpp | 4 +- DDrawCompat/Dll/Dll.h | 2 +- DDrawCompat/Dll/DllMain.cpp | 4 + DDrawCompat/Gdi/PresentationWindow.cpp | 10 +- DDrawCompat/Gdi/PresentationWindow.h | 1 + 9 files changed, 245 insertions(+), 98 deletions(-) diff --git a/DDrawCompat/Common/Hook.cpp b/DDrawCompat/Common/Hook.cpp index a7b763f..4be47ba 100644 --- a/DDrawCompat/Common/Hook.cpp +++ b/DDrawCompat/Common/Hook.cpp @@ -1,36 +1,30 @@ -#include +#undef CINTERFACE + #include #include -#include #include #include -#include #include -#include +#include +#include #include #include +#include namespace { - struct HookedFunctionInfo - { - HMODULE module; - void*& origFunction; - void* newFunction; - }; - - std::map g_hookedFunctions; + IDebugClient* g_debugClient = nullptr; + IDebugControl* g_debugControl = nullptr; + IDebugSymbols* g_debugSymbols = nullptr; + IDebugDataSpaces4* g_debugDataSpaces = nullptr; + ULONG64 g_debugBase = 0; + bool g_isDbgEngInitialized = false; PIMAGE_NT_HEADERS getImageNtHeaders(HMODULE module); std::filesystem::path getModulePath(HMODULE module); - - std::map::iterator findOrigFunc(void* origFunc) - { - return std::find_if(g_hookedFunctions.begin(), g_hookedFunctions.end(), - [=](const auto& i) { return origFunc == i.first || origFunc == i.second.origFunction; }); - } + bool initDbgEng(); FARPROC* findProcAddressInIat(HMODULE module, const char* procName) { @@ -91,6 +85,28 @@ namespace return ntHeaders; } + unsigned getInstructionSize(void* instruction) + { + const unsigned MAX_INSTRUCTION_SIZE = 15; + HRESULT result = g_debugDataSpaces->WriteVirtual(g_debugBase, instruction, MAX_INSTRUCTION_SIZE, nullptr); + if (FAILED(result)) + { + LOG_ONCE("ERROR: DbgEng: WriteVirtual failed: " << Compat::hex(result)); + return 0; + } + + ULONG64 endOffset = 0; + result = g_debugControl->Disassemble(g_debugBase, 0, nullptr, 0, nullptr, &endOffset); + if (FAILED(result)) + { + LOG_ONCE("ERROR: DbgEng: Disassemble failed: " << Compat::hex(result) << " " + << Compat::hexDump(instruction, MAX_INSTRUCTION_SIZE)); + return 0; + } + + return static_cast(endOffset - g_debugBase); + } + std::filesystem::path getModulePath(HMODULE module) { char path[MAX_PATH] = {}; @@ -100,28 +116,11 @@ namespace void hookFunction(void*& origFuncPtr, void* newFuncPtr, const char* funcName) { - void* stubFuncPtr = nullptr; - if (GetModuleHandle("ntdll") == Compat::getModuleHandleFromAddress(origFuncPtr)) - { - // Avoid hooking ntdll stubs (e.g. ntdll/NtdllDialogWndProc_A instead of user32/DefDlgProcA) - if (0xFF == reinterpret_cast(origFuncPtr)[0] && - 0x25 == reinterpret_cast(origFuncPtr)[1]) - { - void* jmpTarget = **reinterpret_cast(reinterpret_cast(origFuncPtr) + 2); - if (GetModuleHandle("user32") == Compat::getModuleHandleFromAddress(jmpTarget)) - { - stubFuncPtr = origFuncPtr; - origFuncPtr = jmpTarget; - } - } - } + BYTE* targetFunc = reinterpret_cast(origFuncPtr); - const auto it = findOrigFunc(origFuncPtr); - if (it != g_hookedFunctions.end()) - { - origFuncPtr = it->second.origFunction; - return; - } + std::ostringstream oss; +#ifdef DEBUGLOGS + oss << Compat::funcPtrToStr(targetFunc); char origFuncPtrStr[20] = {}; if (!funcName) @@ -129,46 +128,171 @@ namespace sprintf_s(origFuncPtrStr, "%p", origFuncPtr); funcName = origFuncPtrStr; } +#endif - void* const hookedFuncPtr = origFuncPtr; - if (stubFuncPtr) + while (true) { - LOG_DEBUG << "Hooking function: " << funcName << " (" << Compat::funcPtrToStr(stubFuncPtr) << " -> " - << Compat::funcPtrToStr(origFuncPtr) << ')'; - } - else - { - LOG_DEBUG << "Hooking function: " << funcName << " (" << Compat::funcPtrToStr(hookedFuncPtr) << ')'; + if (0xE9 == targetFunc[0]) + { + targetFunc += 1 + *reinterpret_cast(targetFunc + 1); + } + else if (0xEB == targetFunc[0]) + { + targetFunc += 1 + static_cast(targetFunc[1]); + } + else if (0xFF == targetFunc[0] && 0x25 == targetFunc[1]) + { + targetFunc = **reinterpret_cast(targetFunc + 2); + } + else + { + break; + } +#ifdef DEBUGLOGS + oss << " -> " << Compat::funcPtrToStr(targetFunc); +#endif } - DetourTransactionBegin(); - const bool attachSuccessful = NO_ERROR == DetourAttach(&origFuncPtr, newFuncPtr); - const bool commitSuccessful = NO_ERROR == DetourTransactionCommit(); - if (!attachSuccessful || !commitSuccessful) + LOG_DEBUG << "Hooking function: " << funcName << " (" << oss.str() << ')'; + + if (Compat::getModuleHandleFromAddress(targetFunc) == Dll::g_currentModule) { - LOG_DEBUG << "ERROR: Failed to hook a function: " << funcName; + Compat::Log() << "ERROR: Target function is already hooked: " << funcName; return; } + if (!initDbgEng()) + { + return; + } + + unsigned totalInstructionSize = 0; + while (totalInstructionSize < 5) + { + unsigned instructionSize = getInstructionSize(targetFunc + totalInstructionSize); + if (0 == instructionSize) + { + return; + } + totalInstructionSize += instructionSize; + } + + LOG_DEBUG << "Decoded instructions: " << Compat::hexDump(targetFunc, totalInstructionSize); + + BYTE* trampoline = static_cast( + VirtualAlloc(nullptr, totalInstructionSize + 5, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE)); + memcpy(trampoline, targetFunc, totalInstructionSize); + trampoline[totalInstructionSize] = 0xE9; + reinterpret_cast(trampoline[totalInstructionSize + 1]) = targetFunc - (trampoline + 5); + DWORD oldProtect = 0; + VirtualProtect(trampoline, totalInstructionSize + 5, PAGE_EXECUTE_READ, &oldProtect); + + VirtualProtect(targetFunc, totalInstructionSize, PAGE_EXECUTE_READWRITE, &oldProtect); + targetFunc[0] = 0xE9; + reinterpret_cast(targetFunc[1]) = static_cast(newFuncPtr) - (targetFunc + 5); + memset(targetFunc + 5, 0xCC, totalInstructionSize - 5); + VirtualProtect(targetFunc, totalInstructionSize, PAGE_EXECUTE_READ, &oldProtect); + + FlushInstructionCache(GetCurrentProcess(), nullptr, 0); + HMODULE module = nullptr; GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_PIN, - static_cast(hookedFuncPtr), &module); + reinterpret_cast(targetFunc), &module); - g_hookedFunctions.emplace( - std::make_pair(hookedFuncPtr, HookedFunctionInfo{ module, origFuncPtr, newFuncPtr })); + origFuncPtr = trampoline; } - void unhookFunction(const std::map::iterator& hookedFunc) + bool initDbgEng() { - DetourTransactionBegin(); - DetourDetach(&hookedFunc->second.origFunction, hookedFunc->second.newFunction); - DetourTransactionCommit(); - g_hookedFunctions.erase(hookedFunc); + if (g_isDbgEngInitialized) + { + return 0 != g_debugBase; + } + g_isDbgEngInitialized = true; + + CoInitialize(nullptr); + + HRESULT result = S_OK; + if (FAILED(result = DebugCreate(IID_IDebugClient, reinterpret_cast(&g_debugClient))) || + FAILED(result = g_debugClient->QueryInterface(IID_IDebugControl, reinterpret_cast(&g_debugControl))) || + FAILED(result = g_debugClient->QueryInterface(IID_IDebugSymbols, reinterpret_cast(&g_debugSymbols))) || + FAILED(result = g_debugClient->QueryInterface(IID_IDebugDataSpaces4, reinterpret_cast(&g_debugDataSpaces)))) + { + Compat::Log() << "ERROR: DbgEng: object creation failed: " << Compat::hex(result); + return false; + } + + char dllPath[MAX_PATH] = {}; + GetModuleFileName(Dll::g_currentModule, dllPath, sizeof(dllPath)); + + result = g_debugClient->OpenDumpFile(dllPath); + if (FAILED(result)) + { + Compat::Log() << "ERROR: DbgEng: OpenDumpFile failed: " << Compat::hex(result); + return false; + } + + g_debugControl->SetEngineOptions(DEBUG_ENGOPT_DISABLE_MODULE_SYMBOL_LOAD); + result = g_debugControl->WaitForEvent(0, INFINITE); + if (FAILED(result)) + { + Compat::Log() << "ERROR: DbgEng: WaitForEvent failed: " << Compat::hex(result); + return false; + } + + DEBUG_MODULE_PARAMETERS dmp = {}; + result = g_debugSymbols->GetModuleParameters(1, 0, 0, &dmp); + if (FAILED(result)) + { + Compat::Log() << "ERROR: DbgEng: GetModuleParameters failed: " << Compat::hex(result); + return false; + } + + ULONG size = 0; + result = g_debugDataSpaces->GetValidRegionVirtual(dmp.Base, dmp.Size, &g_debugBase, &size); + if (FAILED(result) || 0 == g_debugBase) + { + Compat::Log() << "ERROR: DbgEng: GetValidRegionVirtual failed: " << Compat::hex(result); + return false; + } + + return true; } } namespace Compat { + void closeDbgEng() + { + if (g_debugClient) + { + g_debugClient->EndSession(DEBUG_END_PASSIVE); + } + if (g_debugDataSpaces) + { + g_debugDataSpaces->Release(); + g_debugDataSpaces = nullptr; + } + if (g_debugSymbols) + { + g_debugSymbols->Release(); + g_debugSymbols = nullptr; + } + if (g_debugControl) + { + g_debugControl->Release(); + g_debugControl = nullptr; + } + if (g_debugClient) + { + g_debugClient->Release(); + g_debugClient = nullptr; + } + + g_debugBase = 0; + g_isDbgEngInitialized = false; + } + std::string funcPtrToStr(void* funcPtr) { std::ostringstream oss; @@ -325,13 +449,4 @@ namespace Compat } } } - - void unhookFunction(void* origFunc) - { - auto it = findOrigFunc(origFunc); - if (it != g_hookedFunctions.end()) - { - ::unhookFunction(it); - } - } } diff --git a/DDrawCompat/Common/Hook.h b/DDrawCompat/Common/Hook.h index 72be52e..a87fa7b 100644 --- a/DDrawCompat/Common/Hook.h +++ b/DDrawCompat/Common/Hook.h @@ -13,6 +13,7 @@ namespace Compat { + void closeDbgEng(); std::string funcPtrToStr(void* funcPtr); HMODULE getModuleHandleFromAddress(void* address); @@ -32,5 +33,4 @@ namespace Compat } void removeShim(HMODULE module, const char* funcName); - void unhookFunction(void* origFunc); } diff --git a/DDrawCompat/Common/Log.h b/DDrawCompat/Common/Log.h index 10bf2ab..60c58df 100644 --- a/DDrawCompat/Common/Log.h +++ b/DDrawCompat/Common/Log.h @@ -56,6 +56,14 @@ namespace Compat { using ::operator<<; + template + struct Array + { + Array(const Elem* elem, const unsigned long size) : elem(elem), size(size) {} + const Elem* elem; + const unsigned long size; + }; + template struct Hex { @@ -63,12 +71,10 @@ namespace Compat T val; }; - template - struct Array + struct HexByte { - Array(const Elem* elem, const unsigned long size) : elem(elem), size(size) {} - const Elem* elem; - const unsigned long size; + explicit HexByte(BYTE val) : val(val) {} + BYTE val; }; template @@ -102,13 +108,6 @@ namespace Compat std::ostream& m_os; }; - template - std::ostream& operator<<(std::ostream& os, Hex hex) - { - os << "0x" << std::hex << hex.val << std::dec; - return os; - } - template std::ostream& operator<<(std::ostream& os, Array array) { @@ -127,6 +126,19 @@ namespace Compat return os << ']'; } + template + std::ostream& operator<<(std::ostream& os, Hex hex) + { + return os << "0x" << std::hex << hex.val << std::dec; + } + + inline std::ostream& operator<<(std::ostream& os, HexByte hexByte) + { + os.fill('0'); + os.width(2); + return os << std::hex << static_cast(hexByte.val) << std::dec; + } + template std::ostream& operator<<(std::ostream& os, Out out) { @@ -137,15 +149,20 @@ namespace Compat } } + template + detail::Array array(const Elem* elem, const unsigned long size) + { + return detail::Array(elem, size); + } + template detail::Hex hex(T val) { return detail::Hex(val); } - template - detail::Array array(const Elem* elem, const unsigned long size) + inline detail::Array hexDump(const void* buf, const unsigned long size) { - return detail::Array(elem, size); + return detail::Array(static_cast(buf), size); } template detail::Out out(const T& val) diff --git a/DDrawCompat/DDrawCompat.vcxproj b/DDrawCompat/DDrawCompat.vcxproj index d716825..888fc8d 100644 --- a/DDrawCompat/DDrawCompat.vcxproj +++ b/DDrawCompat/DDrawCompat.vcxproj @@ -59,19 +59,21 @@ ddraw - $(ProjectDir);C:\Program Files %28x86%29\Microsoft Research\Detours Express 3.0\include;$(IncludePath) - C:\Program Files %28x86%29\Microsoft Research\Detours Express 3.0\lib.X86;$(LibraryPath) false + $(ProjectDir);$(IncludePath) + $(LibraryPath) ddraw - $(ProjectDir);C:\Program Files %28x86%29\Microsoft Research\Detours Express 3.0\include;$(IncludePath) - C:\Program Files %28x86%29\Microsoft Research\Detours Express 3.0\lib.X86;$(LibraryPath) + $(ProjectDir);$(IncludePath) + $(LibraryPath) + false ddraw - $(ProjectDir);C:\Program Files %28x86%29\Microsoft Research\Detours Express 3.0\include;$(IncludePath) - C:\Program Files %28x86%29\Microsoft Research\Detours Express 3.0\lib.X86;$(LibraryPath) + $(ProjectDir);$(IncludePath) + $(LibraryPath) + false @@ -87,7 +89,7 @@ stdcpp17 - dxguid.lib;detours.lib;msimg32.lib;oleacc.lib;uxtheme.lib;dwmapi.lib;winmm.lib;%(AdditionalDependencies) + dbgeng.lib;dxguid.lib;msimg32.lib;oleacc.lib;uxtheme.lib;dwmapi.lib;winmm.lib;%(AdditionalDependencies) DebugFull true $(IntDir)$(TargetName).lib @@ -105,7 +107,7 @@ stdcpp17 - dxguid.lib;detours.lib;msimg32.lib;oleacc.lib;uxtheme.lib;dwmapi.lib;winmm.lib;%(AdditionalDependencies) + dbgeng.lib;dxguid.lib;msimg32.lib;oleacc.lib;uxtheme.lib;dwmapi.lib;winmm.lib;%(AdditionalDependencies) DebugFull true $(IntDir)$(TargetName).lib @@ -123,7 +125,7 @@ stdcpp17 - dxguid.lib;detours.lib;msimg32.lib;oleacc.lib;uxtheme.lib;dwmapi.lib;winmm.lib;%(AdditionalDependencies) + dbgeng.lib;dxguid.lib;msimg32.lib;oleacc.lib;uxtheme.lib;dwmapi.lib;winmm.lib;%(AdditionalDependencies) DebugFull true $(IntDir)$(TargetName).lib diff --git a/DDrawCompat/Dll/Dll.cpp b/DDrawCompat/Dll/Dll.cpp index 8e50a1b..81976a5 100644 --- a/DDrawCompat/Dll/Dll.cpp +++ b/DDrawCompat/Dll/Dll.cpp @@ -10,9 +10,9 @@ namespace Dll Procs g_origProcs = {}; Procs g_jmpTargetProcs = {}; - HANDLE createThread(unsigned(__stdcall* threadProc)(void*), unsigned int* threadId, int priority) + HANDLE createThread(unsigned(__stdcall* threadProc)(void*), unsigned int* threadId, int priority, unsigned initFlags) { - HANDLE thread = reinterpret_cast(_beginthreadex(nullptr, 0, threadProc, nullptr, 0, threadId)); + HANDLE thread = reinterpret_cast(_beginthreadex(nullptr, 0, threadProc, nullptr, initFlags, threadId)); if (thread) { SetThreadPriority(thread, priority); diff --git a/DDrawCompat/Dll/Dll.h b/DDrawCompat/Dll/Dll.h index a16c261..b2fa3b1 100644 --- a/DDrawCompat/Dll/Dll.h +++ b/DDrawCompat/Dll/Dll.h @@ -67,7 +67,7 @@ namespace Dll #undef ADD_FARPROC_MEMBER }; - HANDLE createThread(unsigned(__stdcall* threadProc)(void*), unsigned int* threadId, int priority); + HANDLE createThread(unsigned(__stdcall* threadProc)(void*), unsigned int* threadId, int priority, unsigned initFlags = 0); void pinModule(HMODULE module); void pinModule(LPCSTR moduleName); void pinModule(LPCWSTR moduleName); diff --git a/DDrawCompat/Dll/DllMain.cpp b/DDrawCompat/Dll/DllMain.cpp index 99a8719..4f5b07d 100644 --- a/DDrawCompat/Dll/DllMain.cpp +++ b/DDrawCompat/Dll/DllMain.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -122,6 +123,8 @@ namespace Direct3d::installHooks(dd, dd7); Compat::Log() << "Installing GDI hooks"; Gdi::installHooks(); + Compat::closeDbgEng(); + Gdi::PresentationWindow::startThread(); Compat::Log() << "Finished installing hooks"; isAlreadyInstalled = true; } @@ -258,6 +261,7 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) Win32::MemoryManagement::installHooks(); Win32::MsgHooks::installHooks(); Time::init(); + Compat::closeDbgEng(); const DWORD disableMaxWindowedMode = 12; CALL_ORIG_PROC(SetAppCompatData)(disableMaxWindowedMode, 0); diff --git a/DDrawCompat/Gdi/PresentationWindow.cpp b/DDrawCompat/Gdi/PresentationWindow.cpp index 3fa6b01..a7d6247 100644 --- a/DDrawCompat/Gdi/PresentationWindow.cpp +++ b/DDrawCompat/Gdi/PresentationWindow.cpp @@ -10,6 +10,7 @@ namespace const UINT WM_SETPRESENTATIONWINDOWPOS = WM_USER + 1; const UINT WM_SETPRESENTATIONWINDOWRGN = WM_USER + 2; + HANDLE g_presentationWindowThread = nullptr; unsigned g_presentationWindowThreadId = 0; HWND g_messageWindow = nullptr; @@ -97,6 +98,7 @@ namespace } Gdi::WinProc::installHooks(); + Compat::closeDbgEng(); MSG msg = {}; while (GetMessage(&msg, nullptr, 0, 0)) @@ -139,7 +141,8 @@ namespace Gdi wc.lpszClassName = "DDrawCompatPresentationWindow"; CALL_ORIG_FUNC(RegisterClassA)(&wc); - Dll::createThread(presentationWindowThreadProc, &g_presentationWindowThreadId, THREAD_PRIORITY_TIME_CRITICAL); + g_presentationWindowThread = Dll::createThread(presentationWindowThreadProc, &g_presentationWindowThreadId, + THREAD_PRIORITY_TIME_CRITICAL, CREATE_SUSPENDED); } bool isPresentationWindow(HWND hwnd) @@ -156,5 +159,10 @@ namespace Gdi { sendMessageBlocking(hwnd, WM_SETPRESENTATIONWINDOWRGN, reinterpret_cast(rgn), 0); } + + void startThread() + { + ResumeThread(g_presentationWindowThread); + } } } diff --git a/DDrawCompat/Gdi/PresentationWindow.h b/DDrawCompat/Gdi/PresentationWindow.h index 52ffba6..ed46999 100644 --- a/DDrawCompat/Gdi/PresentationWindow.h +++ b/DDrawCompat/Gdi/PresentationWindow.h @@ -11,6 +11,7 @@ namespace Gdi bool isPresentationWindow(HWND hwnd); void setWindowPos(HWND hwnd, const WINDOWPOS& wp); void setWindowRgn(HWND hwnd, HRGN rgn); + void startThread(); void installHooks(); }