#include "pch.h" #include "xivfixes.h" #include "hooks.h" #include "logging.h" #include "utils.h" using TFnGetInputDeviceManager = void* (); static TFnGetInputDeviceManager* GetGetInputDeviceManager(HWND hwnd) { static TFnGetInputDeviceManager* pCached = nullptr; if (pCached) return pCached; char szClassName[256]; GetClassNameA(hwnd, szClassName, static_cast(sizeof szClassName)); WNDCLASSEXA wcx{}; GetClassInfoExA(g_hGameInstance, szClassName, &wcx); const auto match = utils::signature_finder() .look_in(g_hGameInstance, ".text") .look_for_hex("41 81 fe 19 02 00 00 0f 87 ?? ?? 00 00 0f 84 ?? ?? 00 00") .find_one(); auto ptr = match.data() + match.size() + *reinterpret_cast(match.data() + match.size() - 4); ptr += 4; // CMP RBX, 0x7 ptr += 2; // JNZ ptr += 7; // MOV RCX, ptr += 3; // TEST RCX, RCX ptr += 2; // JZ ptr += 5; // CALL ptr += *reinterpret_cast(ptr - 4); return pCached = reinterpret_cast(ptr); } void xivfixes::prevent_devicechange_crashes(bool bApply) { static const char* LogTag = "[xivfixes:prevent_devicechange_crashes]"; static std::optional> s_hookCreateWindowExA; static std::optional s_hookWndProc; if (bApply) { s_hookCreateWindowExA.emplace("user32.dll", "CreateWindowExA", 0); s_hookCreateWindowExA->set_detour([](DWORD dwExStyle, LPCSTR lpClassName, LPCSTR lpWindowName, DWORD dwStyle, int X, int Y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam)->HWND { const auto hWnd = s_hookCreateWindowExA->call_original(dwExStyle, lpClassName, lpWindowName, dwStyle, X, Y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam); if (!hWnd || hInstance != g_hGameInstance || 0 != strcmp(lpClassName, "FFXIVGAME")) return hWnd; logging::print("{} CreateWindow(0x{:08X}, \"{}\", \"{}\", 0x{:08X}, {}, {}, {}, {}, 0x{:X}, 0x{:X}, 0x{:X}, 0x{:X}) called; unhooking CreateWindowExA and hooking WndProc.", LogTag, dwExStyle, lpClassName, lpWindowName, dwStyle, X, Y, nWidth, nHeight, reinterpret_cast(hWndParent), reinterpret_cast(hMenu), reinterpret_cast(hInstance), reinterpret_cast(lpParam)); s_hookCreateWindowExA.reset(); s_hookWndProc.emplace(hWnd); s_hookWndProc->set_detour([](HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -> LRESULT { if (uMsg == WM_DEVICECHANGE && wParam == DBT_DEVNODES_CHANGED) { if (!GetGetInputDeviceManager(hWnd)()) { logging::print("{} WndProc(0x{:X}, WM_DEVICECHANGE, DBT_DEVNODES_CHANGED, {}) called but the game does not have InputDeviceManager initialized; doing nothing.", LogTag, reinterpret_cast(hWnd), lParam); return 0; } } return s_hookWndProc->call_original(hWnd, uMsg, wParam, lParam); }); return hWnd; }); } else { logging::print("{} Disable", LogTag); s_hookCreateWindowExA.reset(); // This will effectively revert any other WndProc alterations, including Dalamud. s_hookWndProc.reset(); } } void xivfixes::disable_game_openprocess_access_check(bool bApply) { static const char* LogTag = "[xivfixes:disable_game_openprocess_access_check]"; static std::optional> hook; if (bApply) { hook.emplace("kernel32.dll", "OpenProcess", 0); hook->set_detour([](DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId)->HANDLE { if (dwProcessId == GetCurrentProcessId()) { logging::print("{} OpenProcess(0{:08X}, {}, {}) was invoked by thread {}.", LogTag, dwDesiredAccess, bInheritHandle, dwProcessId, GetCurrentThreadId()); // Prevent game from feeling unsafe that it restarts if (dwDesiredAccess & PROCESS_VM_WRITE) { logging::print("{} Returning failure with last error code set to ERROR_ACCESS_DENIED(5).", LogTag); SetLastError(ERROR_ACCESS_DENIED); return {}; } } return hook->call_original(dwDesiredAccess, bInheritHandle, dwProcessId); }); } else { logging::print("{} Disable", LogTag); hook.reset(); } } void xivfixes::redirect_openprocess(bool bApply) { static const char* LogTag = "[xivfixes:redirect_openprocess]"; static std::optional> hook; if (bApply) { logging::print("{} Enable", LogTag); hook.emplace(::OpenProcess); hook->set_detour([](DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId)->HANDLE { if (dwProcessId == GetCurrentProcessId()) { logging::print("{} OpenProcess(0{:08X}, {}, {}) was invoked by thread {}. Redirecting to DuplicateHandle.", LogTag, dwDesiredAccess, bInheritHandle, dwProcessId, GetCurrentThreadId()); if (HANDLE res; DuplicateHandle(GetCurrentProcess(), GetCurrentProcess(), GetCurrentProcess(), &res, dwDesiredAccess, bInheritHandle, 0)) return res; return {}; } return hook->call_original(dwDesiredAccess, bInheritHandle, dwProcessId); }); } else { logging::print("{} Disable", LogTag); hook.reset(); } }