You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

280 lines
9.0 KiB
C++

12 months ago
#include <cmath>
#include <emmintrin.h>
#include "module.h"
#if defined _WIN32 && _M_X64
#include <windows.h>
#elif defined __linux__ && __x86_64__
#include <link.h>
#include <unistd.h>
#include <string.h>
#else
#error "Unsupported platform"
#endif
///////////////////////////////////////////////////////////////////////////////
// For converting a string pattern with wildcards to an array of bytes and mask.
std::pair<std::vector<uint8_t>, std::string> PatternToMaskedBytes(const std::string_view svInput)
{
char* pszPatternStart = const_cast<char*>(svInput.data());
char* pszPatternEnd = pszPatternStart + svInput.size();
std::vector<uint8_t> vBytes;
std::string svMask;
for (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(static_cast<uint8_t>(strtoul(pszCurrentByte, &pszCurrentByte, 16)));
svMask += 'x';
}
}
return make_pair(vBytes, svMask);
};
//-----------------------------------------------------------------------------
// Purpose: constructor
// Input : svModuleName
//-----------------------------------------------------------------------------
CModule::CModule(const std::string_view svModuleName) : m_svModuleName(svModuleName)
{
#if defined _WIN32 && _M_X64
m_pModuleBase = reinterpret_cast<uintptr_t>(GetModuleHandleA(svModuleName.data()));
#else
struct dl_data
{
ElfW(Addr) addr;
const char* moduleName;
} dldata{0, svModuleName.data()};
dl_iterate_phdr([](dl_phdr_info* info, size_t /* size */, void* data)
{
dl_data* dldata = reinterpret_cast<dl_data*>(data);
if (strstr(info->dlpi_name, dldata->moduleName) != nullptr)
{
dldata->addr = info->dlpi_addr;
}
return 0;
}, &dldata);
m_pModuleBase = reinterpret_cast<uintptr_t>(dldata.addr);
#endif
Init();
LoadSections();
}
//-----------------------------------------------------------------------------
// Purpose: constructor
// Input : addr
//-----------------------------------------------------------------------------
CModule::CModule(const CMemory addr)
{
#if defined _WIN32 && _M_X64
MEMORY_BASIC_INFORMATION mbi;
if (!VirtualQuery(addr, &mbi, sizeof(mbi)) || mbi.AllocationBase == nullptr)
{
return;
}
m_pModuleBase = reinterpret_cast<uintptr_t>(mbi.AllocationBase);
char szPath[MAX_PATH];
size_t nLen = GetModuleFileNameA(reinterpret_cast<HMODULE>(mbi.AllocationBase), szPath, sizeof(szPath));
m_svModuleName.assign(szPath, nLen);
#else
Dl_info info;
if (!dladdr(addr, &info) || !info.dli_fbase || !info.dli_fname)
{
return;
}
m_pModuleBase = reinterpret_cast<uintptr_t>(info.dli_fbase);
m_svModuleName.assign(info.dli_fname);
#endif
m_svModuleName.assign(m_svModuleName.substr(m_svModuleName.find_last_of("/\\") + 1)); // Leave only file name.
Init();
LoadSections();
}
//-----------------------------------------------------------------------------
// Purpose: initializes module descriptors
//-----------------------------------------------------------------------------
void CModule::Init()
{
#if defined _WIN32 && _M_X64
IMAGE_DOS_HEADER* pDOSHeader = reinterpret_cast<IMAGE_DOS_HEADER*>(m_pModuleBase);
IMAGE_NT_HEADERS64* pNTHeaders = reinterpret_cast<IMAGE_NT_HEADERS64*>(m_pModuleBase + pDOSHeader->e_lfanew);
const IMAGE_SECTION_HEADER* hSection = IMAGE_FIRST_SECTION(pNTHeaders); // Get first image section.
for (WORD i = 0; i < pNTHeaders->FileHeader.NumberOfSections; i++) // Loop through the sections.
{
const IMAGE_SECTION_HEADER& hCurrentSection = hSection[i]; // Get current section.
m_vModuleSections.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.
}
#else
Elf64_Ehdr* pEhdr = reinterpret_cast<Elf64_Ehdr*>(m_pModuleBase);
Elf64_Phdr* pPhdr = reinterpret_cast<Elf64_Phdr*>(m_pModuleBase + pEhdr->e_phoff);
for (Elf64_Half i = 0; i < pEhdr->e_phnum; i++) // Loop through the sections.
{
Elf64_Phdr& phdr = pPhdr[i];
if (phdr.p_type == PT_LOAD && phdr.p_flags == (PF_X | PF_R))
{
/* From glibc, elf/dl-load.c:
* c->mapend = ((ph->p_vaddr + ph->p_filesz + GLRO(dl_pagesize) - 1)
* & ~(GLRO(dl_pagesize) - 1));
*
* In glibc, the segment file size is aligned up to the nearest page size and
* added to the virtual address of the segment. We just want the size here.
*/
size_t pagesize = sysconf(_SC_PAGESIZE);
size_t nSectionSize = (phdr.p_filesz + pagesize - 1) & ~(pagesize - 1);
// We can't get get the section names, but most likely it will be exactly .text.
m_vModuleSections.push_back(ModuleSections_t(".text", m_pModuleBase + phdr.p_vaddr, nSectionSize));
break;
}
}
#endif
}
//-----------------------------------------------------------------------------
// Purpose: initializes the default executable segments
//-----------------------------------------------------------------------------
void CModule::LoadSections()
{
m_ExecutableCode = GetSectionByName(".text");
}
//-----------------------------------------------------------------------------
// Purpose: find array of bytes in process memory using SIMD instructions
// Input : *szPattern -
// *szMask -
// Output : CMemory
//-----------------------------------------------------------------------------
CMemory CModule::FindPatternSIMD(const uint8_t* szPattern, const char* szMask, const ModuleSections_t* moduleSection) const
{
const ModuleSections_t* section = moduleSection ? moduleSection : &m_ExecutableCode;
if (!section->IsSectionValid())
{
return CMemory();
}
const uintptr_t nBase = section->m_pSectionBase;
const size_t nSize = section->m_nSectionSize;
const size_t nMaskLen = strlen(szMask);
const uint8_t* pData = reinterpret_cast<uint8_t*>(nBase);
const uint8_t* pEnd = pData + nSize - nMaskLen;
int nMasks[64]; // 64*16 = enough masks for 1024 bytes.
const uint8_t iNumMasks = static_cast<uint8_t>(std::ceil(static_cast<float>(nMaskLen) / 16.f));
memset(nMasks, 0, iNumMasks * sizeof(int));
for (uint8_t i = 0; i < iNumMasks; i++)
{
for (int8_t j = std::min<int8_t>(nMaskLen - static_cast<size_t>(i) * 16, 16) - 1; j >= 0; --j)
{
if (szMask[i * 16 + j] == 'x')
{
nMasks[i] |= 1 << j;
}
}
}
const __m128i xmm1 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(szPattern));
__m128i xmm2, xmm3, msks;
for (; pData != pEnd; _mm_prefetch(reinterpret_cast<const char*>(++pData + 64), _MM_HINT_NTA))
{
if (szPattern[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])
{
bool bFound = true;
for (uint8_t i = 1; i < iNumMasks; i++)
{
xmm2 = _mm_loadu_si128(reinterpret_cast<const __m128i*>((pData + i * 16)));
xmm3 = _mm_loadu_si128(reinterpret_cast<const __m128i*>((szPattern + i * 16)));
msks = _mm_cmpeq_epi8(xmm2, xmm3);
if ((_mm_movemask_epi8(msks) & nMasks[i]) != nMasks[i])
{
bFound = false;
break;
}
}
if (bFound)
{
return static_cast<CMemory>((&*(const_cast<uint8_t*>(pData))));
}
}
}
}
return CMemory();
}
//-----------------------------------------------------------------------------
// Purpose: find a string pattern in process memory using SIMD instructions
// Input : &svPattern
// &moduleSection
// Output : CMemory
//-----------------------------------------------------------------------------
CMemory CModule::FindPatternSIMD(const std::string_view svPattern, const ModuleSections_t* moduleSection) const
{
const std::pair patternInfo = PatternToMaskedBytes(svPattern);
return FindPatternSIMD(patternInfo.first.data(), patternInfo.second.c_str(), moduleSection);
}
//-----------------------------------------------------------------------------
// Purpose: get the module section by name (example: '.rdata', '.text')
// Input : *svModuleName -
// Output : ModuleSections_t
//-----------------------------------------------------------------------------
const CModule::ModuleSections_t CModule::GetSectionByName(const std::string_view svSectionName) const
{
for (const ModuleSections_t& section : m_vModuleSections)
{
if (section.m_svSectionName == svSectionName)
return section;
}
return ModuleSections_t();
}
//-----------------------------------------------------------------------------
// Purpose: returns the module base
//-----------------------------------------------------------------------------
uintptr_t CModule::GetModuleBase(void) const
{
return m_pModuleBase;
}
//-----------------------------------------------------------------------------
// Purpose: returns the module name
//-----------------------------------------------------------------------------
std::string_view CModule::GetModuleName(void) const
{
return m_svModuleName;
}