diff --git a/src/Zenova/Platform/PlatformImpl.h b/src/Zenova/Platform/PlatformImpl.h index a8e9d43..36d4af7 100644 --- a/src/Zenova/Platform/PlatformImpl.h +++ b/src/Zenova/Platform/PlatformImpl.h @@ -6,5 +6,7 @@ namespace Zenova { namespace PlatformImpl { bool Init(void*); void Destroy(); + void* LoadModModuleAndResolveImports(const std::string& module); + void ResolveModModuleImports(void* hModule, const std::string& moduleName); } } \ No newline at end of file diff --git a/src/Zenova/Platform/Windows.cpp b/src/Zenova/Platform/Windows.cpp index 2373bb0..08e7541 100644 --- a/src/Zenova/Platform/Windows.cpp +++ b/src/Zenova/Platform/Windows.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include "Zenova.h" #include "Zenova/Globals.h" @@ -87,6 +88,98 @@ namespace Zenova::PlatformImpl { FreeLibraryAndExitThread(reinterpret_cast(CleanupVariables), 0); } } + + void* LoadModModuleAndResolveImports(const std::string& module) + { + void* hModule = LoadLibraryExA(module.c_str(), NULL, DONT_RESOLVE_DLL_REFERENCES); + ResolveModModuleImports(hModule, module); + return hModule; + } + + typedef BOOL(APIENTRY* DllMainFunction)(HMODULE, DWORD, LPVOID); + void ResolveModModuleImports(void* hModule, const std::string& moduleName) + { + if (!hModule) { + Zenova_Log(Warning, "[{}] Could not resolve the imports for the module because it was nullptr.", __FUNCTION__); + Platform::DebugPause(); + return; + } + + uintptr_t hModuleAddress = reinterpret_cast(hModule); + PIMAGE_DOS_HEADER dosHeader = reinterpret_cast(hModuleAddress); + PIMAGE_NT_HEADERS ntHeader = reinterpret_cast(hModuleAddress + dosHeader->e_lfanew); + + IMAGE_DATA_DIRECTORY importDir = ntHeader->OptionalHeader.DataDirectory[1]; + PIMAGE_IMPORT_DESCRIPTOR imports = reinterpret_cast(hModuleAddress + importDir.VirtualAddress); + if (!imports) { + Zenova_Log(Warning, "[{}] Could not resolve the imports for the module because the imports data directory could not be found in the PE.", __FUNCTION__); + Platform::DebugPause(); + return; + } + + std::map, ULONGLONG*> symbolTableAddresses; + while (imports->Characteristics != 0) { + PIMAGE_THUNK_DATA importLookupTable = reinterpret_cast(hModuleAddress + imports->OriginalFirstThunk); + PIMAGE_THUNK_DATA importAddressTable = reinterpret_cast(hModuleAddress + imports->FirstThunk); + + if (imports->OriginalFirstThunk && imports->FirstThunk) { + LPCSTR moduleName = reinterpret_cast(hModuleAddress + imports->Name); + std::string importModNameId(moduleName); + { + size_t dot = importModNameId.find_last_of('.'); + if (dot != std::string::npos) { + importModNameId = importModNameId.substr(0, dot); + } + } + + uintptr_t moduleBase = Platform::GetModuleBaseAddress(moduleName); + if (!moduleBase) { + moduleBase = (uintptr_t)manager.loadMod(importModNameId); + if (!moduleBase) + moduleBase = (uintptr_t)LoadLibraryA(moduleName); + } + + while (importLookupTable->u1.AddressOfData != 0) { + if (!(importLookupTable->u1.Ordinal & IMAGE_ORDINAL_FLAG)) { + PIMAGE_IMPORT_BY_NAME nameData = + reinterpret_cast(hModuleAddress + importLookupTable->u1.AddressOfData); + + symbolTableAddresses.insert({ { moduleBase, nameData->Name }, &(importAddressTable->u1.Function) }); + } + importLookupTable++; + importAddressTable++; + } + } + + imports++; + } + + for (auto& [infos, function] : symbolTableAddresses) { + auto& [moduleBase, entrySymbol] = infos; + if (moduleBase) { + ULONGLONG procAddress = reinterpret_cast( + Platform::GetModuleFunction(reinterpret_cast(moduleBase), entrySymbol.c_str())); + + if (procAddress && function) { + u32 oldPageProtection = Platform::SetPageProtect(function, sizeof(ULONGLONG), ProtectionFlags::Execute | ProtectionFlags::Read | ProtectionFlags::Write); + (*function) = procAddress; + Platform::SetPageProtect(function, sizeof(ULONGLONG), oldPageProtection); + } + } + else { + Zenova_Log(Warning, "[{}] Could not resolve the address for '{}' because the module could not be loaded.", __FUNCTION__, entrySymbol); + return; + } + } + + if (ntHeader->OptionalHeader.AddressOfEntryPoint) { + reinterpret_cast(hModuleAddress + ntHeader->OptionalHeader.AddressOfEntryPoint)( + reinterpret_cast(hModule), + DLL_PROCESS_ATTACH, + nullptr + ); + } + } } namespace Zenova { diff --git a/src/Zenova/Profile/Manager.cpp b/src/Zenova/Profile/Manager.cpp index bcd67bb..9a31a63 100644 --- a/src/Zenova/Profile/Manager.cpp +++ b/src/Zenova/Profile/Manager.cpp @@ -1,5 +1,5 @@ #include "Manager.h" - +#include #include "Zenova/Log.h" #include "Zenova/Mod.h" #include "Zenova/Globals.h" @@ -102,29 +102,35 @@ namespace Zenova { load(profile); } - void Manager::loadMod(const std::string& modName) + void* Manager::loadMod(const std::string& modName) { if (std::find_if(mods.begin(), mods.end(), [&modName](const ModInfo& mod) { return mod.mNameId == modName; }) != mods.end()) - return; + return nullptr; + std::string folder = manager.dataFolder + "\\mods\\" + modName + "\\"; + if (!std::filesystem::exists(folder)) + return nullptr; + logger.info("Loading {}", modName); // todo: verify path - std::string folder = manager.dataFolder + "\\mods\\" + modName + "\\"; ModInfo mod(folder); + void* modHandle = mod.loadModule(); - if (mod.loadModule()) { + if (modHandle) { ModContext ctx = { folder }; CALL_MOD_FUNC(mod, ModLoad, ctx) PackManager::addMod(folder); mods.push_back(std::move(mod)); + return modHandle; } else { logger.warn("Failed to load {}", mod.mName); Platform::ErrorPrinter(); + return nullptr; } } diff --git a/src/Zenova/Profile/Manager.h b/src/Zenova/Profile/Manager.h index 8a8dee6..3360e52 100644 --- a/src/Zenova/Profile/Manager.h +++ b/src/Zenova/Profile/Manager.h @@ -5,7 +5,7 @@ #include #include "ProfileInfo.h" -#include "ModInfo.h" +#include "Zenova/Profile/ModInfo.h" namespace Zenova { class Manager { @@ -28,7 +28,7 @@ namespace Zenova { void update(); void load(const ProfileInfo& profile); void swap(const ProfileInfo& profile); - void loadMod(const std::string& modName); + void* loadMod(const std::string& modName); std::string getVersion(); size_t getModCount(); diff --git a/src/Zenova/Profile/ModInfo.cpp b/src/Zenova/Profile/ModInfo.cpp index 6ad4bb2..a2f06bc 100644 --- a/src/Zenova/Profile/ModInfo.cpp +++ b/src/Zenova/Profile/ModInfo.cpp @@ -1,4 +1,4 @@ -#include "ModInfo.h" +#include "Zenova/Profile/ModInfo.h" #include #include @@ -17,14 +17,6 @@ namespace Zenova { mName = JsonHelper::FindString(modDocument, "name"); mDescription = JsonHelper::FindString(modDocument, "description"); mVersion = JsonHelper::FindString(modDocument, "version"); - auto& dependenciesObject = JsonHelper::FindMember(modDocument, "dependencies", false); - if (!dependenciesObject.IsNull() && dependenciesObject.IsArray()) - { - for (auto& object : dependenciesObject.GetArray()) - { - mDependencies.push_back(object.GetString()); - } - } } } @@ -34,8 +26,7 @@ namespace Zenova { mNameId(std::move(mod.mNameId)), mName(std::move(mod.mName)), mDescription(std::move(mod.mDescription)), - mVersion(std::move(mod.mVersion)), - mDependencies(std::move(mod.mDependencies)) + mVersion(std::move(mod.mVersion)) {} ModInfo::~ModInfo() { @@ -44,16 +35,7 @@ namespace Zenova { void* ModInfo::loadModule() { - loadDependencies(); - mHandle = Platform::LoadModule(mModFolder + mNameId); + mHandle = PlatformImpl::LoadModModuleAndResolveImports(mModFolder + mNameId); return mHandle; } - - void ModInfo::loadDependencies() const - { - for (auto& dependencyName : mDependencies) - { - manager.loadMod(dependencyName); - } - } } diff --git a/src/Zenova/Profile/ModInfo.h b/src/Zenova/Profile/ModInfo.h index 54194d7..0f63ce1 100644 --- a/src/Zenova/Profile/ModInfo.h +++ b/src/Zenova/Profile/ModInfo.h @@ -16,13 +16,10 @@ namespace Zenova { std::string mName = ""; std::string mDescription = ""; std::string mVersion = ""; - std::vector mDependencies = {}; - ModInfo(const std::string& modFolder); ModInfo(ModInfo&&) noexcept; ~ModInfo(); void* loadModule(); - void loadDependencies() const; }; }