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>
This commit is contained in:
parent
e1eb2a6f4b
commit
4b0726d977
|
@ -3,6 +3,7 @@
|
||||||
find_package(minhook REQUIRED)
|
find_package(minhook REQUIRED)
|
||||||
find_package(libcurl REQUIRED)
|
find_package(libcurl REQUIRED)
|
||||||
find_package(minizip REQUIRED)
|
find_package(minizip REQUIRED)
|
||||||
|
find_package(silver-bun REQUIRED)
|
||||||
|
|
||||||
add_library(
|
add_library(
|
||||||
NorthstarDLL SHARED
|
NorthstarDLL SHARED
|
||||||
|
@ -50,8 +51,6 @@ add_library(
|
||||||
"core/macros.h"
|
"core/macros.h"
|
||||||
"core/memalloc.cpp"
|
"core/memalloc.cpp"
|
||||||
"core/memalloc.h"
|
"core/memalloc.h"
|
||||||
"core/memory.cpp"
|
|
||||||
"core/memory.h"
|
|
||||||
"core/sourceinterface.cpp"
|
"core/sourceinterface.cpp"
|
||||||
"core/sourceinterface.h"
|
"core/sourceinterface.h"
|
||||||
"core/tier0.cpp"
|
"core/tier0.cpp"
|
||||||
|
@ -172,6 +171,7 @@ target_link_libraries(
|
||||||
PRIVATE minhook
|
PRIVATE minhook
|
||||||
libcurl
|
libcurl
|
||||||
minizip
|
minizip
|
||||||
|
silver-bun
|
||||||
WS2_32.lib
|
WS2_32.lib
|
||||||
Crypt32.lib
|
Crypt32.lib
|
||||||
Cryptui.lib
|
Cryptui.lib
|
||||||
|
|
|
@ -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()
|
|
@ -1,5 +1,4 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "memory.h"
|
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
|
@ -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<uintptr_t>(pAddress)) {}
|
|
||||||
|
|
||||||
// operators
|
|
||||||
CMemoryAddress::operator uintptr_t() const
|
|
||||||
{
|
|
||||||
return m_nAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
CMemoryAddress::operator void*() const
|
|
||||||
{
|
|
||||||
return reinterpret_cast<void*>(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<uintptr_t*>(ret);
|
|
||||||
|
|
||||||
return CMemoryAddress(ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
// patching
|
|
||||||
void CMemoryAddress::Patch(const uint8_t* pBytes, const size_t nSize)
|
|
||||||
{
|
|
||||||
if (nSize)
|
|
||||||
WriteProcessMemory(GetCurrentProcess(), reinterpret_cast<LPVOID>(m_nAddress), pBytes, nSize, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CMemoryAddress::Patch(const std::initializer_list<uint8_t> 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<uint8_t> HexBytesToString(const char* pHexString)
|
|
||||||
{
|
|
||||||
std::vector<uint8_t> 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<uint8_t> 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<LPCVOID>(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<size_t>(mInfo.SizeOfImage);
|
|
||||||
m_pModuleBase = reinterpret_cast<uintptr_t>(mInfo.lpBaseOfDll);
|
|
||||||
m_nAddress = m_pModuleBase;
|
|
||||||
|
|
||||||
if (!m_nModuleSize || !m_pModuleBase)
|
|
||||||
return;
|
|
||||||
|
|
||||||
m_pDOSHeader = reinterpret_cast<IMAGE_DOS_HEADER*>(m_pModuleBase);
|
|
||||||
m_pNTHeaders = reinterpret_cast<IMAGE_NT_HEADERS64*>(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<const char*>(hCurrentSection.Name)),
|
|
||||||
static_cast<uintptr_t>(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<uintptr_t>(GetProcAddress(reinterpret_cast<HMODULE>(m_nAddress), pExportName)));
|
|
||||||
}
|
|
||||||
|
|
||||||
CMemoryAddress CModule::FindPattern(const uint8_t* pPattern, const char* pMask)
|
|
||||||
{
|
|
||||||
if (!m_ExecutableCode.IsSectionValid())
|
|
||||||
return CMemoryAddress();
|
|
||||||
|
|
||||||
uint64_t nBase = static_cast<uint64_t>(m_ExecutableCode.m_pSectionBase);
|
|
||||||
uint64_t nSize = static_cast<uint64_t>(m_ExecutableCode.m_nSectionSize);
|
|
||||||
|
|
||||||
const uint8_t* pData = reinterpret_cast<uint8_t*>(nBase);
|
|
||||||
const uint8_t* pEnd = pData + static_cast<uint32_t>(nSize) - strlen(pMask);
|
|
||||||
|
|
||||||
int nMasks[64]; // 64*16 = enough masks for 1024 bytes.
|
|
||||||
int iNumMasks = static_cast<int>(ceil(static_cast<float>(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<LONG*>(&nMasks[i]), j);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
__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)
|
|
||||||
{
|
|
||||||
return CMemoryAddress(const_cast<uint8_t*>(pData));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
goto CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
return CMemoryAddress((&*(const_cast<uint8_t*>(pData))));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CONTINUE:;
|
|
||||||
}
|
|
||||||
|
|
||||||
return CMemoryAddress();
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::pair<std::vector<uint8_t>, std::string> MaskedBytesFromPattern(const char* pPatternString)
|
|
||||||
{
|
|
||||||
std::vector<uint8_t> 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());
|
|
||||||
}
|
|
|
@ -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 <typename T> T RCast()
|
|
||||||
{
|
|
||||||
return reinterpret_cast<T>(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<uint8_t> 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<ModuleSections_t> 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);
|
|
||||||
};
|
|
|
@ -24,7 +24,7 @@ ON_DLL_LOAD("tier0.dll", Tier0GameFuncs, (CModule module))
|
||||||
TryCreateGlobalMemAlloc();
|
TryCreateGlobalMemAlloc();
|
||||||
|
|
||||||
// setup tier0 funcs
|
// setup tier0 funcs
|
||||||
CommandLine = module.GetExport("CommandLine").RCast<CommandLineType>();
|
CommandLine = module.GetExportedFunction("CommandLine").RCast<CommandLineType>();
|
||||||
Plat_FloatTime = module.GetExport("Plat_FloatTime").RCast<Plat_FloatTimeType>();
|
Plat_FloatTime = module.GetExportedFunction("Plat_FloatTime").RCast<Plat_FloatTimeType>();
|
||||||
ThreadInServerFrameThread = module.GetExport("ThreadInServerFrameThread").RCast<ThreadInServerFrameThreadType>();
|
ThreadInServerFrameThread = module.GetExportedFunction("ThreadInServerFrameThread").RCast<ThreadInServerFrameThreadType>();
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
// Note: this file is tier1/interface.cpp in primedev, but given that tier0 is yet to be split
|
// 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
|
// 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());
|
HMODULE hModule = GetModuleHandleA(svModuleName.c_str());
|
||||||
|
|
||||||
|
|
|
@ -9,4 +9,4 @@
|
||||||
|
|
||||||
typedef void* (*CreateInterfaceFn)(const char* pName, int* pReturnCode);
|
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);
|
||||||
|
|
|
@ -135,7 +135,7 @@ ON_DLL_LOAD_DEDI_RELIESON("engine.dll", DedicatedServer, ServerPresence, (CModul
|
||||||
{
|
{
|
||||||
// CModAppSystemGroup::Create
|
// CModAppSystemGroup::Create
|
||||||
// force the engine into dedicated mode by changing the first comparison to IsServerOnly to an assignment
|
// 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
|
// cmp => mov
|
||||||
base.Offset(1).Patch("C6 87");
|
base.Offset(1).Patch("C6 87");
|
||||||
|
@ -262,7 +262,7 @@ ON_DLL_LOAD_DEDI("tier0.dll", DedicatedServerOrigin, (CModule module))
|
||||||
// disable origin on dedicated
|
// 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
|
// 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
|
// 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
|
// clang-format off
|
||||||
|
|
|
@ -37,7 +37,8 @@ typedef void (*callable_v)(void* v);
|
||||||
#include "logging/logging.h"
|
#include "logging/logging.h"
|
||||||
#include "MinHook.h"
|
#include "MinHook.h"
|
||||||
#include "curl/curl.h"
|
#include "curl/curl.h"
|
||||||
|
#include "silver-bun/module.h"
|
||||||
|
#include "silver-bun/memaddr.h"
|
||||||
#include "core/hooks.h"
|
#include "core/hooks.h"
|
||||||
#include "core/memory.h"
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -15,7 +15,7 @@ size_t __fastcall ShouldAllowAlltalk()
|
||||||
ON_DLL_LOAD_RELIESON("engine.dll", ServerAllTalk, ConVar, (CModule module))
|
ON_DLL_LOAD_RELIESON("engine.dll", ServerAllTalk, ConVar, (CModule module))
|
||||||
{
|
{
|
||||||
// replace strcmp function called in CClient::ProcessVoiceData with our own code that calls ShouldAllowAllTalk
|
// 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
|
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
|
// (uint8_t*)&ShouldAllowAlltalk doesn't work for some reason? need to make it a uint64 first
|
||||||
|
|
|
@ -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;
|
auto entry = msg->m_ConVars + i;
|
||||||
|
|
||||||
// Safety check for memory access
|
// Safety check for memory access
|
||||||
if (CMemoryAddress(entry).IsMemoryReadable(sizeof(*entry)))
|
if (CMemory(entry).IsMemoryReadable(sizeof(*entry)))
|
||||||
{
|
{
|
||||||
// Find null terminators
|
// Find null terminators
|
||||||
bool nameValid = false, valValid = false;
|
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
|
// 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.Patch("C7 05");
|
||||||
addr.Offset(2).Patch((BYTE*)&writeAddress, sizeof(writeAddress));
|
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
|
// Prevent these from actually doing anything
|
||||||
for (auto exportName : ANTITAMPER_EXPORTS)
|
for (auto exportName : ANTITAMPER_EXPORTS)
|
||||||
{
|
{
|
||||||
CMemoryAddress exportAddr = module.GetExport(exportName);
|
CMemory exportAddr = module.GetExportedFunction(exportName);
|
||||||
if (exportAddr)
|
if (exportAddr)
|
||||||
{
|
{
|
||||||
// Just return, none of them have any args or are userpurge
|
// Just return, none of them have any args or are userpurge
|
||||||
|
|
|
@ -67,7 +67,7 @@ bool __fastcall CheckUTF8Valid(INT64* a1, DWORD* a2, char* strData)
|
||||||
{
|
{
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
if (!CMemoryAddress(v4).IsMemoryReadable(1))
|
if (!CMemory(v4).IsMemoryReadable(1))
|
||||||
return false; // INVALID
|
return false; // INVALID
|
||||||
|
|
||||||
v11 = *v4++; // crash potential
|
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
|
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
|
// 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
|
// only call if we're parsing utf8 data from the network (i.e. communities), otherwise we get perf issues
|
||||||
void* pReturnAddress =
|
void* pReturnAddress =
|
||||||
|
@ -195,5 +195,5 @@ ON_DLL_LOAD("engine.dll", EngineExploitFixes_UTF8Parser, (CModule module))
|
||||||
{
|
{
|
||||||
AUTOHOOK_DISPATCH()
|
AUTOHOOK_DISPATCH()
|
||||||
|
|
||||||
sub_F1320 = module.FindPattern("83 F9 7F 77 08 88 0A").RCast<INT64(__fastcall*)(DWORD, char*)>();
|
sub_F1320 = module.FindPatternSIMD("83 F9 7F 77 08 88 0A").RCast<INT64(__fastcall*)(DWORD, char*)>();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1289,9 +1289,9 @@ KeyValues* KeyValues::MakeCopy(void) const
|
||||||
|
|
||||||
ON_DLL_LOAD("vstdlib.dll", KeyValues, (CModule module))
|
ON_DLL_LOAD("vstdlib.dll", KeyValues, (CModule module))
|
||||||
{
|
{
|
||||||
V_UTF8ToUnicode = module.GetExport("V_UTF8ToUnicode").RCast<int (*)(const char*, wchar_t*, int)>();
|
V_UTF8ToUnicode = module.GetExportedFunction("V_UTF8ToUnicode").RCast<int (*)(const char*, wchar_t*, int)>();
|
||||||
V_UnicodeToUTF8 = module.GetExport("V_UnicodeToUTF8").RCast<int (*)(const wchar_t*, char*, int)>();
|
V_UnicodeToUTF8 = module.GetExportedFunction("V_UnicodeToUTF8").RCast<int (*)(const wchar_t*, char*, int)>();
|
||||||
KeyValuesSystem = module.GetExport("KeyValuesSystem").RCast<CKeyValuesSystem* (*)()>();
|
KeyValuesSystem = module.GetExportedFunction("KeyValuesSystem").RCast<CKeyValuesSystem* (*)()>();
|
||||||
}
|
}
|
||||||
|
|
||||||
AUTOHOOK_INIT()
|
AUTOHOOK_INIT()
|
||||||
|
|
|
@ -60,7 +60,7 @@ int GetMaxPlayers()
|
||||||
return 32;
|
return 32;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T> void ChangeOffset(CMemoryAddress addr, unsigned int offset)
|
template <class T> void ChangeOffset(CMemory addr, unsigned int offset)
|
||||||
{
|
{
|
||||||
addr.Patch((BYTE*)&offset, sizeof(T));
|
addr.Patch((BYTE*)&offset, sizeof(T));
|
||||||
}
|
}
|
||||||
|
@ -296,7 +296,7 @@ ON_DLL_LOAD("server.dll", MaxPlayersOverride_Server, (CModule module))
|
||||||
AUTOHOOK_DISPATCH_MODULE(server.dll)
|
AUTOHOOK_DISPATCH_MODULE(server.dll)
|
||||||
|
|
||||||
// get required data
|
// get required data
|
||||||
serverBase = (HMODULE)module.m_nAddress;
|
serverBase = (HMODULE)module.GetModuleBase();
|
||||||
RandomIntZeroMax = (decltype(RandomIntZeroMax))(GetProcAddress(GetModuleHandleA("vstdlib.dll"), "RandomIntZeroMax"));
|
RandomIntZeroMax = (decltype(RandomIntZeroMax))(GetProcAddress(GetModuleHandleA("vstdlib.dll"), "RandomIntZeroMax"));
|
||||||
|
|
||||||
// patch max players amount
|
// patch max players amount
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
add_library(
|
||||||
|
silver-bun STATIC
|
||||||
|
"memaddr.cpp"
|
||||||
|
"memaddr.h"
|
||||||
|
"module.cpp"
|
||||||
|
"module.h"
|
||||||
|
"utils.cpp"
|
||||||
|
"utils.h"
|
||||||
|
)
|
|
@ -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);
|
||||||
|
}
|
|
@ -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;
|
||||||
|
};
|
|
@ -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();
|
||||||
|
}
|
|
@ -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;
|
||||||
|
};
|
|
@ -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);
|
||||||
|
};
|
||||||
|
}
|
|
@ -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;
|
Loading…
Reference in New Issue