-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Persist rule matches over sub regions
- Loading branch information
Dorian Eikenberg
committed
Jan 2, 2024
1 parent
b386ceb
commit 42ef320
Showing
16 changed files
with
391 additions
and
153 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} |
Oops, something went wrong.