From 4b0726d97788edff5d83476cb52057f409d623af Mon Sep 17 00:00:00 2001 From: Jack <66967891+ASpoonPlaysGames@users.noreply.github.com> Date: Mon, 4 Mar 2024 00:01:32 +0000 Subject: Update silver-bun to `72c74b4` (#664) Bumps the vendored silver-bun library to the newest commit in upstream Co-authored-by: F1F7Y Co-authored-by: IcePixelx <41352111+IcePixelx@users.noreply.github.com> --- primedev/Northstar.cmake | 4 +- primedev/cmake/Findsilver-bun.cmake | 9 + primedev/core/hooks.h | 1 - primedev/core/memory.cpp | 347 --------------- primedev/core/memory.h | 90 ---- primedev/core/tier0.cpp | 6 +- primedev/core/tier1.cpp | 2 +- primedev/core/tier1.h | 2 +- primedev/dedicated/dedicated.cpp | 4 +- primedev/pch.h | 3 +- primedev/server/alltalk.cpp | 2 +- primedev/shared/exploit_fixes/exploitfixes.cpp | 8 +- .../exploit_fixes/exploitfixes_utf8parser.cpp | 6 +- primedev/shared/keyvalues.cpp | 6 +- primedev/shared/maxplayers.cpp | 4 +- primedev/thirdparty/silver-bun/CMakeLists.txt | 9 + primedev/thirdparty/silver-bun/memaddr.cpp | 368 ++++++++++++++++ primedev/thirdparty/silver-bun/memaddr.h | 154 +++++++ primedev/thirdparty/silver-bun/module.cpp | 474 +++++++++++++++++++++ primedev/thirdparty/silver-bun/module.h | 76 ++++ primedev/thirdparty/silver-bun/utils.cpp | 127 ++++++ primedev/thirdparty/silver-bun/utils.h | 18 + 22 files changed, 1259 insertions(+), 461 deletions(-) create mode 100644 primedev/cmake/Findsilver-bun.cmake delete mode 100644 primedev/core/memory.cpp delete mode 100644 primedev/core/memory.h create mode 100644 primedev/thirdparty/silver-bun/CMakeLists.txt create mode 100644 primedev/thirdparty/silver-bun/memaddr.cpp create mode 100644 primedev/thirdparty/silver-bun/memaddr.h create mode 100644 primedev/thirdparty/silver-bun/module.cpp create mode 100644 primedev/thirdparty/silver-bun/module.h create mode 100644 primedev/thirdparty/silver-bun/utils.cpp create mode 100644 primedev/thirdparty/silver-bun/utils.h (limited to 'primedev') diff --git a/primedev/Northstar.cmake b/primedev/Northstar.cmake index 6b9c2380..aef630c8 100644 --- a/primedev/Northstar.cmake +++ b/primedev/Northstar.cmake @@ -3,6 +3,7 @@ find_package(minhook REQUIRED) find_package(libcurl REQUIRED) find_package(minizip REQUIRED) +find_package(silver-bun REQUIRED) add_library( NorthstarDLL SHARED @@ -50,8 +51,6 @@ add_library( "core/macros.h" "core/memalloc.cpp" "core/memalloc.h" - "core/memory.cpp" - "core/memory.h" "core/sourceinterface.cpp" "core/sourceinterface.h" "core/tier0.cpp" @@ -172,6 +171,7 @@ target_link_libraries( PRIVATE minhook libcurl minizip + silver-bun WS2_32.lib Crypt32.lib Cryptui.lib diff --git a/primedev/cmake/Findsilver-bun.cmake b/primedev/cmake/Findsilver-bun.cmake new file mode 100644 index 00000000..fa445aba --- /dev/null +++ b/primedev/cmake/Findsilver-bun.cmake @@ -0,0 +1,9 @@ +if(NOT silver-bun_FOUND) + check_init_submodule(${PROJECT_SOURCE_DIR}/primedev/thirdparty/silver-bun) + + add_subdirectory(${PROJECT_SOURCE_DIR}/primedev/thirdparty/silver-bun silver-bun) + set(silver-bun_FOUND + 1 + PARENT_SCOPE + ) +endif() diff --git a/primedev/core/hooks.h b/primedev/core/hooks.h index 7c1b001c..f842afbb 100644 --- a/primedev/core/hooks.h +++ b/primedev/core/hooks.h @@ -1,5 +1,4 @@ #pragma once -#include "memory.h" #include #include diff --git a/primedev/core/memory.cpp b/primedev/core/memory.cpp deleted file mode 100644 index 41110aee..00000000 --- a/primedev/core/memory.cpp +++ /dev/null @@ -1,347 +0,0 @@ -#include "memory.h" - -CMemoryAddress::CMemoryAddress() : m_nAddress(0) {} -CMemoryAddress::CMemoryAddress(const uintptr_t nAddress) : m_nAddress(nAddress) {} -CMemoryAddress::CMemoryAddress(const void* pAddress) : m_nAddress(reinterpret_cast(pAddress)) {} - -// operators -CMemoryAddress::operator uintptr_t() const -{ - return m_nAddress; -} - -CMemoryAddress::operator void*() const -{ - return reinterpret_cast(m_nAddress); -} - -CMemoryAddress::operator bool() const -{ - return m_nAddress != 0; -} - -bool CMemoryAddress::operator==(const CMemoryAddress& other) const -{ - return m_nAddress == other.m_nAddress; -} - -bool CMemoryAddress::operator!=(const CMemoryAddress& other) const -{ - return m_nAddress != other.m_nAddress; -} - -bool CMemoryAddress::operator==(const uintptr_t& addr) const -{ - return m_nAddress == addr; -} - -bool CMemoryAddress::operator!=(const uintptr_t& addr) const -{ - return m_nAddress != addr; -} - -CMemoryAddress CMemoryAddress::operator+(const CMemoryAddress& other) const -{ - return Offset(other.m_nAddress); -} - -CMemoryAddress CMemoryAddress::operator-(const CMemoryAddress& other) const -{ - return CMemoryAddress(m_nAddress - other.m_nAddress); -} - -CMemoryAddress CMemoryAddress::operator+(const uintptr_t& addr) const -{ - return Offset(addr); -} - -CMemoryAddress CMemoryAddress::operator-(const uintptr_t& addr) const -{ - return CMemoryAddress(m_nAddress - addr); -} - -CMemoryAddress CMemoryAddress::operator*() const -{ - return Deref(); -} - -// traversal -CMemoryAddress CMemoryAddress::Offset(const uintptr_t nOffset) const -{ - return CMemoryAddress(m_nAddress + nOffset); -} - -CMemoryAddress CMemoryAddress::Deref(const int nNumDerefs) const -{ - uintptr_t ret = m_nAddress; - for (int i = 0; i < nNumDerefs; i++) - ret = *reinterpret_cast(ret); - - return CMemoryAddress(ret); -} - -// patching -void CMemoryAddress::Patch(const uint8_t* pBytes, const size_t nSize) -{ - if (nSize) - WriteProcessMemory(GetCurrentProcess(), reinterpret_cast(m_nAddress), pBytes, nSize, NULL); -} - -void CMemoryAddress::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; - - size_t 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_msg(false, "Failed to parse invalid hex string."); - val = -1; - } - - result += (j == 0) ? val * 16 : val; - } - ret.push_back(result); - } - - i++; - } - - return ret; -} - -void CMemoryAddress::Patch(const char* pBytes) -{ - std::vector vBytes = HexBytesToString(pBytes); - Patch(vBytes.data(), vBytes.size()); -} - -void CMemoryAddress::NOP(const size_t nSize) -{ - uint8_t* pBytes = new uint8_t[nSize]; - - memset(pBytes, 0x90, nSize); - Patch(pBytes, nSize); - - delete[] pBytes; -} - -bool CMemoryAddress::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)) {} - -CMemoryAddress CModule::GetExport(const char* pExportName) -{ - return CMemoryAddress(reinterpret_cast(GetProcAddress(reinterpret_cast(m_nAddress), pExportName))); -} - -CMemoryAddress CModule::FindPattern(const uint8_t* pPattern, const char* pMask) -{ - if (!m_ExecutableCode.IsSectionValid()) - return CMemoryAddress(); - - 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 CMemoryAddress(const_cast(pData)); - } - } - else - goto CONTINUE; - } - - return CMemoryAddress((&*(const_cast(pData)))); - } - } - - CONTINUE:; - } - - return CMemoryAddress(); -} - -inline std::pair, std::string> MaskedBytesFromPattern(const char* pPatternString) -{ - std::vector vRet; - std::string sMask; - - size_t 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_msg(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); -} - -CMemoryAddress CModule::FindPattern(const char* pPattern) -{ - const auto pattern = MaskedBytesFromPattern(pPattern); - return FindPattern(pattern.first.data(), pattern.second.c_str()); -} diff --git a/primedev/core/memory.h b/primedev/core/memory.h deleted file mode 100644 index a978963e..00000000 --- a/primedev/core/memory.h +++ /dev/null @@ -1,90 +0,0 @@ -#pragma once - -class CMemoryAddress -{ -public: - uintptr_t m_nAddress; - -public: - CMemoryAddress(); - CMemoryAddress(const uintptr_t nAddress); - CMemoryAddress(const void* pAddress); - - // operators - operator uintptr_t() const; - operator void*() const; - operator bool() const; - - bool operator==(const CMemoryAddress& other) const; - bool operator!=(const CMemoryAddress& other) const; - bool operator==(const uintptr_t& addr) const; - bool operator!=(const uintptr_t& addr) const; - - CMemoryAddress operator+(const CMemoryAddress& other) const; - CMemoryAddress operator-(const CMemoryAddress& other) const; - CMemoryAddress operator+(const uintptr_t& other) const; - CMemoryAddress operator-(const uintptr_t& other) const; - CMemoryAddress operator*() const; - - template T RCast() - { - return reinterpret_cast(m_nAddress); - } - - // traversal - CMemoryAddress Offset(const uintptr_t nOffset) const; - CMemoryAddress Deref(const int nNumDerefs = 1) const; - - // patching - void Patch(const uint8_t* pBytes, const size_t nSize); - void Patch(const std::initializer_list bytes); - void Patch(const char* pBytes); - void NOP(const size_t nSize); - - bool IsMemoryReadable(const size_t nSize); -}; - -// based on https://github.com/Mauler125/r5sdk/blob/master/r5dev/public/include/module.h -class CModule : public CMemoryAddress -{ -public: - struct ModuleSections_t - { - ModuleSections_t(void) = default; - ModuleSections_t(const std::string& svSectionName, uintptr_t pSectionBase, size_t nSectionSize) - : m_svSectionName(svSectionName), m_pSectionBase(pSectionBase), m_nSectionSize(nSectionSize) - { - } - - bool IsSectionValid(void) const - { - return m_nSectionSize != 0; - } - - std::string m_svSectionName; // Name of section. - uintptr_t m_pSectionBase {}; // Start address of section. - size_t m_nSectionSize {}; // Size of section. - }; - - ModuleSections_t m_ExecutableCode; - ModuleSections_t m_ExceptionTable; - ModuleSections_t m_RunTimeData; - ModuleSections_t m_ReadOnlyData; - -private: - std::string m_svModuleName; - uintptr_t m_pModuleBase {}; - DWORD m_nModuleSize {}; - IMAGE_NT_HEADERS64* m_pNTHeaders = nullptr; - IMAGE_DOS_HEADER* m_pDOSHeader = nullptr; - std::vector m_vModuleSections; - -public: - CModule() = delete; // no default, we need a module name - CModule(const HMODULE pModule); - CModule(const char* pModuleName); - - CMemoryAddress GetExport(const char* pExportName); - CMemoryAddress FindPattern(const uint8_t* pPattern, const char* pMask); - CMemoryAddress FindPattern(const char* pPattern); -}; diff --git a/primedev/core/tier0.cpp b/primedev/core/tier0.cpp index 1f59722c..dd5ac245 100644 --- a/primedev/core/tier0.cpp +++ b/primedev/core/tier0.cpp @@ -24,7 +24,7 @@ ON_DLL_LOAD("tier0.dll", Tier0GameFuncs, (CModule module)) TryCreateGlobalMemAlloc(); // setup tier0 funcs - CommandLine = module.GetExport("CommandLine").RCast(); - Plat_FloatTime = module.GetExport("Plat_FloatTime").RCast(); - ThreadInServerFrameThread = module.GetExport("ThreadInServerFrameThread").RCast(); + CommandLine = module.GetExportedFunction("CommandLine").RCast(); + Plat_FloatTime = module.GetExportedFunction("Plat_FloatTime").RCast(); + ThreadInServerFrameThread = module.GetExportedFunction("ThreadInServerFrameThread").RCast(); } diff --git a/primedev/core/tier1.cpp b/primedev/core/tier1.cpp index a2995496..f857fdba 100644 --- a/primedev/core/tier1.cpp +++ b/primedev/core/tier1.cpp @@ -3,7 +3,7 @@ // Note: this file is tier1/interface.cpp in primedev, but given that tier0 is yet to be split // I am following the existing "pattern" and putting this here -CMemoryAddress Sys_GetFactoryPtr(const std::string& svModuleName, const std::string& svFactoryName) +CMemory Sys_GetFactoryPtr(const std::string& svModuleName, const std::string& svFactoryName) { HMODULE hModule = GetModuleHandleA(svModuleName.c_str()); diff --git a/primedev/core/tier1.h b/primedev/core/tier1.h index 5be58274..d162e7c8 100644 --- a/primedev/core/tier1.h +++ b/primedev/core/tier1.h @@ -9,4 +9,4 @@ typedef void* (*CreateInterfaceFn)(const char* pName, int* pReturnCode); -CMemoryAddress Sys_GetFactoryPtr(const std::string& svModuleName, const std::string& svFact); +CMemory Sys_GetFactoryPtr(const std::string& svModuleName, const std::string& svFact); diff --git a/primedev/dedicated/dedicated.cpp b/primedev/dedicated/dedicated.cpp index df6e5787..5c679b76 100644 --- a/primedev/dedicated/dedicated.cpp +++ b/primedev/dedicated/dedicated.cpp @@ -135,7 +135,7 @@ ON_DLL_LOAD_DEDI_RELIESON("engine.dll", DedicatedServer, ServerPresence, (CModul { // CModAppSystemGroup::Create // force the engine into dedicated mode by changing the first comparison to IsServerOnly to an assignment - CMemoryAddress base = module.Offset(0x1C4EBD); + CMemory base = module.Offset(0x1C4EBD); // cmp => mov base.Offset(1).Patch("C6 87"); @@ -262,7 +262,7 @@ ON_DLL_LOAD_DEDI("tier0.dll", DedicatedServerOrigin, (CModule module)) // disable origin on dedicated // 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 - module.GetExport("Tier0_InitOrigin").Patch("C3"); + module.GetExportedFunction("Tier0_InitOrigin").Patch("C3"); } // clang-format off diff --git a/primedev/pch.h b/primedev/pch.h index 8a03b857..141995c7 100644 --- a/primedev/pch.h +++ b/primedev/pch.h @@ -37,7 +37,8 @@ typedef void (*callable_v)(void* v); #include "logging/logging.h" #include "MinHook.h" #include "curl/curl.h" +#include "silver-bun/module.h" +#include "silver-bun/memaddr.h" #include "core/hooks.h" -#include "core/memory.h" #endif diff --git a/primedev/server/alltalk.cpp b/primedev/server/alltalk.cpp index 74119309..4eb5aef7 100644 --- a/primedev/server/alltalk.cpp +++ b/primedev/server/alltalk.cpp @@ -15,7 +15,7 @@ size_t __fastcall ShouldAllowAlltalk() ON_DLL_LOAD_RELIESON("engine.dll", ServerAllTalk, ConVar, (CModule module)) { // replace strcmp function called in CClient::ProcessVoiceData with our own code that calls ShouldAllowAllTalk - CMemoryAddress base = module.Offset(0x1085FA); + CMemory base = module.Offset(0x1085FA); base.Patch("48 B8"); // mov rax, 64 bit int // (uint8_t*)&ShouldAllowAlltalk doesn't work for some reason? need to make it a uint64 first diff --git a/primedev/shared/exploit_fixes/exploitfixes.cpp b/primedev/shared/exploit_fixes/exploitfixes.cpp index cade4084..7850f7b0 100644 --- a/primedev/shared/exploit_fixes/exploitfixes.cpp +++ b/primedev/shared/exploit_fixes/exploitfixes.cpp @@ -103,7 +103,7 @@ bool, __fastcall, (void* pMsg)) // 48 8B D1 48 8B 49 18 48 8B 01 48 FF 60 10 auto entry = msg->m_ConVars + i; // Safety check for memory access - if (CMemoryAddress(entry).IsMemoryReadable(sizeof(*entry))) + if (CMemory(entry).IsMemoryReadable(sizeof(*entry))) { // Find null terminators bool nameValid = false, valValid = false; @@ -421,9 +421,9 @@ ON_DLL_LOAD("engine.dll", EngineExploitFixes, (CModule module)) // patch to set bWasWritingStringTableSuccessful in CNetworkStringTableContainer::WriteBaselines if it fails { - CMemoryAddress writeAddress(&bWasWritingStringTableSuccessful - module.Offset(0x234EDC).m_nAddress); + CMemory writeAddress(&bWasWritingStringTableSuccessful - module.Offset(0x234EDC).GetPtr()); - CMemoryAddress addr = module.Offset(0x234ED2); + CMemory addr = module.Offset(0x234ED2); addr.Patch("C7 05"); addr.Offset(2).Patch((BYTE*)&writeAddress, sizeof(writeAddress)); @@ -451,7 +451,7 @@ ON_DLL_LOAD_RELIESON("server.dll", ServerExploitFixes, ConVar, (CModule module)) // Prevent these from actually doing anything for (auto exportName : ANTITAMPER_EXPORTS) { - CMemoryAddress exportAddr = module.GetExport(exportName); + CMemory exportAddr = module.GetExportedFunction(exportName); if (exportAddr) { // Just return, none of them have any args or are userpurge diff --git a/primedev/shared/exploit_fixes/exploitfixes_utf8parser.cpp b/primedev/shared/exploit_fixes/exploitfixes_utf8parser.cpp index 3d97f750..d63ba38a 100644 --- a/primedev/shared/exploit_fixes/exploitfixes_utf8parser.cpp +++ b/primedev/shared/exploit_fixes/exploitfixes_utf8parser.cpp @@ -67,7 +67,7 @@ bool __fastcall CheckUTF8Valid(INT64* a1, DWORD* a2, char* strData) { while (1) { - if (!CMemoryAddress(v4).IsMemoryReadable(1)) + if (!CMemory(v4).IsMemoryReadable(1)) return false; // INVALID v11 = *v4++; // crash potential @@ -174,7 +174,7 @@ AUTOHOOK(Rson_ParseUTF8, engine.dll + 0xEF670, bool, __fastcall, (INT64* a1, DWORD* a2, char* strData)) // 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 // clang-format on { - static void* targetRetAddr = CModule("engine.dll").FindPattern("84 C0 75 2C 49 8B 16"); + static void* targetRetAddr = CModule("engine.dll").FindPatternSIMD("84 C0 75 2C 49 8B 16"); // only call if we're parsing utf8 data from the network (i.e. communities), otherwise we get perf issues void* pReturnAddress = @@ -195,5 +195,5 @@ ON_DLL_LOAD("engine.dll", EngineExploitFixes_UTF8Parser, (CModule module)) { AUTOHOOK_DISPATCH() - sub_F1320 = module.FindPattern("83 F9 7F 77 08 88 0A").RCast(); + sub_F1320 = module.FindPatternSIMD("83 F9 7F 77 08 88 0A").RCast(); } diff --git a/primedev/shared/keyvalues.cpp b/primedev/shared/keyvalues.cpp index 88753723..36f891eb 100644 --- a/primedev/shared/keyvalues.cpp +++ b/primedev/shared/keyvalues.cpp @@ -1289,9 +1289,9 @@ KeyValues* KeyValues::MakeCopy(void) const ON_DLL_LOAD("vstdlib.dll", KeyValues, (CModule module)) { - V_UTF8ToUnicode = module.GetExport("V_UTF8ToUnicode").RCast(); - V_UnicodeToUTF8 = module.GetExport("V_UnicodeToUTF8").RCast(); - KeyValuesSystem = module.GetExport("KeyValuesSystem").RCast(); + V_UTF8ToUnicode = module.GetExportedFunction("V_UTF8ToUnicode").RCast(); + V_UnicodeToUTF8 = module.GetExportedFunction("V_UnicodeToUTF8").RCast(); + KeyValuesSystem = module.GetExportedFunction("KeyValuesSystem").RCast(); } AUTOHOOK_INIT() diff --git a/primedev/shared/maxplayers.cpp b/primedev/shared/maxplayers.cpp index 711193d4..69d625f2 100644 --- a/primedev/shared/maxplayers.cpp +++ b/primedev/shared/maxplayers.cpp @@ -60,7 +60,7 @@ int GetMaxPlayers() return 32; } -template void ChangeOffset(CMemoryAddress addr, unsigned int offset) +template void ChangeOffset(CMemory addr, unsigned int offset) { addr.Patch((BYTE*)&offset, sizeof(T)); } @@ -296,7 +296,7 @@ ON_DLL_LOAD("server.dll", MaxPlayersOverride_Server, (CModule module)) AUTOHOOK_DISPATCH_MODULE(server.dll) // get required data - serverBase = (HMODULE)module.m_nAddress; + serverBase = (HMODULE)module.GetModuleBase(); RandomIntZeroMax = (decltype(RandomIntZeroMax))(GetProcAddress(GetModuleHandleA("vstdlib.dll"), "RandomIntZeroMax")); // patch max players amount diff --git a/primedev/thirdparty/silver-bun/CMakeLists.txt b/primedev/thirdparty/silver-bun/CMakeLists.txt new file mode 100644 index 00000000..c39dd8bc --- /dev/null +++ b/primedev/thirdparty/silver-bun/CMakeLists.txt @@ -0,0 +1,9 @@ +add_library( + silver-bun STATIC + "memaddr.cpp" + "memaddr.h" + "module.cpp" + "module.h" + "utils.cpp" + "utils.h" + ) diff --git a/primedev/thirdparty/silver-bun/memaddr.cpp b/primedev/thirdparty/silver-bun/memaddr.cpp new file mode 100644 index 00000000..929d1682 --- /dev/null +++ b/primedev/thirdparty/silver-bun/memaddr.cpp @@ -0,0 +1,368 @@ +//===========================================================================// +// +// Purpose: Implementation of the CMemory class. +// +// Original commit: https://github.com/IcePixelx/silver-bun/commit/72c74b455bf4d02b424096ad2f30cd65535f814c +// +//===========================================================================// + +#include "memaddr.h" +#include "utils.h" + +//----------------------------------------------------------------------------- +// Purpose: check array of opcodes starting from current address +// Input : &vOpcodeArray - +// Output : true if equal, false otherwise +//----------------------------------------------------------------------------- +bool CMemory::CheckOpCodes(const std::vector& vOpcodeArray) const +{ + uintptr_t ref = ptr; + + // Loop forward in the ptr class member. + for (auto [byteAtCurrentAddress, i] = std::tuple{ uint8_t(), (size_t)0 }; i < vOpcodeArray.size(); i++, ref++) + { + byteAtCurrentAddress = *reinterpret_cast(ref); + + // If byte at ptr doesn't equal in the byte array return false. + if (byteAtCurrentAddress != vOpcodeArray[i]) + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Checx if memory is readable +// Input : nSize - +//----------------------------------------------------------------------------- +bool CMemory::IsMemoryReadable(const size_t nSize) const +{ + static SYSTEM_INFO sysInfo; + if (!sysInfo.dwPageSize) + GetSystemInfo(&sysInfo); + + MEMORY_BASIC_INFORMATION memInfo; + if (!VirtualQuery(reinterpret_cast(GetPtr()), &memInfo, sizeof(memInfo))) + return false; + + return memInfo.RegionSize >= nSize && memInfo.State & MEM_COMMIT && !(memInfo.Protect & PAGE_NOACCESS); +} + +//----------------------------------------------------------------------------- +// Purpose: patch size with nop opcodes +// Input : nSize - +//----------------------------------------------------------------------------- +void CMemory::NOP(const size_t nSize) const +{ + std::vector vOpcodeArray; + vOpcodeArray.resize(nSize); + memset(vOpcodeArray.data(), 0x90, nSize); + Patch(vOpcodeArray); +} + +//----------------------------------------------------------------------------- +// Purpose: patch array of opcodes +// Input : *pszOpcodes - +//----------------------------------------------------------------------------- +void CMemory::Patch(const char* pszOpcodes) const +{ + const std::vector vOpcodeArray = Utils::StringPatternToBytes(pszOpcodes); + Patch(vOpcodeArray); +} + +//----------------------------------------------------------------------------- +// Purpose: patch array of opcodes starting from current address +// Input : *pOpcodeArray - +// nSize - +//----------------------------------------------------------------------------- +void CMemory::Patch(const uint8_t* pOpcodeArray, const size_t nSize) const +{ + const std::vector vOpcodeArray(pOpcodeArray, pOpcodeArray + nSize * sizeof(uint8_t)); + Patch(vOpcodeArray); +} + +//----------------------------------------------------------------------------- +// Purpose: patch array of opcodes starting from current address +// Input : &vOpcodeArray - +//----------------------------------------------------------------------------- +void CMemory::Patch(const std::vector& vOpcodeArray) const +{ + DWORD oldProt = NULL; + + SIZE_T dwSize = vOpcodeArray.size(); + VirtualProtect(reinterpret_cast(ptr), dwSize, PAGE_EXECUTE_READWRITE, &oldProt); // Patch page to be able to read and write to it. + + for (size_t i = 0; i < vOpcodeArray.size(); i++) + { + *reinterpret_cast(ptr + i) = vOpcodeArray[i]; // Write opcodes to Address. + } + + dwSize = vOpcodeArray.size(); + VirtualProtect(reinterpret_cast(ptr), dwSize, oldProt, &oldProt); // Restore protection. +} + +//----------------------------------------------------------------------------- +// Purpose: patch string constant at current address +// Input : *szString - +//----------------------------------------------------------------------------- +void CMemory::PatchString(const char* szString) const +{ + DWORD oldProt = NULL; + SIZE_T dwSize = strlen(szString); + + VirtualProtect(reinterpret_cast(ptr), dwSize, PAGE_EXECUTE_READWRITE, &oldProt); // Patch page to be able to read and write to it. + + for (size_t i = 0; i < dwSize; i++) + { + *reinterpret_cast(ptr + i) = szString[i]; // Write string to Address. + } + + VirtualProtect(reinterpret_cast(ptr), dwSize, oldProt, &oldProt); // Restore protection. +} + +//----------------------------------------------------------------------------- +// Purpose: find array of bytes in process memory +// Input : *szPattern - +// searchDirect - +// opCodesToScan - +// occurrence - +// Output : CMemory +//----------------------------------------------------------------------------- +CMemory CMemory::FindPattern(const char* szPattern, const Direction searchDirect, const int opCodesToScan, const ptrdiff_t occurrence) const +{ + uint8_t* pScanBytes = reinterpret_cast(ptr); // Get the base of the module. + + const std::vector PatternBytes = Utils::PatternToBytes(szPattern); // Convert our pattern to a byte array. + const std::pair bytesInfo = std::make_pair(PatternBytes.size(), PatternBytes.data()); // Get the size and data of our bytes. + ptrdiff_t occurrences = 0; + + for (long i = 01; i < opCodesToScan + bytesInfo.first; i++) + { + bool bFound = true; + int nMemOffset = searchDirect == Direction::DOWN ? i : -i; + + for (DWORD j = 0ul; j < bytesInfo.first; j++) + { + // If either the current byte equals to the byte in our pattern or our current byte in the pattern is a wildcard + // our if clause will be false. + uint8_t currentByte = *(pScanBytes + nMemOffset + j); + _mm_prefetch(reinterpret_cast(static_cast(currentByte + nMemOffset + 64)), _MM_HINT_T0); // precache some data in L1. + if (currentByte != bytesInfo.second[j] && bytesInfo.second[j] != -1) + { + bFound = false; + break; + } + } + + if (bFound) + { + occurrences++; + if (occurrence == occurrences) + { + return CMemory(&*(pScanBytes + nMemOffset)); + } + } + } + + return CMemory(); +} + +//----------------------------------------------------------------------------- +// Purpose: find array of bytes in process memory starting from current address +// Input : *szPattern - +// searchDirect - +// opCodesToScan - +// occurrence - +// Output : CMemory +//----------------------------------------------------------------------------- +CMemory CMemory::FindPatternSelf(const char* szPattern, const Direction searchDirect, const int opCodesToScan, const ptrdiff_t occurrence) +{ + uint8_t* pScanBytes = reinterpret_cast(ptr); // Get the base of the module. + + const std::vector PatternBytes = Utils::PatternToBytes(szPattern); // Convert our pattern to a byte array. + const std::pair bytesInfo = std::make_pair(PatternBytes.size(), PatternBytes.data()); // Get the size and data of our bytes. + ptrdiff_t occurrences = 0; + + for (long i = 01; i < opCodesToScan + bytesInfo.first; i++) + { + bool bFound = true; + int nMemOffset = searchDirect == Direction::DOWN ? i : -i; + + for (DWORD j = 0ul; j < bytesInfo.first; j++) + { + // If either the current byte equals to the byte in our pattern or our current byte in the pattern is a wildcard + // our if clause will be false. + uint8_t currentByte = *(pScanBytes + nMemOffset + j); + _mm_prefetch(reinterpret_cast(static_cast(currentByte + nMemOffset + 64)), _MM_HINT_T0); // precache some data in L1. + if (currentByte != bytesInfo.second[j] && bytesInfo.second[j] != -1) + { + bFound = false; + break; + } + } + + if (bFound) + { + occurrences++; + if (occurrence == occurrences) + { + ptr = uintptr_t(&*(pScanBytes + nMemOffset)); + return *this; + } + } + } + + ptr = uintptr_t(); + return *this; +} + +//----------------------------------------------------------------------------- +// Purpose: ResolveRelativeAddress wrapper +// Input : opcodeOffset - +// nextInstructionOffset - +// Output : CMemory +//----------------------------------------------------------------------------- +CMemory CMemory::FollowNearCall(const ptrdiff_t opcodeOffset, const ptrdiff_t nextInstructionOffset) const +{ + return ResolveRelativeAddress(opcodeOffset, nextInstructionOffset); +} + +//----------------------------------------------------------------------------- +// Purpose: ResolveRelativeAddressSelf wrapper +// Input : opcodeOffset - +// nextInstructionOffset - +// Output : CMemory +//----------------------------------------------------------------------------- +CMemory CMemory::FollowNearCallSelf(const ptrdiff_t opcodeOffset, const ptrdiff_t nextInstructionOffset) +{ + return ResolveRelativeAddressSelf(opcodeOffset, nextInstructionOffset); +} + +//----------------------------------------------------------------------------- +// Purpose: resolves the relative pointer to offset +// Input : registerOffset - +// nextInstructionOffset - +// Output : CMemory +//----------------------------------------------------------------------------- +CMemory CMemory::ResolveRelativeAddress(const ptrdiff_t registerOffset, const ptrdiff_t nextInstructionOffset) const +{ + // Skip register. + const uintptr_t skipRegister = ptr + registerOffset; + + // Get 4-byte long relative Address. + const int32_t relativeAddress = *reinterpret_cast(skipRegister); + + // Get location of next instruction. + const uintptr_t nextInstruction = ptr + nextInstructionOffset; + + // Get function location via adding relative Address to next instruction. + return CMemory(nextInstruction + relativeAddress); +} + +//----------------------------------------------------------------------------- +// Purpose: resolves the relative pointer to offset from current address +// Input : registerOffset - +// nextInstructionOffset - +// Output : CMemory +//----------------------------------------------------------------------------- +CMemory CMemory::ResolveRelativeAddressSelf(const ptrdiff_t registerOffset, const ptrdiff_t nextInstructionOffset) +{ + // Skip register. + const uintptr_t skipRegister = ptr + registerOffset; + + // Get 4-byte long relative Address. + const int32_t relativeAddress = *reinterpret_cast(skipRegister); + + // Get location of next instruction. + const uintptr_t nextInstruction = ptr + nextInstructionOffset; + + // Get function location via adding relative Address to next instruction. + ptr = nextInstruction + relativeAddress; + return *this; +} + +//----------------------------------------------------------------------------- +// Purpose: resolve all 'call' references to ptr +// (This is very slow only use for mass patching.) +// Input : sectionBase - +// sectionSize - +// Output : std::vector +//----------------------------------------------------------------------------- +std::vector CMemory::FindAllCallReferences(const uintptr_t sectionBase, const size_t sectionSize) +{ + std::vector referencesInfo = {}; + + uint8_t* pTextStart = reinterpret_cast(sectionBase); + for (size_t i = 0ull; i < sectionSize - 0x5; i++, _mm_prefetch(reinterpret_cast(pTextStart + 64), _MM_HINT_NTA)) + { + if (pTextStart[i] == 0xE8) + { + CMemory memAddr = CMemory(&pTextStart[i]); + if (!memAddr.Offset(0x1).CheckOpCodes({ 0x00, 0x00, 0x00, 0x00 })) // Check if its not a dynamic resolved call. + { + if (memAddr.FollowNearCall() == *this) + referencesInfo.push_back(memAddr); + } + } + } + + return referencesInfo; +} + +//----------------------------------------------------------------------------- +// Purpose: patch virtual method to point to a user set function +// Input : virtualTable - +// pHookMethod - +// methodIndex - +// ppOriginalMethod - +// Output : void** via ppOriginalMethod +//----------------------------------------------------------------------------- +void CMemory::HookVirtualMethod(const uintptr_t virtualTable, const void* pHookMethod, const ptrdiff_t methodIndex, void** ppOriginalMethod) +{ + DWORD oldProt = NULL; + + // Calculate delta to next virtual method. + const uintptr_t virtualMethod = virtualTable + (methodIndex * sizeof(ptrdiff_t)); + + // Preserve original function. + const uintptr_t originalFunction = *reinterpret_cast(virtualMethod); + + // Set page for current virtual method to execute n read n write. + VirtualProtect(reinterpret_cast(virtualMethod), sizeof(virtualMethod), PAGE_EXECUTE_READWRITE, &oldProt); + + // Set virtual method to our hook. + *reinterpret_cast(virtualMethod) = reinterpret_cast(pHookMethod); + + // Restore original page. + VirtualProtect(reinterpret_cast(virtualMethod), sizeof(virtualMethod), oldProt, &oldProt); + + // Move original function into argument. + *ppOriginalMethod = reinterpret_cast(originalFunction); +} + +//----------------------------------------------------------------------------- +// Purpose: patch iat entry to point to a user set function +// Input : pImportedMethod - +// pHookMethod - +// ppOriginalMethod - +// Output : void** via ppOriginalMethod +//----------------------------------------------------------------------------- +void CMemory::HookImportedFunction(const uintptr_t pImportedMethod, const void* pHookMethod, void** ppOriginalMethod) +{ + DWORD oldProt = NULL; + + // Preserve original function. + const uintptr_t originalFunction = *reinterpret_cast(pImportedMethod); + + // Set page for current iat entry to execute n read n write. + VirtualProtect(reinterpret_cast(pImportedMethod), sizeof(void*), PAGE_EXECUTE_READWRITE, &oldProt); + + // Set method to our hook. + *reinterpret_cast(pImportedMethod) = reinterpret_cast(pHookMethod); + + // Restore original page. + VirtualProtect(reinterpret_cast(pImportedMethod), sizeof(void*), oldProt, &oldProt); + + // Move original function into argument. + *ppOriginalMethod = reinterpret_cast(originalFunction); +} diff --git a/primedev/thirdparty/silver-bun/memaddr.h b/primedev/thirdparty/silver-bun/memaddr.h new file mode 100644 index 00000000..3060c0cd --- /dev/null +++ b/primedev/thirdparty/silver-bun/memaddr.h @@ -0,0 +1,154 @@ +//===========================================================================// +// +// Purpose: Implementation of the CMemory class. +// +// Original commit: https://github.com/IcePixelx/silver-bun/commit/72c74b455bf4d02b424096ad2f30cd65535f814c +// +//===========================================================================// +#pragma once + +#include +#include +#include +#include +#include + +class CMemory +{ +public: + enum class Direction : int + { + DOWN = 0, + UP, + }; + + CMemory(void) = default; + CMemory(const uintptr_t ptr) : ptr(ptr) {} + CMemory(const void* ptr) : ptr(uintptr_t(ptr)) {} + + inline operator uintptr_t(void) const + { + return ptr; + } + + inline operator void*(void) const + { + return reinterpret_cast(ptr); + } + + inline operator bool(void) const + { + return ptr != NULL; + } + + inline bool operator!= (const CMemory& addr) const + { + return ptr != addr.ptr; + } + + inline bool operator== (const CMemory& addr) const + { + return ptr == addr.ptr; + } + + inline bool operator== (const uintptr_t& addr) const + { + return ptr == addr; + } + + inline uintptr_t GetPtr(void) const + { + return ptr; + } + + template inline T GetValue(void) const + { + return *reinterpret_cast(ptr); + } + + template inline T GetVirtualFunctionIndex(void) const + { + return *reinterpret_cast(ptr) / 8; + } + + template inline T CCast(void) const + { + return (T)ptr; + } + + template inline T RCast(void) const + { + return reinterpret_cast(ptr); + } + + inline CMemory Offset(ptrdiff_t offset) const + { + return CMemory(ptr + offset); + } + + inline CMemory OffsetSelf(ptrdiff_t offset) + { + ptr += offset; + return *this; + } + + inline CMemory Deref(int deref = 1) const + { + uintptr_t reference = ptr; + + while (deref--) + { + if (reference) + reference = *reinterpret_cast(reference); + } + + return CMemory(reference); + } + + inline CMemory DerefSelf(int deref = 1) + { + while (deref--) + { + if (ptr) + ptr = *reinterpret_cast(ptr); + } + + return *this; + } + + inline CMemory WalkVTable(ptrdiff_t vfuncIndex) + { + uintptr_t reference = ptr + (sizeof(uintptr_t) * vfuncIndex); + return CMemory(reference); + } + + inline CMemory WalkVTableSelf(ptrdiff_t vfuncIndex) + { + ptr += (sizeof(uintptr_t) * vfuncIndex); + return *this; + } + + bool CheckOpCodes(const std::vector& vOpcodeArray) const; + bool IsMemoryReadable(const size_t nSize) const; + + void NOP(const size_t nSize) const; + void Patch(const char* pszOpcodes) const; + void Patch(const uint8_t* pOpcodeArray, const size_t nSize) const; + void Patch(const std::vector& vOpcodeArray) const; + void PatchString(const char* szString) const; + + CMemory FindPattern(const char* szPattern, const Direction searchDirect = Direction::DOWN, const int opCodesToScan = 512, const ptrdiff_t occurrence = 1) const; + CMemory FindPatternSelf(const char* szPattern, const Direction searchDirect = Direction::DOWN, const int opCodesToScan = 512, const ptrdiff_t occurrence = 1); + std::vector FindAllCallReferences(const uintptr_t sectionBase, const size_t sectionSize); + + CMemory FollowNearCall(const ptrdiff_t opcodeOffset = 0x1, const ptrdiff_t nextInstructionOffset = 0x5) const; + CMemory FollowNearCallSelf(const ptrdiff_t opcodeOffset = 0x1, const ptrdiff_t nextInstructionOffset = 0x5); + CMemory ResolveRelativeAddress(const ptrdiff_t registerOffset = 0x0, const ptrdiff_t nextInstructionOffset = 0x4) const; + CMemory ResolveRelativeAddressSelf(const ptrdiff_t registerOffset = 0x0, const ptrdiff_t nextInstructionOffset = 0x4); + + static void HookVirtualMethod(const uintptr_t virtualTable, const void* pHookMethod, const ptrdiff_t methodIndex, void** ppOriginalMethod); + static void HookImportedFunction(const uintptr_t pImportedMethod, const void* pHookMethod, void** ppOriginalMethod); + +private: + uintptr_t ptr = 0; +}; diff --git a/primedev/thirdparty/silver-bun/module.cpp b/primedev/thirdparty/silver-bun/module.cpp new file mode 100644 index 00000000..4b9330e0 --- /dev/null +++ b/primedev/thirdparty/silver-bun/module.cpp @@ -0,0 +1,474 @@ +//===========================================================================// +// +// Purpose: Implementation of the CModule class. +// +// Original commit: https://github.com/IcePixelx/silver-bun/commit/72c74b455bf4d02b424096ad2f30cd65535f814c +// +//===========================================================================// + +#include "module.h" +#include "utils.h" + +//----------------------------------------------------------------------------- +// Purpose: constructor +// Input : *szModuleName - +//----------------------------------------------------------------------------- +CModule::CModule(HMODULE hModule) +{ + m_pModuleBase = reinterpret_cast(hModule); + + CHAR szModuleName[MAX_PATH]; + DWORD dwSize = GetModuleFileNameA(hModule, szModuleName, sizeof(szModuleName)); + m_ModuleName = strrchr(szModuleName, '\\') + 1; + + + Init(); + LoadSections(); +} + +//----------------------------------------------------------------------------- +// Purpose: constructor +// Input : *szModuleName - +//----------------------------------------------------------------------------- +CModule::CModule(const char* szModuleName) +{ + m_pModuleBase = reinterpret_cast(GetModuleHandleA(szModuleName)); + m_ModuleName = szModuleName; + + Init(); + LoadSections(); +} + +//----------------------------------------------------------------------------- +// Purpose: initializes module descriptors +//----------------------------------------------------------------------------- +void CModule::Init() +{ + m_pDOSHeader = reinterpret_cast(m_pModuleBase); + m_pNTHeaders = reinterpret_cast(m_pModuleBase + m_pDOSHeader->e_lfanew); + m_nModuleSize = static_cast(m_pNTHeaders->OptionalHeader.SizeOfImage); + + 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. + m_ModuleSections.push_back(ModuleSections_t(reinterpret_cast(hCurrentSection.Name), + static_cast(m_pModuleBase + hCurrentSection.VirtualAddress), hCurrentSection.SizeOfRawData)); // Push back a struct with the section data. + } +} + +//----------------------------------------------------------------------------- +// Purpose: initializes the default executable segments +//----------------------------------------------------------------------------- +void CModule::LoadSections() +{ + m_ExecutableCode = GetSectionByName(".text"); + m_ExceptionTable = GetSectionByName(".pdata"); + m_RunTimeData = GetSectionByName(".data"); + m_ReadOnlyData = GetSectionByName(".rdata"); +} + +//----------------------------------------------------------------------------- +// Purpose: Gets memory at relative offset +// Input : nOffset - +// Output : CMemory +//----------------------------------------------------------------------------- +CMemory CModule::Offset(const uintptr_t nOffset) const +{ + return CMemory(m_pModuleBase + nOffset); +} + +//----------------------------------------------------------------------------- +// Purpose: find array of bytes in process memory using SIMD instructions +// Input : *pPattern - +// *szMask - +// *moduleSection - +// nOccurrence - +// Output : CMemory +//----------------------------------------------------------------------------- +CMemory CModule::FindPatternSIMD(const uint8_t* pPattern, const char* szMask, + const ModuleSections_t* moduleSection, const size_t nOccurrence) const +{ + if (!m_ExecutableCode.IsSectionValid()) + return CMemory(); + + const bool bSectionValid = moduleSection ? moduleSection->IsSectionValid() : false; + + const uintptr_t nBase = bSectionValid ? moduleSection->m_pSectionBase : m_ExecutableCode.m_pSectionBase; + const uintptr_t nSize = bSectionValid ? moduleSection->m_nSectionSize : m_ExecutableCode.m_nSectionSize; + + const size_t nMaskLen = strlen(szMask); + const uint8_t* pData = reinterpret_cast(nBase); + const uint8_t* pEnd = pData + nSize - nMaskLen; + + size_t nOccurrenceCount = 0; + int nMasks[64]; // 64*16 = enough masks for 1024 bytes. + const int iNumMasks = static_cast(ceil(static_cast(nMaskLen) / 16.f)); + + memset(nMasks, '\0', iNumMasks * sizeof(int)); + for (intptr_t i = 0; i < iNumMasks; ++i) + { + for (intptr_t j = strnlen(szMask + i * 16, 16) - 1; j >= 0; --j) + { + if (szMask[i * 16 + j] == 'x') + { + _bittestandset(reinterpret_cast(&nMasks[i]), static_cast(j)); + } + } + } + const __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) + { + if (nOccurrenceCount == nOccurrence) + { + return static_cast(const_cast(pData)); + } + nOccurrenceCount++; + } + } + else + { + goto cont; + } + } + if (nOccurrenceCount == nOccurrence) + { + return static_cast((&*(const_cast(pData)))); + } + nOccurrenceCount++; + } + }cont:; + } + return CMemory(); +} + +//----------------------------------------------------------------------------- +// Purpose: find a string pattern in process memory using SIMD instructions +// Input : *szPattern - +// *moduleSection - +// Output : CMemory +//----------------------------------------------------------------------------- +CMemory CModule::FindPatternSIMD(const char* szPattern, const ModuleSections_t* moduleSection) const +{ + const std::pair, std::string> patternInfo = Utils::PatternToMaskedBytes(szPattern); + return FindPatternSIMD(patternInfo.first.data(), patternInfo.second.c_str(), moduleSection); +} + +//----------------------------------------------------------------------------- +// Purpose: find address of reference to string constant in executable memory +// Input : *szString - +// bNullTerminator - +// Output : CMemory +//----------------------------------------------------------------------------- +CMemory CModule::FindString(const char* szString, const ptrdiff_t nOccurrence, bool bNullTerminator) const +{ + if (!m_ExecutableCode.IsSectionValid()) + return CMemory(); + + const CMemory stringAddress = FindStringReadOnly(szString, bNullTerminator); // Get Address for the string in the .rdata section. + + if (!stringAddress) + return CMemory(); + + uint8_t* pLatestOccurrence = nullptr; + uint8_t* pTextStart = reinterpret_cast(m_ExecutableCode.m_pSectionBase); // Get the start of the .text section. + ptrdiff_t dOccurrencesFound = 0; + CMemory resultAddress; + + for (size_t i = 0ull; i < m_ExecutableCode.m_nSectionSize - 0x5; i++) + { + byte byte = pTextStart[i]; + if (byte == 0x8D) // 0x8D = LEA + { + const CMemory skipOpCode = CMemory(reinterpret_cast(&pTextStart[i])).OffsetSelf(0x2); // Skip next 2 opcodes, those being the instruction and the register. + const int32_t relativeAddress = skipOpCode.GetValue(); // Get 4-byte long string relative Address + const uintptr_t nextInstruction = skipOpCode.Offset(0x4).GetPtr(); // Get location of next instruction. + const CMemory potentialLocation = CMemory(nextInstruction + relativeAddress); // Get potential string location. + + if (potentialLocation == stringAddress) + { + dOccurrencesFound++; + if (nOccurrence == dOccurrencesFound) + { + return CMemory(&pTextStart[i]); + } + + pLatestOccurrence = &pTextStart[i]; // Stash latest occurrence. + } + } + } + + return CMemory(pLatestOccurrence); +} + +//----------------------------------------------------------------------------- +// Purpose: find address of input string constant in read only memory +// Input : *szString - +// bNullTerminator - +// Output : CMemory +//----------------------------------------------------------------------------- +CMemory CModule::FindStringReadOnly(const char* szString, bool bNullTerminator) const +{ + if (!m_ReadOnlyData.IsSectionValid()) + return CMemory(); + + const std::vector vBytes = Utils::StringToBytes(szString, bNullTerminator); // Convert our string to a byte array. + const std::pair bytesInfo = std::make_pair(vBytes.size(), vBytes.data()); // Get the size and data of our bytes. + + const uint8_t* pBase = reinterpret_cast(m_ReadOnlyData.m_pSectionBase); // Get start of .rdata section. + + for (size_t i = 0ull; i < m_ReadOnlyData.m_nSectionSize - bytesInfo.first; i++) + { + bool bFound = true; + + // If either the current byte equals to the byte in our pattern or our current byte in the pattern is a wildcard + // our if clause will be false. + for (size_t j = 0ull; j < bytesInfo.first; j++) + { + if (pBase[i + j] != bytesInfo.second[j] && bytesInfo.second[j] != -1) + { + bFound = false; + break; + } + } + + if (bFound) + { + return CMemory(&pBase[i]); + } + } + + return CMemory(); +} + +//----------------------------------------------------------------------------- +// Purpose: find 'free' page in r/w/x sections +// Input : nSize - +// Output : CMemory +//----------------------------------------------------------------------------- +CMemory CModule::FindFreeDataPage(const size_t nSize) const +{ + auto checkDataSection = [](const void* address, const std::size_t size) + { + MEMORY_BASIC_INFORMATION membInfo = { 0 }; + + VirtualQuery(address, &membInfo, sizeof(membInfo)); + + if (membInfo.AllocationBase && membInfo.BaseAddress && membInfo.State == MEM_COMMIT && !(membInfo.Protect & PAGE_GUARD) && membInfo.Protect != PAGE_NOACCESS) + { + if ((membInfo.Protect & (PAGE_EXECUTE_READWRITE | PAGE_READWRITE)) && membInfo.RegionSize >= size) + { + return ((membInfo.Protect & (PAGE_EXECUTE_READWRITE | PAGE_READWRITE)) && membInfo.RegionSize >= size) ? true : false; + } + } + return false; + }; + + // This is very unstable, this doesn't check for the actual 'page' sizes. + // Also can be optimized to search per 'section'. + const uintptr_t endOfModule = m_pModuleBase + m_pNTHeaders->OptionalHeader.SizeOfImage - sizeof(uintptr_t); + for (uintptr_t currAddr = endOfModule; m_pModuleBase < currAddr; currAddr -= sizeof(uintptr_t)) + { + if (*reinterpret_cast(currAddr) == 0 && checkDataSection(reinterpret_cast(currAddr), nSize)) + { + bool bIsGoodPage = true; + uint32_t nPageCount = 0; + + for (; nPageCount < nSize && bIsGoodPage; nPageCount += sizeof(uintptr_t)) + { + const uintptr_t pageData = *reinterpret_cast(currAddr + nPageCount); + if (pageData != 0) + bIsGoodPage = false; + } + + if (bIsGoodPage && nPageCount >= nSize) + return currAddr; + } + } + + return CMemory(); +} + +//----------------------------------------------------------------------------- +// Purpose: get address of a virtual method table by rtti type descriptor name +// Input : *szTableName - +// nRefIndex - +// Output : CMemory +//----------------------------------------------------------------------------- +CMemory CModule::GetVirtualMethodTable(const char* szTableName, const size_t nRefIndex) +{ + if (!m_ReadOnlyData.IsSectionValid()) // Process decided to rename the readonlydata section if this fails. + return CMemory(); + + ModuleSections_t moduleSection(".data", m_RunTimeData.m_pSectionBase, m_RunTimeData.m_nSectionSize); + + const auto tableNameInfo = Utils::StringToMaskedBytes(szTableName, false); + CMemory rttiTypeDescriptor = FindPatternSIMD(tableNameInfo.first.data(), tableNameInfo.second.c_str(), &moduleSection).OffsetSelf(-0x10); + if (!rttiTypeDescriptor) + return CMemory(); + + uintptr_t scanStart = m_ReadOnlyData.m_pSectionBase; // Get the start address of our scan. + + const uintptr_t scanEnd = (m_ReadOnlyData.m_pSectionBase + m_ReadOnlyData.m_nSectionSize) - 0x4; // Calculate the end of our scan. + const uintptr_t rttiTDRva = rttiTypeDescriptor.GetPtr() - m_pModuleBase; // The RTTI gets referenced by a 4-Byte RVA address. We need to scan for that address. + while (scanStart < scanEnd) + { + moduleSection = { ".rdata", scanStart, m_ReadOnlyData.m_nSectionSize }; + CMemory reference = FindPatternSIMD(reinterpret_cast(&rttiTDRva), "xxxx", &moduleSection, nRefIndex); + if (!reference) + break; + + CMemory referenceOffset = reference.Offset(-0xC); + if (referenceOffset.GetValue() != 1) // Check if we got a RTTI Object Locator for this reference by checking if -0xC is 1, which is the 'signature' field which is always 1 on x64. + { + scanStart = reference.Offset(0x4).GetPtr(); // Set location to current reference + 0x4 so we avoid pushing it back again into the vector. + continue; + } + + moduleSection = { ".rdata", m_ReadOnlyData.m_pSectionBase, m_ReadOnlyData.m_nSectionSize }; + return FindPatternSIMD(reinterpret_cast(&referenceOffset), "xxxxxxxx", &moduleSection).OffsetSelf(0x8); + } + + return CMemory(); +} + +//----------------------------------------------------------------------------- +// Purpose: get address of imported function in this module +// Input : *szModuleName - +// *szFunctionName - +// bGetFunctionReference - +// Output : CMemory +//----------------------------------------------------------------------------- +CMemory CModule::GetImportedFunction(const char* szModuleName, const char* szFunctionName, const bool bGetFunctionReference) const +{ + if (!m_pDOSHeader || m_pDOSHeader->e_magic != IMAGE_DOS_SIGNATURE) // Is dosHeader valid? + return CMemory(); + + if (!m_pNTHeaders || m_pNTHeaders->Signature != IMAGE_NT_SIGNATURE) // Is ntHeader valid? + return CMemory(); + + // Get the location of IMAGE_IMPORT_DESCRIPTOR for this module by adding the IMAGE_DIRECTORY_ENTRY_IMPORT relative virtual address onto our module base address. + IMAGE_IMPORT_DESCRIPTOR* pImageImportDescriptors = reinterpret_cast(m_pModuleBase + m_pNTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress); + if (!pImageImportDescriptors) + return CMemory(); + + for (IMAGE_IMPORT_DESCRIPTOR* pIID = pImageImportDescriptors; pIID->Name != 0; pIID++) + { + // Get virtual relative Address of the imported module name. Then add module base Address to get the actual location. + const char* szImportedModuleName = reinterpret_cast(reinterpret_cast(m_pModuleBase + pIID->Name)); + + if (_stricmp(szImportedModuleName, szModuleName) == 0) // Is this our wanted imported module?. + { + // Original First Thunk to get function name. + IMAGE_THUNK_DATA* pOgFirstThunk = reinterpret_cast(m_pModuleBase + pIID->OriginalFirstThunk); + + // To get actual function address. + IMAGE_THUNK_DATA* pFirstThunk = reinterpret_cast(m_pModuleBase + pIID->FirstThunk); + for (; pOgFirstThunk->u1.AddressOfData; ++pOgFirstThunk, ++pFirstThunk) + { + // Get image import by name. + const IMAGE_IMPORT_BY_NAME* pImageImportByName = reinterpret_cast(m_pModuleBase + pOgFirstThunk->u1.AddressOfData); + + if (strcmp(pImageImportByName->Name, szFunctionName) == 0) // Is this our wanted imported function? + { + // Grab function address from firstThunk. +#if _WIN64 + uintptr_t* pFunctionAddress = &pFirstThunk->u1.Function; +#else + uintptr_t* pFunctionAddress = reinterpret_cast(&pFirstThunk->u1.Function); +#endif // #if _WIN64 + + // Reference or address? + return bGetFunctionReference ? CMemory(pFunctionAddress) : CMemory(*pFunctionAddress); // Return as CMemory class. + } + } + + } + } + return CMemory(); +} + +//----------------------------------------------------------------------------- +// Purpose: get address of exported function in this module +// Input : *szFunctionName - +// bNullTerminator - +// Output : CMemory +//----------------------------------------------------------------------------- +CMemory CModule::GetExportedFunction(const char* szFunctionName) const +{ + if (!m_pDOSHeader || m_pDOSHeader->e_magic != IMAGE_DOS_SIGNATURE) // Is dosHeader valid? + return CMemory(); + + if (!m_pNTHeaders || m_pNTHeaders->Signature != IMAGE_NT_SIGNATURE) // Is ntHeader valid? + return CMemory(); + + // Get the location of IMAGE_EXPORT_DIRECTORY for this module by adding the IMAGE_DIRECTORY_ENTRY_EXPORT relative virtual address onto our module base address. + const IMAGE_EXPORT_DIRECTORY* pImageExportDirectory = reinterpret_cast(m_pModuleBase + m_pNTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); + if (!pImageExportDirectory) + return CMemory(); + + // Are there any exported functions? + if (!pImageExportDirectory->NumberOfFunctions) + return CMemory(); + + // Get the location of the functions via adding the relative virtual address from the struct into our module base address. + const DWORD* pAddressOfFunctions = reinterpret_cast(m_pModuleBase + pImageExportDirectory->AddressOfFunctions); + if (!pAddressOfFunctions) + return CMemory(); + + // Get the names of the functions via adding the relative virtual address from the struct into our module base Address. + const DWORD* pAddressOfName = reinterpret_cast(m_pModuleBase + pImageExportDirectory->AddressOfNames); + if (!pAddressOfName) + return CMemory(); + + // Get the ordinals of the functions via adding the relative virtual Address from the struct into our module base address. + DWORD* pAddressOfOrdinals = reinterpret_cast(m_pModuleBase + pImageExportDirectory->AddressOfNameOrdinals); + if (!pAddressOfOrdinals) + return CMemory(); + + for (DWORD i = 0; i < pImageExportDirectory->NumberOfNames; i++) // Iterate through all the functions. + { + // Get virtual relative Address of the function name. Then add module base Address to get the actual location. + const char* ExportFunctionName = reinterpret_cast(reinterpret_cast(m_pModuleBase + pAddressOfName[i])); + + if (strcmp(ExportFunctionName, szFunctionName) == 0) // Is this our wanted exported function? + { + // Get the function ordinal. Then grab the relative virtual address of our wanted function. Then add module base address so we get the actual location. + return CMemory(m_pModuleBase + pAddressOfFunctions[reinterpret_cast(pAddressOfOrdinals)[i]]); // Return as CMemory class. + } + } + return CMemory(); +} + +//----------------------------------------------------------------------------- +// Purpose: get the module section by name (example: '.rdata', '.text') +// Input : *szSectionName - +// Output : ModuleSections_t +//----------------------------------------------------------------------------- +CModule::ModuleSections_t CModule::GetSectionByName(const char* szSectionName) const +{ + for (const ModuleSections_t& section : m_ModuleSections) + { + if (section.m_SectionName.compare(szSectionName) == 0) + return section; + } + + return ModuleSections_t(); +} diff --git a/primedev/thirdparty/silver-bun/module.h b/primedev/thirdparty/silver-bun/module.h new file mode 100644 index 00000000..5683ee14 --- /dev/null +++ b/primedev/thirdparty/silver-bun/module.h @@ -0,0 +1,76 @@ +//===========================================================================// +// +// Purpose: Implementation of the CModule class. +// +// Original commit: https://github.com/IcePixelx/silver-bun/commit/72c74b455bf4d02b424096ad2f30cd65535f814c +// +//===========================================================================// +#pragma once + +#include "memaddr.h" +#include +#include +#include +#include +#include +#include +#include + +class CModule +{ +public: + struct ModuleSections_t + { + ModuleSections_t(void) = default; + ModuleSections_t(const char* sectionName, uintptr_t pSectionBase, size_t nSectionSize) : + m_SectionName(sectionName), m_pSectionBase(pSectionBase), m_nSectionSize(nSectionSize) {} + + inline bool IsSectionValid(void) const { return m_nSectionSize != 0; } + + std::string m_SectionName; // Name of section. + uintptr_t m_pSectionBase; // Start address of section. + size_t m_nSectionSize; // Size of section. + }; + + CModule(void) = default; + CModule(HMODULE hModule); + CModule(const char* szModuleName); + + void Init(); + void LoadSections(); + + CMemory Offset(const uintptr_t nOffset) const; + + CMemory FindPatternSIMD(const char* szPattern, const ModuleSections_t* moduleSection = nullptr) const; + CMemory FindString(const char* szString, const ptrdiff_t occurrence = 1, bool nullTerminator = false) const; + CMemory FindStringReadOnly(const char* szString, bool nullTerminator) const; + CMemory FindFreeDataPage(const size_t nSize) const; + + CMemory GetVirtualMethodTable(const char* szTableName, const size_t nRefIndex = 0); + CMemory GetImportedFunction(const char* szModuleName, const char* szFunctionName, const bool bGetFunctionReference) const; + CMemory GetExportedFunction(const char* szFunctionName) const; + ModuleSections_t GetSectionByName(const char* szSectionName) const; + + inline const std::vector& GetSections() const { return m_ModuleSections; } + inline uintptr_t GetModuleBase(void) const { return m_pModuleBase; } + inline DWORD GetModuleSize(void) const { return m_nModuleSize; } + inline const std::string& GetModuleName(void) const { return m_ModuleName; } + inline uintptr_t GetRVA(const uintptr_t nAddress) const { return (nAddress - GetModuleBase()); } + + IMAGE_NT_HEADERS64* m_pNTHeaders; + IMAGE_DOS_HEADER* m_pDOSHeader; + + ModuleSections_t m_ExecutableCode; + ModuleSections_t m_ExceptionTable; + ModuleSections_t m_RunTimeData; + ModuleSections_t m_ReadOnlyData; + +private: + CMemory FindPatternSIMD(const uint8_t* pPattern, const char* szMask, + const ModuleSections_t* moduleSection = nullptr, const size_t nOccurrence = 0) const; + + std::string m_ModuleName; + uintptr_t m_pModuleBase; + DWORD m_nModuleSize; + std::vector m_ModuleSections; +}; diff --git a/primedev/thirdparty/silver-bun/utils.cpp b/primedev/thirdparty/silver-bun/utils.cpp new file mode 100644 index 00000000..6f7b397e --- /dev/null +++ b/primedev/thirdparty/silver-bun/utils.cpp @@ -0,0 +1,127 @@ +#include "utils.h" + +namespace Utils +{ + //---------------------------------------------------------------------------------------- + // Purpose: For converting a string pattern to an array of bytes. Doesnt support wildcards + //---------------------------------------------------------------------------------------- + std::vector StringPatternToBytes(const char* szInput) + { + const char* pszPatternStart = const_cast(szInput); + const char* pszPatternEnd = pszPatternStart + strlen(szInput); + std::vector vBytes; + + for (const char* pszCurrentByte = pszPatternStart; pszCurrentByte < pszPatternEnd; ++pszCurrentByte) + { + vBytes.push_back(strtoul(pszCurrentByte, const_cast(&pszCurrentByte), 16)); + } + return vBytes; + }; + + //---------------------------------------------------------------------------------------- + // Purpose: For converting a string pattern with wildcards to an array of bytes. + //---------------------------------------------------------------------------------------- + std::vector PatternToBytes(const char* szInput) + { + const char* pszPatternStart = const_cast(szInput); + const char* pszPatternEnd = pszPatternStart + strlen(szInput); + std::vector vBytes; + + for (const char* pszCurrentByte = pszPatternStart; pszCurrentByte < pszPatternEnd; ++pszCurrentByte) + { + if (*pszCurrentByte == '?') + { + ++pszCurrentByte; + if (*pszCurrentByte == '?') + { + ++pszCurrentByte; // Skip double wildcard. + } + vBytes.push_back(-1); // Push the byte back as invalid. + } + else + { + vBytes.push_back(strtoul(pszCurrentByte, const_cast(&pszCurrentByte), 16)); + } + } + return vBytes; + }; + + //---------------------------------------------------------------------------------------- + // Purpose: For converting a string pattern with wildcards to an array of bytes and mask. + //---------------------------------------------------------------------------------------- + std::pair, std::string> PatternToMaskedBytes(const char* szInput) + { + const char* pszPatternStart = const_cast(szInput); + const char* pszPatternEnd = pszPatternStart + strlen(szInput); + + std::vector vBytes; + std::string svMask; + + for (const char* pszCurrentByte = pszPatternStart; pszCurrentByte < pszPatternEnd; ++pszCurrentByte) + { + if (*pszCurrentByte == '?') + { + ++pszCurrentByte; + if (*pszCurrentByte == '?') + { + ++pszCurrentByte; // Skip double wildcard. + } + vBytes.push_back(0); // Push the byte back as invalid. + svMask += '?'; + } + else + { + vBytes.push_back(uint8_t(strtoul(pszCurrentByte, const_cast(&pszCurrentByte), 16))); + svMask += 'x'; + } + } + return make_pair(vBytes, svMask); + }; + + //---------------------------------------------------------------------------------------- + // Purpose: For converting a string to an array of bytes. + //---------------------------------------------------------------------------------------- + std::vector StringToBytes(const char* szInput, bool bNullTerminator) + { + const char* pszStringStart = const_cast(szInput); + const char* pszStringEnd = pszStringStart + strlen(szInput); + std::vector vBytes; + + for (const char* pszCurrentByte = pszStringStart; pszCurrentByte < pszStringEnd; ++pszCurrentByte) + { + // Dereference character and push back the byte. + vBytes.push_back(*pszCurrentByte); + } + + if (bNullTerminator) + { + vBytes.push_back('\0'); + } + return vBytes; + }; + + //---------------------------------------------------------------------------------------- + // Purpose: For converting a string to an array of masked bytes. + //---------------------------------------------------------------------------------------- + std::pair, std::string> StringToMaskedBytes(const char* szInput, bool bNullTerminator) + { + const char* pszStringStart = const_cast(szInput); + const char* pszStringEnd = pszStringStart + strlen(szInput); + std::vector vBytes; + std::string svMask; + + for (const char* pszCurrentByte = pszStringStart; pszCurrentByte < pszStringEnd; ++pszCurrentByte) + { + // Dereference character and push back the byte. + vBytes.push_back(*pszCurrentByte); + svMask += 'x'; + } + + if (bNullTerminator) + { + vBytes.push_back(0x0); + svMask += 'x'; + } + return make_pair(vBytes, svMask); + }; +} diff --git a/primedev/thirdparty/silver-bun/utils.h b/primedev/thirdparty/silver-bun/utils.h new file mode 100644 index 00000000..15e43a92 --- /dev/null +++ b/primedev/thirdparty/silver-bun/utils.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace Utils +{ + std::vector StringPatternToBytes(const char* szInput); + std::vector PatternToBytes(const char* szInput); + std::pair, std::string> PatternToMaskedBytes(const char* szInput); + std::vector StringToBytes(const char* szInput, bool bNullTerminator); + std::pair, std::string> StringToMaskedBytes(const char* szInput, bool bNullTerminator); +} + +typedef const unsigned char* rsig_t; -- cgit v1.2.3