diff --git a/Dalamud.Boot/Dalamud.Boot.vcxproj b/Dalamud.Boot/Dalamud.Boot.vcxproj
index 7b9c52f46..458969974 100644
--- a/Dalamud.Boot/Dalamud.Boot.vcxproj
+++ b/Dalamud.Boot/Dalamud.Boot.vcxproj
@@ -121,6 +121,7 @@
NotUsing
NotUsing
+
NotUsing
NotUsing
diff --git a/Dalamud.Boot/Dalamud.Boot.vcxproj.filters b/Dalamud.Boot/Dalamud.Boot.vcxproj.filters
index b4e788664..594264a3a 100644
--- a/Dalamud.Boot/Dalamud.Boot.vcxproj.filters
+++ b/Dalamud.Boot/Dalamud.Boot.vcxproj.filters
@@ -67,6 +67,9 @@
MinHook
+
+ Dalamud.Boot DLL
+
diff --git a/Dalamud.Boot/bootconfig.h b/Dalamud.Boot/bootconfig.h
index 667369212..661052fe8 100644
--- a/Dalamud.Boot/bootconfig.h
+++ b/Dalamud.Boot/bootconfig.h
@@ -3,8 +3,23 @@
#include "utils.h"
namespace bootconfig {
- inline bool is_wait_messagebox() {
- return utils::get_env(L"DALAMUD_WAIT_MESSAGEBOX");
+ enum WaitMessageboxFlags : int {
+ None = 0,
+ BeforeInitialize = 1 << 0,
+ BeforeDalamudEntrypoint = 1 << 1,
+ };
+
+ inline WaitMessageboxFlags wait_messagebox() {
+ return static_cast(utils::get_env(L"DALAMUD_WAIT_MESSAGEBOX"));
+ }
+
+ enum DotNetOpenProcessHookMode : int {
+ ImportHooks = 0,
+ DirectHook = 1,
+ };
+
+ inline DotNetOpenProcessHookMode dotnet_openprocess_hook_mode() {
+ return static_cast(utils::get_env(L"DALAMUD_DOTNET_OPENPROCESS_HOOKMODE"));
}
inline bool is_show_console() {
@@ -20,6 +35,18 @@ namespace bootconfig {
}
inline bool is_veh_full() {
- return utils::get_env("DALAMUD_IS_VEH_FULL");
+ return utils::get_env(L"DALAMUD_IS_VEH_FULL");
+ }
+
+ inline bool gamefix_is_enabled(const wchar_t* name) {
+ static const auto list = utils::get_env_list(L"DALAMUD_GAMEFIX_LIST");
+ for (const auto& item : list)
+ if (item == name)
+ return true;
+ return false;
+ }
+
+ inline std::vector gamefix_unhookdll_list() {
+ return utils::get_env_list(L"DALAMUD_UNHOOK_DLLS");
}
}
diff --git a/Dalamud.Boot/dllmain.cpp b/Dalamud.Boot/dllmain.cpp
index b31ae0d23..7bccd08e7 100644
--- a/Dalamud.Boot/dllmain.cpp
+++ b/Dalamud.Boot/dllmain.cpp
@@ -27,7 +27,7 @@ DllExport DWORD WINAPI Initialize(LPVOID lpParam, HANDLE hMainThreadContinue) {
logging::print("Dalamud.Boot Injectable, (c) 2021 XIVLauncher Contributors");
logging::print("Built at: " __DATE__ "@" __TIME__);
- if (bootconfig::is_wait_messagebox())
+ if (bootconfig::wait_messagebox() & bootconfig::WaitMessageboxFlags::BeforeInitialize)
MessageBoxW(nullptr, L"Press OK to continue", L"Dalamud Boot", MB_OK);
logging::print("Applying fixes...");
@@ -81,6 +81,9 @@ DllExport DWORD WINAPI Initialize(LPVOID lpParam, HANDLE hMainThreadContinue) {
// ============================== Dalamud ==================================== //
+ if (bootconfig::wait_messagebox() & bootconfig::WaitMessageboxFlags::BeforeDalamudEntrypoint)
+ MessageBoxW(nullptr, L"Press OK to continue", L"Dalamud Boot", MB_OK);
+
logging::print("Initializing Dalamud...");
entrypoint_fn(lpParam, hMainThreadContinue);
logging::print("Done!");
diff --git a/Dalamud.Boot/hooks.cpp b/Dalamud.Boot/hooks.cpp
new file mode 100644
index 000000000..367dbde47
--- /dev/null
+++ b/Dalamud.Boot/hooks.cpp
@@ -0,0 +1,146 @@
+#include "pch.h"
+
+#include "hooks.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([this](HMODULE hModule, LPCSTR lpProcName) { return get_proc_address_handler(hModule, lpProcName); }) {
+}
+
+hooks::getprocaddress_singleton_import_hook::~getprocaddress_singleton_import_hook() {
+ LdrUnregisterDllNotification(m_ldrDllNotificationCookie);
+}
+
+std::shared_ptr hooks::getprocaddress_singleton_import_hook::set_handler(std::wstring dllName, std::string functionName, void* pfnDetour) {
+ const auto hModule = GetModuleHandleW(dllName.c_str());
+ if (!hModule)
+ throw std::out_of_range("Specified DLL is not found.");
+
+ const auto pfn = m_pfnGetProcAddress(hModule, functionName.c_str());
+ if (!pfn)
+ throw std::out_of_range("Could not find the specified function.");
+
+ auto& target = m_targetFns[hModule][functionName];
+ if (target)
+ throw std::runtime_error("Specified function has already been hooked.");
+
+ target = pfnDetour;
+ m_dllNameMap[hModule] = unicode::convert(dllName);
+ for (const auto& mod : utils::loaded_module::all_modules())
+ hook_module(mod);
+
+ return { pfn,[pThis = this->shared_from_this(), hModule, functionName](void*) {
+ auto& modFns = pThis->m_targetFns[hModule];
+ auto& hooks = pThis->m_hooks[hModule];
+ modFns.erase(functionName);
+ hooks.erase(functionName);
+ if (modFns.empty()) {
+ pThis->m_targetFns.erase(hModule);
+ pThis->m_hooks.erase(hModule);
+ pThis->m_dllNameMap.erase(hModule);
+ }
+ } };
+}
+
+std::shared_ptr hooks::getprocaddress_singleton_import_hook::get_instance() {
+ static std::weak_ptr s_instance;
+ std::shared_ptr res;
+
+ res = s_instance.lock();
+ if (res)
+ return res;
+
+ static std::mutex m_mtx;
+ const auto lock = std::lock_guard(m_mtx);
+ res = s_instance.lock();
+ if (res)
+ return res;
+
+ s_instance = res = std::make_shared();
+ res->initialize();
+ return res;
+}
+
+void hooks::getprocaddress_singleton_import_hook::initialize() {
+ m_getProcAddressHandler = set_handler(L"kernel32.dll", "GetProcAddress", m_thunk.get_thunk());
+
+ LdrRegisterDllNotification(0, [](ULONG notiReason, const LDR_DLL_NOTIFICATION_DATA* pData, void* context) {
+ if (notiReason == LDR_DLL_NOTIFICATION_REASON_LOADED) {
+ const auto dllName = unicode::convert(pData->Loaded.FullDllName->Buffer);
+ logging::print(R"({} "{}" has been loaded at 0x{:X} ~ 0x{:X} (0x{:X}); finding import table items to hook.)",
+ LogTag, dllName,
+ reinterpret_cast(pData->Loaded.DllBase),
+ reinterpret_cast(pData->Loaded.DllBase) + pData->Loaded.SizeOfImage,
+ pData->Loaded.SizeOfImage);
+ reinterpret_cast(context)->hook_module(utils::loaded_module(pData->Loaded.DllBase));
+ } else if (notiReason == LDR_DLL_NOTIFICATION_REASON_UNLOADED) {
+ const auto dllName = unicode::convert(pData->Unloaded.FullDllName->Buffer);
+ logging::print(R"({} "{}" has been unloaded.)", LogTag, dllName);
+ }
+ }, this, &m_ldrDllNotificationCookie);
+}
+
+FARPROC hooks::getprocaddress_singleton_import_hook::get_proc_address_handler(HMODULE hModule, LPCSTR lpProcName) {
+ if (const auto it1 = m_targetFns.find(hModule); it1 != m_targetFns.end()) {
+ if (const auto it2 = it1->second.find(lpProcName); it2 != it1->second.end()) {
+ logging::print(R"({} Redirecting GetProcAddress("{}", "{}"))", LogTag, m_dllNameMap[hModule], lpProcName);
+
+ return reinterpret_cast(it2->second);
+ }
+ }
+ return this->m_pfnGetProcAddress(hModule, lpProcName);
+}
+
+void hooks::getprocaddress_singleton_import_hook::hook_module(const utils::loaded_module& mod) {
+ if (mod.is_current_process())
+ return;
+
+ const auto path = unicode::convert(mod.path().wstring());
+
+ for (const auto& [hModule, targetFns] : m_targetFns) {
+ for (const auto& [targetFn, pfnThunk] : targetFns) {
+ const auto& dllName = m_dllNameMap[hModule];
+ if (void* pGetProcAddressImport; mod.find_imported_function_pointer(dllName.c_str(), targetFn.c_str(), 0, pGetProcAddressImport)) {
+ auto& hook = m_hooks[hModule][targetFn][mod];
+ if (!hook) {
+ logging::print("{} Hooking {}!{} imported by {}", LogTag, dllName, targetFn, unicode::convert(mod.path().wstring()));
+
+ hook.emplace(static_cast(pGetProcAddressImport), pfnThunk);
+ }
+ }
+ }
+ }
+}
diff --git a/Dalamud.Boot/hooks.h b/Dalamud.Boot/hooks.h
index 3ad907495..547b18fb7 100644
--- a/Dalamud.Boot/hooks.h
+++ b/Dalamud.Boot/hooks.h
@@ -1,15 +1,21 @@
#pragma once
#include
+#include