diff --git a/Dalamud.Boot/Dalamud.Boot.vcxproj b/Dalamud.Boot/Dalamud.Boot.vcxproj
index 891eb0275..fe33e5406 100644
--- a/Dalamud.Boot/Dalamud.Boot.vcxproj
+++ b/Dalamud.Boot/Dalamud.Boot.vcxproj
@@ -53,7 +53,7 @@
Windows
true
false
- dbghelp.lib;%(AdditionalDependencies)
+ dbghelp.lib;Version.lib;%(AdditionalDependencies)
..\lib\CoreCLR;%(AdditionalLibraryDirectories)
diff --git a/Dalamud.Boot/xivfixes.cpp b/Dalamud.Boot/xivfixes.cpp
index 7a72c4147..e807f76ca 100644
--- a/Dalamud.Boot/xivfixes.cpp
+++ b/Dalamud.Boot/xivfixes.cpp
@@ -13,7 +13,6 @@ static std::span assume_nonempty_span(std::span t, const char* descr) {
throw std::runtime_error(std::format("Unexpected empty span found: {}", descr));
return t;
}
-
void xivfixes::unhook_dll(bool bApply) {
static const auto LogTag = "[xivfixes:unhook_dll]";
static const auto LogTagW = L"[xivfixes:unhook_dll]";
@@ -24,15 +23,15 @@ void xivfixes::unhook_dll(bool bApply) {
return;
const auto mods = utils::loaded_module::all_modules();
- for (size_t i = 0; i < mods.size(); i++) {
- const auto& mod = mods[i];
+
+ const auto test_module = [&](size_t i, const utils::loaded_module & mod) {
std::filesystem::path path;
try {
path = mod.path();
logging::I("{} [{}/{}] Module 0x{:X} ~ 0x{:X} (0x{:X}): \"{}\"", LogTagW, i + 1, mods.size(), mod.address_int(), mod.address_int() + mod.image_size(), mod.image_size(), path.wstring());
} catch (const std::exception& e) {
- logging::W("{} [{}/{}] Module 0x{:X}: Failed to resolve path: {}", LogTag, i + 1, mods.size(), mod.address_int(), e.what());
- continue;
+ logging::W("{} [{}/{}] Module 0x{:X}: Failed to resolve path: {}", LogTag, i + 1, mods.size(), mod.address_int(), e.what());
+ return;
}
const auto moduleName = unicode::convert(path.filename().wstring());
@@ -45,7 +44,7 @@ void xivfixes::unhook_dll(bool bApply) {
auto hFsDllRaw = CreateFileW(path.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr);
if (hFsDllRaw == INVALID_HANDLE_VALUE) {
logging::W("{} Module loaded in current process but could not open file: Win32 error {}", LogTag, GetLastError());
- continue;
+ return;
}
auto hFsDll = std::unique_ptr(hFsDllRaw, &CloseHandle);
@@ -54,11 +53,11 @@ void xivfixes::unhook_dll(bool bApply) {
if (DWORD read{}; ReadFile(hFsDll.get(), &buf[0], static_cast(buf.size()), &read, nullptr)) {
if (read < section.size_bytes()) {
logging::W("{} ReadFile: read {} bytes < requested {} bytes", LogTagW, read, section.size_bytes());
- continue;
+ return;
}
} else {
logging::I("{} ReadFile: Win32 error {}", LogTagW, GetLastError());
- continue;
+ return;
}
auto doRestore = false;
@@ -96,7 +95,7 @@ void xivfixes::unhook_dll(bool bApply) {
const auto names = mod.span_as(exportDirectory.AddressOfNames, exportDirectory.NumberOfNames);
const auto ordinals = mod.span_as(exportDirectory.AddressOfNameOrdinals, exportDirectory.NumberOfNames);
const auto functions = mod.span_as(exportDirectory.AddressOfFunctions, exportDirectory.NumberOfFunctions);
-
+
std::string resolvedExportName;
for (size_t j = 0; j < names.size(); ++j) {
std::string_view name;
@@ -109,7 +108,7 @@ void xivfixes::unhook_dll(bool bApply) {
name = std::string_view(pcszName, strnlen(pcszName, 256));
logging::W("{} Name #{} points to a seemingly valid address outside the executable: {}", LogTag, j, name);
}
-
+
if (ordinals[j] >= functions.size()) {
logging::W("{} Ordinal #{} points to function index #{} >= #{}. Skipping.", LogTag, j, ordinals[j], functions.size());
continue;
@@ -142,7 +141,20 @@ void xivfixes::unhook_dll(bool bApply) {
} catch (const std::exception& e) {
logging::W("{} Error: {}", LogTag, e.what());
}
- }
+ };
+
+ const auto aaaa = [&]() {
+ for (size_t i = 0; i < mods.size(); i++) {
+ const auto& mod = mods[i];
+ __try {
+ test_module(i, mod);
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ logging::W("{} Error: Access Violation", LogTag);
+ }
+ }
+ };
+
+ aaaa();
}
using TFnGetInputDeviceManager = void* ();
@@ -196,11 +208,12 @@ void xivfixes::prevent_devicechange_crashes(bool bApply) {
logging::I(R"({} 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("FFXIVGAME:WndProc (prevent_devicechange_crashes)", hWnd);
s_hookWndProc->set_detour([](HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -> LRESULT {
- if (uMsg == WM_DEVICECHANGE && wParam == DBT_DEVNODES_CHANGED) {
+
+ if (uMsg == WM_CREATE) {
+ s_hookCreateWindowExA.reset();
+ } else if (uMsg == WM_DEVICECHANGE && wParam == DBT_DEVNODES_CHANGED) {
if (!GetGetInputDeviceManager(hWnd)()) {
logging::I("{} 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;
@@ -229,6 +242,64 @@ void xivfixes::prevent_devicechange_crashes(bool bApply) {
}
}
+static bool is_xivalex(const std::filesystem::path& dllPath) {
+ DWORD verHandle = 0;
+ std::vector block;
+ block.resize(GetFileVersionInfoSizeW(dllPath.c_str(), &verHandle));
+ if (block.empty())
+ return false;
+ if (!GetFileVersionInfoW(dllPath.c_str(), 0, static_cast(block.size()), &block[0]))
+ return false;
+ struct LANGANDCODEPAGE {
+ WORD wLanguage;
+ WORD wCodePage;
+ } * lpTranslate;
+ UINT cbTranslate;
+ if (!VerQueryValueW(&block[0],
+ TEXT("\\VarFileInfo\\Translation"),
+ reinterpret_cast(&lpTranslate),
+ &cbTranslate)) {
+ return false;
+ }
+
+ for (size_t i = 0; i < (cbTranslate / sizeof(struct LANGANDCODEPAGE)); i++) {
+ wchar_t* buf = nullptr;
+ UINT size = 0;
+ if (!VerQueryValueW(&block[0],
+ std::format(L"\\StringFileInfo\\{:04x}{:04x}\\FileDescription",
+ lpTranslate[i].wLanguage,
+ lpTranslate[i].wCodePage).c_str(),
+ reinterpret_cast(&buf),
+ &size)) {
+ continue;
+ }
+ auto currName = std::wstring_view(buf, size);
+ while (!currName.empty() && currName.back() == L'\0')
+ currName = currName.substr(0, currName.size() - 1);
+ if (currName.empty())
+ continue;
+ if (currName == L"XivAlexander Main DLL")
+ return true;
+ }
+ return false;
+}
+
+static bool is_openprocess_already_dealt_with() {
+ static const auto s_value = [] {
+ for (const auto& mod : utils::loaded_module::all_modules()) {
+ try {
+ if (is_xivalex(mod.path()))
+ return true;
+
+ } catch (...) {
+ // pass
+ }
+ }
+ return false;
+ }();
+ return s_value;
+}
+
void xivfixes::disable_game_openprocess_access_check(bool bApply) {
static const char* LogTag = "[xivfixes:disable_game_openprocess_access_check]";
static std::optional> s_hook;
@@ -238,6 +309,10 @@ void xivfixes::disable_game_openprocess_access_check(bool bApply) {
logging::I("{} Turned off via environment variable.", LogTag);
return;
}
+ if (is_openprocess_already_dealt_with()) {
+ logging::I("{} Someone else already did it.", LogTag);
+ return;
+ }
s_hook.emplace("kernel32.dll!OpenProcess (import, disable_game_openprocess_access_check)", "kernel32.dll", "OpenProcess", 0);
s_hook->set_detour([](DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId)->HANDLE {
@@ -275,6 +350,10 @@ void xivfixes::redirect_openprocess(bool bApply) {
logging::I("{} Turned off via environment variable.", LogTag);
return;
}
+ if (is_openprocess_already_dealt_with()) {
+ logging::I("{} Someone else already did it.", LogTag);
+ return;
+ }
if (bootconfig::dotnet_openprocess_hook_mode() == bootconfig::ImportHooks) {
auto hook = std::make_shared>("kernel32.dll!OpenProcess (global import, redirect_openprocess)", L"kernel32.dll", "OpenProcess");