aboutsummaryrefslogtreecommitdiff
path: root/primedev/thirdparty/silver-bun
diff options
context:
space:
mode:
authorJack <66967891+ASpoonPlaysGames@users.noreply.github.com>2024-03-04 00:01:32 +0000
committerGitHub <noreply@github.com>2024-03-04 01:01:32 +0100
commit4b0726d97788edff5d83476cb52057f409d623af (patch)
treedad38132c6353af708a71af8e795aa34a19b4b3a /primedev/thirdparty/silver-bun
parente1eb2a6f4b858e903603226098861c3b62d5d1a4 (diff)
downloadNorthstarLauncher-4b0726d97788edff5d83476cb52057f409d623af.tar.gz
NorthstarLauncher-4b0726d97788edff5d83476cb52057f409d623af.zip
Update silver-bun to `72c74b4` (#664)
Bumps the vendored silver-bun library to the newest commit in upstream Co-authored-by: F1F7Y <filip.bartos07@proton.me> Co-authored-by: IcePixelx <41352111+IcePixelx@users.noreply.github.com>
Diffstat (limited to 'primedev/thirdparty/silver-bun')
-rw-r--r--primedev/thirdparty/silver-bun/CMakeLists.txt9
-rw-r--r--primedev/thirdparty/silver-bun/memaddr.cpp368
-rw-r--r--primedev/thirdparty/silver-bun/memaddr.h154
-rw-r--r--primedev/thirdparty/silver-bun/module.cpp474
-rw-r--r--primedev/thirdparty/silver-bun/module.h76
-rw-r--r--primedev/thirdparty/silver-bun/utils.cpp127
-rw-r--r--primedev/thirdparty/silver-bun/utils.h18
7 files changed, 1226 insertions, 0 deletions
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<uint8_t>& vOpcodeArray) const
+{
+ uintptr_t ref = ptr;
+
+ // Loop forward in the ptr class member.
+ for (auto [byteAtCurrentAddress, i] = std::tuple<uint8_t, size_t>{ uint8_t(), (size_t)0 }; i < vOpcodeArray.size(); i++, ref++)
+ {
+ byteAtCurrentAddress = *reinterpret_cast<uint8_t*>(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<LPCVOID>(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<uint8_t> 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<uint8_t> 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<uint8_t> 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<uint8_t>& vOpcodeArray) const
+{
+ DWORD oldProt = NULL;
+
+ SIZE_T dwSize = vOpcodeArray.size();
+ VirtualProtect(reinterpret_cast<void*>(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<uint8_t*>(ptr + i) = vOpcodeArray[i]; // Write opcodes to Address.
+ }
+
+ dwSize = vOpcodeArray.size();
+ VirtualProtect(reinterpret_cast<void*>(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<void*>(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<uint8_t*>(ptr + i) = szString[i]; // Write string to Address.
+ }
+
+ VirtualProtect(reinterpret_cast<void*>(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<uint8_t*>(ptr); // Get the base of the module.
+
+ const std::vector<int> PatternBytes = Utils::PatternToBytes(szPattern); // Convert our pattern to a byte array.
+ const std::pair<size_t, const int*> bytesInfo = std::make_pair<size_t, const int*>(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<const CHAR*>(static_cast<int64_t>(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<uint8_t*>(ptr); // Get the base of the module.
+
+ const std::vector<int> PatternBytes = Utils::PatternToBytes(szPattern); // Convert our pattern to a byte array.
+ const std::pair<size_t, const int*> bytesInfo = std::make_pair<size_t, const int*>(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<const CHAR*>(static_cast<int64_t>(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<int32_t*>(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<int32_t*>(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<CMemory>
+//-----------------------------------------------------------------------------
+std::vector<CMemory> CMemory::FindAllCallReferences(const uintptr_t sectionBase, const size_t sectionSize)
+{
+ std::vector <CMemory> referencesInfo = {};
+
+ uint8_t* pTextStart = reinterpret_cast<uint8_t*>(sectionBase);
+ for (size_t i = 0ull; i < sectionSize - 0x5; i++, _mm_prefetch(reinterpret_cast<const char*>(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<uintptr_t*>(virtualMethod);
+
+ // Set page for current virtual method to execute n read n write.
+ VirtualProtect(reinterpret_cast<void*>(virtualMethod), sizeof(virtualMethod), PAGE_EXECUTE_READWRITE, &oldProt);
+
+ // Set virtual method to our hook.
+ *reinterpret_cast<uintptr_t*>(virtualMethod) = reinterpret_cast<uintptr_t>(pHookMethod);
+
+ // Restore original page.
+ VirtualProtect(reinterpret_cast<void*>(virtualMethod), sizeof(virtualMethod), oldProt, &oldProt);
+
+ // Move original function into argument.
+ *ppOriginalMethod = reinterpret_cast<void*>(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<uintptr_t*>(pImportedMethod);
+
+ // Set page for current iat entry to execute n read n write.
+ VirtualProtect(reinterpret_cast<void*>(pImportedMethod), sizeof(void*), PAGE_EXECUTE_READWRITE, &oldProt);
+
+ // Set method to our hook.
+ *reinterpret_cast<uintptr_t*>(pImportedMethod) = reinterpret_cast<uintptr_t>(pHookMethod);
+
+ // Restore original page.
+ VirtualProtect(reinterpret_cast<void*>(pImportedMethod), sizeof(void*), oldProt, &oldProt);
+
+ // Move original function into argument.
+ *ppOriginalMethod = reinterpret_cast<void*>(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 <iostream>
+#include <string>
+#include <vector>
+#include <windows.h>
+#include <psapi.h>
+
+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<void*>(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<class T> inline T GetValue(void) const
+ {
+ return *reinterpret_cast<T*>(ptr);
+ }
+
+ template<class T> inline T GetVirtualFunctionIndex(void) const
+ {
+ return *reinterpret_cast<T*>(ptr) / 8;
+ }
+
+ template<typename T> inline T CCast(void) const
+ {
+ return (T)ptr;
+ }
+
+ template<typename T> inline T RCast(void) const
+ {
+ return reinterpret_cast<T>(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<uintptr_t*>(reference);
+ }
+
+ return CMemory(reference);
+ }
+
+ inline CMemory DerefSelf(int deref = 1)
+ {
+ while (deref--)
+ {
+ if (ptr)
+ ptr = *reinterpret_cast<uintptr_t*>(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<uint8_t>& 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<uint8_t>& 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<CMemory> 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<uintptr_t>(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<uintptr_t>(GetModuleHandleA(szModuleName));
+ m_ModuleName = szModuleName;
+
+ Init();
+ LoadSections();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: initializes module descriptors
+//-----------------------------------------------------------------------------
+void CModule::Init()
+{
+ m_pDOSHeader = reinterpret_cast<IMAGE_DOS_HEADER*>(m_pModuleBase);
+ m_pNTHeaders = reinterpret_cast<decltype(m_pNTHeaders)>(m_pModuleBase + m_pDOSHeader->e_lfanew);
+ m_nModuleSize = static_cast<size_t>(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<const char*>(hCurrentSection.Name),
+ static_cast<uintptr_t>(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<uint8_t*>(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<int>(ceil(static_cast<float>(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<LONG*>(&nMasks[i]), static_cast<LONG>(j));
+ }
+ }
+ }
+ const __m128i xmm1 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(pPattern));
+ __m128i xmm2, xmm3, msks;
+ for (; pData != pEnd; _mm_prefetch(reinterpret_cast<const char*>(++pData + 64), _MM_HINT_NTA))
+ {
+ if (pPattern[0] == pData[0])
+ {
+ xmm2 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(pData));
+ msks = _mm_cmpeq_epi8(xmm1, xmm2);
+ if ((_mm_movemask_epi8(msks) & nMasks[0]) == nMasks[0])
+ {
+ for (uintptr_t i = 1; i < static_cast<uintptr_t>(iNumMasks); ++i)
+ {
+ xmm2 = _mm_loadu_si128(reinterpret_cast<const __m128i*>((pData + i * 16)));
+ xmm3 = _mm_loadu_si128(reinterpret_cast<const __m128i*>((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<CMemory>(const_cast<uint8_t*>(pData));
+ }
+ nOccurrenceCount++;
+ }
+ }
+ else
+ {
+ goto cont;
+ }
+ }
+ if (nOccurrenceCount == nOccurrence)
+ {
+ return static_cast<CMemory>((&*(const_cast<uint8_t*>(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::vector<uint8_t>, 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<uint8_t*>(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<uintptr_t>(&pTextStart[i])).OffsetSelf(0x2); // Skip next 2 opcodes, those being the instruction and the register.
+ const int32_t relativeAddress = skipOpCode.GetValue<int32_t>(); // 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<int> vBytes = Utils::StringToBytes(szString, bNullTerminator); // Convert our string to a byte array.
+ const std::pair<size_t, const int*> bytesInfo = std::make_pair<size_t, const int*>(vBytes.size(), vBytes.data()); // Get the size and data of our bytes.
+
+ const uint8_t* pBase = reinterpret_cast<uint8_t*>(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<uintptr_t*>(currAddr) == 0 && checkDataSection(reinterpret_cast<void*>(currAddr), nSize))
+ {
+ bool bIsGoodPage = true;
+ uint32_t nPageCount = 0;
+
+ for (; nPageCount < nSize && bIsGoodPage; nPageCount += sizeof(uintptr_t))
+ {
+ const uintptr_t pageData = *reinterpret_cast<std::uintptr_t*>(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<rsig_t>(&rttiTDRva), "xxxx", &moduleSection, nRefIndex);
+ if (!reference)
+ break;
+
+ CMemory referenceOffset = reference.Offset(-0xC);
+ if (referenceOffset.GetValue<int32_t>() != 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<rsig_t>(&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<IMAGE_IMPORT_DESCRIPTOR*>(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<char*>(reinterpret_cast<DWORD*>(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<IMAGE_THUNK_DATA*>(m_pModuleBase + pIID->OriginalFirstThunk);
+
+ // To get actual function address.
+ IMAGE_THUNK_DATA* pFirstThunk = reinterpret_cast<IMAGE_THUNK_DATA*>(m_pModuleBase + pIID->FirstThunk);
+ for (; pOgFirstThunk->u1.AddressOfData; ++pOgFirstThunk, ++pFirstThunk)
+ {
+ // Get image import by name.
+ const IMAGE_IMPORT_BY_NAME* pImageImportByName = reinterpret_cast<IMAGE_IMPORT_BY_NAME*>(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<uintptr_t*>(&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<IMAGE_EXPORT_DIRECTORY*>(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<DWORD*>(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<DWORD*>(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<DWORD*>(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<char*>(reinterpret_cast<DWORD*>(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<WORD*>(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 <iostream>
+#include <string>
+#include <vector>
+#include <windows.h>
+#include <psapi.h>
+#include <intrin.h>
+#include <algorithm>
+
+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<CModule::ModuleSections_t>& 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<ModuleSections_t> 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<uint8_t> StringPatternToBytes(const char* szInput)
+ {
+ const char* pszPatternStart = const_cast<char*>(szInput);
+ const char* pszPatternEnd = pszPatternStart + strlen(szInput);
+ std::vector<uint8_t> vBytes;
+
+ for (const char* pszCurrentByte = pszPatternStart; pszCurrentByte < pszPatternEnd; ++pszCurrentByte)
+ {
+ vBytes.push_back(strtoul(pszCurrentByte, const_cast<char**>(&pszCurrentByte), 16));
+ }
+ return vBytes;
+ };
+
+ //----------------------------------------------------------------------------------------
+ // Purpose: For converting a string pattern with wildcards to an array of bytes.
+ //----------------------------------------------------------------------------------------
+ std::vector<int> PatternToBytes(const char* szInput)
+ {
+ const char* pszPatternStart = const_cast<char*>(szInput);
+ const char* pszPatternEnd = pszPatternStart + strlen(szInput);
+ std::vector<int> 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<char**>(&pszCurrentByte), 16));
+ }
+ }
+ return vBytes;
+ };
+
+ //----------------------------------------------------------------------------------------
+ // Purpose: For converting a string pattern with wildcards to an array of bytes and mask.
+ //----------------------------------------------------------------------------------------
+ std::pair<std::vector<uint8_t>, std::string> PatternToMaskedBytes(const char* szInput)
+ {
+ const char* pszPatternStart = const_cast<char*>(szInput);
+ const char* pszPatternEnd = pszPatternStart + strlen(szInput);
+
+ std::vector<uint8_t> 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<char**>(&pszCurrentByte), 16)));
+ svMask += 'x';
+ }
+ }
+ return make_pair(vBytes, svMask);
+ };
+
+ //----------------------------------------------------------------------------------------
+ // Purpose: For converting a string to an array of bytes.
+ //----------------------------------------------------------------------------------------
+ std::vector<int> StringToBytes(const char* szInput, bool bNullTerminator)
+ {
+ const char* pszStringStart = const_cast<char*>(szInput);
+ const char* pszStringEnd = pszStringStart + strlen(szInput);
+ std::vector<int> 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::vector<uint8_t>, std::string> StringToMaskedBytes(const char* szInput, bool bNullTerminator)
+ {
+ const char* pszStringStart = const_cast<char*>(szInput);
+ const char* pszStringEnd = pszStringStart + strlen(szInput);
+ std::vector<uint8_t> 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 <iostream>
+#include <string>
+#include <vector>
+#include <windows.h>
+#include <psapi.h>
+
+namespace Utils
+{
+ std::vector<uint8_t> StringPatternToBytes(const char* szInput);
+ std::vector<int> PatternToBytes(const char* szInput);
+ std::pair<std::vector<uint8_t>, std::string> PatternToMaskedBytes(const char* szInput);
+ std::vector<int> StringToBytes(const char* szInput, bool bNullTerminator);
+ std::pair<std::vector<uint8_t>, std::string> StringToMaskedBytes(const char* szInput, bool bNullTerminator);
+}
+
+typedef const unsigned char* rsig_t;