From b8a7feabea6456f7fa5e8403c8d9bd2630401045 Mon Sep 17 00:00:00 2001 From: GeckoEidechse <40122905+GeckoEidechse@users.noreply.github.com> Date: Mon, 28 Mar 2022 23:48:05 +0200 Subject: More exploit fixes by KIttenPopo (#126) * Quick fix for a bug I caused * Typo * Update kitten-fixes branch to my repo (#122) * Added string hash macro * Added convenient vtfunc macro * Made lil ConCommand creation macro * Fixed multiple NET_SetConVar exploits * Quick fixerino * Fix convar struct (and other things) * Revive clang-format (but good, i think) * Update .clang-format * Reformatted code to meet .clang-format requirements * Minor formatting fixes * Fixed Northstar "crashing" when console is closed * Update .clang-format * Quick fix for a bug I caused * Typo * NSMem Update * ExplotFixes: Only block excessive convar counts if server * Update ExploitFixes.cpp * Update ExploitFixes.cpp * Updated bytepatch format * reformatted all code for clang-format * Updated my clang-format to v13.0.0 * 3 fixes in 1 - ANTITAMPER fixed - NSMem simplification update - Fixed bad byte string in serverauthentication.cpp * Improved ExploitFixes logging and NET_SetConVar patch * clang-format unironically sabotaged my code * Made ns_exploitfixes_log on by default * Fixed IsMemoryReadable (oops) Co-authored-by: KittenPopo --- LauncherInjector/main.cpp | 20 +- NorthstarDedicatedTest/ExploitFixes.cpp | 155 +++++++++-- NorthstarDedicatedTest/ExploitFixes.h | 2 +- NorthstarDedicatedTest/ExploitFixes_UTF8Parser.h | 310 ++++++++++----------- NorthstarDedicatedTest/NSMem.h | 200 +++++++------ NorthstarDedicatedTest/clientvideooverrides.cpp | 7 +- NorthstarDedicatedTest/concommand.h | 3 +- NorthstarDedicatedTest/convar.cpp | 2 - NorthstarDedicatedTest/convar.h | 2 +- NorthstarDedicatedTest/cvar.h | 3 +- NorthstarDedicatedTest/dedicated.cpp | 28 +- NorthstarDedicatedTest/dedicatedmaterialsystem.cpp | 4 +- NorthstarDedicatedTest/languagehooks.cpp | 4 +- NorthstarDedicatedTest/latencyflex.cpp | 4 +- NorthstarDedicatedTest/logging.cpp | 3 +- NorthstarDedicatedTest/masterserver.cpp | 16 +- NorthstarDedicatedTest/maxplayers.cpp | 4 +- NorthstarDedicatedTest/misccommands.cpp | 42 +-- NorthstarDedicatedTest/miscserverfixes.cpp | 4 +- NorthstarDedicatedTest/modmanager.cpp | 20 +- NorthstarDedicatedTest/modmanager.h | 3 +- NorthstarDedicatedTest/pch.h | 10 +- NorthstarDedicatedTest/playlist.cpp | 6 +- NorthstarDedicatedTest/rpakfilesystem.cpp | 11 +- NorthstarDedicatedTest/serverauthentication.cpp | 29 +- NorthstarDedicatedTest/version.cpp | 18 +- 26 files changed, 520 insertions(+), 390 deletions(-) diff --git a/LauncherInjector/main.cpp b/LauncherInjector/main.cpp index 54c65059..1e3310ff 100644 --- a/LauncherInjector/main.cpp +++ b/LauncherInjector/main.cpp @@ -295,21 +295,16 @@ int main(int argc, char* argv[]) { printf("[*] Loading stubs\n"); HMODULE gssao, gtxaa, d3d11; - if (!(gssao = GetModuleHandleA("GFSDK_SSAO.win64.dll")) && - !(gtxaa = GetModuleHandleA("GFSDK_TXAA.win64.dll")) && + if (!(gssao = GetModuleHandleA("GFSDK_SSAO.win64.dll")) && !(gtxaa = GetModuleHandleA("GFSDK_TXAA.win64.dll")) && !(d3d11 = GetModuleHandleA("d3d11.dll"))) { - if (!(gssao = LoadDediStub("GFSDK_SSAO.win64.dll")) || - !(gtxaa = LoadDediStub("GFSDK_TXAA.win64.dll")) || + if (!(gssao = LoadDediStub("GFSDK_SSAO.win64.dll")) || !(gtxaa = LoadDediStub("GFSDK_TXAA.win64.dll")) || !(d3d11 = LoadDediStub("d3d11.dll"))) { - if ((!gssao || FreeLibrary(gssao)) && - (!gtxaa || FreeLibrary(gtxaa)) && - (!d3d11 || FreeLibrary(d3d11))) + if ((!gssao || FreeLibrary(gssao)) && (!gtxaa || FreeLibrary(gtxaa)) && (!d3d11 || FreeLibrary(d3d11))) { - printf( - "[*] WARNING: Failed to load d3d11/gfsdk stubs from bin/x64_dedi. " - "The stubs have been unloaded and the original libraries will be used instead.\n"); + printf("[*] WARNING: Failed to load d3d11/gfsdk stubs from bin/x64_dedi. " + "The stubs have been unloaded and the original libraries will be used instead.\n"); } else { @@ -326,9 +321,8 @@ int main(int argc, char* argv[]) else { // this should never happen - printf( - "[*] WARNING: Failed to load stubs because conflicting modules are already loaded, so those will be used instead " - "(did Northstar initialize too late?).\n"); + printf("[*] WARNING: Failed to load stubs because conflicting modules are already loaded, so those will be used instead " + "(did Northstar initialize too late?).\n"); } } diff --git a/NorthstarDedicatedTest/ExploitFixes.cpp b/NorthstarDedicatedTest/ExploitFixes.cpp index 36bd36f4..4c91ef75 100644 --- a/NorthstarDedicatedTest/ExploitFixes.cpp +++ b/NorthstarDedicatedTest/ExploitFixes.cpp @@ -5,6 +5,19 @@ #include "NSMem.h" #include "cvar.h" +ConVar* ns_exploitfixes_log; +#define SHOULD_LOG (ns_exploitfixes_log->m_Value.m_nValue > 0) +#define BLOCKED_INFO(s) \ + ([=]() -> bool { \ + if (SHOULD_LOG) \ + { \ + std::stringstream stream; \ + stream << "ExploitFixes.cpp: " << BLOCK_PREFIX << s; \ + spdlog::error(stream.str()); \ + } \ + return false; \ + }()) + // Make sure 3 or less floats are valid bool ValidateFloats(float a, float b = 0, float c = 0) { return !isnan(a) && !isnan(b) && !isnan(c); } @@ -66,9 +79,31 @@ KHOOK(CClient_ProcessSetConVar, ("engine.dll", "48 8B D1 48 8B 49 18 48 8B 01 48 auto msg = (NET_SetConVar*)pMsg; - constexpr int SETCONVAR_SANITY_AMOUNT_LIMIT = 20; - if (msg->m_ConVars_count < 1 || msg->m_ConVars_count > SETCONVAR_SANITY_AMOUNT_LIMIT) - return false; // Nope + bool areWeServer; + + { + // Figure out of we are the client or the server + // To do this, we utilize the msg's m_pMessageHandler pointer + // m_pMessageHandler points to a virtual class that handles all net messages + // The first virtual table function of our m_pMessageHandler will differ if it is IServerMessageHandler or IClientMessageHandler + void* msgHandlerVTableFirstFunc = **(void****)(msg->m_pMessageHandler); + static auto engineBaseAddress = (uintptr_t)GetModuleHandleA("engine.dll"); + auto offset = uintptr_t(msgHandlerVTableFirstFunc) - engineBaseAddress; + + constexpr uintptr_t CLIENTSTATE_FIRST_VFUNC_OFFSET = 0x8A15C; + areWeServer = offset != CLIENTSTATE_FIRST_VFUNC_OFFSET; + } + + std::string BLOCK_PREFIX = std::string{"NET_SetConVar ("} + (areWeServer ? "server" : "client") + "): Blocked dangerous/invalid msg: "; + + if (areWeServer) + { + constexpr int SETCONVAR_SANITY_AMOUNT_LIMIT = 69; + if (msg->m_ConVars_count < 1 || msg->m_ConVars_count > SETCONVAR_SANITY_AMOUNT_LIMIT) + { + return BLOCKED_INFO("Invalid m_ConVars_count (" << msg->m_ConVars_count << ")"); + } + } for (int i = 0; i < msg->m_ConVars_count; i++) { @@ -89,25 +124,41 @@ KHOOK(CClient_ProcessSetConVar, ("engine.dll", "48 8B D1 48 8B 49 18 48 8B 01 48 } if (!nameValid || !valValid) - return false; // Missing null terminators + return BLOCKED_INFO("Missing null terminators"); auto realVar = g_pCVar->FindVar(entry->name); - if (!realVar) - // Not an actual cvar, no thanks - return false; + if (realVar) + memcpy(entry->name, realVar->m_ConCommandBase.m_pszName, strlen(realVar->m_ConCommandBase.m_pszName) + 1); // Force name to match case - // Force name to match case - memcpy(entry->name, realVar->m_ConCommandBase.m_pszName, strlen(realVar->m_ConCommandBase.m_pszName) + 1); + bool isValidFlags = true; + if (areWeServer) + { + if (realVar) + isValidFlags = ConVar::IsFlagSet(realVar, FCVAR_USERINFO); // ConVar MUST be userinfo var + } + else + { + // TODO: Should probably have some sanity checks, but can't find any that are consistent + } - if (!ConVar::IsFlagSet(realVar, FCVAR_USERINFO) || ConVar::IsFlagSet(realVar, FCVAR_CHEAT)) + if (!isValidFlags) { - return false; + if (!realVar) + { + return BLOCKED_INFO("Invalid flags on nonexistant cvar (how tho???)"); + } + else + { + return BLOCKED_INFO( + "Invalid flags (" << std::hex << "0x" << realVar->m_ConCommandBase.m_nFlags << "), var is " << entry->name); + } + } } else { - return false; // Not risking that one, they all gotta be readable + return BLOCKED_INFO("Unreadable memory at " << (void*)entry); // Not risking that one, they all gotta be readable } } @@ -130,15 +181,27 @@ KHOOK(CClient_ProcessUsercmds, ("engine.dll", "40 55 56 48 83 EC 58"), bool, __f auto msg = (CLC_Move*)pMsg; - if (msg->m_nBackupCommands < 0 || msg->m_nNewCommands < 1) - return false; // Nice try buster + const char* BLOCK_PREFIX = "ProcessUserCmds: "; + + if (msg->m_nBackupCommands < 0) + { + return BLOCKED_INFO("Invalid m_nBackupCommands (" << msg->m_nBackupCommands << ")"); + } + + if (msg->m_nNewCommands < 0) + { + return BLOCKED_INFO("Invalid m_nNewCommands (" << msg->m_nNewCommands << ")"); + } constexpr int NUMCMD_SANITY_LIMIT = 16; if ((msg->m_nNewCommands + msg->m_nBackupCommands) > NUMCMD_SANITY_LIMIT) - return false; // Im good + { + return BLOCKED_INFO("Command count is too high (new: " << msg->m_nNewCommands << ", backup: " << msg->m_nBackupCommands << ")"); + + } if (msg->m_nLength <= 0) - return false; + return BLOCKED_INFO("Invalid message length (" << msg->m_nLength << ")"); return oCClient_ProcessUsercmds(thisptr, pMsg); } @@ -175,23 +238,56 @@ KHOOK(ReadUsercmd, ("server.dll", "4C 89 44 24 ? 53 55 56 57"), void, __fastcall DWORD dword98; float frameTime; }; + auto cmd = (SV_CUserCmd*)pCmd_move; + auto fromCmd = (SV_CUserCmd*)pCmd_from; + + std::string BLOCK_PREFIX = "ReadUsercmd (command_number delta: " + std::to_string(cmd->command_number - fromCmd->command_number) + "): "; + + if (cmd->worldViewAngles.IsInvalid()) + { + BLOCKED_INFO("CMD has invalid worldViewAngles"); + goto INVALID_CMD; + } - if (cmd->worldViewAngles.IsInvalid() || cmd->localViewAngles.IsInvalid() || cmd->attackangles.IsInvalid() || - cmd->cameraAngles.IsInvalid()) + if (cmd->attackangles.IsInvalid()) { + BLOCKED_INFO("CMD has invalid attackangles"); + goto INVALID_CMD; + } + + if (cmd->localViewAngles.IsInvalid()) + { + BLOCKED_INFO("CMD has invalid localViewAngles"); + goto INVALID_CMD; + } + + if (cmd->cameraAngles.IsInvalid()) + { + BLOCKED_INFO("CMD has invalid cameraAngles"); goto INVALID_CMD; } if (cmd->frameTime <= 0 || cmd->tick_count == 0 || cmd->command_time <= 0) + { + BLOCKED_INFO( + "Bogus cmd timing (tick_count: " << cmd->tick_count << ", frameTime: " << cmd->frameTime + << ", commandTime : " << cmd->command_time << ")"); goto INVALID_CMD; // No simulation of bogus-timed cmds + } + - if (!cmd->move.IsValid() || // Prevent player freeze (and even server crash) exploit - !cmd->cameraPos.IsValid()) // IIRC this can crash spectating clients or anyone watching replays + if (!cmd->move.IsValid()) + { + BLOCKED_INFO("Invalid move vector"); goto INVALID_CMD; + } - if (!ValidateFloats(cmd->cameraPos.x, cmd->cameraPos.y, cmd->cameraPos.z)) - goto INVALID_CMD; // IIRC this can crash spectating clients or anyone watching replays + if (!cmd->cameraPos.IsValid()) + { + BLOCKED_INFO("Invalid cameraPos"); // IIRC this can crash spectating clients or anyone watching replays + goto INVALID_CMD; + } return; INVALID_CMD: @@ -227,7 +323,8 @@ KHOOK( { if (!ExploitFixes_UTF8Parser::CheckValid(a1, a2, strData)) { - spdlog::warn("ParseUTF8 Hook: Ignoring potentially-crashing utf8 string"); + const char* BLOCK_PREFIX = "ParseUTF8 Hook: "; + BLOCKED_INFO("Ignoring potentially-crashing utf8 string"); return false; } } @@ -244,8 +341,8 @@ void DoBytePatches() // patches to make commands run from client/ui script still work // note: this is likely preventable in a nicer way? test prolly - NSMem::BytePatch(engineBase + 0x4FB65, {0xEB, 0x11}); - NSMem::BytePatch(engineBase + 0x4FBAC, {0xEB, 0x16}); + NSMem::BytePatch(engineBase + 0x4FB65, "EB 11"); + NSMem::BytePatch(engineBase + 0x4FBAC, "EB 16"); // disconnect concommand { @@ -266,16 +363,15 @@ void DoBytePatches() for (auto exportName : ANTITAMPER_EXPORTS) { - auto address = (uintptr_t)GetProcAddress(NULL, exportName); + auto address = (uintptr_t)GetProcAddress(GetModuleHandleA("server.dll"), exportName); if (!address) { spdlog::warn("Failed to find AntiTamper function export \"{}\"", exportName); } else { - // Just return, none of them have any args or are userpurge - NSMem::BytePatch(address, {0xC3}); + NSMem::BytePatch(address, "C3"); spdlog::info("Patched AntiTamper function export \"{}\"", exportName); } @@ -302,4 +398,7 @@ void ExploitFixes::LoadCallback(HMODULE unused) MessageBoxA(0, "FAILED to initialize all exploit patches.", "Northstar", MB_ICONERROR); exit(0); } + + ns_exploitfixes_log = + new ConVar("ns_exploitfixes_log", "1", FCVAR_GAMEDLL, "Whether to log whenever ExploitFixes.cpp blocks/corrects something"); } \ No newline at end of file diff --git a/NorthstarDedicatedTest/ExploitFixes.h b/NorthstarDedicatedTest/ExploitFixes.h index 7a407a3d..16196cbf 100644 --- a/NorthstarDedicatedTest/ExploitFixes.h +++ b/NorthstarDedicatedTest/ExploitFixes.h @@ -5,5 +5,5 @@ namespace ExploitFixes { - void LoadCallback(HMODULE unused); +void LoadCallback(HMODULE unused); } \ No newline at end of file diff --git a/NorthstarDedicatedTest/ExploitFixes_UTF8Parser.h b/NorthstarDedicatedTest/ExploitFixes_UTF8Parser.h index b06d442b..09c26293 100644 --- a/NorthstarDedicatedTest/ExploitFixes_UTF8Parser.h +++ b/NorthstarDedicatedTest/ExploitFixes_UTF8Parser.h @@ -6,170 +6,170 @@ namespace ExploitFixes_UTF8Parser { - bool __fastcall CheckValid(INT64* a1, DWORD* a2, char* strData) - { - static auto sub_F1320 = (INT64(__fastcall*)(DWORD a1, char* a2))NSMem::PatternScan("engine.dll", "83 F9 7F 77 08 88 0A"); +bool __fastcall CheckValid(INT64* a1, DWORD* a2, char* strData) +{ + static auto sub_F1320 = (INT64(__fastcall*)(DWORD a1, char* a2))NSMem::PatternScan("engine.dll", "83 F9 7F 77 08 88 0A"); - DWORD v3; // eax - char* v4; // rbx - char v5; // si - char* _strData; // rdi - char* v7; // rbp - char v11; // al - DWORD v12; // er9 - DWORD v13; // ecx - DWORD v14; // edx - DWORD v15; // er8 - int v16; // eax - DWORD v17; // er9 - int v18; // eax - DWORD v19; // er9 - DWORD v20; // ecx - int v21; // eax - int v22; // er9 - DWORD v23; // edx - int v24; // eax - int v25; // er9 - DWORD v26; // er9 - DWORD v27; // er10 - DWORD v28; // ecx - DWORD v29; // edx - DWORD v30; // er8 - int v31; // eax - DWORD v32; // er10 - int v33; // eax - DWORD v34; // er10 - DWORD v35; // ecx - int v36; // eax - int v37; // er10 - DWORD v38; // edx - int v39; // eax - int v40; // er10 - DWORD v41; // er10 - INT64 v43; // r8 - INT64 v44; // rdx - INT64 v45; // rcx - INT64 v46; // rax - INT64 v47; // rax - char v48; // al - INT64 v49; // r8 - INT64 v50; // rdx - INT64 v51; // rcx - INT64 v52; // rax - INT64 v53; // rax + DWORD v3; // eax + char* v4; // rbx + char v5; // si + char* _strData; // rdi + char* v7; // rbp + char v11; // al + DWORD v12; // er9 + DWORD v13; // ecx + DWORD v14; // edx + DWORD v15; // er8 + int v16; // eax + DWORD v17; // er9 + int v18; // eax + DWORD v19; // er9 + DWORD v20; // ecx + int v21; // eax + int v22; // er9 + DWORD v23; // edx + int v24; // eax + int v25; // er9 + DWORD v26; // er9 + DWORD v27; // er10 + DWORD v28; // ecx + DWORD v29; // edx + DWORD v30; // er8 + int v31; // eax + DWORD v32; // er10 + int v33; // eax + DWORD v34; // er10 + DWORD v35; // ecx + int v36; // eax + int v37; // er10 + DWORD v38; // edx + int v39; // eax + int v40; // er10 + DWORD v41; // er10 + INT64 v43; // r8 + INT64 v44; // rdx + INT64 v45; // rcx + INT64 v46; // rax + INT64 v47; // rax + char v48; // al + INT64 v49; // r8 + INT64 v50; // rdx + INT64 v51; // rcx + INT64 v52; // rax + INT64 v53; // rax - v3 = a2[2]; - v4 = (char*)(a1[1] + *a2); - v5 = 0; - _strData = strData; - v7 = &v4[*((UINT16*)a2 + 2)]; - if (v3 >= 2) + v3 = a2[2]; + v4 = (char*)(a1[1] + *a2); + v5 = 0; + _strData = strData; + v7 = &v4[*((UINT16*)a2 + 2)]; + if (v3 >= 2) + { + ++v4; + --v7; + if (v3 != 2) { - ++v4; - --v7; - if (v3 != 2) + while (1) { - while (1) - { - if (!NSMem::IsMemoryReadable(v4, 1)) - return false; // INVALID + if (!NSMem::IsMemoryReadable(v4, 1)) + return false; // INVALID - v11 = *v4++; // crash potential - if (v11 != 92) - goto LABEL_6; - v11 = *v4++; - if (v11 == 110) - break; - switch (v11) - { - case 't': - v11 = 9; - goto LABEL_6; - case 'r': - v11 = 13; - goto LABEL_6; - case 'b': - v11 = 8; - goto LABEL_6; - case 'f': - v11 = 12; - goto LABEL_6; - } - if (v11 != 117) - goto LABEL_6; - v12 = *v4 | 0x20; - v13 = v4[1] | 0x20; - v14 = v4[2] | 0x20; - v15 = v4[3] | 0x20; - v16 = 87; - if (v12 <= 0x39) - v16 = 48; - v17 = v12 - v16; - v18 = 87; - v19 = v17 << 12; - if (v13 <= 0x39) - v18 = 48; - v20 = v13 - v18; - v21 = 87; - v22 = (v20 << 8) | v19; - if (v14 <= 0x39) - v21 = 48; - v23 = v14 - v21; - v24 = 87; - v25 = (16 * v23) | v22; - if (v15 <= 0x39) - v24 = 48; - v4 += 4; - v26 = (v15 - v24) | v25; - if (v26 - 55296 <= 0x7FF) - { - if (v26 >= 0xDC00) - return true; - if (*v4 != 92 || v4[1] != 117) - return true; + v11 = *v4++; // crash potential + if (v11 != 92) + goto LABEL_6; + v11 = *v4++; + if (v11 == 110) + break; + switch (v11) + { + case 't': + v11 = 9; + goto LABEL_6; + case 'r': + v11 = 13; + goto LABEL_6; + case 'b': + v11 = 8; + goto LABEL_6; + case 'f': + v11 = 12; + goto LABEL_6; + } + if (v11 != 117) + goto LABEL_6; + v12 = *v4 | 0x20; + v13 = v4[1] | 0x20; + v14 = v4[2] | 0x20; + v15 = v4[3] | 0x20; + v16 = 87; + if (v12 <= 0x39) + v16 = 48; + v17 = v12 - v16; + v18 = 87; + v19 = v17 << 12; + if (v13 <= 0x39) + v18 = 48; + v20 = v13 - v18; + v21 = 87; + v22 = (v20 << 8) | v19; + if (v14 <= 0x39) + v21 = 48; + v23 = v14 - v21; + v24 = 87; + v25 = (16 * v23) | v22; + if (v15 <= 0x39) + v24 = 48; + v4 += 4; + v26 = (v15 - v24) | v25; + if (v26 - 55296 <= 0x7FF) + { + if (v26 >= 0xDC00) + return true; + if (*v4 != 92 || v4[1] != 117) + return true; - v27 = v4[2] | 0x20; - v28 = v4[3] | 0x20; - v29 = v4[4] | 0x20; - v30 = v4[5] | 0x20; - v31 = 87; - if (v27 <= 0x39) - v31 = 48; - v32 = v27 - v31; - v33 = 87; - v34 = v32 << 12; - if (v28 <= 0x39) - v33 = 48; - v35 = v28 - v33; - v36 = 87; - v37 = (v35 << 8) | v34; - if (v29 <= 0x39) - v36 = 48; - v38 = v29 - v36; - v39 = 87; - v40 = (16 * v38) | v37; - if (v30 <= 0x39) - v39 = 48; - v4 += 6; - v41 = ((v30 - v39) | v40) - 56320; - if (v41 > 0x3FF) - return true; - v26 = v41 | ((v26 - 55296) << 10); - } - _strData += (DWORD)sub_F1320(v26, _strData); - LABEL_7: - if (v4 == v7) - goto LABEL_48; + v27 = v4[2] | 0x20; + v28 = v4[3] | 0x20; + v29 = v4[4] | 0x20; + v30 = v4[5] | 0x20; + v31 = 87; + if (v27 <= 0x39) + v31 = 48; + v32 = v27 - v31; + v33 = 87; + v34 = v32 << 12; + if (v28 <= 0x39) + v33 = 48; + v35 = v28 - v33; + v36 = 87; + v37 = (v35 << 8) | v34; + if (v29 <= 0x39) + v36 = 48; + v38 = v29 - v36; + v39 = 87; + v40 = (16 * v38) | v37; + if (v30 <= 0x39) + v39 = 48; + v4 += 6; + v41 = ((v30 - v39) | v40) - 56320; + if (v41 > 0x3FF) + return true; + v26 = v41 | ((v26 - 55296) << 10); } - v11 = 10; - LABEL_6: - v5 |= v11; - *_strData++ = v11; - goto LABEL_7; + _strData += (DWORD)sub_F1320(v26, _strData); + LABEL_7: + if (v4 == v7) + goto LABEL_48; } + v11 = 10; + LABEL_6: + v5 |= v11; + *_strData++ = v11; + goto LABEL_7; } - LABEL_48: - return true; } +LABEL_48: + return true; +} } // namespace ExploitFixes_UTF8Parser \ No newline at end of file diff --git a/NorthstarDedicatedTest/NSMem.h b/NorthstarDedicatedTest/NSMem.h index a5bbd42f..50928dd2 100644 --- a/NorthstarDedicatedTest/NSMem.h +++ b/NorthstarDedicatedTest/NSMem.h @@ -6,119 +6,147 @@ #pragma region Pattern Scanning namespace NSMem { - inline void* PatternScan(void* module, const int* pattern, int patternSize, int offset) +inline std::vector HexBytesToString(const char* str) +{ + std::vector patternNums; + int size = strlen(str); + for (int i = 0; i < size; i++) { - if (!module) - return NULL; - - auto dosHeader = (PIMAGE_DOS_HEADER)module; - auto ntHeaders = (PIMAGE_NT_HEADERS)((BYTE*)module + dosHeader->e_lfanew); - - auto sizeOfImage = ntHeaders->OptionalHeader.SizeOfImage; + char c = str[i]; - auto scanBytes = (BYTE*)module; + // If this is a space character, ignore it + if (c == ' ' || c == '\t') + continue; - for (auto i = 0; i < sizeOfImage - patternSize; ++i) + if (c == '?') { - bool found = true; - for (auto j = 0; j < patternSize; ++j) + // Add a wildcard (-1) + patternNums.push_back(-1); + } + else if (i < size - 1) + { + BYTE result = 0; + for (int j = 0; j < 2; j++) { - if (scanBytes[i + j] != pattern[j] && pattern[j] != -1) + int val = 0; + char c = *(str + i + j); + if (c >= 'a') { - found = false; - break; + val = c - 'a' + 0xA; + } + else if (c >= 'A') + { + val = c - 'A' + 0xA; + } + else if (isdigit(c)) + { + val = c - '0'; + } + else + { + assert(false, "Failed to parse invalid hex string."); + val = -1; } - } - if (found) - { - uintptr_t addressInt = (uintptr_t)(&scanBytes[i]) + offset; - return (uint8_t*)addressInt; + result += (j == 0) ? val * 16 : val; } + patternNums.push_back(result); } - return nullptr; + i++; } - inline void* PatternScan(const char* moduleName, const char* pattern, int offset = 0) - { - std::vector patternNums; + return patternNums; +} - bool lastChar = 0; - int size = strlen(pattern); - for (int i = 0; i < size; i++) - { - char c = pattern[i]; +inline void* PatternScan(void* module, const int* pattern, int patternSize, int offset) +{ + if (!module) + return NULL; - // If this is a space character, ignore it - if (c == ' ' || c == '\t') - continue; + auto dosHeader = (PIMAGE_DOS_HEADER)module; + auto ntHeaders = (PIMAGE_NT_HEADERS)((BYTE*)module + dosHeader->e_lfanew); - if (c == '?') - { - // Add a wildcard (-1) - patternNums.push_back(-1); - } - else if (i < size - 1) + auto sizeOfImage = ntHeaders->OptionalHeader.SizeOfImage; + + auto scanBytes = (BYTE*)module; + + for (auto i = 0; i < sizeOfImage - patternSize; ++i) + { + bool found = true; + for (auto j = 0; j < patternSize; ++j) + { + if (scanBytes[i + j] != pattern[j] && pattern[j] != -1) { - BYTE result = 0; - for (int j = 0; j < 2; j++) - { - int val = 0; - char c = (pattern + i + j)[0]; - if (c >= 'a') - { - val = c - 'a' + 0xA; - } - else if (c >= 'A') - { - val = c - 'A' + 0xA; - } - else - { - val = c - '0'; - } - - result += (j == 0) ? val * 16 : val; - } - patternNums.push_back(result); + found = false; + break; } + } - i++; + if (found) + { + uintptr_t addressInt = (uintptr_t)(&scanBytes[i]) + offset; + return (uint8_t*)addressInt; } - return PatternScan(GetModuleHandleA(moduleName), &patternNums[0], patternNums.size(), offset); } - inline void BytePatch(uintptr_t address, const BYTE* vals, int size) - { - WriteProcessMemory(GetCurrentProcess(), (LPVOID)address, vals, size, NULL); - } + return nullptr; +} - inline void BytePatch(uintptr_t address, std::initializer_list vals) - { - std::vector bytes = vals; - if (!bytes.empty()) - BytePatch(address, &bytes[0], bytes.size()); - } +inline void* PatternScan(const char* moduleName, const char* pattern, int offset = 0) +{ + std::vector patternNums = HexBytesToString(pattern); - inline void NOP(uintptr_t address, int size) - { - BYTE* buf = (BYTE*)malloc(size); - memset(buf, 0x90, size); - BytePatch(address, buf, size); - free(buf); - } + return PatternScan(GetModuleHandleA(moduleName), &patternNums[0], patternNums.size(), offset); +} - inline bool IsMemoryReadable(void* ptr, size_t size) - { - BYTE* buffer = (BYTE*)malloc(size); +inline void BytePatch(uintptr_t address, const BYTE* vals, int size) +{ + WriteProcessMemory(GetCurrentProcess(), (LPVOID)address, vals, size, NULL); +} - size_t numWritten = 0; - ReadProcessMemory(GetCurrentProcess(), ptr, buffer, size, &numWritten); - free(buffer); +inline void BytePatch(uintptr_t address, std::initializer_list vals) +{ + std::vector bytes = vals; + if (!bytes.empty()) + BytePatch(address, &bytes[0], bytes.size()); +} - return numWritten == size; - } +inline void BytePatch(uintptr_t address, const char* bytesStr) +{ + std::vector byteInts = HexBytesToString(bytesStr); + std::vector bytes; + for (int v : byteInts) + bytes.push_back(v); + + if (!bytes.empty()) + BytePatch(address, &bytes[0], bytes.size()); +} + +inline void NOP(uintptr_t address, int size) +{ + BYTE* buf = (BYTE*)malloc(size); + memset(buf, 0x90, size); + BytePatch(address, buf, size); + free(buf); +} + +inline bool IsMemoryReadable(void* ptr, size_t size) +{ + static SYSTEM_INFO sysInfo; + if (!sysInfo.dwPageSize) + GetSystemInfo(&sysInfo); // This should always be 4096 unless ur playing on NES or some shit but whatever + + MEMORY_BASIC_INFORMATION memInfo; + + if (!VirtualQuery(ptr, &memInfo, sizeof(memInfo))) + return false; + + if (memInfo.RegionSize < size) + return false; + + return (memInfo.State & MEM_COMMIT) && !(memInfo.Protect & PAGE_NOACCESS); +} } // namespace NSMem #pragma region KHOOK diff --git a/NorthstarDedicatedTest/clientvideooverrides.cpp b/NorthstarDedicatedTest/clientvideooverrides.cpp index 0b5ff5f5..d5674f51 100644 --- a/NorthstarDedicatedTest/clientvideooverrides.cpp +++ b/NorthstarDedicatedTest/clientvideooverrides.cpp @@ -2,7 +2,7 @@ #include "clientvideooverrides.h" #include "modmanager.h" -typedef void*(*BinkOpenType)(const char* path, uint32_t flags); +typedef void* (*BinkOpenType)(const char* path, uint32_t flags); BinkOpenType BinkOpen; void* BinkOpenHook(const char* path, uint32_t flags) @@ -16,7 +16,7 @@ void* BinkOpenHook(const char* path, uint32_t flags) { if (!mod.Enabled) continue; - + if (std::find(mod.BinkVideos.begin(), mod.BinkVideos.end(), filename) != mod.BinkVideos.end()) fileOwner = &mod; } @@ -34,5 +34,6 @@ void* BinkOpenHook(const char* path, uint32_t flags) void InitialiseClientVideoOverrides(HMODULE baseAddress) { HookEnabler hook; - ENABLER_CREATEHOOK(hook, GetProcAddress(GetModuleHandleA("bink2w64.dll"), "BinkOpen"), &BinkOpenHook, reinterpret_cast(&BinkOpen)); + ENABLER_CREATEHOOK( + hook, GetProcAddress(GetModuleHandleA("bink2w64.dll"), "BinkOpen"), &BinkOpenHook, reinterpret_cast(&BinkOpen)); } \ No newline at end of file diff --git a/NorthstarDedicatedTest/concommand.h b/NorthstarDedicatedTest/concommand.h index eb812c32..8f5d59e0 100644 --- a/NorthstarDedicatedTest/concommand.h +++ b/NorthstarDedicatedTest/concommand.h @@ -106,5 +106,4 @@ class ConCommand : public ConCommandBase void RegisterConCommand(const char* name, void (*callback)(const CCommand&), const char* helpString, int flags); void InitialiseConCommands(HMODULE baseAddress); -#define MAKE_CONCMD(name, helpStr, flags, fn) \ -RegisterConCommand(name, fn, helpStr, flags); \ No newline at end of file +#define MAKE_CONCMD(name, helpStr, flags, fn) RegisterConCommand(name, fn, helpStr, flags); \ No newline at end of file diff --git a/NorthstarDedicatedTest/convar.cpp b/NorthstarDedicatedTest/convar.cpp index 3f2728d0..c750bd4d 100644 --- a/NorthstarDedicatedTest/convar.cpp +++ b/NorthstarDedicatedTest/convar.cpp @@ -36,8 +36,6 @@ void InitialiseConVars(HMODULE baseAddress) HookEnabler hook; ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x417FA0, &ConVar::IsFlagSet, reinterpret_cast(&CvarIsFlagSet)); - - } //----------------------------------------------------------------------------- diff --git a/NorthstarDedicatedTest/convar.h b/NorthstarDedicatedTest/convar.h index fc5b0eb9..72dc1d3c 100644 --- a/NorthstarDedicatedTest/convar.h +++ b/NorthstarDedicatedTest/convar.h @@ -129,7 +129,7 @@ class ConVar }; ConCommandBase m_ConCommandBase{}; // 0x0000 - const char* defaultVal{}; // 0x0040 + const char* defaultVal{}; // 0x0040 CVValue_t m_Value{}; // 0x0048 bool m_bHasMin{}; // 0x005C float m_fMinVal{}; // 0x0060 diff --git a/NorthstarDedicatedTest/cvar.h b/NorthstarDedicatedTest/cvar.h index e254af4e..8e993b19 100644 --- a/NorthstarDedicatedTest/cvar.h +++ b/NorthstarDedicatedTest/cvar.h @@ -26,8 +26,7 @@ class CCVarIteratorInternal // Fully reversed table, just look at the virtual fu //----------------------------------------------------------------------------- class CCvar { - public: - + public: M_VMETHOD(ConCommandBase*, FindCommandBase, 14, (const char* pszCommandName), (this, pszCommandName)); M_VMETHOD(ConVar*, FindVar, 16, (const char* pszVarName), (this, pszVarName)); M_VMETHOD(ConCommand*, FindCommand, 18, (const char* pszCommandName), (this, pszCommandName)); diff --git a/NorthstarDedicatedTest/dedicated.cpp b/NorthstarDedicatedTest/dedicated.cpp index e0206d1f..c97ac91d 100644 --- a/NorthstarDedicatedTest/dedicated.cpp +++ b/NorthstarDedicatedTest/dedicated.cpp @@ -7,8 +7,8 @@ bool IsDedicated() { - // return CommandLine()->CheckParm("-dedicated"); - return strstr(GetCommandLineA(), "-dedicated"); + static bool result = strstr(GetCommandLineA(), "-dedicated"); + return result; } // CDedidcatedExports defs @@ -136,10 +136,10 @@ void InitialiseDedicated(HMODULE engineAddress) auto ptr = ea + 0x1C4EBD; // cmp => mov - NSMem::BytePatch(ptr + 1, {0xC6, 0x87}); + NSMem::BytePatch(ptr + 1, "C6 87"); // 00 => 01 - NSMem::BytePatch(ptr + 7, {0x01}); + NSMem::BytePatch(ptr + 7, "01"); } { @@ -160,7 +160,7 @@ void InitialiseDedicated(HMODULE engineAddress) // previously patched these, took me a couple weeks to figure out they were the issue // removing these will mess up register state when this function is over, so we'll write HS_RUN to the wrong address // so uhh, don't do that - //NSMem::NOP(ea + 0x156B4C + 7, 8); + // NSMem::NOP(ea + 0x156B4C + 7, 8); NSMem::NOP(ea + 0x156B4C + 15, 9); } @@ -188,7 +188,7 @@ void InitialiseDedicated(HMODULE engineAddress) { // Host_Init // change the number of rpaks to load from 6 to 1, so we only load common.rpak - NSMem::BytePatch(ea + 0x15653B + 1, {0x01}); + NSMem::BytePatch(ea + 0x15653B + 1, "01"); } { @@ -212,10 +212,11 @@ void InitialiseDedicated(HMODULE engineAddress) { // func that checks if origin is inited // always return 1 - NSMem::BytePatch(ea + 0x183B70, { - 0xB0, 0x01, // mov al,01 - 0xC3 // ret - }); + NSMem::BytePatch( + ea + 0x183B70, { + 0xB0, 0x01, // mov al,01 + 0xC3 // ret + }); } { @@ -301,9 +302,10 @@ void InitialiseDedicatedOrigin(HMODULE baseAddress) // for any big ea lawyers, this can't be used to play the game without origin, game will throw a fit if you try to do anything without // an origin id as a client for dedi it's fine though, game doesn't care if origin is disabled as long as there's only a server - NSMem::BytePatch((uintptr_t)GetProcAddress(GetModuleHandleA("tier0.dll"), "Tier0_InitOrigin"), { - 0xC3 // ret - }); + NSMem::BytePatch( + (uintptr_t)GetProcAddress(GetModuleHandleA("tier0.dll"), "Tier0_InitOrigin"), { + 0xC3 // ret + }); } typedef void (*PrintFatalSquirrelErrorType)(void* sqvm); diff --git a/NorthstarDedicatedTest/dedicatedmaterialsystem.cpp b/NorthstarDedicatedTest/dedicatedmaterialsystem.cpp index 8fa2b58d..b3452fc8 100644 --- a/NorthstarDedicatedTest/dedicatedmaterialsystem.cpp +++ b/NorthstarDedicatedTest/dedicatedmaterialsystem.cpp @@ -61,9 +61,7 @@ void InitialiseDedicatedMaterialSystem(HMODULE baseAddress) { // CMaterialSystem::FindMaterial // make the game always use the error material - NSMem::BytePatch((uintptr_t)baseAddress + 0x5F0F1, { - 0xE9, 0x34, 0x03, 0x00 - }); + NSMem::BytePatch((uintptr_t)baseAddress + 0x5F0F1, {0xE9, 0x34, 0x03, 0x00}); } // previously had DisableDedicatedWindowCreation stuff here, removing for now since shit and unstable diff --git a/NorthstarDedicatedTest/languagehooks.cpp b/NorthstarDedicatedTest/languagehooks.cpp index 8d60ca22..95638ef2 100644 --- a/NorthstarDedicatedTest/languagehooks.cpp +++ b/NorthstarDedicatedTest/languagehooks.cpp @@ -15,7 +15,7 @@ GetGameLanguageType GetGameLanguageOriginal; bool CheckLangAudioExists(char* lang) { - std::string path {"r2\\sound\\general_"}; + std::string path{"r2\\sound\\general_"}; path += lang; path += ".mstr"; return fs::exists(path); @@ -31,7 +31,7 @@ std::vector file_list(fs::path dir, std::regex ext_pattern) using iterator = fs::directory_iterator; const iterator end; - for (iterator iter {dir}; iter != end; ++iter) + for (iterator iter{dir}; iter != end; ++iter) { const std::string filename = iter->path().filename().string(); std::smatch matches; diff --git a/NorthstarDedicatedTest/latencyflex.cpp b/NorthstarDedicatedTest/latencyflex.cpp index 4ac1c760..623ac06b 100644 --- a/NorthstarDedicatedTest/latencyflex.cpp +++ b/NorthstarDedicatedTest/latencyflex.cpp @@ -8,9 +8,9 @@ OnRenderStartType OnRenderStart; ConVar* Cvar_r_latencyflex; -HMODULE m_lfxModule {}; +HMODULE m_lfxModule{}; typedef void (*PFN_winelfx_WaitAndBeginFrame)(); -PFN_winelfx_WaitAndBeginFrame m_winelfx_WaitAndBeginFrame {}; +PFN_winelfx_WaitAndBeginFrame m_winelfx_WaitAndBeginFrame{}; void OnRenderStartHook() { diff --git a/NorthstarDedicatedTest/logging.cpp b/NorthstarDedicatedTest/logging.cpp index 98e80592..25b9a530 100644 --- a/NorthstarDedicatedTest/logging.cpp +++ b/NorthstarDedicatedTest/logging.cpp @@ -68,8 +68,7 @@ long __stdcall ExceptionFilter(EXCEPTION_POINTERS* exceptionInfo) else if (exceptionInfo0 == 1) exceptionCause << "Attempted to write to: 0x" << (void*)exceptionInfo1; else if (exceptionInfo0 == 8) - exceptionCause << "Data Execution Prevention (DEP) at: 0x" << (void*)std::hex - << exceptionInfo1; + exceptionCause << "Data Execution Prevention (DEP) at: 0x" << (void*)std::hex << exceptionInfo1; else exceptionCause << "Unknown access violation at: 0x" << (void*)exceptionInfo1; diff --git a/NorthstarDedicatedTest/masterserver.cpp b/NorthstarDedicatedTest/masterserver.cpp index 076c0a14..ab6388c3 100644 --- a/NorthstarDedicatedTest/masterserver.cpp +++ b/NorthstarDedicatedTest/masterserver.cpp @@ -557,7 +557,7 @@ void MasterServerManager::AuthenticateWithOwnServer(char* uid, char* playerToken curl_easy_setopt( curl, CURLOPT_URL, fmt::format("{}/client/auth_with_self?id={}&playerToken={}", Cvar_ns_masterserver_hostname->GetString(), uidStr, tokenStr) - .c_str()); + .c_str()); curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST"); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteToStringBufferCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer); @@ -699,7 +699,7 @@ void MasterServerManager::AuthenticateWithServer(char* uid, char* playerToken, c fmt::format( "{}/client/auth_with_server?id={}&playerToken={}&server={}&password={}", Cvar_ns_masterserver_hostname->GetString(), uidStr, tokenStr, serverIdStr, escapedPassword) - .c_str()); + .c_str()); curl_free(escapedPassword); } @@ -833,7 +833,7 @@ void MasterServerManager::AddSelfToServerList( "{}/server/add_server?port={}&authPort={}&name={}&description={}&map={}&playlist={}&maxPlayers={}&password={}", Cvar_ns_masterserver_hostname->GetString(), port, authPort, nameEscaped, descEscaped, mapEscaped, playlistEscaped, maxPlayers, passwordEscaped) - .c_str()); + .c_str()); curl_free(nameEscaped); curl_free(descEscaped); @@ -937,7 +937,7 @@ void MasterServerManager::AddSelfToServerList( Cvar_ns_player_auth_port->GetInt(), escapedNameNew, escapedDescNew, escapedMapNew, escapedPlaylistNew, g_ServerAuthenticationManager->m_additionalPlayerData.size(), maxPlayers, escapedPasswordNew) - .c_str()); + .c_str()); curl_free(escapedNameNew); curl_free(escapedDescNew); @@ -1038,7 +1038,7 @@ void MasterServerManager::UpdateServerMapAndPlaylist(char* map, char* playlist, fmt::format( "{}/server/update_values?id={}&map={}&playlist={}&maxPlayers={}", Cvar_ns_masterserver_hostname->GetString(), m_ownServerId, mapEscaped, playlistEscaped, maxPlayers) - .c_str()); + .c_str()); curl_free(mapEscaped); curl_free(playlistEscaped); @@ -1077,7 +1077,7 @@ void MasterServerManager::UpdateServerPlayerCount(int playerCount) curl, CURLOPT_URL, fmt::format( "{}/server/update_values?id={}&playerCount={}", Cvar_ns_masterserver_hostname->GetString(), m_ownServerId, playerCount) - .c_str()); + .c_str()); CURLcode result = curl_easy_perform(curl); @@ -1117,7 +1117,7 @@ void MasterServerManager::WritePlayerPersistentData(char* playerId, char* pdata, fmt::format( "{}/accounts/write_persistence?id={}&serverId={}", Cvar_ns_masterserver_hostname->GetString(), strPlayerId, m_ownServerId) - .c_str()); + .c_str()); curl_easy_setopt(curl, CURLOPT_POST, 1L); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteToStringBufferCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer); @@ -1262,7 +1262,7 @@ void CHostState__State_GameShutdownHook(CHostState* hostState) CHostState__State_GameShutdown(hostState); } -MasterServerManager::MasterServerManager() : m_pendingConnectionInfo{}, m_ownServerId{ "" }, m_ownClientAuthToken{ "" } {} +MasterServerManager::MasterServerManager() : m_pendingConnectionInfo{}, m_ownServerId{""}, m_ownClientAuthToken{""} {} void InitialiseSharedMasterServer(HMODULE baseAddress) { diff --git a/NorthstarDedicatedTest/maxplayers.cpp b/NorthstarDedicatedTest/maxplayers.cpp index 54f1a896..9550553c 100644 --- a/NorthstarDedicatedTest/maxplayers.cpp +++ b/NorthstarDedicatedTest/maxplayers.cpp @@ -46,9 +46,7 @@ constexpr int Team_PlayerArray_AddedSize = PAD_NUMBER(Team_PlayerArray_AddedLeng constexpr int Team_AddedSize = Team_PlayerArray_AddedSize; #include "NSMem.h" -template void ChangeOffset(void* addr, unsigned int offset) { - NSMem::BytePatch((uintptr_t)addr, (BYTE*)&offset, sizeof(T)); -} +template void ChangeOffset(void* addr, unsigned int offset) { NSMem::BytePatch((uintptr_t)addr, (BYTE*)&offset, sizeof(T)); } /* typedef bool(*MatchRecvPropsToSendProps_R_Type)(__int64 lookup, __int64 tableNameBroken, __int64 sendTable, __int64 recvTable); diff --git a/NorthstarDedicatedTest/misccommands.cpp b/NorthstarDedicatedTest/misccommands.cpp index dca87947..cdadff8d 100644 --- a/NorthstarDedicatedTest/misccommands.cpp +++ b/NorthstarDedicatedTest/misccommands.cpp @@ -10,7 +10,8 @@ void AddMiscConCommands() { MAKE_CONCMD( "force_newgame", "forces a map load through directly setting g_pHostState->m_iNextState to HS_NEW_GAME", FCVAR_NONE, - [](const CCommand& arg) { + [](const CCommand& arg) + { if (arg.ArgC() < 2) return; @@ -20,29 +21,34 @@ void AddMiscConCommands() MAKE_CONCMD( "ns_start_reauth_and_leave_to_lobby", "called by the server, used to reauth and return the player to lobby when leaving a game", - FCVAR_SERVER_CAN_EXECUTE, [](const CCommand& arg) { + FCVAR_SERVER_CAN_EXECUTE, + [](const CCommand& arg) + { // hack for special case where we're on a local server, so we erase our own newly created auth data on disconnect g_MasterServerManager->m_bNewgameAfterSelfAuth = true; g_MasterServerManager->AuthenticateWithOwnServer(g_LocalPlayerUserID, g_MasterServerManager->m_ownClientAuthToken); }); // this is a concommand because we make a deferred call to it from another thread - MAKE_CONCMD("ns_end_reauth_and_leave_to_lobby", "", FCVAR_NONE, [](const CCommand& arg) { - Cbuf_AddText( - Cbuf_GetCurrentPlayer(), fmt::format("serverfilter {}", g_ServerAuthenticationManager->m_authData.begin()->first).c_str(), - cmd_source_t::kCommandSrcCode); - Cbuf_Execute(); - - // weird way of checking, but check if client script vm is initialised, mainly just to allow players to cancel this - if (g_ClientSquirrelManager->sqvm) + MAKE_CONCMD( + "ns_end_reauth_and_leave_to_lobby", "", FCVAR_NONE, + [](const CCommand& arg) { - g_ServerAuthenticationManager->m_bNeedLocalAuthForNewgame = true; + Cbuf_AddText( + Cbuf_GetCurrentPlayer(), fmt::format("serverfilter {}", g_ServerAuthenticationManager->m_authData.begin()->first).c_str(), + cmd_source_t::kCommandSrcCode); + Cbuf_Execute(); - // this won't set playlist correctly on remote clients, don't think they can set playlist until they've left which sorta fucks - // things should maybe set this in HostState_NewGame? - SetCurrentPlaylist("tdm"); - strcpy(g_pHostState->m_levelName, "mp_lobby"); - g_pHostState->m_iNextState = HS_NEW_GAME; - } - }); + // weird way of checking, but check if client script vm is initialised, mainly just to allow players to cancel this + if (g_ClientSquirrelManager->sqvm) + { + g_ServerAuthenticationManager->m_bNeedLocalAuthForNewgame = true; + + // this won't set playlist correctly on remote clients, don't think they can set playlist until they've left which sorta + // fucks things should maybe set this in HostState_NewGame? + SetCurrentPlaylist("tdm"); + strcpy(g_pHostState->m_levelName, "mp_lobby"); + g_pHostState->m_iNextState = HS_NEW_GAME; + } + }); } \ No newline at end of file diff --git a/NorthstarDedicatedTest/miscserverfixes.cpp b/NorthstarDedicatedTest/miscserverfixes.cpp index 7d977d64..30cd5777 100644 --- a/NorthstarDedicatedTest/miscserverfixes.cpp +++ b/NorthstarDedicatedTest/miscserverfixes.cpp @@ -10,7 +10,7 @@ void InitialiseMiscServerFixes(HMODULE baseAddress) // ret at the start of the concommand GenerateObjFile as it can crash servers { - NSMem::BytePatch(ba + 0x38D920, {0xC3}); + NSMem::BytePatch(ba + 0x38D920, "C3"); } // nop out call to VGUI shutdown since it crashes the game when quitting from the console @@ -21,7 +21,7 @@ void InitialiseMiscServerFixes(HMODULE baseAddress) // ret at the start of CServerGameClients::ClientCommandKeyValues as it has no benefit and is forwarded to client (i.e. security issue) // this prevents the attack vector of client=>server=>client, however server=>client also has clientside patches { - NSMem::BytePatch(ba + 0x153920, {0xC3}); + NSMem::BytePatch(ba + 0x153920, "C3"); } } diff --git a/NorthstarDedicatedTest/modmanager.cpp b/NorthstarDedicatedTest/modmanager.cpp index 99bb5fdb..0ac7e4b6 100644 --- a/NorthstarDedicatedTest/modmanager.cpp +++ b/NorthstarDedicatedTest/modmanager.cpp @@ -194,7 +194,7 @@ ModManager::ModManager() m_hScriptsRsonHash = STR_HASH("scripts\\vscripts\\scripts.rson"); m_hPdefHash = STR_HASH( "cfg\\server\\persistent_player_data_version_231.pdef" // this can have multiple versions, but we use 231 so that's what we hash - ); + ); LoadMods(); } @@ -283,7 +283,8 @@ void ModManager::LoadMods() // preexisting convars note: we don't delete convars if they already exist because they're used for script stuff, unfortunately this // causes us to leak memory on reload, but not much, potentially find a way to not do this at some point for (ModConVar* convar : mod.ConVars) - if (!g_pCVar->FindVar(convar->Name.c_str())) // make sure convar isn't registered yet, unsure if necessary but idk what behaviour is for defining same convar multiple times + if (!g_pCVar->FindVar(convar->Name.c_str())) // make sure convar isn't registered yet, unsure if necessary but idk what + // behaviour is for defining same convar multiple times new ConVar(convar->Name.c_str(), convar->DefaultValue.c_str(), convar->Flags, convar->HelpString.c_str()); // read vpk paths @@ -305,8 +306,7 @@ void ModManager::LoadMods() dVpkJson.Parse( vpkJsonStringStream.str().c_str()); - bUseVPKJson = - !dVpkJson.HasParseError() && dVpkJson.IsObject(); + bUseVPKJson = !dVpkJson.HasParseError() && dVpkJson.IsObject(); } for (fs::directory_entry file : fs::directory_iterator(mod.ModDirectory / "vpk")) @@ -324,7 +324,8 @@ void ModManager::LoadMods() (file.path().parent_path() / formattedPath.substr(strlen("english"), formattedPath.find(".bsp") - 3)).string(); ModVPKEntry& modVpk = mod.Vpks.emplace_back(); - modVpk.m_bAutoLoad = !bUseVPKJson || (dVpkJson.HasMember("Preload") && dVpkJson["Preload"].IsObject() && dVpkJson["Preload"].HasMember(vpkName) && dVpkJson["Preload"][vpkName].IsTrue()); + modVpk.m_bAutoLoad = !bUseVPKJson || (dVpkJson.HasMember("Preload") && dVpkJson["Preload"].IsObject() && + dVpkJson["Preload"].HasMember(vpkName) && dVpkJson["Preload"][vpkName].IsTrue()); modVpk.m_sVpkPath = vpkName; if (m_hasLoadedMods && modVpk.m_bAutoLoad) @@ -352,8 +353,7 @@ void ModManager::LoadMods() dRpakJson.Parse( rpakJsonStringStream.str().c_str()); - bUseRpakJson = - !dRpakJson.HasParseError() && dRpakJson.IsObject(); + bUseRpakJson = !dRpakJson.HasParseError() && dRpakJson.IsObject(); } // read pak aliases @@ -377,11 +377,13 @@ void ModManager::LoadMods() std::string pakName(file.path().filename().string()); ModRpakEntry& modPak = mod.Rpaks.emplace_back(); - modPak.m_bAutoLoad = !bUseRpakJson || (dRpakJson.HasMember("Preload") && dRpakJson["Preload"].IsObject() && dRpakJson["Preload"].HasMember(pakName) && dRpakJson["Preload"][pakName].IsTrue()); + modPak.m_bAutoLoad = + !bUseRpakJson || (dRpakJson.HasMember("Preload") && dRpakJson["Preload"].IsObject() && + dRpakJson["Preload"].HasMember(pakName) && dRpakJson["Preload"][pakName].IsTrue()); modPak.m_sPakPath = pakName; // not using atm because we need to resolve path to rpak - //if (m_hasLoadedMods && modPak.m_bAutoLoad) + // if (m_hasLoadedMods && modPak.m_bAutoLoad) // g_PakLoadManager->LoadPakAsync(pakName.c_str()); } } diff --git a/NorthstarDedicatedTest/modmanager.h b/NorthstarDedicatedTest/modmanager.h index c79c507d..7f12559f 100644 --- a/NorthstarDedicatedTest/modmanager.h +++ b/NorthstarDedicatedTest/modmanager.h @@ -97,7 +97,8 @@ class Mod std::string Pdiff; // only need one per mod std::vector Rpaks; - std::unordered_map RpakAliases; // paks we alias to other rpaks, e.g. to load sp_crashsite paks on the map mp_crashsite + std::unordered_map + RpakAliases; // paks we alias to other rpaks, e.g. to load sp_crashsite paks on the map mp_crashsite // other stuff diff --git a/NorthstarDedicatedTest/pch.h b/NorthstarDedicatedTest/pch.h index cc44c107..c5c78dc7 100644 --- a/NorthstarDedicatedTest/pch.h +++ b/NorthstarDedicatedTest/pch.h @@ -31,17 +31,15 @@ template ReturnType CallVFunc(int index, return (*reinterpret_cast(thisPtr))[index](thisPtr, args...); } -template constexpr T CallVFunc_Alt(void* classBase, Args... args) noexcept { +template constexpr T CallVFunc_Alt(void* classBase, Args... args) noexcept +{ return ((*(T(__thiscall***)(void*, Args...))(classBase))[index])(classBase, args...); } - #define STR_HASH(s) (std::hash()(s)) // Example usage: M_VMETHOD(int, GetEntityIndex, 8, (CBaseEntity* ent), (this, ent)) -#define M_VMETHOD(returnType, name, index, args, argsRaw) \ -FORCEINLINE returnType name args noexcept { \ - return CallVFunc_Alt argsRaw; \ -} +#define M_VMETHOD(returnType, name, index, args, argsRaw) \ + FORCEINLINE returnType name args noexcept { return CallVFunc_Alt argsRaw; } #endif \ No newline at end of file diff --git a/NorthstarDedicatedTest/playlist.cpp b/NorthstarDedicatedTest/playlist.cpp index a0a2dc33..fdb23893 100644 --- a/NorthstarDedicatedTest/playlist.cpp +++ b/NorthstarDedicatedTest/playlist.cpp @@ -98,11 +98,9 @@ void InitialisePlaylistHooks(HMODULE baseAddress) // patch to prevent clc_SetPlaylistVarOverride from being able to crash servers if we reach max overrides due to a call to Error (why is // this possible respawn, wtf) todo: add a warning for this { - NSMem::BytePatch(ba + 0x18ED8D, { - 0xC3 // jmp => ret - }); + NSMem::BytePatch(ba + 0x18ED8D, "C3"); } // patch to allow setplaylistvaroverride to be called before map init on dedicated and private match launched through the game NSMem::NOP(ba + 0x18ED17, 6); -} +} \ No newline at end of file diff --git a/NorthstarDedicatedTest/rpakfilesystem.cpp b/NorthstarDedicatedTest/rpakfilesystem.cpp index ba7cf3d2..6853029b 100644 --- a/NorthstarDedicatedTest/rpakfilesystem.cpp +++ b/NorthstarDedicatedTest/rpakfilesystem.cpp @@ -7,7 +7,7 @@ typedef void* (*LoadCommonPaksForMapType)(char* map); LoadCommonPaksForMapType LoadCommonPaksForMap; typedef void* (*LoadPakSyncType)(const char* path, void* unknownSingleton, int flags); -typedef int(*LoadPakAsyncType)(const char* path, void* unknownSingleton, int flags, void* callback0, void* callback1); +typedef int (*LoadPakAsyncType)(const char* path, void* unknownSingleton, int flags, void* callback0, void* callback1); // there are more i'm just too lazy to add struct PakLoadFuncs @@ -59,20 +59,21 @@ void LoadPreloadPaks() for (ModRpakEntry& pak : mod.Rpaks) if (pak.m_bAutoLoad) g_PakLoadManager->LoadPakAsync((modPakPath / pak.m_sPakPath).string().c_str()); - } + } bShouldPreload = true; } LoadPakSyncType LoadPakSyncOriginal; void* LoadPakSyncHook(char* path, void* unknownSingleton, int flags) -{ +{ HandlePakAliases(&path); - // note: we don't handle loading any preloaded custom paks synchronously since LoadPakSync is never actually called in retail, just load them async instead + // note: we don't handle loading any preloaded custom paks synchronously since LoadPakSync is never actually called in retail, just load + // them async instead LoadPreloadPaks(); spdlog::info("LoadPakSync {}", path); - return LoadPakSyncOriginal(path, unknownSingleton, flags); + return LoadPakSyncOriginal(path, unknownSingleton, flags); } LoadPakAsyncType LoadPakAsyncOriginal; diff --git a/NorthstarDedicatedTest/serverauthentication.cpp b/NorthstarDedicatedTest/serverauthentication.cpp index 4581e4b6..46fa6491 100644 --- a/NorthstarDedicatedTest/serverauthentication.cpp +++ b/NorthstarDedicatedTest/serverauthentication.cpp @@ -628,25 +628,26 @@ void InitialiseServerAuthentication(HMODULE baseAddress) // patch to disable kicking based on incorrect serverfilter in connectclient, since we repurpose it for use as an auth token { - NSMem::BytePatch(ba + 0x114655, { - 0xEB // jz => jmp - }); + NSMem::BytePatch( + ba + 0x114655, + "EB" // jz => jmp + ); } // patch to disable fairfight marking players as cheaters and kicking them { - NSMem::BytePatch(ba + 0x101012, { - 0xE9, // jz => jmp - 0x90, - 0x0 - }); + NSMem::BytePatch( + ba + 0x101012, + "E9 90 00" // jz => jmp + ); } // patch to allow same of multiple account { - NSMem::BytePatch(ba + 0x114510, { - 0xEB, // jz => jmp - }); + NSMem::BytePatch( + ba + 0x114510, + "EB" // jz => jmp + ); } // patch to set bWasWritingStringTableSuccessful in CNetworkStringTableContainer::WriteBaselines if it fails @@ -654,9 +655,11 @@ void InitialiseServerAuthentication(HMODULE baseAddress) uintptr_t writeAddress = (uintptr_t)(&bWasWritingStringTableSuccessful - (ba + 0x234EDC)); auto addr = ba + 0x234ED2; - NSMem::BytePatch(addr, { 0xC7, 0x05 }); + NSMem::BytePatch(addr, "C7 05"); NSMem::BytePatch(addr + 2, (BYTE*)&writeAddress, sizeof(writeAddress)); - NSMem::BytePatch(addr + 6, {0, 0, 0, 0}); + + NSMem::BytePatch(addr + 6, "00 00 00 00"); + NSMem::NOP(addr + 10, 5); } } diff --git a/NorthstarDedicatedTest/version.cpp b/NorthstarDedicatedTest/version.cpp index dfeb9670..a0bcee20 100644 --- a/NorthstarDedicatedTest/version.cpp +++ b/NorthstarDedicatedTest/version.cpp @@ -4,7 +4,8 @@ char version[16]; char NSUserAgent[32]; -void InitialiseVersion() { +void InitialiseVersion() +{ HRSRC hResInfo; DWORD dwSize; HGLOBAL hResData; @@ -14,13 +15,16 @@ void InitialiseVersion() { HINSTANCE hInst = ::GetModuleHandle(NULL); hResInfo = FindResourceW(hInst, MAKEINTRESOURCE(1), RT_VERSION); - if (hResInfo != NULL) { + if (hResInfo != NULL) + { dwSize = SizeofResource(hInst, hResInfo); hResData = LoadResource(hInst, hResInfo); - if (hResData != NULL) { + if (hResData != NULL) + { pRes = LockResource(hResData); pResCopy = LocalAlloc(LMEM_FIXED, dwSize); - if (pResCopy != 0) { + if (pResCopy != 0) + { CopyMemory(pResCopy, pRes, dwSize); VerQueryValueW(pResCopy, L"\\", (LPVOID*)&lpFfi, &uLen); @@ -35,11 +39,13 @@ void InitialiseVersion() { // We actually use the rightmost integer do determine whether or not we're a debug/dev build // If it is set to 1 (as in resources.rc), we are a dev build // On github CI, we set this 1 to a 0 automatically as we replace the 0.0.0.1 with the real version number - if (dwRightMost == 1) { + if (dwRightMost == 1) + { sprintf(version, "%d.%d.%d.%d+dev", dwLeftMost, dwSecondLeft, dwSecondRight, dwRightMost); sprintf(NSUserAgent, "R2Northstar/%d.%d.%d+dev", dwLeftMost, dwSecondLeft, dwSecondRight); } - else { + else + { sprintf(version, "%d.%d.%d.%d", dwLeftMost, dwSecondLeft, dwSecondRight, dwRightMost); sprintf(NSUserAgent, "R2Northstar/%d.%d.%d", dwLeftMost, dwSecondLeft, dwSecondRight); } -- cgit v1.2.3 From 52f3f5795fa516862c0a4030df8d43f4c0cbda65 Mon Sep 17 00:00:00 2001 From: Emma Miler <27428383+emma-miler@users.noreply.github.com> Date: Mon, 28 Mar 2022 23:51:56 +0200 Subject: Update plugins.cpp (#124) --- NorthstarDedicatedTest/plugins.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/NorthstarDedicatedTest/plugins.cpp b/NorthstarDedicatedTest/plugins.cpp index b725a5a8..62d781bb 100644 --- a/NorthstarDedicatedTest/plugins.cpp +++ b/NorthstarDedicatedTest/plugins.cpp @@ -128,11 +128,12 @@ SQRESULT SQ_UpdateGameStateClient(void* sqvm) AcquireSRWLockExclusive(&gameStateLock); AcquireSRWLockExclusive(&serverInfoLock); gameState.players = ClientSq_getinteger(sqvm, 1); - gameState.ourScore = ClientSq_getinteger(sqvm, 2); - gameState.secondHighestScore = ClientSq_getinteger(sqvm, 3); - gameState.highestScore = ClientSq_getinteger(sqvm, 4); - serverInfo.roundBased = ClientSq_getbool(sqvm, 5); - serverInfo.scoreLimit = ClientSq_getbool(sqvm, 6); + serverInfo.maxPlayers = ClientSq_getinteger(sqvm, 2); + gameState.ourScore = ClientSq_getinteger(sqvm, 3); + gameState.secondHighestScore = ClientSq_getinteger(sqvm, 4); + gameState.highestScore = ClientSq_getinteger(sqvm, 5); + serverInfo.roundBased = ClientSq_getbool(sqvm, 6); + serverInfo.scoreLimit = ClientSq_getbool(sqvm, 7); ReleaseSRWLockExclusive(&gameStateLock); ReleaseSRWLockExclusive(&serverInfoLock); return SQRESULT_NOTNULL; @@ -174,9 +175,7 @@ SQRESULT SQ_UpdateServerInfoBetweenRounds(void* sqvm) SQRESULT SQ_UpdateTimeInfo(void* sqvm) { AcquireSRWLockExclusive(&serverInfoLock); - int endTimeFromNow = ceil(ClientSq_getfloat(sqvm, 1)); - const auto p1 = std::chrono::system_clock::now().time_since_epoch(); - serverInfo.endTime = std::chrono::duration_cast(p1).count() + endTimeFromNow; + serverInfo.endTime = ceil(ClientSq_getfloat(sqvm, 1)); ReleaseSRWLockExclusive(&serverInfoLock); return SQRESULT_NOTNULL; } @@ -397,7 +396,7 @@ void InitialisePluginCommands(HMODULE baseAddress) "", SQ_UpdateGameStateUI); g_ClientSquirrelManager->AddFuncRegistration( "void", "NSUpdateGameStateClient", - "int playerCount, int outScore, int secondHighestScore, int highestScore, bool roundBased, int scoreLimit", "", + "int playerCount, int maxPlayers, int outScore, int secondHighestScore, int highestScore, bool roundBased, int scoreLimit", "", SQ_UpdateGameStateClient); g_UISquirrelManager->AddFuncRegistration( "void", "NSUpdateServerInfo", -- cgit v1.2.3