aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKittenPopo <Pokeberry123@gmail.com>2022-03-23 13:03:56 -0700
committerGitHub <noreply@github.com>2022-03-23 20:03:56 +0000
commitde7deafc7e6ba53deec8dd3c05d2ea5e7cf42264 (patch)
tree5bc5c82c9b720adbf02cdaeb505f46b48a02178f
parent5893c7700653e5f3d51c4f7eba7f6f25a2fe5be7 (diff)
downloadNorthstarLauncher-de7deafc7e6ba53deec8dd3c05d2ea5e7cf42264.tar.gz
NorthstarLauncher-de7deafc7e6ba53deec8dd3c05d2ea5e7cf42264.zip
Implement KittenPopo exploit fixes (and sanity changes) (#112)
* Added main exploit fixes * Fixed typo in sigscanning.cpp * Fully implemented * Added proper includes for new files * Update README.md * typo * spare me my sanity (fixed ridiculous code) * Added rest of KittenMemUtils * Rename KittenMemUtils * Removed all messy memory edits, implemented NSMem instead * Update NorthstarDedicatedTest.vcxproj * [1] Move everything from securitypatches to ExploitFixes * [2] Move everything from securitypatches to ExploitFixes * Fixed module offsets in stack trace * Fixed UTF8 Parsing (Multiplayer Crash) * Implemented UT8 fix * Update NorthstarDedicatedTest.vcxproj * Update hookutils.cpp * Small fixes * all my homies hate clang-format * Temporarily restore README.md
-rw-r--r--.clang-format14
-rw-r--r--LauncherInjector/main.cpp3
-rw-r--r--NorthstarDedicatedTest/ExploitFixes.cpp196
-rw-r--r--NorthstarDedicatedTest/ExploitFixes.h8
-rw-r--r--NorthstarDedicatedTest/ExploitFixes_UTF8Parser.h175
-rw-r--r--NorthstarDedicatedTest/NSMem.h152
-rw-r--r--NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj8
-rw-r--r--NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters24
-rw-r--r--NorthstarDedicatedTest/buildainfile.cpp26
-rw-r--r--NorthstarDedicatedTest/dedicated.cpp215
-rw-r--r--NorthstarDedicatedTest/dedicatedmaterialsystem.cpp11
-rw-r--r--NorthstarDedicatedTest/dllmain.cpp6
-rw-r--r--NorthstarDedicatedTest/hookutils.cpp16
-rw-r--r--NorthstarDedicatedTest/hookutils.h12
-rw-r--r--NorthstarDedicatedTest/include/spdlog/fmt/bundled/chrono.h2
-rw-r--r--NorthstarDedicatedTest/logging.cpp5
-rw-r--r--NorthstarDedicatedTest/maxplayers.cpp7
-rw-r--r--NorthstarDedicatedTest/miscserverfixes.cpp20
-rw-r--r--NorthstarDedicatedTest/playlist.cpp18
-rw-r--r--NorthstarDedicatedTest/plugin_abi.h6
-rw-r--r--NorthstarDedicatedTest/securitypatches.cpp58
-rw-r--r--NorthstarDedicatedTest/securitypatches.h2
-rw-r--r--NorthstarDedicatedTest/serverauthentication.cpp49
-rw-r--r--NorthstarDedicatedTest/sigscanning.cpp12
-rw-r--r--README.md2
-rw-r--r--loader_wsock32_proxy/hookutils.cpp17
26 files changed, 649 insertions, 415 deletions
diff --git a/.clang-format b/.clang-format
deleted file mode 100644
index 5fafec43..00000000
--- a/.clang-format
+++ /dev/null
@@ -1,14 +0,0 @@
-Language: Cpp
-Standard: Auto
-IndentWidth: 4
-TabWidth: 4
-UseCRLF: false
-SpacesInSquareBrackets: false
-SpacesInParentheses: false
-UseTab: Always
-ColumnLimit: 140
-BreakBeforeBraces: Allman
-AlignAfterOpenBracket: AlwaysBreak
-IndentExternBlock: Indent
-PointerAlignment: Left
-SortIncludes: false \ No newline at end of file
diff --git a/LauncherInjector/main.cpp b/LauncherInjector/main.cpp
index f721a54f..54c65059 100644
--- a/LauncherInjector/main.cpp
+++ b/LauncherInjector/main.cpp
@@ -293,8 +293,6 @@ int main(int argc, char* argv[])
if (dedicated && !nostubs)
{
- // clang-format keeps messing with the easy-to-read if statements, so
- // clang-format off
printf("[*] Loading stubs\n");
HMODULE gssao, gtxaa, d3d11;
if (!(gssao = GetModuleHandleA("GFSDK_SSAO.win64.dll")) &&
@@ -332,7 +330,6 @@ int main(int argc, char* argv[])
"[*] WARNING: Failed to load stubs because conflicting modules are already loaded, so those will be used instead "
"(did Northstar initialize too late?).\n");
}
- // clang-format on
}
{
diff --git a/NorthstarDedicatedTest/ExploitFixes.cpp b/NorthstarDedicatedTest/ExploitFixes.cpp
new file mode 100644
index 00000000..d36b4175
--- /dev/null
+++ b/NorthstarDedicatedTest/ExploitFixes.cpp
@@ -0,0 +1,196 @@
+#include "pch.h"
+
+#include "ExploitFixes.h"
+#include "ExploitFixes_UTF8Parser.h"
+#include "NSMem.h"
+
+// 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);
+}
+
+struct Vector {
+ float x, y, z;
+
+ Vector(float x = 0, float y = 0, float z = 0) : x(x), y(y), z(z) {}
+
+ bool IsValid() {
+ return ValidateFloats(x, y, z);
+ }
+};
+
+struct Angle {
+ float pitch, yaw, roll;
+
+ Angle(float pitch = 0, float yaw = 0, float roll = 0) : pitch(pitch), yaw(yaw), roll(roll) {}
+
+ bool IsInvalid() {
+ if (!ValidateFloats(pitch, yaw, roll))
+ return false;
+
+ return
+ (pitch > 90 || pitch < -90)
+ || (yaw > 180 || yaw < -180)
+ || (roll > 180 || roll < -180);
+ }
+};
+
+#define BLOCK_NETMSG_FUNC(name, pattern) \
+ KHOOK(name, ("engine.dll", pattern), bool, __fastcall, (void* thisptr, void* buffer)) { return false; }
+
+// Servers can literally request a screenshot from any client, yeah no
+BLOCK_NETMSG_FUNC(CLC_Screenshot_WriteToBuffer, "48 89 5C 24 ? 57 48 83 EC 20 8B 42 10");
+BLOCK_NETMSG_FUNC(CLC_Screenshot_ReadFromBuffer, "48 89 5C 24 ? 48 89 6C 24 ? 48 89 74 24 ? 57 48 83 EC 20 48 8B DA 48 8B 52 38");
+
+// This is unused ingame and a big exploit vector
+BLOCK_NETMSG_FUNC(Base_CmdKeyValues_ReadFromBuffer, "40 55 48 81 EC ? ? ? ? 48 8D 6C 24 ? 48 89 5D 70");
+
+// Purpose: prevent invalid user CMDs
+KHOOK(CClient_ProcessUsercmds, ("engine.dll", "40 55 56 48 83 EC 58"), bool, __fastcall, (void* thisptr, void* pMsg)) {
+ struct __declspec(align(8)) CLC_Move {
+ BYTE gap0[24];
+ void* m_pMessageHandler;
+ int m_nBackupCommands;
+ int m_nNewCommands;
+ int m_nLength;
+ // bf_read m_DataIn;
+ // bf_write m_DataOut;
+ };
+
+ auto msg = (CLC_Move*)pMsg;
+
+ if (msg->m_nBackupCommands < 0 || msg->m_nNewCommands < 1)
+ return false; // Nice try buster
+
+ constexpr int NUMCMD_SANITY_LIMIT = 16;
+ if ((msg->m_nNewCommands + msg->m_nBackupCommands) > NUMCMD_SANITY_LIMIT)
+ return false; // Im good
+
+ if (msg->m_nLength <= 0)
+ return false;
+
+ return oCClient_ProcessUsercmds(thisptr, pMsg);
+}
+
+KHOOK(ReadUsercmd, ("server.dll", "4C 89 44 24 ? 53 55 56 57"), void, __fastcall, (void* buf, void* pCmd_move, void* pCmd_from)) {
+ // Let normal usercmd read happen first, it's safe
+ oReadUsercmd(buf, pCmd_move, pCmd_from);
+
+ // Now let's make sure the CMD we read isnt messed up to prevent numerous exploits (including server crashing)
+ struct __declspec(align(4)) SV_CUserCmd
+ {
+ DWORD command_number;
+ DWORD tick_count;
+ float command_time;
+ Angle worldViewAngles;
+ BYTE gap18[4];
+ Angle localViewAngles;
+ Angle attackangles;
+ Vector move;
+ DWORD buttons;
+ BYTE impulse;
+ short weaponselect;
+ DWORD meleetarget;
+ BYTE gap4C[24];
+ char headoffset;
+ BYTE gap65[11];
+ Vector cameraPos;
+ Angle cameraAngles;
+ BYTE gap88[4];
+ int tickSomething;
+ DWORD dword90;
+ DWORD predictedServerEventAck;
+ DWORD dword98;
+ float frameTime;
+ };
+ auto cmd = (SV_CUserCmd*)pCmd_move;
+
+ if (
+ cmd->worldViewAngles.IsInvalid() ||
+ cmd->localViewAngles.IsInvalid() ||
+ cmd->attackangles.IsInvalid() ||
+ cmd->cameraAngles.IsInvalid()) {
+ goto INVALID_CMD;
+ }
+
+ if (cmd->frameTime <= 0 || cmd->tick_count == 0 || cmd->command_time <= 0)
+ 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
+ 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
+
+ return;
+INVALID_CMD:
+
+ // Fix any gameplay-affecting cmd properties
+ // NOTE: Currently tickcount/frametime is set to 0, this ~shouldn't~ cause any problems
+ cmd->worldViewAngles = cmd->localViewAngles = cmd->attackangles = cmd->cameraAngles = Angle(0,0,0);
+ cmd->tick_count = cmd->frameTime = 0;
+ cmd->move = cmd->cameraPos = Vector(0, 0, 0);
+ cmd->buttons = 0;
+ cmd->meleetarget = 0;
+}
+
+// basically: by default r2 isn't set as a valve mod, meaning that m_bRestrictServerCommands is false
+// this is HORRIBLE for security, because it means servers can run arbitrary concommands on clients
+// especially since we have script commands this could theoretically be awful
+#include "gameutils.h"
+KHOOK(IsValveMod, ("engine.dll", "48 83 EC 28 48 8B 0D ? ? ? ? 48 8D 15 ? ? ? ? E8 ? ? ? ? 85 C0 74 63"), bool, __fastcall, ()) {
+ return !CommandLine()->CheckParm("-norestrictservercommands");
+}
+
+// Fix respawn's crappy UTF8 parser so it doesn't crash -_-
+// This also means you can launch multiplayer with "communities_enabled 1" and not crash, you're welcome
+KHOOK(CrashFunc_ParseUTF8, ("engine.dll", "48 89 5C 24 ? 48 89 6C 24 ? 48 89 74 24 ? 57 41 54 41 55 41 56 41 57 48 83 EC 20 8B 1A"),
+ bool, __fastcall, (INT64* a1, DWORD* a2, char* strData)) {
+
+ static void* targetRetAddr = NSMem::PatternScan("engine.dll", "84 C0 75 2C 49 8B 16");
+
+ if (_ReturnAddress() == targetRetAddr) {
+ if (!ExploitFixes_UTF8Parser::CheckValid(a1, a2, strData)) {
+ spdlog::warn("ParseUTF8 Hook: Ignoring potentially-crashing utf8 string");
+ return false;
+ }
+ }
+
+ return oCrashFunc_ParseUTF8(a1, a2, strData);
+}
+
+//////////////////////////////////////////////////
+
+void DoBytePatches() {
+ uintptr_t engineBase = (uintptr_t)GetModuleHandleA("engine.dll");
+
+ // 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});
+
+ // disconnect concommand
+ {
+ uintptr_t addr = engineBase + 0x5ADA2D;
+ int val = *(int*)addr | FCVAR_SERVER_CAN_EXECUTE;
+ NSMem::BytePatch(addr, (BYTE*)&val, sizeof(int));
+ }
+}
+
+void ExploitFixes::LoadCallback(HMODULE unused) {
+ spdlog::info("ExploitFixes::LoadCallback ...");
+
+ spdlog::info("\tByte patching...");
+ DoBytePatches();
+
+ if (KHook::InitAllHooks()) {
+ spdlog::info("\tInitialized " + std::to_string(KHook::_allHooks.size()) + " exploit-patch hooks.");
+ } else {
+ spdlog::critical("\tFAILED to initialize all exploit patches.");
+
+ // Force exit?
+ MessageBoxA(0, "FAILED to initialize all exploit patches.", "Northstar", MB_ICONERROR);
+ exit(0);
+ }
+} \ No newline at end of file
diff --git a/NorthstarDedicatedTest/ExploitFixes.h b/NorthstarDedicatedTest/ExploitFixes.h
new file mode 100644
index 00000000..49928640
--- /dev/null
+++ b/NorthstarDedicatedTest/ExploitFixes.h
@@ -0,0 +1,8 @@
+// KittenPopo's exploit fix hooks, feel free to add more here
+
+#pragma once
+#include "pch.h"
+
+namespace ExploitFixes {
+ void LoadCallback(HMODULE unused);
+}
diff --git a/NorthstarDedicatedTest/ExploitFixes_UTF8Parser.h b/NorthstarDedicatedTest/ExploitFixes_UTF8Parser.h
new file mode 100644
index 00000000..6b767a0c
--- /dev/null
+++ b/NorthstarDedicatedTest/ExploitFixes_UTF8Parser.h
@@ -0,0 +1,175 @@
+// Reimplementation of a exploitable UTF decoding function in titanfall
+
+#include "NSMem.h"
+
+#pragma once
+
+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");
+
+ 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)
+ {
+ ++v4;
+ --v7;
+ if (v3 != 2)
+ {
+ while (1)
+ {
+
+ 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;
+
+ 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;
+ }
+ v11 = 10;
+ LABEL_6:
+ v5 |= v11;
+ *_strData++ = v11;
+ goto LABEL_7;
+ }
+ }
+ LABEL_48:
+ return true;
+ }
+} \ No newline at end of file
diff --git a/NorthstarDedicatedTest/NSMem.h b/NorthstarDedicatedTest/NSMem.h
new file mode 100644
index 00000000..a6ddf033
--- /dev/null
+++ b/NorthstarDedicatedTest/NSMem.h
@@ -0,0 +1,152 @@
+#pragma once
+#include "pch.h"
+
+// KittenPopo's memory stuff, made for northstar (because I really can't handle working with northstar's original memory stuff tbh)
+
+#pragma region Pattern Scanning
+namespace NSMem {
+ inline void* PatternScan(void* module, const int* pattern, int patternSize, int offset) {
+ 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;
+
+ 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) {
+ found = false;
+ break;
+ }
+ }
+
+ if (found) {
+ uintptr_t addressInt = (uintptr_t)(&scanBytes[i]) + offset;
+ return (uint8_t*)addressInt;
+ }
+ }
+
+ return nullptr;
+ }
+
+ inline void* PatternScan(const char* moduleName, const char* pattern, int offset = 0) {
+ std::vector<int> patternNums;
+
+ bool lastChar = 0;
+ int size = strlen(pattern);
+ for (int i = 0; i < size; i++) {
+ char c = pattern[i];
+
+ // If this is a space character, ignore it
+ if (c == ' ' || c == '\t')
+ continue;
+
+ if (c == '?') {
+ // Add a wildcard (-1)
+ patternNums.push_back(-1);
+ } else if (i < size - 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);
+ }
+
+ i++;
+ }
+ 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);
+ }
+
+ inline void BytePatch(uintptr_t address, std::initializer_list<BYTE> vals) {
+ std::vector<BYTE> bytes = vals;
+ 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) {
+ BYTE* buffer = (BYTE*)malloc(size);
+
+ size_t numWritten = 0;
+ ReadProcessMemory(GetCurrentProcess(), ptr, buffer, size, &numWritten);
+ free(buffer);
+
+ return numWritten == size;
+ }
+}
+
+#pragma region KHOOK
+struct KHookPatternInfo {
+ const char* moduleName, *pattern;
+ int offset = 0;
+
+ KHookPatternInfo(const char* moduleName, const char* pattern, int offset = 0)
+ : moduleName(moduleName), pattern(pattern), offset(offset) {}
+};
+
+struct KHook {
+ KHookPatternInfo targetFunc;
+ void* targetFuncAddr;
+ void* hookFunc;
+ void** original;
+
+ static inline std::vector<KHook*> _allHooks;
+
+ KHook(KHookPatternInfo targetFunc, void* hookFunc, void** original) : targetFunc(targetFunc) {
+ this->hookFunc = hookFunc;
+ this->original = original;
+ _allHooks.push_back(this);
+ }
+
+ bool Setup() {
+ targetFuncAddr = NSMem::PatternScan(targetFunc.moduleName, targetFunc.pattern, targetFunc.offset);
+ if (!targetFuncAddr)
+ return false;
+
+ return MH_CreateHook(targetFuncAddr, hookFunc, original) == MH_OK;
+ }
+
+ // Returns true if succeeded
+ static bool InitAllHooks() {
+ for (KHook* hook : _allHooks) {
+ if (hook->Setup()) {
+ spdlog::info("KHook hooked at {}", hook->targetFuncAddr);
+ } else {
+ return false;
+ }
+ }
+
+ return MH_EnableHook(MH_ALL_HOOKS) == MH_OK;
+ }
+};
+#define KHOOK(name, funcPatternInfo, returnType, convention, args) \
+ returnType convention hk##name args; \
+ auto o##name = (returnType(convention*) args)0; \
+ KHook k##name = KHook(KHookPatternInfo funcPatternInfo, &hk##name, (void**)&o##name); \
+ returnType convention hk##name args
+#pragma endregion \ No newline at end of file
diff --git a/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj b/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj
index e931fa51..4a383d37 100644
--- a/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj
+++ b/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj
@@ -549,7 +549,6 @@
<ClInclude Include="rpakfilesystem.h" />
<ClInclude Include="scriptbrowserhooks.h" />
<ClInclude Include="scriptmainmenupromos.h" />
- <ClInclude Include="securitypatches.h" />
<ClInclude Include="scriptmodmenu.h" />
<ClInclude Include="scriptserverbrowser.h" />
<ClInclude Include="scriptsrson.h" />
@@ -560,6 +559,9 @@
<ClInclude Include="sourceinterface.h" />
<ClInclude Include="squirrel.h" />
<ClInclude Include="state.h" />
+ <ClInclude Include="ExploitFixes.h" />
+ <ClInclude Include="ExploitFixes_UTF8Parser.h" />
+ <ClInclude Include="NSMem.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="audio.cpp" />
@@ -605,7 +607,6 @@
<ClCompile Include="rpakfilesystem.cpp" />
<ClCompile Include="scriptbrowserhooks.cpp" />
<ClCompile Include="scriptmainmenupromos.cpp" />
- <ClCompile Include="securitypatches.cpp" />
<ClCompile Include="scriptmodmenu.cpp" />
<ClCompile Include="scriptserverbrowser.cpp" />
<ClCompile Include="scriptsrson.cpp" />
@@ -617,6 +618,7 @@
<ClCompile Include="sourceconsole.cpp" />
<ClCompile Include="sourceinterface.cpp" />
<ClCompile Include="squirrel.cpp" />
+ <ClCompile Include="ExploitFixes.cpp" />
</ItemGroup>
<ItemGroup>
<None Include="include\crypto\bn_conf.h.in" />
@@ -655,4 +657,4 @@
<ImportGroup Label="ExtensionTargets">
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.targets" />
</ImportGroup>
-</Project> \ No newline at end of file
+</Project>
diff --git a/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters b/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters
index a5460132..c03d822f 100644
--- a/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters
+++ b/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters
@@ -127,6 +127,12 @@
<Filter Include="Header Files\Shared\Math">
<UniqueIdentifier>{44a83740-9d70-480d-9a7a-43b81f8eab9e}</UniqueIdentifier>
</Filter>
+ <Filter Include="Source Files\Shared\Exploit Fixes">
+ <UniqueIdentifier>{4a8a695a-a103-4b1f-b314-0ec19a253119}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Shared\Exploit Fixes\UTF8Parser">
+ <UniqueIdentifier>{b30e08b1-b962-4264-8cbb-a0a31924b93e}</UniqueIdentifier>
+ </Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h">
@@ -582,9 +588,6 @@
<ClInclude Include="playlist.h">
<Filter>Header Files\Server</Filter>
</ClInclude>
- <ClInclude Include="securitypatches.h">
- <Filter>Header Files\Client</Filter>
- </ClInclude>
<ClInclude Include="dedicatedmaterialsystem.h">
<Filter>Header Files\Dedicated</Filter>
</ClInclude>
@@ -1488,6 +1491,15 @@
<ClInclude Include="clientvideooverrides.h">
<Filter>Header Files\Client</Filter>
</ClInclude>
+ <ClInclude Include="ExploitFixes.h">
+ <Filter>Source Files\Shared\Exploit Fixes</Filter>
+ </ClInclude>
+ <ClInclude Include="NSMem.h">
+ <Filter>Source Files\Shared\Exploit Fixes</Filter>
+ </ClInclude>
+ <ClInclude Include="ExploitFixes_UTF8Parser.h">
+ <Filter>Source Files\Shared\Exploit Fixes\UTF8Parser</Filter>
+ </ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="dllmain.cpp">
@@ -1565,9 +1577,6 @@
<ClCompile Include="playlist.cpp">
<Filter>Source Files\Server</Filter>
</ClCompile>
- <ClCompile Include="securitypatches.cpp">
- <Filter>Source Files\Client</Filter>
- </ClCompile>
<ClCompile Include="dedicatedmaterialsystem.cpp">
<Filter>Source Files\Dedicated</Filter>
</ClCompile>
@@ -1646,6 +1655,9 @@
<ClCompile Include="clientvideooverrides.cpp">
<Filter>Source Files\Client</Filter>
</ClCompile>
+ <ClCompile Include="ExploitFixes.cpp">
+ <Filter>Source Files\Shared\Exploit Fixes</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<MASM Include="audio_asm.asm">
diff --git a/NorthstarDedicatedTest/buildainfile.cpp b/NorthstarDedicatedTest/buildainfile.cpp
index 3adb8324..3f30eff3 100644
--- a/NorthstarDedicatedTest/buildainfile.cpp
+++ b/NorthstarDedicatedTest/buildainfile.cpp
@@ -4,6 +4,7 @@
#include "hookutils.h"
#include <fstream>
#include <filesystem>
+#include "NSMem.h"
namespace fs = std::filesystem;
@@ -390,27 +391,10 @@ void InitialiseBuildAINFileHooks(HMODULE baseAddress)
pUnkServerMapversionGlobal = (char**)((char*)baseAddress + 0xBFBE08);
pMapName = (char*)baseAddress + 0x1053370;
+ uintptr_t base = (uintptr_t)baseAddress;
+
// remove a check that prevents a logging function in link generation from working
// due to the sheer amount of logging this is a massive perf hit to generation, but spewlog_enable 0 exists so whatever
- {
- void* ptr = (char*)baseAddress + 0x3889B6;
- TempReadWrite rw(ptr);
- *((char*)ptr) = (char)0x90;
- *((char*)ptr + 1) = (char)0x90;
- *((char*)ptr + 2) = (char)0x90;
- *((char*)ptr + 3) = (char)0x90;
- *((char*)ptr + 4) = (char)0x90;
- *((char*)ptr + 5) = (char)0x90;
- }
-
- {
- void* ptr = (char*)baseAddress + 0x3889BF;
- TempReadWrite rw(ptr);
- *((char*)ptr) = (char)0x90;
- *((char*)ptr + 1) = (char)0x90;
- *((char*)ptr + 2) = (char)0x90;
- *((char*)ptr + 3) = (char)0x90;
- *((char*)ptr + 4) = (char)0x90;
- *((char*)ptr + 5) = (char)0x90;
- }
+ NSMem::NOP(base + 0x3889B6, 6);
+ NSMem::NOP(base + 0x3889BF, 6);
} \ No newline at end of file
diff --git a/NorthstarDedicatedTest/dedicated.cpp b/NorthstarDedicatedTest/dedicated.cpp
index 9372b7a5..e0206d1f 100644
--- a/NorthstarDedicatedTest/dedicated.cpp
+++ b/NorthstarDedicatedTest/dedicated.cpp
@@ -117,278 +117,125 @@ DWORD WINAPI ConsoleInputThread(PVOID pThreadParameter)
return 0;
}
+#include "NSMem.h"
void InitialiseDedicated(HMODULE engineAddress)
{
spdlog::info("InitialiseDedicated");
+ uintptr_t ea = (uintptr_t)engineAddress;
+
{
// Host_Init
// prevent a particle init that relies on client dll
-
- char* ptr = (char*)engineAddress + 0x156799;
- TempReadWrite rw(ptr);
-
- *ptr = (char)0x90;
- *(ptr + 1) = (char)0x90;
- *(ptr + 2) = (char)0x90;
- *(ptr + 3) = (char)0x90;
- *(ptr + 4) = (char)0x90;
+ NSMem::NOP(ea + 0x156799, 5);
}
{
// CModAppSystemGroup::Create
// force the engine into dedicated mode by changing the first comparison to IsServerOnly to an assignment
- char* ptr = (char*)engineAddress + 0x1C4EBD;
- TempReadWrite rw(ptr);
+ auto ptr = ea + 0x1C4EBD;
// cmp => mov
- *(ptr + 1) = (char)0xC6;
- *(ptr + 2) = (char)0x87;
+ NSMem::BytePatch(ptr + 1, {0xC6, 0x87});
// 00 => 01
- *((char*)ptr + 7) = (char)0x01;
+ NSMem::BytePatch(ptr + 7, {0x01});
}
{
// Some init that i'm not sure of that crashes
- char* ptr = (char*)engineAddress + 0x156A63;
- TempReadWrite rw(ptr);
-
// nop the call to it
- *ptr = (char)0x90;
- *(ptr + 1) = (char)0x90;
- *(ptr + 2) = (char)0x90;
- *(ptr + 3) = (char)0x90;
- *(ptr + 4) = (char)0x90;
+ NSMem::NOP(ea + 0x156A63, 5);
}
{
// runframeserver
- char* ptr = (char*)engineAddress + 0x159819;
- TempReadWrite rw(ptr);
-
// nop some access violations
- *ptr = (char)0x90;
- *(ptr + 1) = (char)0x90;
- *(ptr + 2) = (char)0x90;
- *(ptr + 3) = (char)0x90;
- *(ptr + 4) = (char)0x90;
- *(ptr + 5) = (char)0x90;
- *(ptr + 6) = (char)0x90;
- *(ptr + 7) = (char)0x90;
- *(ptr + 8) = (char)0x90;
- *(ptr + 9) = (char)0x90;
- *(ptr + 10) = (char)0x90;
- *(ptr + 11) = (char)0x90;
- *(ptr + 12) = (char)0x90;
- *(ptr + 13) = (char)0x90;
- *(ptr + 14) = (char)0x90;
- *(ptr + 15) = (char)0x90;
- *(ptr + 16) = (char)0x90;
+ NSMem::NOP(ea + 0x159819, 17);
}
{
- // HostState_State_NewGame
- char* ptr = (char*)engineAddress + 0x156B4C;
- TempReadWrite rw(ptr);
-
- // nop some access violations
- *ptr = (char)0x90;
- *(ptr + 1) = (char)0x90;
- *(ptr + 2) = (char)0x90;
- *(ptr + 3) = (char)0x90;
- *(ptr + 4) = (char)0x90;
- *(ptr + 5) = (char)0x90;
- *(ptr + 6) = (char)0x90;
+ NSMem::NOP(ea + 0x156B4C, 7);
// 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
- //*(ptr + 7) = (char)0x90;
- //*(ptr + 8) = (char)0x90;
- //*(ptr + 9) = (char)0x90;
- //*(ptr + 10) = (char)0x90;
- //*(ptr + 11) = (char)0x90;
- //*(ptr + 12) = (char)0x90;
- //*(ptr + 13) = (char)0x90;
- //*(ptr + 14) = (char)0x90;
-
- *(ptr + 15) = (char)0x90;
- *(ptr + 16) = (char)0x90;
- *(ptr + 17) = (char)0x90;
- *(ptr + 18) = (char)0x90;
- *(ptr + 19) = (char)0x90;
- *(ptr + 20) = (char)0x90;
- *(ptr + 21) = (char)0x90;
- *(ptr + 22) = (char)0x90;
- *(ptr + 23) = (char)0x90;
+ //NSMem::NOP(ea + 0x156B4C + 7, 8);
+
+ NSMem::NOP(ea + 0x156B4C + 15, 9);
}
{
// HostState_State_NewGame
- char* ptr = (char*)engineAddress + 0xB934C;
- TempReadWrite rw(ptr);
-
// nop an access violation
- *ptr = (char)0x90;
- *(ptr + 1) = (char)0x90;
- *(ptr + 2) = (char)0x90;
- *(ptr + 3) = (char)0x90;
- *(ptr + 4) = (char)0x90;
- *(ptr + 5) = (char)0x90;
- *(ptr + 6) = (char)0x90;
- *(ptr + 7) = (char)0x90;
- *(ptr + 8) = (char)0x90;
+ NSMem::NOP(ea + 0xB934C, 9);
}
{
// CEngineAPI::Connect
- char* ptr = (char*)engineAddress + 0x1C4D7D;
- TempReadWrite rw(ptr);
-
// remove call to Shader_Connect
- *ptr = (char)0x90;
- *(ptr + 1) = (char)0x90;
- *(ptr + 2) = (char)0x90;
- *(ptr + 3) = (char)0x90;
- *(ptr + 4) = (char)0x90;
+ NSMem::NOP(ea + 0x1C4D7D, 5);
}
// currently does not work, crashes stuff, likely gotta keep this here
//{
// // CEngineAPI::Connect
- // char* ptr = (char*)engineAddress + 0x1C4E07;
- // TempReadWrite rw(ptr);
- //
- // // remove calls to register ui rpak asset types
- // *ptr = 0x90;
- // *(ptr + 1) = (char)0x90;
- // *(ptr + 2) = (char)0x90;
- // *(ptr + 3) = (char)0x90;
- // *(ptr + 4) = (char)0x90;
+ // // remove calls to register ui rpak asset types
+ // NSMem::NOP(ea + 0x1C4E07, 5);
//}
// not sure if this should be done, not loading ui at least is good, but should everything be gone?
{
// Host_Init
- char* ptr = (char*)engineAddress + 0x15653B;
- TempReadWrite rw(ptr);
-
// change the number of rpaks to load from 6 to 1, so we only load common.rpak
- *(ptr + 1) = (char)0x01;
+ NSMem::BytePatch(ea + 0x15653B + 1, {0x01});
}
{
// Host_Init
- char* ptr = (char*)engineAddress + 0x156595;
- TempReadWrite rw(ptr);
-
// remove call to ui loading stuff
- *ptr = (char)0x90;
- *(ptr + 1) = (char)0x90;
- *(ptr + 2) = (char)0x90;
- *(ptr + 3) = (char)0x90;
- *(ptr + 4) = (char)0x90;
+ NSMem::NOP(ea + 0x156595, 5);
}
{
// some function that gets called from RunFrameServer
- char* ptr = (char*)engineAddress + 0x15A0BB;
- TempReadWrite rw(ptr);
-
// nop a function that makes requests to stryder, this will eventually access violation if left alone and isn't necessary anyway
- *ptr = (char)0x90;
- *(ptr + 1) = (char)0x90;
- *(ptr + 2) = (char)0x90;
- *(ptr + 3) = (char)0x90;
- *(ptr + 4) = (char)0x90;
+ NSMem::NOP(ea + 0x15A0BB, 5);
}
{
// RunFrameServer
- char* ptr = (char*)engineAddress + 0x159BF3;
- TempReadWrite rw(ptr);
-
// nop a function that access violations
- *ptr = (char)0x90;
- *(ptr + 1) = (char)0x90;
- *(ptr + 2) = (char)0x90;
- *(ptr + 3) = (char)0x90;
- *(ptr + 4) = (char)0x90;
+ NSMem::NOP(ea + 0x159BF3, 5);
}
{
// func that checks if origin is inited
- char* ptr = (char*)engineAddress + 0x183B70;
- TempReadWrite rw(ptr);
-
// always return 1
- *ptr = (char)0xB0; // mov al,01
- *(ptr + 1) = (char)0x01;
- *(ptr + 2) = (char)0xC3; // ret
+ NSMem::BytePatch(ea + 0x183B70, {
+ 0xB0, 0x01, // mov al,01
+ 0xC3 // ret
+ });
}
{
// HostState_State_ChangeLevel
- char* ptr = (char*)engineAddress + 0x1552ED;
- TempReadWrite rw(ptr);
-
// nop clientinterface call
- *ptr = (char)0x90;
- *(ptr + 1) = (char)0x90;
- *(ptr + 2) = (char)0x90;
- *(ptr + 3) = (char)0x90;
- *(ptr + 4) = (char)0x90;
- *(ptr + 5) = (char)0x90;
- *(ptr + 6) = (char)0x90;
- *(ptr + 7) = (char)0x90;
- *(ptr + 8) = (char)0x90;
- *(ptr + 9) = (char)0x90;
- *(ptr + 10) = (char)0x90;
- *(ptr + 11) = (char)0x90;
- *(ptr + 12) = (char)0x90;
- *(ptr + 13) = (char)0x90;
- *(ptr + 14) = (char)0x90;
- *(ptr + 15) = (char)0x90;
+ NSMem::NOP(ea + 0x1552ED, 16);
}
{
// HostState_State_ChangeLevel
- char* ptr = (char*)engineAddress + 0x155363;
- TempReadWrite rw(ptr);
-
// nop clientinterface call
- *ptr = (char)0x90;
- *(ptr + 1) = (char)0x90;
- *(ptr + 2) = (char)0x90;
- *(ptr + 3) = (char)0x90;
- *(ptr + 4) = (char)0x90;
- *(ptr + 5) = (char)0x90;
- *(ptr + 6) = (char)0x90;
- *(ptr + 7) = (char)0x90;
- *(ptr + 8) = (char)0x90;
- *(ptr + 9) = (char)0x90;
- *(ptr + 10) = (char)0x90;
- *(ptr + 11) = (char)0x90;
- *(ptr + 12) = (char)0x90;
- *(ptr + 13) = (char)0x90;
- *(ptr + 14) = (char)0x90;
- *(ptr + 15) = (char)0x90;
+ NSMem::NOP(ea + 0x155363, 16);
}
// note: previously had DisableDedicatedWindowCreation patches here, but removing those rn since they're all shit and unstable and bad
// and such check commit history if any are needed for reimplementation
{
// IVideoMode::CreateGameWindow
- char* ptr = (char*)engineAddress + 0x1CD146;
- TempReadWrite rw(ptr);
-
// nop call to ShowWindow
- *ptr = (char)0x90;
- *(ptr + 1) = (char)0x90;
- *(ptr + 2) = (char)0x90;
- *(ptr + 3) = (char)0x90;
- *(ptr + 4) = (char)0x90;
+ NSMem::NOP(ea + 0x1CD146, 5);
}
CDedicatedExports* dedicatedExports = new CDedicatedExports;
@@ -454,9 +301,9 @@ 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
- char* ptr = (char*)GetProcAddress(GetModuleHandleA("tier0.dll"), "Tier0_InitOrigin");
- TempReadWrite rw(ptr);
- *ptr = (char)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 00652698..8fa2b58d 100644
--- a/NorthstarDedicatedTest/dedicatedmaterialsystem.cpp
+++ b/NorthstarDedicatedTest/dedicatedmaterialsystem.cpp
@@ -4,6 +4,7 @@
#include "dedicatedmaterialsystem.h"
#include "hookutils.h"
#include "gameutils.h"
+#include "NSMem.h"
typedef HRESULT (*__stdcall D3D11CreateDeviceType)(
void* pAdapter, int DriverType, HMODULE Software, UINT Flags, int* pFeatureLevels, UINT FeatureLevels, UINT SDKVersion, void** ppDevice,
@@ -59,14 +60,10 @@ void InitialiseDedicatedMaterialSystem(HMODULE baseAddress)
{
// CMaterialSystem::FindMaterial
- char* ptr = (char*)baseAddress + 0x5F0F1;
- TempReadWrite rw(ptr);
-
// make the game always use the error material
- *ptr = (char)0xE9;
- *(ptr + 1) = (char)0x34;
- *(ptr + 2) = (char)0x03;
- *(ptr + 3) = (char)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/dllmain.cpp b/NorthstarDedicatedTest/dllmain.cpp
index f838d82a..55951b66 100644
--- a/NorthstarDedicatedTest/dllmain.cpp
+++ b/NorthstarDedicatedTest/dllmain.cpp
@@ -18,7 +18,6 @@
#include "chatcommand.h"
#include "modlocalisation.h"
#include "playlist.h"
-#include "securitypatches.h"
#include "miscserverscript.h"
#include "clientauthhooks.h"
#include "latencyflex.h"
@@ -48,6 +47,7 @@
#include "rapidjson/stringbuffer.h"
#include "rapidjson/writer.h"
#include "rapidjson/error/en.h"
+#include "ExploitFixes.h"
typedef void (*initPluginFuncPtr)(void* getPluginObject);
@@ -225,7 +225,6 @@ bool InitialiseNorthstar()
// client-exclusive patches
{
AddDllLoadCallbackForClient("tier0.dll", InitialiseTier0LanguageHooks);
- AddDllLoadCallbackForClient("engine.dll", InitialiseClientEngineSecurityPatches);
AddDllLoadCallbackForClient("client.dll", InitialiseClientSquirrel);
AddDllLoadCallbackForClient("client.dll", InitialiseSourceConsole);
AddDllLoadCallbackForClient("engine.dll", InitialiseChatCommands);
@@ -274,6 +273,9 @@ bool InitialiseNorthstar()
// mod manager after everything else
AddDllLoadCallback("engine.dll", InitialiseModManager);
+ // activate exploit fixes
+ AddDllLoadCallback("server.dll", ExploitFixes::LoadCallback);
+
// run callbacks for any libraries that are already loaded by now
CallAllPendingDLLLoadCallbacks();
diff --git a/NorthstarDedicatedTest/hookutils.cpp b/NorthstarDedicatedTest/hookutils.cpp
index b7d82724..f5df3996 100644
--- a/NorthstarDedicatedTest/hookutils.cpp
+++ b/NorthstarDedicatedTest/hookutils.cpp
@@ -3,22 +3,6 @@
#include <iostream>
-TempReadWrite::TempReadWrite(void* ptr)
-{
- m_ptr = ptr;
- MEMORY_BASIC_INFORMATION mbi;
- VirtualQuery(m_ptr, &mbi, sizeof(mbi));
- VirtualProtect(mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE, &mbi.Protect);
- m_origProtection = mbi.Protect;
-}
-
-TempReadWrite::~TempReadWrite()
-{
- MEMORY_BASIC_INFORMATION mbi;
- VirtualQuery(m_ptr, &mbi, sizeof(mbi));
- VirtualProtect(mbi.BaseAddress, mbi.RegionSize, m_origProtection, &mbi.Protect);
-}
-
void HookEnabler::CreateHook(LPVOID ppTarget, LPVOID ppDetour, LPVOID* ppOriginal, const char* targetName)
{
// the macro for this uses ppTarget's name as targetName, and this typically starts with &
diff --git a/NorthstarDedicatedTest/hookutils.h b/NorthstarDedicatedTest/hookutils.h
index 006bc16d..47ec12bf 100644
--- a/NorthstarDedicatedTest/hookutils.h
+++ b/NorthstarDedicatedTest/hookutils.h
@@ -1,18 +1,6 @@
#pragma once
#include <vector>
-// Sets an area of memory as writeable until the TempReadWrite object goes out of scope
-class TempReadWrite
-{
- private:
- DWORD m_origProtection;
- void* m_ptr;
-
- public:
- TempReadWrite(void* ptr);
- ~TempReadWrite();
-};
-
// Enables all hooks created with the HookEnabler object when it goes out of scope and handles hook errors
class HookEnabler
{
diff --git a/NorthstarDedicatedTest/include/spdlog/fmt/bundled/chrono.h b/NorthstarDedicatedTest/include/spdlog/fmt/bundled/chrono.h
index 1a3b8d5e..bdfe2aa0 100644
--- a/NorthstarDedicatedTest/include/spdlog/fmt/bundled/chrono.h
+++ b/NorthstarDedicatedTest/include/spdlog/fmt/bundled/chrono.h
@@ -101,7 +101,6 @@ FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
return from;
} // function
-// clang-format off
/**
* converts From to To if possible, otherwise ec is set.
*
@@ -114,7 +113,6 @@ FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
* subnormal | best effort
* -Inf | -Inf
*/
-// clang-format on
template <typename To, typename From,
FMT_ENABLE_IF(!std::is_same<From, To>::value)>
FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
diff --git a/NorthstarDedicatedTest/logging.cpp b/NorthstarDedicatedTest/logging.cpp
index 975bc879..9b2a0925 100644
--- a/NorthstarDedicatedTest/logging.cpp
+++ b/NorthstarDedicatedTest/logging.cpp
@@ -153,7 +153,10 @@ long __stdcall ExceptionFilter(EXCEPTION_POINTERS* exceptionInfo)
GetModuleFileNameExA(GetCurrentProcess(), backtraceModuleHandle, backtraceModuleFullName, MAX_PATH);
char* backtraceModuleName = strrchr(backtraceModuleFullName, '\\') + 1;
- spdlog::error(" {} + {}", backtraceModuleName, (void*)framesToCapture[i]);
+ void* actualAddress = (void*)framesToCapture[i];
+ void* relativeAddress = (void*)(uintptr_t(actualAddress) - uintptr_t(backtraceModuleHandle));
+
+ spdlog::error(" {} + {} ({})", backtraceModuleName, relativeAddress, actualAddress);
}
spdlog::error("RAX: 0x{0:x}", exceptionContext->Rax);
diff --git a/NorthstarDedicatedTest/maxplayers.cpp b/NorthstarDedicatedTest/maxplayers.cpp
index 674d3ef0..54f1a896 100644
--- a/NorthstarDedicatedTest/maxplayers.cpp
+++ b/NorthstarDedicatedTest/maxplayers.cpp
@@ -45,10 +45,9 @@ constexpr int Team_PlayerArray_AddedLength = NEW_MAX_PLAYERS - 32;
constexpr int Team_PlayerArray_AddedSize = PAD_NUMBER(Team_PlayerArray_AddedLength * 8, 4);
constexpr int Team_AddedSize = Team_PlayerArray_AddedSize;
-template <class T> void ChangeOffset(void* addr, unsigned int offset)
-{
- TempReadWrite rw(addr);
- *((T*)addr) = offset;
+#include "NSMem.h"
+template <class T> void ChangeOffset(void* addr, unsigned int offset) {
+ NSMem::BytePatch((uintptr_t)addr, (BYTE*)&offset, sizeof(T));
}
/*
diff --git a/NorthstarDedicatedTest/miscserverfixes.cpp b/NorthstarDedicatedTest/miscserverfixes.cpp
index fca9c169..0b9a12db 100644
--- a/NorthstarDedicatedTest/miscserverfixes.cpp
+++ b/NorthstarDedicatedTest/miscserverfixes.cpp
@@ -2,32 +2,26 @@
#include "miscserverfixes.h"
#include "hookutils.h"
+#include "NSMem.h"
+
void InitialiseMiscServerFixes(HMODULE baseAddress)
{
+ uintptr_t ba = (uintptr_t)baseAddress;
+
// ret at the start of the concommand GenerateObjFile as it can crash servers
{
- char* ptr = reinterpret_cast<char*>(baseAddress) + 0x38D920;
- TempReadWrite rw(ptr);
- *ptr = 0xC3;
+ NSMem::BytePatch(ba + 0x38D920, {0xC3});
}
// nop out call to VGUI shutdown since it crashes the game when quitting from the console
{
- char* ptr = reinterpret_cast<char*>(baseAddress) + 0x154A96;
- TempReadWrite rw(ptr);
- *(ptr++) = 0x90; // nop
- *(ptr++) = 0x90; // nop
- *(ptr++) = 0x90; // nop
- *(ptr++) = 0x90; // nop
- *ptr = 0x90; // nop
+ NSMem::NOP(ba + 0x154A96, 5);
}
// 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
{
- char* ptr = reinterpret_cast<char*>(baseAddress) + 0x153920;
- TempReadWrite rw(ptr);
- *ptr = 0xC3;
+ NSMem::BytePatch(ba + 0x153920, {0xC3});
}
}
diff --git a/NorthstarDedicatedTest/playlist.cpp b/NorthstarDedicatedTest/playlist.cpp
index 7f06844f..a0a2dc33 100644
--- a/NorthstarDedicatedTest/playlist.cpp
+++ b/NorthstarDedicatedTest/playlist.cpp
@@ -71,6 +71,7 @@ int GetCurrentGamemodeMaxPlayersHook()
return maxPlayers;
}
+#include "NSMem.h"
void InitialisePlaylistHooks(HMODULE baseAddress)
{
RegisterConCommand("setplaylist", SetPlaylistCommand, "Sets the current playlist", FCVAR_NONE);
@@ -92,21 +93,16 @@ void InitialisePlaylistHooks(HMODULE baseAddress)
ENABLER_CREATEHOOK(
hook, (char*)baseAddress + 0x18C430, &GetCurrentGamemodeMaxPlayersHook, reinterpret_cast<LPVOID*>(&GetCurrentGamemodeMaxPlayers));
+ uintptr_t ba = (uintptr_t)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
{
- void* ptr = (char*)baseAddress + 0x18ED8D;
- TempReadWrite rw(ptr);
- *((char*)ptr) = (char)0xC3; // jmp => ret
+ NSMem::BytePatch(ba + 0x18ED8D, {
+ 0xC3 // jmp => ret
+ });
}
// patch to allow setplaylistvaroverride to be called before map init on dedicated and private match launched through the game
- void* ptr = (char*)baseAddress + 0x18ED17;
- TempReadWrite rw(ptr);
- *((char*)ptr) = (char)0x90;
- *((char*)ptr + 1) = (char)0x90;
- *((char*)ptr + 2) = (char)0x90;
- *((char*)ptr + 3) = (char)0x90;
- *((char*)ptr + 4) = (char)0x90;
- *((char*)ptr + 5) = (char)0x90;
+ NSMem::NOP(ba + 0x18ED17, 6);
}
diff --git a/NorthstarDedicatedTest/plugin_abi.h b/NorthstarDedicatedTest/plugin_abi.h
index edd44ea1..698c33ab 100644
--- a/NorthstarDedicatedTest/plugin_abi.h
+++ b/NorthstarDedicatedTest/plugin_abi.h
@@ -2,8 +2,6 @@
#include <string>
#define ABI_VERSION 1
-//clang-format off
-// I hate clang-format
/// <summary>
/// This enum is used for referencing the different types of objects we can pass to a plugin
/// Anything exposed to a plugin must not a be C++ type, as they could break when compiling with a different compiler.
@@ -67,6 +65,4 @@ struct PlayerInfo
int (*getPlayerInfoChar)(char* out_buf, size_t out_buf_len, PlayerInfoType var);
int (*getPlayerInfoInt)(int* out_ptr, PlayerInfoType var);
int (*getPlayerInfoBool)(bool* out_ptr, PlayerInfoType var);
-};
-
-//clang-format on \ No newline at end of file
+}; \ No newline at end of file
diff --git a/NorthstarDedicatedTest/securitypatches.cpp b/NorthstarDedicatedTest/securitypatches.cpp
deleted file mode 100644
index 9352559a..00000000
--- a/NorthstarDedicatedTest/securitypatches.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-#include "pch.h"
-#include "securitypatches.h"
-#include "hookutils.h"
-#include "concommand.h"
-#include "gameutils.h"
-#include "convar.h"
-
-typedef bool (*IsValveModType)();
-IsValveModType IsValveMod;
-
-bool IsValveModHook()
-{
- // basically: by default r2 isn't set as a valve mod, meaning that m_bRestrictServerCommands is false
- // this is HORRIBLE for security, because it means servers can run arbitrary concommands on clients
- // especially since we have script commands this could theoretically be awful
- return !CommandLine()->CheckParm("-norestrictservercommands");
-}
-
-typedef bool (*SVC_CmdKeyValues__ReadFromBufferType)(void* a1, void* a2);
-SVC_CmdKeyValues__ReadFromBufferType SVC_CmdKeyValues__ReadFromBuffer;
-// never parse server=>client keyvalues for clientcommandkeyvalues
-bool SVC_CmdKeyValues__ReadFromBufferHook(void* a1, void* a2) { return false; }
-
-void InitialiseClientEngineSecurityPatches(HMODULE baseAddress)
-{
- HookEnabler hook;
-
- // note: this could break some things
- ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x1C6360, &IsValveModHook, reinterpret_cast<LPVOID*>(&IsValveMod));
- ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x222E70, &SVC_CmdKeyValues__ReadFromBufferHook, reinterpret_cast<LPVOID*>(&SVC_CmdKeyValues__ReadFromBuffer));
-
- // patches to make commands run from client/ui script still work
- // note: this is likely preventable in a nicer way? test prolly
- {
- void* ptr = (char*)baseAddress + 0x4FB65;
- TempReadWrite rw(ptr);
-
- *((char*)ptr) = (char)0xEB;
- *((char*)ptr + 1) = (char)0x11;
- }
-
- {
- void* ptr = (char*)baseAddress + 0x4FBAC;
- TempReadWrite rw(ptr);
-
- *((char*)ptr) = (char)0xEB;
- *((char*)ptr + 1) = (char)0x16;
- }
-
- // byte patches to patch concommands that this messes up that we need
- {
- // disconnect concommand
- void* ptr = (char*)baseAddress + 0x5ADA2D;
- TempReadWrite rw(ptr);
-
- *((int*)ptr) |= FCVAR_SERVER_CAN_EXECUTE;
- }
-} \ No newline at end of file
diff --git a/NorthstarDedicatedTest/securitypatches.h b/NorthstarDedicatedTest/securitypatches.h
deleted file mode 100644
index 063df46e..00000000
--- a/NorthstarDedicatedTest/securitypatches.h
+++ /dev/null
@@ -1,2 +0,0 @@
-#pragma once
-void InitialiseClientEngineSecurityPatches(HMODULE baseAddress); \ No newline at end of file
diff --git a/NorthstarDedicatedTest/serverauthentication.cpp b/NorthstarDedicatedTest/serverauthentication.cpp
index 26352675..4581e4b6 100644
--- a/NorthstarDedicatedTest/serverauthentication.cpp
+++ b/NorthstarDedicatedTest/serverauthentication.cpp
@@ -14,6 +14,7 @@
#include <filesystem>
#include <thread>
#include "configurables.h"
+#include "NSMem.h"
const char* AUTHSERVER_VERIFY_STRING = "I am a northstar server!";
@@ -623,47 +624,39 @@ void InitialiseServerAuthentication(HMODULE baseAddress)
CCommand__Tokenize = (CCommand__TokenizeType)((char*)baseAddress + 0x418380);
+ uintptr_t ba = (uintptr_t)baseAddress;
+
// patch to disable kicking based on incorrect serverfilter in connectclient, since we repurpose it for use as an auth token
{
- void* ptr = (char*)baseAddress + 0x114655;
- TempReadWrite rw(ptr);
- *((char*)ptr) = (char)0xEB; // jz => jmp
+ NSMem::BytePatch(ba + 0x114655, {
+ 0xEB // jz => jmp
+ });
}
// patch to disable fairfight marking players as cheaters and kicking them
{
- void* ptr = (char*)baseAddress + 0x101012;
- TempReadWrite rw(ptr);
- *((char*)ptr) = (char)0xE9; // jz => jmp
- *((char*)ptr + 1) = (char)0x90;
- *((char*)ptr + 2) = (char)0x0;
+ NSMem::BytePatch(ba + 0x101012, {
+ 0xE9, // jz => jmp
+ 0x90,
+ 0x0
+ });
}
// patch to allow same of multiple account
{
- void* ptr = (char*)baseAddress + 0x114510;
- TempReadWrite rw(ptr);
- *((char*)ptr) = (char)0xEB; // jz => jmp
+ NSMem::BytePatch(ba + 0x114510, {
+ 0xEB, // jz => jmp
+ });
}
// patch to set bWasWritingStringTableSuccessful in CNetworkStringTableContainer::WriteBaselines if it fails
{
- bool* writeAddress = (bool*)(&bWasWritingStringTableSuccessful - ((bool*)baseAddress + 0x234EDC));
-
- void* ptr = (char*)baseAddress + 0x234ED2;
- TempReadWrite rw(ptr);
- *((char*)ptr) = (char)0xC7;
- *((char*)ptr + 1) = (char)0x05;
- *(int*)((char*)ptr + 2) = (int)writeAddress;
- *((char*)ptr + 6) = (char)0x00;
- *((char*)ptr + 7) = (char)0x00;
- *((char*)ptr + 8) = (char)0x00;
- *((char*)ptr + 9) = (char)0x00;
-
- *((char*)ptr + 10) = (char)0x90;
- *((char*)ptr + 11) = (char)0x90;
- *((char*)ptr + 12) = (char)0x90;
- *((char*)ptr + 13) = (char)0x90;
- *((char*)ptr + 14) = (char)0x90;
+ uintptr_t writeAddress = (uintptr_t)(&bWasWritingStringTableSuccessful - (ba + 0x234EDC));
+
+ auto addr = ba + 0x234ED2;
+ NSMem::BytePatch(addr, { 0xC7, 0x05 });
+ NSMem::BytePatch(addr + 2, (BYTE*)&writeAddress, sizeof(writeAddress));
+ NSMem::BytePatch(addr + 6, {0, 0, 0, 0});
+ NSMem::NOP(addr + 10, 5);
}
}
diff --git a/NorthstarDedicatedTest/sigscanning.cpp b/NorthstarDedicatedTest/sigscanning.cpp
index c75c80f0..761f9f08 100644
--- a/NorthstarDedicatedTest/sigscanning.cpp
+++ b/NorthstarDedicatedTest/sigscanning.cpp
@@ -5,7 +5,7 @@
// note: sigscanning is only really intended to be used for resolving stuff like shared function definitions
// we mostly use raw function addresses for stuff
-size_t GetDLLLength(HMODULE moduleHandle)
+size_t GetModuleLength(HMODULE moduleHandle)
{
// based on sigscn code from ttf2sdk, which is in turn based on CSigScan from https://wiki.alliedmods.net/Signature_Scanning
MEMORY_BASIC_INFORMATION mem;
@@ -19,12 +19,14 @@ size_t GetDLLLength(HMODULE moduleHandle)
void* FindSignature(std::string dllName, const char* sig, const char* mask)
{
- HMODULE dllAddress = GetModuleHandleA(dllName.c_str());
- char* dllEnd = (char*)(dllAddress + GetDLLLength(dllAddress));
+ HMODULE module = GetModuleHandleA(dllName.c_str());
+
+ unsigned char* dllAddress = (unsigned char*)module;
+ unsigned char* dllEnd = dllAddress + GetModuleLength(module);
size_t sigLength = strlen(mask);
- for (char* i = (char*)dllAddress; i < dllEnd - sigLength; i++)
+ for (auto i = dllAddress; i < dllEnd - sigLength + 1; i++)
{
int j = 0;
for (; j < sigLength; j++)
@@ -32,7 +34,7 @@ void* FindSignature(std::string dllName, const char* sig, const char* mask)
break;
if (j == sigLength) // loop finished of its own accord
- return (void*)i;
+ return i;
}
return nullptr;
diff --git a/README.md b/README.md
index 855969e2..793dca8e 100644
--- a/README.md
+++ b/README.md
@@ -9,4 +9,4 @@ Check [BUILD.md](https://github.com/R2Northstar/NorthstarLauncher/blob/main/BUIL
## Format
-This project uses [clang-format](https://clang.llvm.org/docs/ClangFormat.html), make sure you run `clang-format -i --style=file LauncherInjector/*.cpp LauncherInjector/*.h NorthstarDedicatedTest/*.cpp NorthstarDedicatedTest/*.h` when opening a Pull Request. Check the tool's website for instructions on how to integrate it with your IDE.
+This project uses [clang-format](https://clang.llvm.org/docs/ClangFormat.html), make sure you run `clang-format -i --style=file LauncherInjector/*.cpp LauncherInjector/*.h NorthstarDedicatedTest/*.cpp NorthstarDedicatedTest/*.h` when opening a Pull Request. Check the tool's website for instructions on how to integrate it with your IDE. \ No newline at end of file
diff --git a/loader_wsock32_proxy/hookutils.cpp b/loader_wsock32_proxy/hookutils.cpp
index 8603cb35..f933f993 100644
--- a/loader_wsock32_proxy/hookutils.cpp
+++ b/loader_wsock32_proxy/hookutils.cpp
@@ -3,23 +3,6 @@
#define ERROR(...) { char err[2048]; sprintf_s(err, __VA_ARGS__); MessageBoxA(GetForegroundWindow(), err, "Northstar Wsock32 Proxy Error", 0); }
-TempReadWrite::TempReadWrite(void* ptr)
-{
- m_ptr = ptr;
- MEMORY_BASIC_INFORMATION mbi;
- VirtualQuery(m_ptr, &mbi, sizeof(mbi));
- VirtualProtect(mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE, &mbi.Protect);
- m_origProtection = mbi.Protect;
-}
-
-TempReadWrite::~TempReadWrite()
-{
- MEMORY_BASIC_INFORMATION mbi;
- VirtualQuery(m_ptr, &mbi, sizeof(mbi));
- VirtualProtect(mbi.BaseAddress, mbi.RegionSize, m_origProtection, &mbi.Protect);
-}
-
-
void HookEnabler::CreateHook(LPVOID ppTarget, LPVOID ppDetour, LPVOID* ppOriginal, const char* targetName)
{
// the macro for this uses ppTarget's name as targetName, and this typically starts with &