From 841881af9ea6ec73b1d505d5a8f7c1f766273724 Mon Sep 17 00:00:00 2001 From: BobTheBob <32057864+BobTheBob9@users.noreply.github.com> Date: Mon, 17 Oct 2022 23:26:07 +0100 Subject: big refactor (#171) * use in-file macros rather than global funcs for registering dll load callbacks * move more things to macros * fix debug crashes * move sqvm funcs to sq managers * get rid of context file * refactor some squirrel stuff and ingame compilation error message * move tier0 and playlist funcs to namespaces * uiscript_reset concommand: don't loop forever if compilation fails * improve showing console for ui script compile errors * standardise concommand func naming in c++ * use lambdas for dll load callbacks so intellisense shits itself less * use cvar change callbacks for unescaping ns_server_name and ns_server_desc * add proper helpstrings to masterserver cvars * add cvar help and find * allow parsing of convar flags from string * normalise mod fs paths to be lowercase * move hoststate to its own file and add host_init hooks * better IsFlagSet def * replace files in ReadFromCache * rename g_ModManager to g_pModManager * formatting changes * make cvar print work on dedi, move demo fix stuff, add findflags * add proper map autocompletes and maps command * formatting changes * separate gameutils into multiple r2 headers * Update keyvalues.cpp * move sqvm funcs into wrappers in the manager class * remove unnecessary header files * lots of cleanup and starting moving to new hooking macros * update more stuff to new hook macros * rename project folder (:tf: commit log) * fix up postbuild commands to use relative dir * almost fully replaced hooking lib * completely remove old hooking * add nsprefix because i forgot to include it * move exploit prevention and limits code out of serverauthentication, and have actual defs for CBasePlayer * use modular ServerPresence system for registering servers * add new memory lib * accidentally pushed broke code oops * lots of stuff idk * implement some more prs * improve rpakfilesystem * fix line endings on vcxproj * Revert "fix line endings on vcxproj" This reverts commit 4ff7d022d2602c2dba37beba8b8df735cf5cd7d9. * add more prs * i swear i committed these how are they not there * Add ability to load Datatables from files (#238) * first version of kinda working custom datatables * Fix copy error * Finish custom datatables * Fix Merge * Fix line endings * Add fallback to rpak when ns_prefere_datatable_from_disk is true * fix typo * Bug fixess * Fix Function Registration hook * Set convar value * Fix Client and Ui VM * enable server auth with ms agian * Add Filters * FIx unused import * Merge remote-tracking branch 'upsteam/bobs-big-refactor-pr' into datatables Co-authored-by: RoyalBlue1 * Add some changes from main to refactor (#243) * Add PR template * Update CI folder location * Delete startup args txt files * Fix line endings (hopefully) (#244) * Fix line endings (hopefully) * Fix more line endings * Update refactor (#250) * Add PR template * Update CI folder location * Delete startup args txt files * Add editorconfig file (#246) * Add editorconfig file It's a cross-editor compatible config file that defines certain editor behaviour (e.g. adding/removing newline at end of file) It is supported by major editors like Visual Studio (Code) and by version control providers like GitHub. Should end the constant adding/removing of final newline in PRs * More settings - unicode by default - trim newlines - use tabs for indentation (ugh) * Ignore folder rename (#245) * Hot reload banlist on player join (#233) * added banlist hotreload * fix formatting * didnt append, cleared whole file oopsie * unfuckedunban not rewriting file * fixed not checking for new line Co-authored-by: ScureX <47725553+ScureX@users.noreply.github.com> * Refactor cleanup (#256) * Fix indentation * Fix path in clang-format command in readme * Refactor cleanup (some formatting fixes) (#257) * Fix some formatting * More formatting fixes * add scriptdatatable.cpp rewrite * Some formatting fixes (#260) * More formatting stuff (#261) * various formatting changes and fixes * Fix changed icon (#264) * clang format, fix issues with server registration and rpak loading * fix more formatting * update postbuild step * set launcher directory and error on fail creating log files * change some stuff in exploitfixes * only unrestrict dev commands when commandline flag is present * fix issues with cvar flag commit * fixup command flags better and reformat * bring up to date with main * fixup formatting * improve cvar flag fixup and remove temp thing from findflags * set serverfilter better * avoid ptr decay when setting auth token * add more entity functions * Fix the MS server registration issues. (#285) * Port ms presence reporter to std::async * Fix crash due to std::optional being assigned nullptr. * Fix formatting. * Wait 20 seconds if MS returns DUPLICATE_SERVER. * Change PERSISTENCE_MAX_SIZE to fix player authentication (#287) The size check added in the refactor was incorrect: - 56306: expected pdata size based on the pdef - 512: allowance for trailing junk (r2 adds 137 bytes of trailing junk) - 100: for some wiggle room Co-Authored-By: pg9182 <96569817+pg9182@users.noreply.github.com> * change miscserverscript to use actual entity arguments rather than player index jank * Fix token clearing hook (#290) A certain someone forgot to put an `0x` in front of their hex number, meaning the offset is wrong. This would cause token to be leaked again Co-authored-by: Maya Co-authored-by: RoyalBlue1 Co-authored-by: GeckoEidechse <40122905+GeckoEidechse@users.noreply.github.com> Co-authored-by: ScureX <47725553+ScureX@users.noreply.github.com> Co-authored-by: Erlite Co-authored-by: Emma Miler Co-authored-by: pg9182 <96569817+pg9182@users.noreply.github.com> --- NorthstarDLL/memory.cpp | 348 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 348 insertions(+) create mode 100644 NorthstarDLL/memory.cpp (limited to 'NorthstarDLL/memory.cpp') diff --git a/NorthstarDLL/memory.cpp b/NorthstarDLL/memory.cpp new file mode 100644 index 00000000..2e994fb2 --- /dev/null +++ b/NorthstarDLL/memory.cpp @@ -0,0 +1,348 @@ +#include "pch.h" +#include "memory.h" + +MemoryAddress::MemoryAddress() : m_nAddress(0) {} +MemoryAddress::MemoryAddress(const uintptr_t nAddress) : m_nAddress(nAddress) {} +MemoryAddress::MemoryAddress(const void* pAddress) : m_nAddress(reinterpret_cast(pAddress)) {} + +// operators +MemoryAddress::operator uintptr_t() const +{ + return m_nAddress; +} + +MemoryAddress::operator void*() const +{ + return reinterpret_cast(m_nAddress); +} + +MemoryAddress::operator bool() const +{ + return m_nAddress != 0; +} + +bool MemoryAddress::operator==(const MemoryAddress& other) const +{ + return m_nAddress == other.m_nAddress; +} + +bool MemoryAddress::operator!=(const MemoryAddress& other) const +{ + return m_nAddress != other.m_nAddress; +} + +bool MemoryAddress::operator==(const uintptr_t& addr) const +{ + return m_nAddress == addr; +} + +bool MemoryAddress::operator!=(const uintptr_t& addr) const +{ + return m_nAddress != addr; +} + +MemoryAddress MemoryAddress::operator+(const MemoryAddress& other) const +{ + return Offset(other.m_nAddress); +} + +MemoryAddress MemoryAddress::operator-(const MemoryAddress& other) const +{ + return MemoryAddress(m_nAddress - other.m_nAddress); +} + +MemoryAddress MemoryAddress::operator+(const uintptr_t& addr) const +{ + return Offset(addr); +} + +MemoryAddress MemoryAddress::operator-(const uintptr_t& addr) const +{ + return MemoryAddress(m_nAddress - addr); +} + +MemoryAddress MemoryAddress::operator*() const +{ + return Deref(); +} + +// traversal +MemoryAddress MemoryAddress::Offset(const uintptr_t nOffset) const +{ + return MemoryAddress(m_nAddress + nOffset); +} + +MemoryAddress MemoryAddress::Deref(const int nNumDerefs) const +{ + uintptr_t ret = m_nAddress; + for (int i = 0; i < nNumDerefs; i++) + ret = *reinterpret_cast(ret); + + return MemoryAddress(ret); +} + +// patching +void MemoryAddress::Patch(const uint8_t* pBytes, const size_t nSize) +{ + if (nSize) + WriteProcessMemory(GetCurrentProcess(), reinterpret_cast(m_nAddress), pBytes, nSize, NULL); +} + +void MemoryAddress::Patch(const std::initializer_list bytes) +{ + uint8_t* pBytes = new uint8_t[bytes.size()]; + + int i = 0; + for (const uint8_t& byte : bytes) + pBytes[i++] = byte; + + Patch(pBytes, bytes.size()); + delete[] pBytes; +} + +inline std::vector HexBytesToString(const char* pHexString) +{ + std::vector ret; + + int size = strlen(pHexString); + for (int i = 0; i < size; i++) + { + // If this is a space character, ignore it + if (isspace(pHexString[i])) + continue; + + if (i < size - 1) + { + BYTE result = 0; + for (int j = 0; j < 2; j++) + { + int val = 0; + char c = *(pHexString + i + j); + if (c >= 'a') + { + 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; + } + + result += (j == 0) ? val * 16 : val; + } + ret.push_back(result); + } + + i++; + } + + return ret; +} + +void MemoryAddress::Patch(const char* pBytes) +{ + std::vector vBytes = HexBytesToString(pBytes); + Patch(vBytes.data(), vBytes.size()); +} + +void MemoryAddress::NOP(const size_t nSize) +{ + uint8_t* pBytes = new uint8_t[nSize]; + + memset(pBytes, 0x90, nSize); + Patch(pBytes, nSize); + + delete[] pBytes; +} + +bool MemoryAddress::IsMemoryReadable(const size_t nSize) +{ + static SYSTEM_INFO sysInfo; + if (!sysInfo.dwPageSize) + GetSystemInfo(&sysInfo); + + MEMORY_BASIC_INFORMATION memInfo; + if (!VirtualQuery(reinterpret_cast(m_nAddress), &memInfo, sizeof(memInfo))) + return false; + + return memInfo.RegionSize >= nSize && memInfo.State & MEM_COMMIT && !(memInfo.Protect & PAGE_NOACCESS); +} + +CModule::CModule(const HMODULE pModule) +{ + MODULEINFO mInfo {0}; + + if (pModule && pModule != INVALID_HANDLE_VALUE) + GetModuleInformation(GetCurrentProcess(), pModule, &mInfo, sizeof(MODULEINFO)); + + m_nModuleSize = static_cast(mInfo.SizeOfImage); + m_pModuleBase = reinterpret_cast(mInfo.lpBaseOfDll); + m_nAddress = m_pModuleBase; + + if (!m_nModuleSize || !m_pModuleBase) + return; + + m_pDOSHeader = reinterpret_cast(m_pModuleBase); + m_pNTHeaders = reinterpret_cast(m_pModuleBase + m_pDOSHeader->e_lfanew); + + const IMAGE_SECTION_HEADER* hSection = IMAGE_FIRST_SECTION(m_pNTHeaders); // Get first image section. + + for (WORD i = 0; i < m_pNTHeaders->FileHeader.NumberOfSections; i++) // Loop through the sections. + { + const IMAGE_SECTION_HEADER& hCurrentSection = hSection[i]; // Get current section. + + ModuleSections_t moduleSection = ModuleSections_t( + std::string(reinterpret_cast(hCurrentSection.Name)), + static_cast(m_pModuleBase + hCurrentSection.VirtualAddress), + hCurrentSection.SizeOfRawData); + + if (!strcmp((const char*)hCurrentSection.Name, ".text")) + m_ExecutableCode = moduleSection; + else if (!strcmp((const char*)hCurrentSection.Name, ".pdata")) + m_ExceptionTable = moduleSection; + else if (!strcmp((const char*)hCurrentSection.Name, ".data")) + m_RunTimeData = moduleSection; + else if (!strcmp((const char*)hCurrentSection.Name, ".rdata")) + m_ReadOnlyData = moduleSection; + + m_vModuleSections.push_back(moduleSection); // Push back a struct with the section data. + } +} + +CModule::CModule(const char* pModuleName) : CModule(GetModuleHandleA(pModuleName)) {} + +MemoryAddress CModule::GetExport(const char* pExportName) +{ + return MemoryAddress(reinterpret_cast(GetProcAddress(reinterpret_cast(m_nAddress), pExportName))); +} + +MemoryAddress CModule::FindPattern(const uint8_t* pPattern, const char* pMask) +{ + if (!m_ExecutableCode.IsSectionValid()) + return MemoryAddress(); + + uint64_t nBase = static_cast(m_ExecutableCode.m_pSectionBase); + uint64_t nSize = static_cast(m_ExecutableCode.m_nSectionSize); + + const uint8_t* pData = reinterpret_cast(nBase); + const uint8_t* pEnd = pData + static_cast(nSize) - strlen(pMask); + + int nMasks[64]; // 64*16 = enough masks for 1024 bytes. + int iNumMasks = static_cast(ceil(static_cast(strlen(pMask)) / 16.f)); + + memset(nMasks, '\0', iNumMasks * sizeof(int)); + for (intptr_t i = 0; i < iNumMasks; ++i) + { + for (intptr_t j = strnlen(pMask + i * 16, 16) - 1; j >= 0; --j) + { + if (pMask[i * 16 + j] == 'x') + { + _bittestandset(reinterpret_cast(&nMasks[i]), j); + } + } + } + __m128i xmm1 = _mm_loadu_si128(reinterpret_cast(pPattern)); + __m128i xmm2, xmm3, msks; + for (; pData != pEnd; _mm_prefetch(reinterpret_cast(++pData + 64), _MM_HINT_NTA)) + { + if (pPattern[0] == pData[0]) + { + xmm2 = _mm_loadu_si128(reinterpret_cast(pData)); + msks = _mm_cmpeq_epi8(xmm1, xmm2); + if ((_mm_movemask_epi8(msks) & nMasks[0]) == nMasks[0]) + { + for (uintptr_t i = 1; i < static_cast(iNumMasks); ++i) + { + xmm2 = _mm_loadu_si128(reinterpret_cast((pData + i * 16))); + xmm3 = _mm_loadu_si128(reinterpret_cast((pPattern + i * 16))); + msks = _mm_cmpeq_epi8(xmm2, xmm3); + if ((_mm_movemask_epi8(msks) & nMasks[i]) == nMasks[i]) + { + if ((i + 1) == iNumMasks) + { + return MemoryAddress(const_cast(pData)); + } + } + else + goto CONTINUE; + } + + return MemoryAddress((&*(const_cast(pData)))); + } + } + + CONTINUE:; + } + + return MemoryAddress(); +} + +inline std::pair, std::string> MaskedBytesFromPattern(const char* pPatternString) +{ + std::vector vRet; + std::string sMask; + + int size = strlen(pPatternString); + for (int i = 0; i < size; i++) + { + // If this is a space character, ignore it + if (isspace(pPatternString[i])) + continue; + + if (pPatternString[i] == '?') + { + // Add a wildcard + vRet.push_back(0); + sMask.append("?"); + } + else if (i < size - 1) + { + BYTE result = 0; + for (int j = 0; j < 2; j++) + { + int val = 0; + char c = *(pPatternString + i + j); + if (c >= 'a') + { + 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 pattern string."); + val = -1; + } + + result += (j == 0) ? val * 16 : val; + } + + vRet.push_back(result); + sMask.append("x"); + } + + i++; + } + + return std::make_pair(vRet, sMask); +} + +MemoryAddress CModule::FindPattern(const char* pPattern) +{ + const auto pattern = MaskedBytesFromPattern(pPattern); + return FindPattern(pattern.first.data(), pattern.second.c_str()); +} -- cgit v1.2.3