Merge pull request #5582 from shuffle2/bad-dll-warnings
Hotpatch EZFRD64 to fix heap-related crashes, improve the UCRT patch
This commit is contained in:
commit
9c8e26bdff
|
@ -96,7 +96,7 @@ if(USE_EGL)
|
|||
endif()
|
||||
|
||||
if(WIN32)
|
||||
set(SRCS ${SRCS} GL/GLInterface/WGL.cpp)
|
||||
set(SRCS ${SRCS} GL/GLInterface/WGL.cpp CompatPatches.cpp)
|
||||
elseif(APPLE)
|
||||
set(SRCS ${SRCS} GL/GLInterface/AGL.mm)
|
||||
elseif(HAIKU)
|
||||
|
@ -125,4 +125,6 @@ if(UNIX)
|
|||
if(HAIKU)
|
||||
target_link_libraries(traversal_server network)
|
||||
endif()
|
||||
elseif(WIN32)
|
||||
target_link_libraries(common PRIVATE "-INCLUDE:enableCompatPatches")
|
||||
endif()
|
||||
|
|
|
@ -125,6 +125,7 @@
|
|||
<ClInclude Include="HttpRequest.h" />
|
||||
<ClInclude Include="IniFile.h" />
|
||||
<ClInclude Include="JitRegister.h" />
|
||||
<ClInclude Include="LdrWatcher.h" />
|
||||
<ClInclude Include="LinearDiskCache.h" />
|
||||
<ClInclude Include="MathUtil.h" />
|
||||
<ClInclude Include="MD5.h" />
|
||||
|
@ -163,6 +164,7 @@
|
|||
<ClCompile Include="CDUtils.cpp" />
|
||||
<ClCompile Include="ColorUtil.cpp" />
|
||||
<ClCompile Include="CommonFuncs.cpp" />
|
||||
<ClCompile Include="CompatPatches.cpp" />
|
||||
<ClCompile Include="Config\Config.cpp" />
|
||||
<ClCompile Include="Config\Layer.cpp" />
|
||||
<ClCompile Include="Config\Section.cpp" />
|
||||
|
@ -179,6 +181,7 @@
|
|||
<ClCompile Include="HttpRequest.cpp" />
|
||||
<ClCompile Include="IniFile.cpp" />
|
||||
<ClCompile Include="JitRegister.cpp" />
|
||||
<ClCompile Include="LdrWatcher.cpp" />
|
||||
<ClCompile Include="Logging\ConsoleListenerWin.cpp" />
|
||||
<ClCompile Include="MathUtil.cpp" />
|
||||
<ClCompile Include="MD5.cpp" />
|
||||
|
@ -197,7 +200,6 @@
|
|||
<ClCompile Include="Thread.cpp" />
|
||||
<ClCompile Include="Timer.cpp" />
|
||||
<ClCompile Include="TraversalClient.cpp" />
|
||||
<ClCompile Include="ucrtFreadWorkaround.cpp" />
|
||||
<ClCompile Include="Version.cpp" />
|
||||
<ClCompile Include="x64ABI.cpp" />
|
||||
<ClCompile Include="x64CPUDetect.cpp" />
|
||||
|
@ -231,4 +233,4 @@
|
|||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
</Project>
|
|
@ -253,6 +253,7 @@
|
|||
<Filter>GL\GLExtensions</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="File.h" />
|
||||
<ClInclude Include="LdrWatcher.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="CDUtils.cpp" />
|
||||
|
@ -317,10 +318,11 @@
|
|||
<ClCompile Include="GL\GLInterface\GLInterface.cpp">
|
||||
<Filter>GL\GLInterface</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ucrtFreadWorkaround.cpp" />
|
||||
<ClCompile Include="Analytics.cpp" />
|
||||
<ClCompile Include="MD5.cpp" />
|
||||
<ClCompile Include="File.cpp" />
|
||||
<ClCompile Include="LdrWatcher.cpp" />
|
||||
<ClCompile Include="CompatPatches.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Text Include="CMakeLists.txt" />
|
||||
|
@ -328,4 +330,4 @@
|
|||
<ItemGroup>
|
||||
<Natvis Include="BitField.natvis" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
|
@ -0,0 +1,274 @@
|
|||
// Copyright 2008 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <Windows.h>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <winternl.h>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/LdrWatcher.h"
|
||||
#include "Common/StringUtil.h"
|
||||
|
||||
typedef NTSTATUS(NTAPI* PRTL_HEAP_COMMIT_ROUTINE)(IN PVOID Base, IN OUT PVOID* CommitAddress,
|
||||
IN OUT PSIZE_T CommitSize);
|
||||
|
||||
typedef struct _RTL_HEAP_PARAMETERS
|
||||
{
|
||||
ULONG Length;
|
||||
SIZE_T SegmentReserve;
|
||||
SIZE_T SegmentCommit;
|
||||
SIZE_T DeCommitFreeBlockThreshold;
|
||||
SIZE_T DeCommitTotalFreeThreshold;
|
||||
SIZE_T MaximumAllocationSize;
|
||||
SIZE_T VirtualMemoryThreshold;
|
||||
SIZE_T InitialCommit;
|
||||
SIZE_T InitialReserve;
|
||||
PRTL_HEAP_COMMIT_ROUTINE CommitRoutine;
|
||||
SIZE_T Reserved[2];
|
||||
} RTL_HEAP_PARAMETERS, *PRTL_HEAP_PARAMETERS;
|
||||
|
||||
typedef PVOID (*RtlCreateHeap_t)(_In_ ULONG Flags, _In_opt_ PVOID HeapBase,
|
||||
_In_opt_ SIZE_T ReserveSize, _In_opt_ SIZE_T CommitSize,
|
||||
_In_opt_ PVOID Lock, _In_opt_ PRTL_HEAP_PARAMETERS Parameters);
|
||||
|
||||
static HANDLE WINAPI HeapCreateLow4GB(_In_ DWORD flOptions, _In_ SIZE_T dwInitialSize,
|
||||
_In_ SIZE_T dwMaximumSize)
|
||||
{
|
||||
auto ntdll = GetModuleHandleW(L"ntdll");
|
||||
if (!ntdll)
|
||||
return nullptr;
|
||||
auto RtlCreateHeap = reinterpret_cast<RtlCreateHeap_t>(GetProcAddress(ntdll, "RtlCreateHeap"));
|
||||
if (!RtlCreateHeap)
|
||||
return nullptr;
|
||||
// These values are arbitrary; just change them if problems are encountered later.
|
||||
uintptr_t target_addr = 0x00200000;
|
||||
size_t max_heap_size = 0x01000000;
|
||||
uintptr_t highest_addr = (1ull << 32) - max_heap_size;
|
||||
void* low_heap = nullptr;
|
||||
for (; !low_heap && target_addr <= highest_addr; target_addr += 0x1000)
|
||||
low_heap = VirtualAlloc((void*)target_addr, max_heap_size, MEM_RESERVE, PAGE_READWRITE);
|
||||
if (!low_heap)
|
||||
return nullptr;
|
||||
return RtlCreateHeap(0, low_heap, 0, 0, nullptr, nullptr);
|
||||
}
|
||||
|
||||
static bool ModifyProtectedRegion(void* address, size_t size, std::function<void()> func)
|
||||
{
|
||||
DWORD old_protect;
|
||||
if (!VirtualProtect(address, size, PAGE_READWRITE, &old_protect))
|
||||
return false;
|
||||
func();
|
||||
if (!VirtualProtect(address, size, old_protect, &old_protect))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Does not do input sanitization - assumes well-behaved input since Ldr has already parsed it.
|
||||
class ImportPatcher
|
||||
{
|
||||
public:
|
||||
ImportPatcher(uintptr_t module_base) : base(module_base)
|
||||
{
|
||||
auto mz = reinterpret_cast<PIMAGE_DOS_HEADER>(base);
|
||||
auto pe = reinterpret_cast<PIMAGE_NT_HEADERS>(base + mz->e_lfanew);
|
||||
directories = pe->OptionalHeader.DataDirectory;
|
||||
}
|
||||
template <typename T>
|
||||
T GetRva(uint32_t rva)
|
||||
{
|
||||
return reinterpret_cast<T>(base + rva);
|
||||
}
|
||||
bool PatchIAT(const char* module_name, const char* function_name, void* value)
|
||||
{
|
||||
auto import_dir = &directories[IMAGE_DIRECTORY_ENTRY_IMPORT];
|
||||
for (auto import_desc = GetRva<PIMAGE_IMPORT_DESCRIPTOR>(import_dir->VirtualAddress);
|
||||
import_desc->OriginalFirstThunk; import_desc++)
|
||||
{
|
||||
auto module = GetRva<const char*>(import_desc->Name);
|
||||
auto names = GetRva<PIMAGE_THUNK_DATA>(import_desc->OriginalFirstThunk);
|
||||
auto thunks = GetRva<PIMAGE_THUNK_DATA>(import_desc->FirstThunk);
|
||||
if (!stricmp(module, module_name))
|
||||
{
|
||||
for (auto name = names; name->u1.Function; name++)
|
||||
{
|
||||
if (!IMAGE_SNAP_BY_ORDINAL(name->u1.Ordinal))
|
||||
{
|
||||
auto import = GetRva<PIMAGE_IMPORT_BY_NAME>(name->u1.AddressOfData);
|
||||
if (!strcmp(import->Name, function_name))
|
||||
{
|
||||
auto index = name - names;
|
||||
return ModifyProtectedRegion(&thunks[index], sizeof(thunks[index]), [=] {
|
||||
thunks[index].u1.Function =
|
||||
reinterpret_cast<decltype(thunks[index].u1.Function)>(value);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
// Function not found
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Module not found
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
uintptr_t base;
|
||||
PIMAGE_DATA_DIRECTORY directories;
|
||||
};
|
||||
|
||||
struct UcrtPatchInfo
|
||||
{
|
||||
u32 checksum;
|
||||
u32 rva;
|
||||
u32 length;
|
||||
};
|
||||
|
||||
bool ApplyUcrtPatch(const wchar_t* name, const UcrtPatchInfo& patch)
|
||||
{
|
||||
auto module = GetModuleHandleW(name);
|
||||
if (!module)
|
||||
return false;
|
||||
auto pe = (PIMAGE_NT_HEADERS)((uintptr_t)module + ((PIMAGE_DOS_HEADER)module)->e_lfanew);
|
||||
if (pe->OptionalHeader.CheckSum != patch.checksum)
|
||||
return false;
|
||||
void* patch_addr = (void*)((uintptr_t)module + patch.rva);
|
||||
size_t patch_size = patch.length;
|
||||
ModifyProtectedRegion(patch_addr, patch_size, [=] { memset(patch_addr, 0x90, patch_size); });
|
||||
FlushInstructionCache(GetCurrentProcess(), patch_addr, patch_size);
|
||||
return true;
|
||||
}
|
||||
|
||||
#pragma comment(lib, "version.lib")
|
||||
|
||||
struct Version
|
||||
{
|
||||
u16 major;
|
||||
u16 minor;
|
||||
u16 build;
|
||||
u16 qfe;
|
||||
Version& operator=(u64&& rhs)
|
||||
{
|
||||
major = static_cast<u16>(rhs >> 48);
|
||||
minor = static_cast<u16>(rhs >> 32);
|
||||
build = static_cast<u16>(rhs >> 16);
|
||||
qfe = static_cast<u16>(rhs);
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
static bool GetModulePath(const wchar_t* name, std::wstring* path)
|
||||
{
|
||||
auto module = GetModuleHandleW(name);
|
||||
if (module == nullptr)
|
||||
return false;
|
||||
DWORD path_len = MAX_PATH;
|
||||
retry:
|
||||
path->resize(path_len);
|
||||
path_len = GetModuleFileNameW(module, const_cast<wchar_t*>(path->data()),
|
||||
static_cast<DWORD>(path->size()));
|
||||
if (!path_len)
|
||||
return false;
|
||||
auto error = GetLastError();
|
||||
if (error == ERROR_SUCCESS)
|
||||
return true;
|
||||
if (error == ERROR_INSUFFICIENT_BUFFER)
|
||||
goto retry;
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool GetModuleVersion(const wchar_t* name, Version* version)
|
||||
{
|
||||
std::wstring path;
|
||||
if (!GetModulePath(name, &path))
|
||||
return false;
|
||||
DWORD handle;
|
||||
DWORD data_len = GetFileVersionInfoSizeW(path.c_str(), &handle);
|
||||
if (!data_len)
|
||||
return false;
|
||||
std::vector<u8> block(data_len);
|
||||
if (!GetFileVersionInfoW(path.c_str(), handle, data_len, block.data()))
|
||||
return false;
|
||||
void* buf;
|
||||
UINT buf_len;
|
||||
if (!VerQueryValueW(block.data(), LR"(\)", &buf, &buf_len))
|
||||
return false;
|
||||
auto info = static_cast<VS_FIXEDFILEINFO*>(buf);
|
||||
*version = (static_cast<u64>(info->dwFileVersionMS) << 32) | info->dwFileVersionLS;
|
||||
return true;
|
||||
}
|
||||
|
||||
void CompatPatchesInstall(LdrWatcher* watcher)
|
||||
{
|
||||
watcher->Install({{L"EZFRD64.dll", L"811EZFRD64.DLL"},
|
||||
[](const LdrDllLoadEvent& event) {
|
||||
// *EZFRD64 is incldued in software packages for cheapo third-party gamepads
|
||||
// (and gamepad adapters). The module cannot handle its heap being above 4GB,
|
||||
// which tends to happen very often on modern Windows.
|
||||
// NOTE: The patch will always be applied, but it will only actually avoid the
|
||||
// crash if applied before module initialization (i.e. called on the Ldr
|
||||
// callout path).
|
||||
auto patcher = ImportPatcher(event.base_address);
|
||||
patcher.PatchIAT("kernel32.dll", "HeapCreate", HeapCreateLow4GB);
|
||||
}});
|
||||
watcher->Install({{L"ucrtbase.dll"},
|
||||
[](const LdrDllLoadEvent& event) {
|
||||
// ucrtbase implements caching between fseek/fread, old versions have a bug
|
||||
// such that some reads return incorrect data. This causes noticable bugs
|
||||
// in dolphin since we use these APIs for reading game images.
|
||||
Version version;
|
||||
if (!GetModuleVersion(event.name.c_str(), &version))
|
||||
return;
|
||||
const u16 fixed_build = 10548;
|
||||
if (version.build >= fixed_build)
|
||||
return;
|
||||
const UcrtPatchInfo patches[] = {
|
||||
// 10.0.10240.16384 (th1.150709-1700)
|
||||
{0xF61ED, 0x6AE7B, 5},
|
||||
// 10.0.10240.16390 (th1_st1.150714-1601)
|
||||
{0xF5ED9, 0x6AE7B, 5},
|
||||
// 10.0.10137.0 (th1.150602-2238)
|
||||
{0xF8B5E, 0x63ED6, 2},
|
||||
};
|
||||
for (const auto& patch : patches)
|
||||
{
|
||||
if (ApplyUcrtPatch(event.name.c_str(), patch))
|
||||
return;
|
||||
}
|
||||
// If we reach here, the version is buggy (afaik) and patching failed
|
||||
auto msg = StringFromFormat(
|
||||
"You are running %S version %d.%d.%d.%d.\n"
|
||||
"An important fix affecting Dolphin was introduced in build %d.\n"
|
||||
"You can use Dolphin, but there will be known bugs.\n"
|
||||
"Please update this file by installing the latest Universal C Runtime.\n",
|
||||
event.name.c_str(), version.major, version.minor, version.build,
|
||||
version.qfe, fixed_build);
|
||||
// Use MessageBox for maximal user annoyance
|
||||
MessageBoxA(nullptr, msg.c_str(), "WARNING: BUGGY UCRT VERSION",
|
||||
MB_ICONEXCLAMATION);
|
||||
}});
|
||||
}
|
||||
|
||||
int __cdecl EnableCompatPatches()
|
||||
{
|
||||
static LdrWatcher watcher;
|
||||
CompatPatchesInstall(&watcher);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Create a segment which is recognized by the linker to be part of the CRT
|
||||
// initialization. XI* = C startup, XC* = C++ startup. "A" placement is reserved
|
||||
// for system use. C startup is before C++.
|
||||
// Use last C++ slot in hopes that makes using C++ from this code safe.
|
||||
#pragma section(".CRT$XCZ", read)
|
||||
|
||||
// Place a symbol in the special segment, make it have C linkage so that
|
||||
// referencing it doesn't require ugly decorated names.
|
||||
// Use /include:enableCompatPatches linker flag to enable this.
|
||||
extern "C" {
|
||||
__declspec(allocate(".CRT$XCZ")) decltype(&EnableCompatPatches)
|
||||
enableCompatPatches = EnableCompatPatches;
|
||||
};
|
|
@ -0,0 +1,178 @@
|
|||
// Copyright 2008 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <Windows.h>
|
||||
#include <TlHelp32.h>
|
||||
#include <string>
|
||||
#include <winternl.h>
|
||||
|
||||
#include "Common/LdrWatcher.h"
|
||||
|
||||
typedef struct _LDR_DLL_LOADED_NOTIFICATION_DATA
|
||||
{
|
||||
ULONG Flags; // Reserved.
|
||||
PCUNICODE_STRING FullDllName; // The full path name of the DLL module.
|
||||
PCUNICODE_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.
|
||||
} LDR_DLL_LOADED_NOTIFICATION_DATA, *PLDR_DLL_LOADED_NOTIFICATION_DATA;
|
||||
|
||||
typedef struct _LDR_DLL_UNLOADED_NOTIFICATION_DATA
|
||||
{
|
||||
ULONG Flags; // Reserved.
|
||||
PCUNICODE_STRING FullDllName; // The full path name of the DLL module.
|
||||
PCUNICODE_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.
|
||||
} LDR_DLL_UNLOADED_NOTIFICATION_DATA, *PLDR_DLL_UNLOADED_NOTIFICATION_DATA;
|
||||
|
||||
typedef union _LDR_DLL_NOTIFICATION_DATA
|
||||
{
|
||||
LDR_DLL_LOADED_NOTIFICATION_DATA Loaded;
|
||||
LDR_DLL_UNLOADED_NOTIFICATION_DATA Unloaded;
|
||||
} LDR_DLL_NOTIFICATION_DATA, *PLDR_DLL_NOTIFICATION_DATA;
|
||||
typedef const LDR_DLL_NOTIFICATION_DATA* PCLDR_DLL_NOTIFICATION_DATA;
|
||||
|
||||
#define LDR_DLL_NOTIFICATION_REASON_LOADED (1)
|
||||
#define LDR_DLL_NOTIFICATION_REASON_UNLOADED (2)
|
||||
|
||||
typedef VOID NTAPI LDR_DLL_NOTIFICATION_FUNCTION(_In_ ULONG NotificationReason,
|
||||
_In_ PCLDR_DLL_NOTIFICATION_DATA NotificationData,
|
||||
_In_opt_ PVOID Context);
|
||||
typedef LDR_DLL_NOTIFICATION_FUNCTION* PLDR_DLL_NOTIFICATION_FUNCTION;
|
||||
|
||||
typedef NTSTATUS(NTAPI* LdrRegisterDllNotification_t)(
|
||||
_In_ ULONG Flags, _In_ PLDR_DLL_NOTIFICATION_FUNCTION NotificationFunction,
|
||||
_In_opt_ PVOID Context, _Out_ PVOID* Cookie);
|
||||
|
||||
typedef NTSTATUS(NTAPI* LdrUnregisterDllNotification_t)(_In_ PVOID Cookie);
|
||||
|
||||
static void LdrObserverRun(const LdrObserver& observer, PCUNICODE_STRING module_name,
|
||||
uintptr_t base_address)
|
||||
{
|
||||
for (auto& needle : observer.module_names)
|
||||
{
|
||||
// Like RtlCompareUnicodeString, but saves dynamically resolving it.
|
||||
// NOTE: Does not compare null terminator.
|
||||
auto compare_length = module_name->Length / sizeof(wchar_t);
|
||||
if (!_wcsnicmp(needle.c_str(), module_name->Buffer, compare_length))
|
||||
observer.action({needle, base_address});
|
||||
}
|
||||
}
|
||||
|
||||
static VOID DllNotificationCallback(ULONG NotificationReason,
|
||||
PCLDR_DLL_NOTIFICATION_DATA NotificationData, PVOID Context)
|
||||
{
|
||||
if (NotificationReason != LDR_DLL_NOTIFICATION_REASON_LOADED)
|
||||
return;
|
||||
auto& data = NotificationData->Loaded;
|
||||
auto observer = static_cast<const LdrObserver*>(Context);
|
||||
LdrObserverRun(*observer, data.BaseDllName, reinterpret_cast<uintptr_t>(data.DllBase));
|
||||
}
|
||||
|
||||
// This only works on Vista+. On lower platforms, it will be a no-op.
|
||||
class LdrDllNotifier
|
||||
{
|
||||
public:
|
||||
static LdrDllNotifier& GetInstance()
|
||||
{
|
||||
static LdrDllNotifier notifier;
|
||||
return notifier;
|
||||
};
|
||||
void Install(LdrObserver* observer);
|
||||
void Uninstall(LdrObserver* observer);
|
||||
|
||||
private:
|
||||
LdrDllNotifier();
|
||||
bool Init();
|
||||
LdrRegisterDllNotification_t LdrRegisterDllNotification{};
|
||||
LdrUnregisterDllNotification_t LdrUnregisterDllNotification{};
|
||||
bool initialized{};
|
||||
};
|
||||
|
||||
LdrDllNotifier::LdrDllNotifier()
|
||||
{
|
||||
initialized = Init();
|
||||
}
|
||||
|
||||
bool LdrDllNotifier::Init()
|
||||
{
|
||||
auto ntdll = GetModuleHandleW(L"ntdll");
|
||||
if (!ntdll)
|
||||
return false;
|
||||
LdrRegisterDllNotification = reinterpret_cast<decltype(LdrRegisterDllNotification)>(
|
||||
GetProcAddress(ntdll, "LdrRegisterDllNotification"));
|
||||
if (!LdrRegisterDllNotification)
|
||||
return false;
|
||||
LdrUnregisterDllNotification = reinterpret_cast<decltype(LdrUnregisterDllNotification)>(
|
||||
GetProcAddress(ntdll, "LdrUnregisterDllNotification"));
|
||||
if (!LdrUnregisterDllNotification)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void LdrDllNotifier::Install(LdrObserver* observer)
|
||||
{
|
||||
if (!initialized)
|
||||
return;
|
||||
void* cookie{};
|
||||
if (!NT_SUCCESS(LdrRegisterDllNotification(0, DllNotificationCallback,
|
||||
static_cast<PVOID>(observer), &cookie)))
|
||||
cookie = {};
|
||||
observer->cookie = cookie;
|
||||
return;
|
||||
}
|
||||
|
||||
void LdrDllNotifier::Uninstall(LdrObserver* observer)
|
||||
{
|
||||
if (!initialized)
|
||||
return;
|
||||
LdrUnregisterDllNotification(observer->cookie);
|
||||
observer->cookie = {};
|
||||
return;
|
||||
}
|
||||
|
||||
LdrWatcher::~LdrWatcher()
|
||||
{
|
||||
UninstallAll();
|
||||
}
|
||||
|
||||
// Needed for RtlInitUnicodeString
|
||||
#pragma comment(lib, "ntdll")
|
||||
|
||||
bool LdrWatcher::InjectCurrentModules(const LdrObserver& observer)
|
||||
{
|
||||
// Use TlHelp32 instead of psapi functions to reduce dolphin's dependency on psapi
|
||||
// (revisit this when Win7 support is dropped).
|
||||
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetCurrentProcessId());
|
||||
if (snapshot == INVALID_HANDLE_VALUE)
|
||||
return false;
|
||||
MODULEENTRY32 entry;
|
||||
entry.dwSize = sizeof(entry);
|
||||
for (BOOL rv = Module32First(snapshot, &entry); rv == TRUE; rv = Module32Next(snapshot, &entry))
|
||||
{
|
||||
UNICODE_STRING module_name;
|
||||
RtlInitUnicodeString(&module_name, entry.szModule);
|
||||
LdrObserverRun(observer, &module_name, reinterpret_cast<uintptr_t>(entry.modBaseAddr));
|
||||
}
|
||||
CloseHandle(snapshot);
|
||||
return true;
|
||||
}
|
||||
|
||||
void LdrWatcher::Install(const LdrObserver& observer)
|
||||
{
|
||||
observers.emplace_back(observer);
|
||||
auto& new_observer = observers.back();
|
||||
// Register for notifications before looking at the list of current modules.
|
||||
// This ensures none are missed, but there is a tiny chance some will be seen twice.
|
||||
LdrDllNotifier::GetInstance().Install(&new_observer);
|
||||
InjectCurrentModules(new_observer);
|
||||
}
|
||||
|
||||
void LdrWatcher::UninstallAll()
|
||||
{
|
||||
for (auto& observer : observers)
|
||||
LdrDllNotifier::GetInstance().Uninstall(&observer);
|
||||
observers.clear();
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
// Copyright 2008 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
struct LdrDllLoadEvent
|
||||
{
|
||||
const std::wstring& name;
|
||||
uintptr_t base_address;
|
||||
};
|
||||
|
||||
class LdrObserver
|
||||
{
|
||||
public:
|
||||
std::vector<std::wstring> module_names;
|
||||
// NOTE: This may be called from a Ldr callout. While most things are probably fine, try to
|
||||
// keep things as simple as possible, and just queue real work onto something else.
|
||||
std::function<void(const LdrDllLoadEvent&)> action;
|
||||
void* cookie{};
|
||||
};
|
||||
|
||||
class LdrWatcher
|
||||
{
|
||||
public:
|
||||
void Install(const LdrObserver& observer);
|
||||
~LdrWatcher();
|
||||
|
||||
private:
|
||||
bool InjectCurrentModules(const LdrObserver& observer);
|
||||
void UninstallAll();
|
||||
std::list<LdrObserver> observers;
|
||||
};
|
|
@ -1,96 +0,0 @@
|
|||
// Copyright 2014 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#if defined(_WIN32)
|
||||
|
||||
#include <Windows.h>
|
||||
#include "CommonTypes.h"
|
||||
|
||||
struct PatchInfo
|
||||
{
|
||||
const wchar_t* module_name;
|
||||
u32 checksum;
|
||||
u32 rva;
|
||||
u32 length;
|
||||
} static const s_patches[] = {
|
||||
// 10.0.10240.16384 (th1.150709-1700)
|
||||
{L"ucrtbase.dll", 0xF61ED, 0x6AE7B, 5},
|
||||
// 10.0.10240.16390 (th1_st1.150714-1601)
|
||||
{L"ucrtbase.dll", 0xF5ED9, 0x6AE7B, 5},
|
||||
// 10.0.10137.0 (th1.150602-2238)
|
||||
{L"ucrtbase.dll", 0xF8B5E, 0x63ED6, 2},
|
||||
// 10.0.10150.0 (th1.150616-1659)
|
||||
{L"ucrtbased.dll", 0x1C1915, 0x91905, 5},
|
||||
};
|
||||
|
||||
bool ApplyPatch(const PatchInfo& patch)
|
||||
{
|
||||
auto module = GetModuleHandleW(patch.module_name);
|
||||
if (module == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto ucrtbase_pe = (PIMAGE_NT_HEADERS)((uintptr_t)module + ((PIMAGE_DOS_HEADER)module)->e_lfanew);
|
||||
if (ucrtbase_pe->OptionalHeader.CheckSum != patch.checksum)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void* patch_addr = (void*)((uintptr_t)module + patch.rva);
|
||||
size_t patch_size = patch.length;
|
||||
|
||||
DWORD old_protect;
|
||||
if (!VirtualProtect(patch_addr, patch_size, PAGE_EXECUTE_READWRITE, &old_protect))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
memset(patch_addr, 0x90, patch_size);
|
||||
|
||||
VirtualProtect(patch_addr, patch_size, old_protect, &old_protect);
|
||||
|
||||
FlushInstructionCache(GetCurrentProcess(), patch_addr, patch_size);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int __cdecl EnableucrtFreadWorkaround()
|
||||
{
|
||||
// This patches ucrtbase such that fseek will always
|
||||
// synchronize the file object's internal buffer.
|
||||
|
||||
bool applied_at_least_one = false;
|
||||
for (const auto& patch : s_patches)
|
||||
{
|
||||
if (ApplyPatch(patch))
|
||||
{
|
||||
applied_at_least_one = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* For forward compat, do not fail if patches don't apply (e.g. version mismatch)
|
||||
if (!applied_at_least_one) {
|
||||
std::abort();
|
||||
}
|
||||
//*/
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Create a segment which is recognized by the linker to be part of the CRT
|
||||
// initialization. XI* = C startup, XC* = C++ startup. "A" placement is reserved
|
||||
// for system use. Thus, the earliest we can get is XIB (C startup is before
|
||||
// C++).
|
||||
#pragma section(".CRT$XIB", read)
|
||||
|
||||
// Place a symbol in the special segment, make it have C linkage so that
|
||||
// referencing it doesn't require ugly decorated names.
|
||||
// Use /include:EnableucrtFreadWorkaround linker flag to enable this.
|
||||
extern "C" {
|
||||
__declspec(allocate(".CRT$XIB")) decltype(&EnableucrtFreadWorkaround)
|
||||
ucrtFreadWorkaround = EnableucrtFreadWorkaround;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -157,8 +157,8 @@
|
|||
</Link>
|
||||
<!--Link Base:Application-->
|
||||
<Link Condition="'$(ConfigurationType)'=='Application'">
|
||||
<!--See Common/ucrtFreadWorkaround.cpp-->
|
||||
<ForceSymbolReferences>ucrtFreadWorkaround</ForceSymbolReferences>
|
||||
<!--See Common/CompatPatches.cpp-->
|
||||
<ForceSymbolReferences>enableCompatPatches</ForceSymbolReferences>
|
||||
<!--TODO fix up ffmpeg garbage-->
|
||||
<AdditionalOptions>/NODEFAULTLIB:libcmt %(AdditionalOptions)</AdditionalOptions>
|
||||
</Link>
|
||||
|
|
Loading…
Reference in New Issue