mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-12 10:17:22 +01:00
Reduce usage of exceptions from Boot (#2373)
* wip * make pretty * Remove CRT version check from IM * fix * Simplify IsDebuggerPresent hook
This commit is contained in:
parent
f613b177a2
commit
9092e36b33
16 changed files with 494 additions and 295 deletions
|
|
@ -8,12 +8,6 @@
|
|||
#include "ntdll.h"
|
||||
#include "utils.h"
|
||||
|
||||
template<typename T>
|
||||
static std::span<T> assume_nonempty_span(std::span<T> t, const char* descr) {
|
||||
if (t.empty())
|
||||
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]";
|
||||
|
|
@ -23,77 +17,90 @@ void xivfixes::unhook_dll(bool bApply) {
|
|||
|
||||
const auto mods = utils::loaded_module::all_modules();
|
||||
|
||||
const auto test_module = [&](size_t i, const utils::loaded_module & mod) {
|
||||
std::filesystem::path path;
|
||||
try {
|
||||
path = mod.path();
|
||||
std::wstring version, description;
|
||||
try {
|
||||
version = utils::format_file_version(mod.get_file_version());
|
||||
} catch (...) {
|
||||
version = L"<unknown>";
|
||||
}
|
||||
|
||||
try {
|
||||
description = mod.get_description();
|
||||
} catch (...) {
|
||||
description = L"<unknown>";
|
||||
}
|
||||
|
||||
logging::I(R"({} [{}/{}] Module 0x{:X} ~ 0x{:X} (0x{:X}): "{}" ("{}" ver {}))", LogTagW, i + 1, mods.size(), mod.address_int(), mod.address_int() + mod.image_size(), mod.image_size(), path.wstring(), description, version);
|
||||
} catch (const std::exception& e) {
|
||||
logging::W("{} [{}/{}] Module 0x{:X}: Failed to resolve path: {}", LogTag, i + 1, mods.size(), mod.address_int(), e.what());
|
||||
for (size_t i = 0; i < mods.size(); i++) {
|
||||
const auto& mod = mods[i];
|
||||
const auto path = mod.path();
|
||||
if (!path) {
|
||||
logging::W(
|
||||
"{} [{}/{}] Module 0x{:X}: Failed to resolve path: {}",
|
||||
LogTag,
|
||||
i + 1,
|
||||
mods.size(),
|
||||
mod.address_int(),
|
||||
path.error().describe());
|
||||
return;
|
||||
}
|
||||
|
||||
const auto moduleName = unicode::convert<std::string>(path.filename().wstring());
|
||||
const auto version = mod.get_file_version()
|
||||
.transform([](const auto& v) { return utils::format_file_version(v.get()); })
|
||||
.value_or(L"<unknown>");
|
||||
|
||||
std::vector<char> buf;
|
||||
std::string formatBuf;
|
||||
const auto description = mod.get_description()
|
||||
.value_or(L"<unknown>");
|
||||
|
||||
logging::I(
|
||||
R"({} [{}/{}] Module 0x{:X} ~ 0x{:X} (0x{:X}): "{}" ("{}" ver {}))",
|
||||
LogTagW,
|
||||
i + 1,
|
||||
mods.size(),
|
||||
mod.address_int(),
|
||||
mod.address_int() + mod.image_size(),
|
||||
mod.image_size(),
|
||||
path->wstring(),
|
||||
description,
|
||||
version);
|
||||
|
||||
const auto moduleName = unicode::convert<std::string>(path->filename().wstring());
|
||||
|
||||
const auto& sectionHeader = mod.section_header(".text");
|
||||
const auto section = mod.span_as<char>(sectionHeader.VirtualAddress, sectionHeader.Misc.VirtualSize);
|
||||
if (section.empty()) {
|
||||
logging::W("{} Error: .text[VA:VA + VS] is empty", LogTag);
|
||||
return;
|
||||
}
|
||||
|
||||
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());
|
||||
return;
|
||||
}
|
||||
|
||||
auto hFsDll = std::unique_ptr<void, decltype(&CloseHandle)>(hFsDllRaw, &CloseHandle);
|
||||
std::vector<char> buf(section.size());
|
||||
SetFilePointer(hFsDll.get(), sectionHeader.PointerToRawData, nullptr, FILE_CURRENT);
|
||||
if (DWORD read{}; ReadFile(hFsDll.get(), &buf[0], static_cast<DWORD>(buf.size()), &read, nullptr)) {
|
||||
if (read < section.size_bytes()) {
|
||||
logging::W("{} ReadFile: read {} bytes < requested {} bytes", LogTagW, read, section.size_bytes());
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
logging::I("{} ReadFile: Win32 error {}", LogTagW, GetLastError());
|
||||
return;
|
||||
}
|
||||
|
||||
const auto doRestore = g_startInfo.BootUnhookDlls.contains(unicode::convert<std::string>(path->filename().u8string()));
|
||||
try {
|
||||
const auto& sectionHeader = mod.section_header(".text");
|
||||
const auto section = assume_nonempty_span(mod.span_as<char>(sectionHeader.VirtualAddress, sectionHeader.Misc.VirtualSize), ".text[VA:VA+VS]");
|
||||
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());
|
||||
return;
|
||||
}
|
||||
auto hFsDll = std::unique_ptr<void, decltype(CloseHandle)*>(hFsDllRaw, &CloseHandle);
|
||||
|
||||
buf.resize(section.size());
|
||||
SetFilePointer(hFsDll.get(), sectionHeader.PointerToRawData, nullptr, FILE_CURRENT);
|
||||
if (DWORD read{}; ReadFile(hFsDll.get(), &buf[0], static_cast<DWORD>(buf.size()), &read, nullptr)) {
|
||||
if (read < section.size_bytes()) {
|
||||
logging::W("{} ReadFile: read {} bytes < requested {} bytes", LogTagW, read, section.size_bytes());
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
logging::I("{} ReadFile: Win32 error {}", LogTagW, GetLastError());
|
||||
return;
|
||||
}
|
||||
|
||||
const auto doRestore = g_startInfo.BootUnhookDlls.contains(unicode::convert<std::string>(path.filename().u8string()));
|
||||
|
||||
std::optional<utils::memory_tenderizer> tenderizer;
|
||||
for (size_t i = 0, instructionLength = 1, printed = 0; i < buf.size(); i += instructionLength) {
|
||||
if (section[i] == buf[i]) {
|
||||
std::string formatBuf;
|
||||
for (size_t inst = 0, instructionLength = 1, printed = 0; inst < buf.size(); inst += instructionLength) {
|
||||
if (section[inst] == buf[inst]) {
|
||||
instructionLength = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto rva = sectionHeader.VirtualAddress + i;
|
||||
const auto rva = sectionHeader.VirtualAddress + inst;
|
||||
nmd_x86_instruction instruction{};
|
||||
if (!nmd_x86_decode(§ion[i], section.size() - i, &instruction, NMD_X86_MODE_64, NMD_X86_DECODER_FLAGS_ALL)) {
|
||||
if (!nmd_x86_decode(§ion[inst], section.size() - inst, &instruction, NMD_X86_MODE_64, NMD_X86_DECODER_FLAGS_ALL)) {
|
||||
instructionLength = 1;
|
||||
if (printed < 64) {
|
||||
logging::W("{} {}+0x{:0X}: dd {:02X}", LogTag, moduleName, rva, static_cast<uint8_t>(section[i]));
|
||||
logging::W("{} {}+0x{:0X}: dd {:02X}", LogTag, moduleName, rva, static_cast<uint8_t>(section[inst]));
|
||||
printed++;
|
||||
}
|
||||
} else {
|
||||
instructionLength = instruction.length;
|
||||
if (printed < 64) {
|
||||
formatBuf.resize(128);
|
||||
nmd_x86_format(&instruction, &formatBuf[0], reinterpret_cast<size_t>(§ion[i]), NMD_X86_FORMAT_FLAGS_DEFAULT | NMD_X86_FORMAT_FLAGS_BYTES);
|
||||
nmd_x86_format(&instruction, &formatBuf[0], reinterpret_cast<size_t>(§ion[inst]), NMD_X86_FORMAT_FLAGS_DEFAULT | NMD_X86_FORMAT_FLAGS_BYTES);
|
||||
formatBuf.resize(strnlen(&formatBuf[0], formatBuf.size()));
|
||||
|
||||
const auto& directory = mod.data_directory(IMAGE_DIRECTORY_ENTRY_EXPORT);
|
||||
|
|
@ -103,25 +110,25 @@ void xivfixes::unhook_dll(bool bApply) {
|
|||
const auto functions = mod.span_as<DWORD>(exportDirectory.AddressOfFunctions, exportDirectory.NumberOfFunctions);
|
||||
|
||||
std::string resolvedExportName;
|
||||
for (size_t j = 0; j < names.size(); ++j) {
|
||||
for (size_t nameIndex = 0; nameIndex < names.size(); ++nameIndex) {
|
||||
std::string_view name;
|
||||
if (const char* pcszName = mod.address_as<char>(names[j]); pcszName < mod.address() || pcszName >= mod.address() + mod.image_size()) {
|
||||
if (const char* pcszName = mod.address_as<char>(names[nameIndex]); pcszName < mod.address() || pcszName >= mod.address() + mod.image_size()) {
|
||||
if (IsBadReadPtr(pcszName, 256)) {
|
||||
logging::W("{} Name #{} points to an invalid address outside the executable. Skipping.", LogTag, j);
|
||||
logging::W("{} Name #{} points to an invalid address outside the executable. Skipping.", LogTag, nameIndex);
|
||||
continue;
|
||||
}
|
||||
|
||||
name = std::string_view(pcszName, strnlen(pcszName, 256));
|
||||
logging::W("{} Name #{} points to a seemingly valid address outside the executable: {}", LogTag, j, name);
|
||||
logging::W("{} Name #{} points to a seemingly valid address outside the executable: {}", LogTag, nameIndex, name);
|
||||
}
|
||||
|
||||
if (ordinals[j] >= functions.size()) {
|
||||
logging::W("{} Ordinal #{} points to function index #{} >= #{}. Skipping.", LogTag, j, ordinals[j], functions.size());
|
||||
if (ordinals[nameIndex] >= functions.size()) {
|
||||
logging::W("{} Ordinal #{} points to function index #{} >= #{}. Skipping.", LogTag, nameIndex, ordinals[nameIndex], functions.size());
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto rva = functions[ordinals[j]];
|
||||
if (rva == §ion[i] - mod.address()) {
|
||||
const auto rva = functions[ordinals[nameIndex]];
|
||||
if (rva == §ion[inst] - mod.address()) {
|
||||
resolvedExportName = std::format("[export:{}]", name);
|
||||
break;
|
||||
}
|
||||
|
|
@ -135,7 +142,7 @@ void xivfixes::unhook_dll(bool bApply) {
|
|||
if (doRestore) {
|
||||
if (!tenderizer)
|
||||
tenderizer.emplace(section, PAGE_EXECUTE_READWRITE);
|
||||
memcpy(§ion[i], &buf[i], instructionLength);
|
||||
memcpy(§ion[inst], &buf[inst], instructionLength);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -147,21 +154,7 @@ void xivfixes::unhook_dll(bool bApply) {
|
|||
} catch (const std::exception& e) {
|
||||
logging::W("{} Error: {}", LogTag, e.what());
|
||||
}
|
||||
};
|
||||
|
||||
// This is needed since try and __try cannot be used in the same function. Lambdas circumvent the limitation.
|
||||
const auto windows_exception_handler = [&]() {
|
||||
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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
windows_exception_handler();
|
||||
}
|
||||
}
|
||||
|
||||
using TFnGetInputDeviceManager = void* ();
|
||||
|
|
@ -294,13 +287,11 @@ static bool is_xivalex(const std::filesystem::path& dllPath) {
|
|||
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
|
||||
}
|
||||
const auto path = mod.path().value_or({});
|
||||
if (path.empty())
|
||||
continue;
|
||||
if (is_xivalex(path))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}();
|
||||
|
|
@ -650,43 +641,22 @@ void xivfixes::symbol_load_patches(bool bApply) {
|
|||
|
||||
void xivfixes::disable_game_debugging_protection(bool bApply) {
|
||||
static const char* LogTag = "[xivfixes:disable_game_debugging_protection]";
|
||||
static const std::vector<uint8_t> patchBytes = {
|
||||
0x31, 0xC0, // XOR EAX, EAX
|
||||
0x90, // NOP
|
||||
0x90, // NOP
|
||||
0x90, // NOP
|
||||
0x90 // NOP
|
||||
};
|
||||
static std::optional<hooks::import_hook<decltype(IsDebuggerPresent)>> s_hookIsDebuggerPresent;
|
||||
|
||||
if (!bApply)
|
||||
return;
|
||||
if (bApply) {
|
||||
if (!g_startInfo.BootEnabledGameFixes.contains("disable_game_debugging_protection")) {
|
||||
logging::I("{} Turned off via environment variable.", LogTag);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!g_startInfo.BootEnabledGameFixes.contains("disable_game_debugging_protection")) {
|
||||
logging::I("{} Turned off via environment variable.", LogTag);
|
||||
return;
|
||||
}
|
||||
|
||||
// Find IsDebuggerPresent in Framework.Tick()
|
||||
const char* matchPtr = utils::signature_finder()
|
||||
.look_in(utils::loaded_module(g_hGameInstance), ".text")
|
||||
.look_for_hex("FF 15 ?? ?? ?? ?? 85 C0 74 13 41")
|
||||
.find_one()
|
||||
.Match.data();
|
||||
|
||||
if (!matchPtr) {
|
||||
logging::E("{} Failed to find signature.", LogTag);
|
||||
return;
|
||||
}
|
||||
|
||||
void* address = const_cast<void*>(static_cast<const void*>(matchPtr));
|
||||
|
||||
DWORD oldProtect;
|
||||
if (VirtualProtect(address, patchBytes.size(), PAGE_EXECUTE_READWRITE, &oldProtect)) {
|
||||
memcpy(address, patchBytes.data(), patchBytes.size());
|
||||
VirtualProtect(address, patchBytes.size(), oldProtect, &oldProtect);
|
||||
logging::I("{} Patch applied at address 0x{:X}.", LogTag, reinterpret_cast<uintptr_t>(address));
|
||||
s_hookIsDebuggerPresent.emplace("kernel32.dll!IsDebuggerPresent", "kernel32.dll", "IsDebuggerPresent", 0);
|
||||
s_hookIsDebuggerPresent->set_detour([]() { return false; });
|
||||
logging::I("{} Enable", LogTag);
|
||||
} else {
|
||||
logging::E("{} Failed to change memory protection.", LogTag);
|
||||
if (s_hookIsDebuggerPresent) {
|
||||
logging::I("{} Disable", LogTag);
|
||||
s_hookIsDebuggerPresent.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue