#include #include #include "module.h" #if defined _WIN32 && _M_X64 #include #elif defined __linux__ && __x86_64__ #include #include #include #else #error "Unsupported platform" #endif /////////////////////////////////////////////////////////////////////////////// // For converting a string pattern with wildcards to an array of bytes and mask. std::pair, std::string> PatternToMaskedBytes(const std::string_view svInput) { char* pszPatternStart = const_cast(svInput.data()); char* pszPatternEnd = pszPatternStart + svInput.size(); std::vector 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(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(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(data); if (strstr(info->dlpi_name, dldata->moduleName) != nullptr) { dldata->addr = info->dlpi_addr; } return 0; }, &dldata); m_pModuleBase = reinterpret_cast(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(mbi.AllocationBase); char szPath[MAX_PATH]; size_t nLen = GetModuleFileNameA(reinterpret_cast(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(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(m_pModuleBase); IMAGE_NT_HEADERS64* pNTHeaders = reinterpret_cast(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(hCurrentSection.Name), static_cast(m_pModuleBase + hCurrentSection.VirtualAddress), hCurrentSection.SizeOfRawData)); // Push back a struct with the section data. } #else Elf64_Ehdr* pEhdr = reinterpret_cast(m_pModuleBase); Elf64_Phdr* pPhdr = reinterpret_cast(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(nBase); const uint8_t* pEnd = pData + nSize - nMaskLen; int nMasks[64]; // 64*16 = enough masks for 1024 bytes. const uint8_t iNumMasks = static_cast(std::ceil(static_cast(nMaskLen) / 16.f)); memset(nMasks, 0, iNumMasks * sizeof(int)); for (uint8_t i = 0; i < iNumMasks; i++) { for (int8_t j = std::min(nMaskLen - static_cast(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(szPattern)); __m128i xmm2, xmm3, msks; for (; pData != pEnd; _mm_prefetch(reinterpret_cast(++pData + 64), _MM_HINT_NTA)) { if (szPattern[0] == pData[0]) { xmm2 = _mm_loadu_si128(reinterpret_cast(pData)); msks = _mm_cmpeq_epi8(xmm1, xmm2); if ((_mm_movemask_epi8(msks) & nMasks[0]) == nMasks[0]) { bool bFound = true; for (uint8_t i = 1; i < iNumMasks; i++) { xmm2 = _mm_loadu_si128(reinterpret_cast((pData + i * 16))); xmm3 = _mm_loadu_si128(reinterpret_cast((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((&*(const_cast(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; }