diff --git a/EDFModLoader/EDFModLoader.vcxproj b/EDFModLoader/EDFModLoader.vcxproj index 2de580a..7e10881 100644 --- a/EDFModLoader/EDFModLoader.vcxproj +++ b/EDFModLoader/EDFModLoader.vcxproj @@ -194,6 +194,7 @@ + @@ -202,6 +203,7 @@ + diff --git a/EDFModLoader/EDFModLoader.vcxproj.filters b/EDFModLoader/EDFModLoader.vcxproj.filters index 6af423d..6c71457 100644 --- a/EDFModLoader/EDFModLoader.vcxproj.filters +++ b/EDFModLoader/EDFModLoader.vcxproj.filters @@ -21,6 +21,9 @@ Source Files + + Source Files + @@ -40,5 +43,8 @@ Header Files + + Header Files + \ No newline at end of file diff --git a/EDFModLoader/OnlineLimit.cpp b/EDFModLoader/OnlineLimit.cpp new file mode 100644 index 0000000..d8a4de4 --- /dev/null +++ b/EDFModLoader/OnlineLimit.cpp @@ -0,0 +1,53 @@ +#include +#include + +#include "OnlineLimit.h" + +// Injects hook into game process +void __fastcall WriteHookToProcess(void *addr, void *data, size_t len) { + DWORD oldProtect; + VirtualProtect(addr, len, PAGE_EXECUTE_READWRITE, &oldProtect); + memcpy(addr, data, len); + VirtualProtect(addr, len, oldProtect, &oldProtect); + // Refresh cpu instruction? + // FlushInstructionCache(handleEXE, addr, len); +} + +// update game's original functions with interruption, need 15 bytes +void __fastcall hookGameBlockWithInt3(void *targetAddr, uintptr_t dataAddr) { + uint8_t hookFunction[] = { + 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // addr + 0xCC // int3 + }; + memcpy(&hookFunction[6], &dataAddr, sizeof(dataAddr)); + + WriteHookToProcess(targetAddr, hookFunction, sizeof(hookFunction)); +} + +// Blocking EDF5 creates public rooms wherever possible. +void __fastcall EDF5OnlineRoomLimit(PBYTE hmodEXE) { + // forced private room creation + // everyone, offset is 0x57F88F + unsigned char everyone1[] = {0x82, 0x94}; + WriteHookToProcess((void *)(hmodEXE + 0x58048F + 3), &everyone1, 2U); + unsigned char everyone2[] = {0x3C, 0x78}; + WriteHookToProcess((void *)(hmodEXE + 0x5820D5 + 3), &everyone2, 2U); + // need password, offset is 0x57FAD0 + unsigned char password1[] = {0x41}; + WriteHookToProcess((void *)(hmodEXE + 0x5806D0 + 3), &password1, 1U); + unsigned char password2[] = {0xDC, 0x78}; + WriteHookToProcess((void *)(hmodEXE + 0x582035 + 3), &password2, 2U); +} + +extern "C" { +void __fastcall ASMreadMissionSavaData(); +uintptr_t readMissionSavaDataRetAddr; +} + +// Disable online when mod folder is enabled. +void __fastcall EDF6OnlineRoomLimit(PBYTE hmodEXE, uintptr_t OlOfs) { + // edf.dll+E2970, Determine mission savedata location, R8 is 1 when it is online. + hookGameBlockWithInt3((void *)(hmodEXE + OlOfs), (uintptr_t)ASMreadMissionSavaData); + readMissionSavaDataRetAddr = (uintptr_t)(hmodEXE + OlOfs+0x10); +} diff --git a/EDFModLoader/OnlineLimit.h b/EDFModLoader/OnlineLimit.h new file mode 100644 index 0000000..7320d1d --- /dev/null +++ b/EDFModLoader/OnlineLimit.h @@ -0,0 +1,12 @@ +#pragma once + +// Injects hook into game process +void __fastcall WriteHookToProcess(void *addr, void *data, size_t len); +// update game's original functions with interruption, need 15 bytes +void __fastcall hookGameBlockWithInt3(void *targetAddr, uintptr_t dataAddr); + +// Blocking EDF5 creates public rooms wherever possible. +void __fastcall EDF5OnlineRoomLimit(PBYTE hmodEXE); + +// Disable online when mod folder is enabled. +void __fastcall EDF6OnlineRoomLimit(PBYTE hmodEXE, uintptr_t OlOfs); diff --git a/EDFModLoader/dllmain.cpp b/EDFModLoader/dllmain.cpp index 7c969a5..5aa6ce2 100644 --- a/EDFModLoader/dllmain.cpp +++ b/EDFModLoader/dllmain.cpp @@ -17,6 +17,7 @@ #include "proxy.h" #include "PluginAPI.h" #include "LoggerTweaks.h" +#include "OnlineLimit.h" #include #include @@ -127,6 +128,7 @@ static BOOL LoadPluginsB = TRUE; static BOOL LoadASI = TRUE; static BOOL Redirect = TRUE; static BOOL GameLog = FALSE; +extern "C" int ActiveModFolder = 0; // Pointer sets typedef struct { @@ -284,6 +286,7 @@ static void *__fastcall crifsio_hook(void *unk1, oddstr *str, void *unk2) { if (FileExistsW(modpath)) { PLOG_DEBUG << "Redirecting access to " << modpath; wstrassign_orig(str, modpath, newlen); + ActiveModFolder = 1; } delete[] modpath; } @@ -472,10 +475,12 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserv } } FuncOffsets pointers = {}; + int EDFversion = 0; if (pointerSet != -1) { PLOG_INFO << "Running under " << psets[pointerSet].ident; pointers = psets[pointerSet].pointers; plugFunc = psets[pointerSet].plugfunc; + EDFversion = psets[pointerSet].version; } else if (lstrcmpiA(hmodName, "EDF6.exe") == 0) { PLOG_INFO << "Running under EDF6"; plugFunc = "EML6_Load"; @@ -528,6 +533,15 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserv if (pointers.gamelog == NULL) { PLOG_ERROR << "Failed to locate game debug logging function"; } + + scanner = LightningScanner::Scanner("4833C44889451F44894DBF4D63F04C8BFA48895597488BF1488D4DE7"); + uintptr_t checkModeType = ScanPtr(scanner.Find(hmodEXE, ScanRange)); + if (pointers.gamelog == NULL) { + pointers.redirect = NULL; + PLOG_ERROR << "Disallow mod folders"; + } else { + EDF6OnlineRoomLimit(hmodEXE, (checkModeType - (0xE2993 - 0xE2970))); + } } else { PLOG_ERROR << "Failed to determine what exe is running"; break; @@ -550,6 +564,10 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserv wstrassign_orig = (wstrassign_func)((PBYTE)hmodEXE + pointers.wstrassign); if (pointers.wstrassign) { SetupHook(pointers.redirect, (PVOID*)&crifsio_orig, crifsio_hook, "Mods folder redirector", Redirect); + // Limit the creation of public rooms. + if (EDFversion == 5) { + EDF5OnlineRoomLimit(hmodEXE); + } } else { PLOG_INFO << "Skipping unsupported " << hmodName << " hook (Mods folder redirector, no std::wstring::assign)"; } diff --git a/EDFModLoader/winmm.asm b/EDFModLoader/winmm.asm index 44c175c..5b2cfe4 100644 --- a/EDFModLoader/winmm.asm +++ b/EDFModLoader/winmm.asm @@ -1,5 +1,7 @@ .data extern PA : qword +extern ActiveModFolder : dword +extern readMissionSavaDataRetAddr : qword extern gamelog_hook_main : proto @@ -22,9 +24,40 @@ extern save_xmm5 : OWORD .code runASM proc -jmp qword ptr [PA] + jmp qword ptr [PA] + int 3 runASM endp +align 16 + +ASMreadMissionSavaData proc + + checkOnline: + cmp r8d, 1 + je toCrash + cmp r8d, 3 + je toCrash + cmp r8d, 5 + je toCrash ; If is offline mode, allow access to game + ofsE1D70: + mov [rsp+18h], rbx + push rbp + push rsi + push rdi + push r12 + push r13 + push r14 + push r15 + jmp readMissionSavaDataRetAddr + toCrash: + ; Otherwise crashes game + mov dword ptr [0], 0 + int 3 + +ASMreadMissionSavaData ENDP + +align 16 + ; The original function normally doesn't touch any registers, or do anything. ; The code that calls this function is optimized for that. ; So we must preserve all volatile registers ourself.