1
0
mirror of https://github.com/narzoul/DDrawCompat synced 2024-12-30 08:55:36 +01:00

Removed dependency on Detours

This commit is contained in:
narzoul 2021-03-28 20:03:55 +02:00
parent ce991009be
commit 4e00ce0447
9 changed files with 245 additions and 98 deletions

View File

@ -1,36 +1,30 @@
#include <algorithm>
#undef CINTERFACE
#include <filesystem>
#include <list>
#include <map>
#include <sstream>
#include <string>
#include <utility>
#include <Windows.h>
#include <detours.h>
#include <initguid.h>
#include <DbgEng.h>
#include <Common/Hook.h>
#include <Common/Log.h>
#include <Dll/Dll.h>
namespace
{
struct HookedFunctionInfo
{
HMODULE module;
void*& origFunction;
void* newFunction;
};
std::map<void*, HookedFunctionInfo> 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<void*, HookedFunctionInfo>::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<unsigned>(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<BYTE*>(origFuncPtr)[0] &&
0x25 == reinterpret_cast<BYTE*>(origFuncPtr)[1])
{
void* jmpTarget = **reinterpret_cast<void***>(reinterpret_cast<BYTE*>(origFuncPtr) + 2);
if (GetModuleHandle("user32") == Compat::getModuleHandleFromAddress(jmpTarget))
{
stubFuncPtr = origFuncPtr;
origFuncPtr = jmpTarget;
}
}
}
BYTE* targetFunc = reinterpret_cast<BYTE*>(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<int*>(targetFunc + 1);
}
else if (0xEB == targetFunc[0])
{
targetFunc += 1 + static_cast<signed char>(targetFunc[1]);
}
else if (0xFF == targetFunc[0] && 0x25 == targetFunc[1])
{
targetFunc = **reinterpret_cast<BYTE***>(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<BYTE*>(
VirtualAlloc(nullptr, totalInstructionSize + 5, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE));
memcpy(trampoline, targetFunc, totalInstructionSize);
trampoline[totalInstructionSize] = 0xE9;
reinterpret_cast<int&>(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<int&>(targetFunc[1]) = static_cast<BYTE*>(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<char*>(hookedFuncPtr), &module);
reinterpret_cast<char*>(targetFunc), &module);
g_hookedFunctions.emplace(
std::make_pair(hookedFuncPtr, HookedFunctionInfo{ module, origFuncPtr, newFuncPtr }));
origFuncPtr = trampoline;
}
void unhookFunction(const std::map<void*, HookedFunctionInfo>::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<void**>(&g_debugClient))) ||
FAILED(result = g_debugClient->QueryInterface(IID_IDebugControl, reinterpret_cast<void**>(&g_debugControl))) ||
FAILED(result = g_debugClient->QueryInterface(IID_IDebugSymbols, reinterpret_cast<void**>(&g_debugSymbols))) ||
FAILED(result = g_debugClient->QueryInterface(IID_IDebugDataSpaces4, reinterpret_cast<void**>(&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);
}
}
}

View File

@ -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);
}

View File

@ -56,6 +56,14 @@ namespace Compat
{
using ::operator<<;
template <typename Elem>
struct Array
{
Array(const Elem* elem, const unsigned long size) : elem(elem), size(size) {}
const Elem* elem;
const unsigned long size;
};
template <typename T>
struct Hex
{
@ -63,12 +71,10 @@ namespace Compat
T val;
};
template <typename Elem>
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 <typename T>
@ -102,13 +108,6 @@ namespace Compat
std::ostream& m_os;
};
template <typename T>
std::ostream& operator<<(std::ostream& os, Hex<T> hex)
{
os << "0x" << std::hex << hex.val << std::dec;
return os;
}
template <typename Elem>
std::ostream& operator<<(std::ostream& os, Array<Elem> array)
{
@ -127,6 +126,19 @@ namespace Compat
return os << ']';
}
template <typename T>
std::ostream& operator<<(std::ostream& os, Hex<T> 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<DWORD>(hexByte.val) << std::dec;
}
template <typename T>
std::ostream& operator<<(std::ostream& os, Out<T> out)
{
@ -137,15 +149,20 @@ namespace Compat
}
}
template <typename Elem>
detail::Array<Elem> array(const Elem* elem, const unsigned long size)
{
return detail::Array<Elem>(elem, size);
}
template <typename T> detail::Hex<T> hex(T val)
{
return detail::Hex<T>(val);
}
template <typename Elem>
detail::Array<Elem> array(const Elem* elem, const unsigned long size)
inline detail::Array<detail::HexByte> hexDump(const void* buf, const unsigned long size)
{
return detail::Array<Elem>(elem, size);
return detail::Array<detail::HexByte>(static_cast<const detail::HexByte*>(buf), size);
}
template <typename T> detail::Out<T> out(const T& val)

View File

@ -59,19 +59,21 @@
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<TargetName>ddraw</TargetName>
<IncludePath>$(ProjectDir);C:\Program Files %28x86%29\Microsoft Research\Detours Express 3.0\include;$(IncludePath)</IncludePath>
<LibraryPath>C:\Program Files %28x86%29\Microsoft Research\Detours Express 3.0\lib.X86;$(LibraryPath)</LibraryPath>
<LinkIncremental>false</LinkIncremental>
<IncludePath>$(ProjectDir);$(IncludePath)</IncludePath>
<LibraryPath>$(LibraryPath)</LibraryPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<TargetName>ddraw</TargetName>
<IncludePath>$(ProjectDir);C:\Program Files %28x86%29\Microsoft Research\Detours Express 3.0\include;$(IncludePath)</IncludePath>
<LibraryPath>C:\Program Files %28x86%29\Microsoft Research\Detours Express 3.0\lib.X86;$(LibraryPath)</LibraryPath>
<IncludePath>$(ProjectDir);$(IncludePath)</IncludePath>
<LibraryPath>$(LibraryPath)</LibraryPath>
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithDebugLogs|Win32'">
<TargetName>ddraw</TargetName>
<IncludePath>$(ProjectDir);C:\Program Files %28x86%29\Microsoft Research\Detours Express 3.0\include;$(IncludePath)</IncludePath>
<LibraryPath>C:\Program Files %28x86%29\Microsoft Research\Detours Express 3.0\lib.X86;$(LibraryPath)</LibraryPath>
<IncludePath>$(ProjectDir);$(IncludePath)</IncludePath>
<LibraryPath>$(LibraryPath)</LibraryPath>
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
@ -87,7 +89,7 @@
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<AdditionalDependencies>dxguid.lib;detours.lib;msimg32.lib;oleacc.lib;uxtheme.lib;dwmapi.lib;winmm.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>dbgeng.lib;dxguid.lib;msimg32.lib;oleacc.lib;uxtheme.lib;dwmapi.lib;winmm.lib;%(AdditionalDependencies)</AdditionalDependencies>
<GenerateDebugInformation>DebugFull</GenerateDebugInformation>
<OptimizeReferences>true</OptimizeReferences>
<ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
@ -105,7 +107,7 @@
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<AdditionalDependencies>dxguid.lib;detours.lib;msimg32.lib;oleacc.lib;uxtheme.lib;dwmapi.lib;winmm.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>dbgeng.lib;dxguid.lib;msimg32.lib;oleacc.lib;uxtheme.lib;dwmapi.lib;winmm.lib;%(AdditionalDependencies)</AdditionalDependencies>
<GenerateDebugInformation>DebugFull</GenerateDebugInformation>
<OptimizeReferences>true</OptimizeReferences>
<ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
@ -123,7 +125,7 @@
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<AdditionalDependencies>dxguid.lib;detours.lib;msimg32.lib;oleacc.lib;uxtheme.lib;dwmapi.lib;winmm.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>dbgeng.lib;dxguid.lib;msimg32.lib;oleacc.lib;uxtheme.lib;dwmapi.lib;winmm.lib;%(AdditionalDependencies)</AdditionalDependencies>
<GenerateDebugInformation>DebugFull</GenerateDebugInformation>
<OptimizeReferences>true</OptimizeReferences>
<ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>

View File

@ -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<HANDLE>(_beginthreadex(nullptr, 0, threadProc, nullptr, 0, threadId));
HANDLE thread = reinterpret_cast<HANDLE>(_beginthreadex(nullptr, 0, threadProc, nullptr, initFlags, threadId));
if (thread)
{
SetThreadPriority(thread, priority);

View File

@ -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);

View File

@ -14,6 +14,7 @@
#include <Direct3d/Hooks.h>
#include <Dll/Dll.h>
#include <Gdi/Gdi.h>
#include <Gdi/PresentationWindow.h>
#include <Gdi/VirtualScreen.h>
#include <Win32/DisplayMode.h>
#include <Win32/MemoryManagement.h>
@ -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);

View File

@ -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<WPARAM>(rgn), 0);
}
void startThread()
{
ResumeThread(g_presentationWindowThread);
}
}
}

View File

@ -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();
}