diff options
author | KittenPopo <Pokeberry123@gmail.com> | 2022-03-23 13:03:56 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-03-23 20:03:56 +0000 |
commit | de7deafc7e6ba53deec8dd3c05d2ea5e7cf42264 (patch) | |
tree | 5bc5c82c9b720adbf02cdaeb505f46b48a02178f /NorthstarDedicatedTest/ExploitFixes.cpp | |
parent | 5893c7700653e5f3d51c4f7eba7f6f25a2fe5be7 (diff) | |
download | NorthstarLauncher-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
Diffstat (limited to 'NorthstarDedicatedTest/ExploitFixes.cpp')
-rw-r--r-- | NorthstarDedicatedTest/ExploitFixes.cpp | 196 |
1 files changed, 196 insertions, 0 deletions
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 |