Skip to content

Commit

Permalink
Persist rule matches over sub regions
Browse files Browse the repository at this point in the history
  • Loading branch information
Dorian Eikenberg committed Jan 2, 2024
1 parent b386ceb commit 42ef320
Show file tree
Hide file tree
Showing 16 changed files with 391 additions and 153 deletions.
7 changes: 6 additions & 1 deletion plugins/inmemoryscanner/src/lib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ add_library(inmemoryscanner-obj OBJECT
InMemory.cpp
OutputXML.cpp
Scanner.cpp
Yara.cpp)
YaraInterface.cpp)
target_compile_features(inmemoryscanner-obj PUBLIC cxx_std_20)
set_target_properties(inmemoryscanner-obj PROPERTIES POSITION_INDEPENDENT_CODE TRUE)
target_include_directories(inmemoryscanner-obj INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>)
Expand All @@ -16,6 +16,11 @@ include(FindPkgConfig)

pkg_check_modules(YARA REQUIRED yara>=4)
target_link_libraries(inmemoryscanner-obj PUBLIC ${YARA_LINK_LIBRARIES})

if (${YARA_VERSION} VERSION_GREATER_EQUAL 4.1)
target_compile_definitions(inmemoryscanner-obj PRIVATE LIBYARA_4_1)
endif ()

pkg_check_modules(TCLAP REQUIRED tclap>=1.2)

include(FetchContent)
Expand Down
4 changes: 4 additions & 0 deletions plugins/inmemoryscanner/src/lib/Common.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,17 @@ namespace InMemoryScanner
{
std::string matchName;
int64_t position;

bool operator==(const Match& rhs) const = default;
};

struct Rule
{
std::string ruleName;
std::string ruleNamespace;
std::vector<Match> matches;

bool operator==(const Rule& rhs) const = default;
};

inline std::size_t bytesToNumberOfPages(std::size_t size)
Expand Down
31 changes: 31 additions & 0 deletions plugins/inmemoryscanner/src/lib/IYaraInterface.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#ifndef INMEMORYSCANNER_IYARAINTERFACE_H
#define INMEMORYSCANNER_IYARAINTERFACE_H

#include "Common.h"
#include <exception>
#include <string>
#include <vector>
#include <vmicore/vmi/IMemoryMapping.h>

namespace InMemoryScanner
{
class YaraException : public std::runtime_error
{
public:
explicit YaraException(const std::string& Message) : std::runtime_error(Message.c_str()){};
};

class IYaraInterface
{
public:
virtual ~IYaraInterface() = default;

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

protected:
IYaraInterface() = default;
};
}

#endif // INMEMORYSCANNER_IYARAINTERFACE_H
4 changes: 2 additions & 2 deletions plugins/inmemoryscanner/src/lib/InMemory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
#include "Config.h"
#include "Dumping.h"
#include "Filenames.h"
#include "Yara.h"
#include "YaraInterface.h"
#include <memory>
#include <string>
#include <tclap/CmdLine.h>
Expand Down Expand Up @@ -44,7 +44,7 @@ namespace InMemoryScanner
{
configuration->overrideDumpMemoryFlag(dumpMemoryArgument.getValue());
}
auto yara = std::make_unique<Yara>(configuration->getSignatureFile());
auto yara = std::make_unique<YaraInterface>(configuration->getSignatureFile());
auto dumping = std::make_unique<Dumping>(pluginInterface, configuration);
scanner = std::make_unique<Scanner>(pluginInterface, configuration, std::move(yara), std::move(dumping));
}
Expand Down
6 changes: 3 additions & 3 deletions plugins/inmemoryscanner/src/lib/Scanner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ namespace InMemoryScanner
{
Scanner::Scanner(PluginInterface* pluginInterface,
std::shared_ptr<IConfig> configuration,
std::unique_ptr<YaraInterface> yaraEngine,
std::unique_ptr<IYaraInterface> yaraInterface,
std::unique_ptr<IDumping> dumping)
: pluginInterface(pluginInterface),
configuration(std::move(configuration)),
yaraEngine(std::move(yaraEngine)),
yaraInterface(std::move(yaraInterface)),
dumping(std::move(dumping)),
logger(pluginInterface->newNamedLogger(INMEMORY_LOGGER_NAME)),
inMemResultsLogger(pluginInterface->newNamedLogger(INMEMORY_LOGGER_NAME))
Expand Down Expand Up @@ -131,7 +131,7 @@ namespace InMemoryScanner
// The semaphore protects the yara rules from being accessed more than YR_MAX_THREADS (32 atm.) times in
// parallel.
semaphore.wait();
auto results = yaraEngine->scanMemory(*mappedRegions);
auto results = yaraInterface->scanMemory(*mappedRegions);
semaphore.notify();

logger->debug("End scanMemory");
Expand Down
6 changes: 3 additions & 3 deletions plugins/inmemoryscanner/src/lib/Scanner.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

#include "Config.h"
#include "Dumping.h"
#include "IYaraInterface.h"
#include "OutputXML.h"
#include "Semaphore.h"
#include "YaraInterface.h"
#include <condition_variable>
#include <memory>
#include <vmicore/plugins/PluginInterface.h>
Expand All @@ -17,7 +17,7 @@ namespace InMemoryScanner
public:
Scanner(VmiCore::Plugin::PluginInterface* pluginInterface,
std::shared_ptr<IConfig> configuration,
std::unique_ptr<YaraInterface> yaraEngine,
std::unique_ptr<IYaraInterface> yaraInterface,
std::unique_ptr<IDumping> dumping);

[[nodiscard]] static std::unique_ptr<std::string> getFilenameFromPath(const std::string& path);
Expand All @@ -31,7 +31,7 @@ namespace InMemoryScanner
private:
VmiCore::Plugin::PluginInterface* pluginInterface;
std::shared_ptr<IConfig> configuration;
std::unique_ptr<YaraInterface> yaraEngine;
std::unique_ptr<IYaraInterface> yaraInterface;
OutputXML outputXml{};
std::unique_ptr<IDumping> dumping;
std::unique_ptr<VmiCore::ILogger> logger;
Expand Down
94 changes: 0 additions & 94 deletions plugins/inmemoryscanner/src/lib/Yara.cpp

This file was deleted.

25 changes: 0 additions & 25 deletions plugins/inmemoryscanner/src/lib/Yara.h

This file was deleted.

134 changes: 134 additions & 0 deletions plugins/inmemoryscanner/src/lib/YaraInterface.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
#include "YaraInterface.h"
#include <fmt/core.h>

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

namespace InMemoryScanner
{
YaraInterface::YaraInterface(const std::string& rulesFile)
{
auto err = yr_initialize();
if (err != ERROR_SUCCESS)
{
throw YaraException("Cannot initialize Yara. Error code: " + std::to_string(err));
}

err = yr_rules_load(rulesFile.c_str(), &rules);
if (err != ERROR_SUCCESS)
{
throw YaraException("Cannot load rules. Error code: " + std::to_string(err));
}
}

YaraInterface::~YaraInterface()
{
yr_rules_destroy(rules);
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)
{
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<YR_MEMORY_BLOCK> blocks;
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);
}
auto blockIterators = std::make_pair(blocks.begin(), blocks.end());
#ifdef LIBYARA_4_1
YR_MEMORY_BLOCK_ITERATOR iterator{.context = &blockIterators,
.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};
#endif

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

return results;
}

int YaraInterface::yaraCallback(YR_SCAN_CONTEXT* context, int message, void* message_data, void* user_data)
{
int ret = 0;
switch (message)
{
case CALLBACK_MSG_RULE_MATCHING:
ret = handleRuleMatch(
context, static_cast<YR_RULE*>(message_data), static_cast<std::vector<Rule>*>(user_data));
break;
case CALLBACK_MSG_RULE_NOT_MATCHING:
[[fallthrough]];
case CALLBACK_MSG_SCAN_FINISHED:
ret = CALLBACK_CONTINUE;
break;
default:
ret = CALLBACK_ERROR;
break;
}

return ret;
}

int YaraInterface::handleRuleMatch(YR_SCAN_CONTEXT* context, YR_RULE* rule, std::vector<Rule>* results)
{
YR_STRING* string = nullptr;
YR_MATCH* match = nullptr;

Rule tmpRule;
tmpRule.ruleName = rule->identifier;
tmpRule.ruleNamespace = rule->ns->name;

yr_rule_strings_foreach(rule, string)
{
yr_string_matches_foreach(context, string, match) // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
{
Match tmpMatch;
tmpMatch.matchName = string->identifier;
tmpMatch.position = match->base + match->offset;

tmpRule.matches.push_back(tmpMatch);
}
}

results->push_back(tmpRule);

return CALLBACK_CONTINUE;
}
}
Loading

0 comments on commit 42ef320

Please sign in to comment.