[[nodiscard]] std::future<std::vector<void*>> Memory::AoBScan(const std::string& pattern, int64_t min, int64_t max)
{
std::pair<std::vector<BYTE>, std::vector<BYTE>> aobPair = Memory::ParsePattern(pattern);
std::vector<BYTE> patternKey(aobPair.first.size(), 0x00);
return Memory::AoBScan({ aobPair.first, patternKey }, aobPair.second, { std::make_pair(min, max) });
}
[[nodiscard]] std::future<std::vector<void*>> Memory::AoBScan(std::pair<std::vector<BYTE>, std::vector<BYTE>> pattern, const std::vector<BYTE>& mask, std::vector<std::pair<int64_t, int64_t>> mins_maxs)
{
return std::async(std::launch::async, [this, pattern, mask, mins_maxs]() {
std::vector<void*> results;
std::unique_ptr<std::vector<BYTE>> decryptedPattern = std::make_unique<std::vector<BYTE>>(Memory::XorDecrypt(pattern.first, pattern.second));
std::vector<MemoryRegion> memoryRegions;
for (auto [min, max] : mins_maxs)
{
if (min < this->GetMinAddress())
min = GetMinAddress();
if (max < min || max > this->GetMaxAddress())
max = this->GetMaxAddress();
for (MemoryRegion region : GetMemoryRegions(this->GetCurrentPHandle(), min, max))
memoryRegions.push_back(region);
}
std::mutex resultsMutex;
Memory::_busy = true;
std::vector<std::thread> threads;
for (const auto& region : memoryRegions) {
threads.emplace_back([this, ®ion, &decryptedPattern, &mask, &results, &resultsMutex, mins_maxs]() {
std::vector<uint8_t> buffer(region.RegionSize);
SIZE_T bytesRead;
if (LI_FN(ReadProcessMemory).get()(this->GetCurrentPHandle(), region.BaseAddress, reinterpret_cast<LPVOID>(buffer.data()), region.RegionSize, &bytesRead)) {
auto matches = ScanRegion(buffer, *decryptedPattern, mask);
std::lock_guard<std::mutex> lock(resultsMutex);
for (int offset : matches)
{
void* matchAddress = static_cast<uint8_t*>(region.BaseAddress) + offset;
if (matchAddress == decryptedPattern.get())
continue;
for (auto [min, max] : mins_maxs)
{
if (min < this->GetMinAddress())
min = this->GetMinAddress();
if (max < min || max > this->GetMaxAddress())
max = this->GetMaxAddress();
if (reinterpret_cast<int64_t>(matchAddress) > max ||
reinterpret_cast<int64_t>(matchAddress) < min ||
std::find(results.begin(), results.end(), matchAddress) != results.end())
continue;
results.push_back(matchAddress);
}
}
}
});
}
for (auto& thread : threads)
thread.join();
Memory::_busy = false;
decryptedPattern.reset(nullptr);
return results;
});
}
std::vector<MemoryRegion> Memory::GetMemoryRegions(HANDLE handle, int64_t min, int64_t max)
{
std::vector<MemoryRegion> regions;
MEMORY_BASIC_INFORMATION memInfo;
void* address = reinterpret_cast<void*>(min);
while (reinterpret_cast<int64_t>(address) < max)
{
if (VirtualQueryEx(this->_phandle, address, &memInfo, sizeof(memInfo)) == 0) break;
if (memInfo.State == MEM_COMMIT && !(memInfo.Protect & PAGE_GUARD) && !(memInfo.Protect & PAGE_NOACCESS) && (memInfo.Protect & PAGE_READWRITE))
regions.emplace_back(memInfo.BaseAddress, static_cast<int>(memInfo.RegionSize));
address = static_cast<uint8_t*>(memInfo.BaseAddress) + memInfo.RegionSize;
}
return regions;
}
std::vector<int> Memory::ScanRegion(const std::vector<uint8_t>& memory, const std::vector<uint8_t>& pattern, const std::vector<uint8_t>& mask)
{
std::vector<int> matches;
int patternLength = pattern.size();
if (patternLength <= 32 && this->_avx2_support)
ScanRegionMemoryAvx2(memory, pattern, mask, matches);
else if (patternLength <= 16 && this->_sse2_support)
ScanRegionMemorySse2(memory, pattern, mask, matches);
else
ScanRegionMemoryFallback(memory, pattern, mask, matches);
return matches;
}
void Memory::ScanRegionMemoryAvx2(const std::vector<uint8_t>& memory, const std::vector<uint8_t>& pattern, const std::vector<uint8_t>& mask, std::vector<int>& matches)
{
int memoryLength = memory.size();
int patternLength = pattern.size();
__m256i vPattern = _mm256_set1_epi8(pattern[0]);
__m256i vMask = _mm256_set1_epi8(mask[0]);
for (int i = 0; i <= memoryLength - 32; i += 32) {
__m256i vMemory = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(&memory[i]));
__m256i vResult = _mm256_and_si256(vMemory, vMask);
__m256i vCmp = _mm256_cmpeq_epi8(vResult, vPattern);
uint32_t matchMask = _mm256_movemask_epi8(vCmp);
while (matchMask != 0) {
unsigned long index;
_BitScanForward(&index, matchMask);
if (CheckMatch(memory, i + index, pattern, mask)) {
matches.push_back(i + index);
}
matchMask &= (matchMask - 1);
}
}
for (int i = memoryLength - 32; i <= memoryLength - patternLength; i++) {
if (CheckMatch(memory, i, pattern, mask)) {
matches.push_back(i);
}
}
}
void Memory::ScanRegionMemorySse2(const std::vector<uint8_t>& memory, const std::vector<uint8_t>& pattern, const std::vector<uint8_t>& mask, std::vector<int>& matches)
{
int memoryLength = memory.size();
int patternLength = pattern.size();
__m128i vPattern = _mm_set1_epi8(pattern[0]);
__m128i vMask = _mm_set1_epi8(mask[0]);
for (int i = 0; i <= memoryLength - 16; i += 16) {
__m128i vMemory = _mm_loadu_si128(reinterpret_cast<const __m128i*>(&memory[i]));
__m128i vResult = _mm_and_si128(vMemory, vMask);
__m128i vCmp = _mm_cmpeq_epi8(vResult, vPattern);
uint16_t matchMask = _mm_movemask_epi8(vCmp);
while (matchMask != 0) {
unsigned long index;
_BitScanForward(&index, matchMask);
if (CheckMatch(memory, i + index, pattern, mask)) {
matches.push_back(i + index);
}
matchMask &= (matchMask - 1);
}
}
for (int i = memoryLength - 16; i <= memoryLength - patternLength; i++) {
if (CheckMatch(memory, i, pattern, mask)) {
matches.push_back(i);
}
}
}
void Memory::ScanRegionMemoryFallback(const std::vector<uint8_t>& memory, const std::vector<uint8_t>& pattern, const std::vector<uint8_t>& mask, std::vector<int>& matches)
{
int memoryLength = memory.size();
int patternLength = pattern.size();
for (int i = 0; i <= memoryLength - patternLength; i++) {
if (CheckMatch(memory, i, pattern, mask)) {
matches.push_back(i);
}
}
}
bool Memory::CheckMatch(const std::vector<uint8_t>& memory, int offset, const std::vector<uint8_t>& pattern, const std::vector<uint8_t>& mask)
{
if (offset + pattern.size() > memory.size()) return false;
for (size_t i = 0; i < pattern.size(); i++)
if ((memory[offset + i] & mask[i]) != (pattern[i] & mask[i])) return false;
return true;
}