From 3d59fa3da0a2d5292d0517c5d5ba59bdc26a3638 Mon Sep 17 00:00:00 2001 From: srkizer Date: Fri, 1 Mar 2024 08:13:33 +0900 Subject: [PATCH] Sanitize PDB root name from loaded modules (#1687) --- Dalamud.Boot/Dalamud.Boot.vcxproj | 4 +- Dalamud.Boot/Dalamud.Boot.vcxproj.filters | 6 ++ Dalamud.Boot/hooks.cpp | 32 +------ Dalamud.Boot/hooks.h | 1 - Dalamud.Boot/ntdll.cpp | 15 +++ Dalamud.Boot/ntdll.h | 33 +++++++ Dalamud.Boot/pch.h | 7 ++ Dalamud.Boot/xivfixes.cpp | 109 +++++++++++++++++++++- Dalamud.Boot/xivfixes.h | 1 + Dalamud.Injector/EntryPoint.cs | 12 ++- 10 files changed, 181 insertions(+), 39 deletions(-) create mode 100644 Dalamud.Boot/ntdll.cpp create mode 100644 Dalamud.Boot/ntdll.h diff --git a/Dalamud.Boot/Dalamud.Boot.vcxproj b/Dalamud.Boot/Dalamud.Boot.vcxproj index ab68c1ec0..298edbcbc 100644 --- a/Dalamud.Boot/Dalamud.Boot.vcxproj +++ b/Dalamud.Boot/Dalamud.Boot.vcxproj @@ -58,7 +58,7 @@ Windows true false - Version.lib;%(AdditionalDependencies) + Version.lib;Shlwapi.lib;%(AdditionalDependencies) ..\lib\CoreCLR;%(AdditionalLibraryDirectories) @@ -137,6 +137,7 @@ NotUsing NotUsing + NotUsing NotUsing @@ -176,6 +177,7 @@ + diff --git a/Dalamud.Boot/Dalamud.Boot.vcxproj.filters b/Dalamud.Boot/Dalamud.Boot.vcxproj.filters index a1b1650e2..87eaf6fcc 100644 --- a/Dalamud.Boot/Dalamud.Boot.vcxproj.filters +++ b/Dalamud.Boot/Dalamud.Boot.vcxproj.filters @@ -73,6 +73,9 @@ Dalamud.Boot DLL + + Dalamud.Boot DLL + @@ -140,6 +143,9 @@ + + Dalamud.Boot DLL + diff --git a/Dalamud.Boot/hooks.cpp b/Dalamud.Boot/hooks.cpp index 7cf489195..1b1280cf0 100644 --- a/Dalamud.Boot/hooks.cpp +++ b/Dalamud.Boot/hooks.cpp @@ -2,39 +2,9 @@ #include "hooks.h" +#include "ntdll.h" #include "logging.h" -enum { - LDR_DLL_NOTIFICATION_REASON_LOADED = 1, - LDR_DLL_NOTIFICATION_REASON_UNLOADED = 2, -}; - -struct LDR_DLL_UNLOADED_NOTIFICATION_DATA { - ULONG Flags; //Reserved. - const UNICODE_STRING* FullDllName; //The full path name of the DLL module. - const UNICODE_STRING* BaseDllName; //The base file name of the DLL module. - PVOID DllBase; //A pointer to the base address for the DLL in memory. - ULONG SizeOfImage; //The size of the DLL image, in bytes. -}; - -struct LDR_DLL_LOADED_NOTIFICATION_DATA { - ULONG Flags; //Reserved. - const UNICODE_STRING* FullDllName; //The full path name of the DLL module. - const UNICODE_STRING* BaseDllName; //The base file name of the DLL module. - PVOID DllBase; //A pointer to the base address for the DLL in memory. - ULONG SizeOfImage; //The size of the DLL image, in bytes. -}; - -union LDR_DLL_NOTIFICATION_DATA { - LDR_DLL_LOADED_NOTIFICATION_DATA Loaded; - LDR_DLL_UNLOADED_NOTIFICATION_DATA Unloaded; -}; - -using PLDR_DLL_NOTIFICATION_FUNCTION = VOID CALLBACK(_In_ ULONG NotificationReason, _In_ const LDR_DLL_NOTIFICATION_DATA* NotificationData, _In_opt_ PVOID Context); - -static const auto LdrRegisterDllNotification = utils::loaded_module(GetModuleHandleW(L"ntdll.dll")).get_exported_function("LdrRegisterDllNotification"); -static const auto LdrUnregisterDllNotification = utils::loaded_module(GetModuleHandleW(L"ntdll.dll")).get_exported_function("LdrUnregisterDllNotification"); - hooks::getprocaddress_singleton_import_hook::getprocaddress_singleton_import_hook() : m_pfnGetProcAddress(GetProcAddress) , m_thunk("kernel32!GetProcAddress(Singleton Import Hook)", diff --git a/Dalamud.Boot/hooks.h b/Dalamud.Boot/hooks.h index ad3b2cc6c..f6ad370d1 100644 --- a/Dalamud.Boot/hooks.h +++ b/Dalamud.Boot/hooks.h @@ -1,6 +1,5 @@ #pragma once -#include #include #include "utils.h" diff --git a/Dalamud.Boot/ntdll.cpp b/Dalamud.Boot/ntdll.cpp new file mode 100644 index 000000000..9bda0e1c4 --- /dev/null +++ b/Dalamud.Boot/ntdll.cpp @@ -0,0 +1,15 @@ +#include "pch.h" + +#include "ntdll.h" + +#include "utils.h" + +NTSTATUS LdrRegisterDllNotification(ULONG Flags, PLDR_DLL_NOTIFICATION_FUNCTION NotificationFunction, PVOID Context, PVOID* Cookie) { + static const auto pfn = utils::loaded_module(GetModuleHandleW(L"ntdll.dll")).get_exported_function("LdrRegisterDllNotification"); + return pfn(Flags, NotificationFunction, Context, Cookie); +} + +NTSTATUS LdrUnregisterDllNotification(PVOID Cookie) { + static const auto pfn = utils::loaded_module(GetModuleHandleW(L"ntdll.dll")).get_exported_function("LdrUnregisterDllNotification"); + return pfn(Cookie); +} diff --git a/Dalamud.Boot/ntdll.h b/Dalamud.Boot/ntdll.h new file mode 100644 index 000000000..c631475d1 --- /dev/null +++ b/Dalamud.Boot/ntdll.h @@ -0,0 +1,33 @@ +#pragma once + +// ntdll exports +enum { + LDR_DLL_NOTIFICATION_REASON_LOADED = 1, + LDR_DLL_NOTIFICATION_REASON_UNLOADED = 2, +}; + +struct LDR_DLL_UNLOADED_NOTIFICATION_DATA { + ULONG Flags; //Reserved. + const UNICODE_STRING* FullDllName; //The full path name of the DLL module. + const UNICODE_STRING* BaseDllName; //The base file name of the DLL module. + PVOID DllBase; //A pointer to the base address for the DLL in memory. + ULONG SizeOfImage; //The size of the DLL image, in bytes. +}; + +struct LDR_DLL_LOADED_NOTIFICATION_DATA { + ULONG Flags; //Reserved. + const UNICODE_STRING* FullDllName; //The full path name of the DLL module. + const UNICODE_STRING* BaseDllName; //The base file name of the DLL module. + PVOID DllBase; //A pointer to the base address for the DLL in memory. + ULONG SizeOfImage; //The size of the DLL image, in bytes. +}; + +union LDR_DLL_NOTIFICATION_DATA { + LDR_DLL_LOADED_NOTIFICATION_DATA Loaded; + LDR_DLL_UNLOADED_NOTIFICATION_DATA Unloaded; +}; + +using PLDR_DLL_NOTIFICATION_FUNCTION = VOID CALLBACK(_In_ ULONG NotificationReason, _In_ const LDR_DLL_NOTIFICATION_DATA* NotificationData, _In_opt_ PVOID Context); + +NTSTATUS LdrRegisterDllNotification(ULONG Flags, PLDR_DLL_NOTIFICATION_FUNCTION NotificationFunction, PVOID Context, PVOID* Cookie); +NTSTATUS LdrUnregisterDllNotification(PVOID Cookie); diff --git a/Dalamud.Boot/pch.h b/Dalamud.Boot/pch.h index a09882c74..c2194c157 100644 --- a/Dalamud.Boot/pch.h +++ b/Dalamud.Boot/pch.h @@ -15,14 +15,20 @@ #include // Windows Header Files (2) +#include #include #include +#include #include #include #include +#include #include #include +// Windows Header Files (3) +#include // Must be loaded after iphlpapi.h + // MSVC Compiler Intrinsic #include @@ -30,6 +36,7 @@ #include // C++ Standard Libraries +#include #include #include #include diff --git a/Dalamud.Boot/xivfixes.cpp b/Dalamud.Boot/xivfixes.cpp index 39cce53c9..f3b6aaa2c 100644 --- a/Dalamud.Boot/xivfixes.cpp +++ b/Dalamud.Boot/xivfixes.cpp @@ -5,9 +5,8 @@ #include "DalamudStartInfo.h" #include "hooks.h" #include "logging.h" +#include "ntdll.h" #include "utils.h" -#include -#include template static std::span assume_nonempty_span(std::span t, const char* descr) { @@ -546,6 +545,109 @@ void xivfixes::prevent_icmphandle_crashes(bool bApply) { } } +void xivfixes::symbol_load_patches(bool bApply) { + static const char* LogTag = "[xivfixes:symbol_load_patches]"; + + static std::optional> s_hookSymInitialize; + static PVOID s_dllNotificationCookie = nullptr; + + static const auto RemoveFullPathPdbInfo = [](const utils::loaded_module& mod) { + const auto ddva = mod.data_directory(IMAGE_DIRECTORY_ENTRY_DEBUG).VirtualAddress; + if (!ddva) + return; + + const auto& ddir = mod.ref_as(ddva); + if (ddir.Type == IMAGE_DEBUG_TYPE_CODEVIEW) { + // The Visual C++ debug information. + // Ghidra calls it "DotNetPdbInfo". + static constexpr DWORD DotNetPdbInfoSignatureValue = 0x53445352; + struct DotNetPdbInfo { + DWORD Signature; // RSDS + GUID Guid; + DWORD Age; + char PdbPath[1]; + }; + + const auto& pdbref = mod.ref_as(ddir.AddressOfRawData); + if (pdbref.Signature == DotNetPdbInfoSignatureValue) { + const auto pathSpan = std::string_view(pdbref.PdbPath, strlen(pdbref.PdbPath)); + const auto pathWide = unicode::convert(pathSpan); + std::wstring windowsDirectory(GetWindowsDirectoryW(nullptr, 0) + 1, L'\0'); + windowsDirectory.resize( + GetWindowsDirectoryW(windowsDirectory.data(), static_cast(windowsDirectory.size()))); + if (!PathIsRelativeW(pathWide.c_str()) && !PathIsSameRootW(windowsDirectory.c_str(), pathWide.c_str())) { + utils::memory_tenderizer pathOverwrite(&pdbref.PdbPath, pathSpan.size(), PAGE_READWRITE); + auto sep = std::find(pathSpan.rbegin(), pathSpan.rend(), '/'); + if (sep == pathSpan.rend()) + sep = std::find(pathSpan.rbegin(), pathSpan.rend(), '\\'); + if (sep != pathSpan.rend()) { + logging::I( + "{} Stripping pdb path folder: {} to {}", + LogTag, + pathSpan, + &*sep + 1); + memmove(const_cast(pathSpan.data()), &*sep + 1, sep - pathSpan.rbegin() + 1); + } else { + logging::I("{} Leaving pdb path unchanged: {}", LogTag, pathSpan); + } + } else { + logging::I("{} Leaving pdb path unchanged: {}", LogTag, pathSpan); + } + } else { + logging::I("{} CODEVIEW struct signature mismatch: got {:08X} instead.", LogTag, pdbref.Signature); + } + } else { + logging::I("{} Debug directory: type {} is unsupported.", LogTag, ddir.Type); + } + }; + + if (bApply) { + if (!g_startInfo.BootEnabledGameFixes.contains("symbol_load_patches")) { + logging::I("{} Turned off via environment variable.", LogTag); + return; + } + + for (const auto& mod : utils::loaded_module::all_modules()) + RemoveFullPathPdbInfo(mod); + + if (!s_dllNotificationCookie) { + const auto res = LdrRegisterDllNotification( + 0, + [](ULONG notiReason, const LDR_DLL_NOTIFICATION_DATA* pData, void* /* context */) { + if (notiReason == LDR_DLL_NOTIFICATION_REASON_LOADED) + RemoveFullPathPdbInfo(pData->Loaded.DllBase); + }, + nullptr, + &s_dllNotificationCookie); + + if (res != STATUS_SUCCESS) { + logging::E("{} LdrRegisterDllNotification failure: 0x{:08X}", LogTag, res); + s_dllNotificationCookie = nullptr; + } + } + + s_hookSymInitialize.emplace("dbghelp.dll!SymInitialize (import, symbol_load_patches)", "dbghelp.dll", "SymInitialize", 0); + s_hookSymInitialize->set_detour([](HANDLE hProcess, PCSTR UserSearchPath, BOOL fInvadeProcess) noexcept { + logging::I("{} Suppressed SymInitialize.", LogTag); + SetLastError(ERROR_NOT_SUPPORTED); + return FALSE; + }); + + logging::I("{} Enable", LogTag); + } + else { + if (s_hookSymInitialize) { + logging::I("{} Disable", LogTag); + s_hookSymInitialize.reset(); + } + + if (s_dllNotificationCookie) { + (void)LdrUnregisterDllNotification(s_dllNotificationCookie); + s_dllNotificationCookie = nullptr; + } + } +} + void xivfixes::apply_all(bool bApply) { for (const auto& [taskName, taskFunction] : std::initializer_list> { @@ -554,7 +656,8 @@ void xivfixes::apply_all(bool bApply) { { "disable_game_openprocess_access_check", &disable_game_openprocess_access_check }, { "redirect_openprocess", &redirect_openprocess }, { "backup_userdata_save", &backup_userdata_save }, - { "prevent_icmphandle_crashes", &prevent_icmphandle_crashes } + { "prevent_icmphandle_crashes", &prevent_icmphandle_crashes }, + { "symbol_load_patches", &symbol_load_patches }, } ) { try { diff --git a/Dalamud.Boot/xivfixes.h b/Dalamud.Boot/xivfixes.h index f534ad7dd..afe2edb45 100644 --- a/Dalamud.Boot/xivfixes.h +++ b/Dalamud.Boot/xivfixes.h @@ -7,6 +7,7 @@ namespace xivfixes { void redirect_openprocess(bool bApply); void backup_userdata_save(bool bApply); void prevent_icmphandle_crashes(bool bApply); + void symbol_load_patches(bool bApply); void apply_all(bool bApply); } diff --git a/Dalamud.Injector/EntryPoint.cs b/Dalamud.Injector/EntryPoint.cs index 2d776b043..9085eae04 100644 --- a/Dalamud.Injector/EntryPoint.cs +++ b/Dalamud.Injector/EntryPoint.cs @@ -395,9 +395,15 @@ namespace Dalamud.Injector startInfo.BootShowConsole = args.Contains("--console"); startInfo.BootEnableEtw = args.Contains("--etw"); startInfo.BootLogPath = GetLogPath(startInfo.LogPath, "dalamud.boot", startInfo.LogName); - startInfo.BootEnabledGameFixes = new List { - "prevent_devicechange_crashes", "disable_game_openprocess_access_check", - "redirect_openprocess", "backup_userdata_save", "prevent_icmphandle_crashes", + startInfo.BootEnabledGameFixes = new() + { + // See: xivfixes.h, xivfixes.cpp + "prevent_devicechange_crashes", + "disable_game_openprocess_access_check", + "redirect_openprocess", + "backup_userdata_save", + "prevent_icmphandle_crashes", + "symbol_load_patches", }; startInfo.BootDotnetOpenProcessHookMode = 0; startInfo.BootWaitMessageBox |= args.Contains("--msgbox1") ? 1 : 0;