Skip to content

Commit

Permalink
WIP: vmi_mmap_guest_2
Browse files Browse the repository at this point in the history
  • Loading branch information
Dorian Eikenberg committed Jan 8, 2024
1 parent 313c231 commit 8c83145
Show file tree
Hide file tree
Showing 21 changed files with 203 additions and 217 deletions.
7 changes: 4 additions & 3 deletions plugins/inmemoryscanner/src/lib/IYaraInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
#define INMEMORYSCANNER_IYARAINTERFACE_H

#include "Common.h"
#include <exception>
#include <span>
#include <stdexcept>
#include <string>
#include <vector>
#include <vmicore/vmi/IMemoryMapping.h>
Expand All @@ -20,8 +21,8 @@ namespace InMemoryScanner
public:
virtual ~IYaraInterface() = default;

virtual std::unique_ptr<std::vector<Rule>>
scanMemory(const std::vector<VmiCore::MappedRegion>& mappedRegions) = 0;
virtual std::vector<Rule> scanMemory(VmiCore::addr_t regionBase,
std::span<const VmiCore::MappedRegion> mappedRegions) = 0;

protected:
IYaraInterface() = default;
Expand Down
30 changes: 16 additions & 14 deletions plugins/inmemoryscanner/src/lib/Scanner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ namespace InMemoryScanner
return verdict;
}

std::vector<uint8_t> Scanner::constructPaddedMemoryRegion(const std::vector<MappedRegion>& regions)
std::vector<uint8_t> Scanner::constructPaddedMemoryRegion(std::span<const MappedRegion> regions)
{
std::vector<uint8_t> result;

Expand All @@ -74,22 +74,24 @@ namespace InMemoryScanner
std::size_t regionSize = 0;
for (const auto& region : regions)
{
regionSize += region.mapping.size();
regionSize += region.asSpan().size();
regionSize += pageSizeInBytes;
}
// last region should not have succeeding padding page
regionSize -= pageSizeInBytes;

result.reserve(regionSize);
// copy first region
std::copy(regions.front().mapping.begin(), regions.front().mapping.end(), std::back_inserter(result));
auto frontRegionSpan = regions.front().asSpan();
std::copy(frontRegionSpan.begin(), frontRegionSpan.end(), std::back_inserter(result));

for (std::size_t i = 1; i < regions.size(); i++)
{
const auto& region = regions[i];
// padding page
result.insert(result.end(), pageSizeInBytes, 0);
std::copy(region.mapping.begin(), region.mapping.end(), std::back_inserter(result));
auto regionSpan = region.asSpan();
std::copy(regionSpan.begin(), regionSpan.end(), std::back_inserter(result));
}

return result;
Expand All @@ -109,41 +111,41 @@ namespace InMemoryScanner
{
auto memoryMapping = pluginInterface->mapProcessMemoryRegion(
memoryRegionDescriptor.base, dtb, bytesToNumberOfPages(memoryRegionDescriptor.size));
auto mappedRegions = memoryMapping->getMappedRegions().lock();
auto mappedRegions = memoryMapping->getMappedRegions();

if (mappedRegions->empty())
if (mappedRegions.empty())
{
logger->debug("Extracted memory region has size 0, skipping");
}
else
{
if (configuration->isDumpingMemoryActivated())
{
logger->debug("Start dumpVadRegionToFile", {{"Size", memoryMapping->getSizeInGuest()}});
logger->debug("Start dumpVadRegionToFile", {{"Size", memoryRegionDescriptor.size}});

auto paddedRegion = constructPaddedMemoryRegion(*mappedRegions);
auto paddedRegion = constructPaddedMemoryRegion(mappedRegions);

dumping->dumpMemoryRegion(processName, pid, memoryRegionDescriptor, paddedRegion);
}

logger->debug("Start scanMemory", {{"Size", memoryMapping->getSizeInGuest()}});
logger->debug("Start scanMemory", {{"Size", memoryRegionDescriptor.size}});

// The semaphore protects the yara rules from being accessed more than YR_MAX_THREADS (32 atm.) times in
// parallel.
semaphore.wait();
auto results = yaraInterface->scanMemory(*mappedRegions);
auto results = yaraInterface->scanMemory(memoryRegionDescriptor.base, mappedRegions);
semaphore.notify();

logger->debug("End scanMemory");

if (!results->empty())
if (!results.empty())
{
for (const auto& result : *results)
for (const auto& result : results)
{
pluginInterface->sendInMemDetectionEvent(result.ruleName);
}
outputXml.addResult(processName, pid, memoryRegionDescriptor.base, *results);
logInMemoryResultToTextFile(processName, pid, memoryRegionDescriptor.base, *results);
outputXml.addResult(processName, pid, memoryRegionDescriptor.base, results);
logInMemoryResultToTextFile(processName, pid, memoryRegionDescriptor.base, results);
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion plugins/inmemoryscanner/src/lib/Scanner.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "Semaphore.h"
#include <condition_variable>
#include <memory>
#include <span>
#include <vmicore/plugins/PluginInterface.h>
#include <yara/limits.h> // NOLINT(modernize-deprecated-headers)

Expand Down Expand Up @@ -41,7 +42,7 @@ namespace InMemoryScanner

[[nodiscard]] bool shouldRegionBeScanned(const VmiCore::MemoryRegion& memoryRegionDescriptor);

static std::vector<uint8_t> constructPaddedMemoryRegion(const std::vector<VmiCore::MappedRegion>& regions);
static std::vector<uint8_t> constructPaddedMemoryRegion(std::span<const VmiCore::MappedRegion> regions);

void scanMemoryRegion(pid_t pid,
uint64_t dtb,
Expand Down
87 changes: 49 additions & 38 deletions plugins/inmemoryscanner/src/lib/YaraInterface.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,44 @@
#include "YaraInterface.h"
#include <bit>
#include <fmt/core.h>

using VmiCore::addr_t;
using VmiCore::MappedRegion;
using BlockIteratorPair = std::pair<std::vector<YR_MEMORY_BLOCK>::iterator, std::vector<YR_MEMORY_BLOCK>::iterator>;
using VmiCore::PagingDefinitions::pageSizeInBytes;

namespace
{
struct YaraIteratorContext
{
std::vector<YR_MEMORY_BLOCK> blocks;
std::size_t index;
};

YR_MEMORY_BLOCK* get_next_block(YR_MEMORY_BLOCK_ITERATOR* iterator)
{
auto* iteratorContext = static_cast<YaraIteratorContext*>(iterator->context);

if (++iteratorContext->index < iteratorContext->blocks.size())
{
return &iteratorContext->blocks[iteratorContext->index];
}

return nullptr;
}

YR_MEMORY_BLOCK* get_first_block(YR_MEMORY_BLOCK_ITERATOR* iterator)
{
auto* iteratorContext = static_cast<YaraIteratorContext*>(iterator->context);
iteratorContext->index = 0;

return &iteratorContext->blocks[iteratorContext->index];
}

const uint8_t* fetch_block_data(YR_MEMORY_BLOCK* block)
{
return static_cast<const uint8_t*>(block->context);
}
}

namespace InMemoryScanner
{
Expand All @@ -27,58 +63,33 @@ namespace InMemoryScanner
yr_finalize();
}

YR_MEMORY_BLOCK* get_next_block(YR_MEMORY_BLOCK_ITERATOR* iterator)
{
auto* blockVectorIterators = reinterpret_cast<BlockIteratorPair*>(iterator->context);
blockVectorIterators->first++;

if (blockVectorIterators->first == blockVectorIterators->second)
{
return nullptr;
}

return &*blockVectorIterators->first;
}

YR_MEMORY_BLOCK* get_first_block(YR_MEMORY_BLOCK_ITERATOR* iterator)
{
return &*reinterpret_cast<BlockIteratorPair*>(iterator->context)->first;
}

const uint8_t* fetch_block_data(YR_MEMORY_BLOCK* block)
std::vector<Rule> YaraInterface::scanMemory(addr_t regionBase, std::span<const MappedRegion> mappedRegions)
{
return reinterpret_cast<const uint8_t*>(block->context);
}

std::unique_ptr<std::vector<Rule>> YaraInterface::scanMemory(const std::vector<MappedRegion>& mappedRegions)
{
auto results = std::make_unique<std::vector<Rule>>();
std::vector<Rule> results;

std::vector<YR_MEMORY_BLOCK> blocks;
blocks.reserve(mappedRegions.size());
YaraIteratorContext iteratorContext{};
iteratorContext.blocks.reserve(mappedRegions.size());
for (const auto& mappedRegion : mappedRegions)
{
blocks.emplace_back(mappedRegion.mapping.size(),
mappedRegion.guestBaseVA - mappedRegions.front().guestBaseVA,
reinterpret_cast<void*>(mappedRegion.mapping.data()),
&fetch_block_data);
iteratorContext.blocks.emplace_back(mappedRegion.num_pages * pageSizeInBytes,
mappedRegion.guestBaseVA - regionBase,
mappedRegion.mappingBase,
&fetch_block_data);
}
auto blockIterators = std::make_pair(blocks.begin(), blocks.end());
#ifdef LIBYARA_4_1
YR_MEMORY_BLOCK_ITERATOR iterator{.context = &blockIterators,
YR_MEMORY_BLOCK_ITERATOR iterator{.context = &iteratorContext,
.first = &get_first_block,
.next = &get_next_block,
.file_size = nullptr,
.last_error = ERROR_SUCCESS};
#else
YR_MEMORY_BLOCK_ITERATOR iterator{
.context = &blockIterators, .first = &get_first_block, .next = &get_next_block};
.context = &iteratorContext, .first = &get_first_block, .next = &get_next_block};
#endif

auto err = yr_rules_scan_mem_blocks(rules, &iterator, 0, yaraCallback, results.get(), 0);
if (err != ERROR_SUCCESS)
if (auto err = yr_rules_scan_mem_blocks(rules, &iterator, 0, yaraCallback, &results, 0); err != ERROR_SUCCESS)
{
throw YaraException("Error scanning memory. Error code: " + std::to_string(err));
throw YaraException(fmt::format("Error scanning memory. Error code: {}", err));
}

return results;
Expand Down
5 changes: 2 additions & 3 deletions plugins/inmemoryscanner/src/lib/YaraInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@

#include "Common.h"
#include "IYaraInterface.h"
#include <memory>
#include <string>
#include <vector>
#include <yara.h>

namespace InMemoryScanner
Expand All @@ -16,7 +14,8 @@ namespace InMemoryScanner

~YaraInterface() override;

std::unique_ptr<std::vector<Rule>> scanMemory(const std::vector<VmiCore::MappedRegion>& mappedRegions) override;
std::vector<Rule> scanMemory(VmiCore::addr_t regionBase,
std::span<const VmiCore::MappedRegion> mappedRegions) override;

private:
YR_RULES* rules = nullptr;
Expand Down
7 changes: 4 additions & 3 deletions plugins/inmemoryscanner/test/FakeYaraInterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@

namespace InMemoryScanner
{
std::unique_ptr<std::vector<Rule>>
FakeYaraInterface::scanMemory([[maybe_unused]] const std::vector<VmiCore::MappedRegion>& mappedRegions)
std::vector<Rule>
FakeYaraInterface::scanMemory([[maybe_unused]] VmiCore::addr_t regionBase,
[[maybe_unused]] std::span<const VmiCore::MappedRegion> mappedRegions)
{
concurrentThreads++;
if (concurrentThreads > YR_MAX_THREADS)
Expand All @@ -14,6 +15,6 @@ namespace InMemoryScanner
}
std::this_thread::sleep_for(std::chrono::seconds(1));
concurrentThreads--;
return std::make_unique<std::vector<Rule>>();
return {};
}
}
3 changes: 2 additions & 1 deletion plugins/inmemoryscanner/test/FakeYaraInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ namespace InMemoryScanner
class FakeYaraInterface : public IYaraInterface
{
public:
std::unique_ptr<std::vector<Rule>> scanMemory(const std::vector<VmiCore::MappedRegion>& mappedRegions) override;
std::vector<Rule> scanMemory(VmiCore::addr_t regionBase,
std::span<const VmiCore::MappedRegion> mappedRegions) override;

bool max_threads_exceeded = false;

Expand Down
35 changes: 15 additions & 20 deletions plugins/inmemoryscanner/test/Scanner_unittest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ using testing::Return;
using testing::Unused;
using VmiCore::ActiveProcessInformation;
using VmiCore::addr_t;
using VmiCore::MappedRegion;
using VmiCore::MemoryRegion;
using VmiCore::MockLogger;
using VmiCore::MockMemoryRegionExtractor;
Expand Down Expand Up @@ -52,12 +53,10 @@ namespace InMemoryScanner

std::filesystem::path inMemoryDumpsPath = "inMemDumps";
std::filesystem::path dumpedRegionsPath = inMemoryDumpsPath / "dumpedRegions";
VmiCore::addr_t startAddress = 0x1234000;
size_t size = 0x666;
addr_t startAddress = 0x1234000;
size_t size = pageSizeInBytes;
std::vector<uint8_t> testPageContent = std::vector<uint8_t>(size, 1);
std::shared_ptr<std::vector<VmiCore::MappedRegion>> regionMappings =
std::make_shared<std::vector<VmiCore::MappedRegion>>(1,
VmiCore::MappedRegion(startAddress, testPageContent));
std::vector<MappedRegion> regionMappings = std::vector(1, MappedRegion(startAddress, testPageContent));

std::unique_ptr<std::vector<std::shared_ptr<const ActiveProcessInformation>>> runningProcesses;

Expand Down Expand Up @@ -98,28 +97,25 @@ namespace InMemoryScanner

createMemoryMapping(testDtb, startAddress, bytesToNumberOfPages(size), regionMappings);
createMemoryMapping(dtbWithSharedBaseImageRegion, startAddress, bytesToNumberOfPages(size), regionMappings);
};
}

std::shared_ptr<const ActiveProcessInformation> getProcessInfoFromRunningProcesses(pid_t pid)
{
return *std::find_if(runningProcesses->cbegin(),
runningProcesses->cend(),
[pid = pid](const std::shared_ptr<const ActiveProcessInformation>& a)
{ return a->pid == pid; });
};
}

void createMemoryMapping(addr_t dtb,
VmiCore::addr_t baseVA,
std::size_t numberOfPages,
std::shared_ptr<std::vector<VmiCore::MappedRegion>> mappedRegions)
void
createMemoryMapping(addr_t dtb, addr_t baseVA, std::size_t numberOfPages, std::span<MappedRegion> mappedRegions)
{
ON_CALL(*pluginInterface, mapProcessMemoryRegion(baseVA, dtb, numberOfPages))
.WillByDefault(
[mappedRegions = std::move(mappedRegions)]()
[mappedRegions = mappedRegions]()
{
auto mapping = std::make_unique<VmiCore::MockMemoryMapping>();
ON_CALL(*mapping, getMappedRegions())
.WillByDefault([mappedRegions = mappedRegions]() { return mappedRegions; });
ON_CALL(*mapping, getMappedRegions()).WillByDefault(Return(mappedRegions));
return mapping;
});
}
Expand All @@ -138,7 +134,7 @@ namespace InMemoryScanner
auto dumping = std::make_unique<NiceMock<MockDumping>>();
dumpingRawPointer = dumping.get();
auto yara = std::make_unique<NiceMock<MockYaraInterface>>();
ON_CALL(*yara, scanMemory(_)).WillByDefault([]() { return std::make_unique<std::vector<Rule>>(); });
ON_CALL(*yara, scanMemory(_, _)).WillByDefault(Return(std::vector<Rule>{}));
scanner.emplace(pluginInterface.get(), configuration, std::move(yara), std::move(dumping));
};
};
Expand Down Expand Up @@ -177,7 +173,7 @@ namespace InMemoryScanner
});
auto dumping = std::make_unique<Dumping>(pluginInterface.get(), configuration);
auto yara = std::make_unique<NiceMock<MockYaraInterface>>();
ON_CALL(*yara, scanMemory(_)).WillByDefault([]() { return std::make_unique<std::vector<Rule>>(); });
ON_CALL(*yara, scanMemory(_, _)).WillByDefault(Return(std::vector<Rule>{}));
scanner.emplace(pluginInterface.get(), configuration, std::move(yara), std::move(dumping));
};

Expand Down Expand Up @@ -225,8 +221,7 @@ namespace InMemoryScanner
[regionMappings = regionMappings]()
{
auto mapping = std::make_unique<VmiCore::MockMemoryMapping>();
EXPECT_CALL(*mapping, getMappedRegions())
.WillOnce([regionMappings = regionMappings]() { return regionMappings; });
EXPECT_CALL(*mapping, getMappedRegions()).WillOnce(Return(regionMappings));
return mapping;
});
EXPECT_NO_THROW(scanner->scanProcess(process));
Expand Down Expand Up @@ -439,8 +434,8 @@ namespace InMemoryScanner
return memoryRegions;
});
auto twoPageRegionContent = std::vector<uint8_t>(2 * pageSizeInBytes, 0xCA);
auto complexMappings = std::make_shared<std::vector<VmiCore::MappedRegion>>(std::vector<VmiCore::MappedRegion>{
{startAddress, testPageContent}, {startAddress + 3 * pageSizeInBytes, twoPageRegionContent}});
std::vector<MappedRegion> complexMappings{{startAddress, testPageContent},
{startAddress + 3 * pageSizeInBytes, twoPageRegionContent}};
createMemoryMapping(dtb, startAddress, bytesToNumberOfPages(complexRegionSize), complexMappings);
auto paddingPage = std::vector<uint8_t>(pageSizeInBytes, 0);
auto expectedPaddedRegion = constructPaddedRegion({testPageContent, paddingPage, twoPageRegionContent});
Expand Down
Loading

0 comments on commit 8c83145

Please sign in to comment.