Skip to content

Commit 313c231

Browse files
author
Dorian Eikenberg
committed
Persist rule matches over sub regions
1 parent 825af98 commit 313c231

16 files changed

+391
-153
lines changed

plugins/inmemoryscanner/src/lib/CMakeLists.txt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ add_library(inmemoryscanner-obj OBJECT
77
InMemory.cpp
88
OutputXML.cpp
99
Scanner.cpp
10-
Yara.cpp)
10+
YaraInterface.cpp)
1111
target_compile_features(inmemoryscanner-obj PUBLIC cxx_std_20)
1212
set_target_properties(inmemoryscanner-obj PROPERTIES POSITION_INDEPENDENT_CODE TRUE)
1313
target_include_directories(inmemoryscanner-obj INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>)
@@ -16,6 +16,11 @@ include(FindPkgConfig)
1616

1717
pkg_check_modules(YARA REQUIRED yara>=4)
1818
target_link_libraries(inmemoryscanner-obj PUBLIC ${YARA_LINK_LIBRARIES})
19+
20+
if (${YARA_VERSION} VERSION_GREATER_EQUAL 4.1)
21+
target_compile_definitions(inmemoryscanner-obj PRIVATE LIBYARA_4_1)
22+
endif ()
23+
1924
pkg_check_modules(TCLAP REQUIRED tclap>=1.2)
2025

2126
include(FetchContent)

plugins/inmemoryscanner/src/lib/Common.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,17 @@ namespace InMemoryScanner
1515
{
1616
std::string matchName;
1717
int64_t position;
18+
19+
bool operator==(const Match& rhs) const = default;
1820
};
1921

2022
struct Rule
2123
{
2224
std::string ruleName;
2325
std::string ruleNamespace;
2426
std::vector<Match> matches;
27+
28+
bool operator==(const Rule& rhs) const = default;
2529
};
2630

2731
inline std::size_t bytesToNumberOfPages(std::size_t size)
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#ifndef INMEMORYSCANNER_IYARAINTERFACE_H
2+
#define INMEMORYSCANNER_IYARAINTERFACE_H
3+
4+
#include "Common.h"
5+
#include <exception>
6+
#include <string>
7+
#include <vector>
8+
#include <vmicore/vmi/IMemoryMapping.h>
9+
10+
namespace InMemoryScanner
11+
{
12+
class YaraException : public std::runtime_error
13+
{
14+
public:
15+
explicit YaraException(const std::string& Message) : std::runtime_error(Message.c_str()){};
16+
};
17+
18+
class IYaraInterface
19+
{
20+
public:
21+
virtual ~IYaraInterface() = default;
22+
23+
virtual std::unique_ptr<std::vector<Rule>>
24+
scanMemory(const std::vector<VmiCore::MappedRegion>& mappedRegions) = 0;
25+
26+
protected:
27+
IYaraInterface() = default;
28+
};
29+
}
30+
31+
#endif // INMEMORYSCANNER_IYARAINTERFACE_H

plugins/inmemoryscanner/src/lib/InMemory.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
#include "Config.h"
1212
#include "Dumping.h"
1313
#include "Filenames.h"
14-
#include "Yara.h"
14+
#include "YaraInterface.h"
1515
#include <memory>
1616
#include <string>
1717
#include <tclap/CmdLine.h>
@@ -44,7 +44,7 @@ namespace InMemoryScanner
4444
{
4545
configuration->overrideDumpMemoryFlag(dumpMemoryArgument.getValue());
4646
}
47-
auto yara = std::make_unique<Yara>(configuration->getSignatureFile());
47+
auto yara = std::make_unique<YaraInterface>(configuration->getSignatureFile());
4848
auto dumping = std::make_unique<Dumping>(pluginInterface, configuration);
4949
scanner = std::make_unique<Scanner>(pluginInterface, configuration, std::move(yara), std::move(dumping));
5050
}

plugins/inmemoryscanner/src/lib/Scanner.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@ namespace InMemoryScanner
2020
{
2121
Scanner::Scanner(PluginInterface* pluginInterface,
2222
std::shared_ptr<IConfig> configuration,
23-
std::unique_ptr<YaraInterface> yaraEngine,
23+
std::unique_ptr<IYaraInterface> yaraInterface,
2424
std::unique_ptr<IDumping> dumping)
2525
: pluginInterface(pluginInterface),
2626
configuration(std::move(configuration)),
27-
yaraEngine(std::move(yaraEngine)),
27+
yaraInterface(std::move(yaraInterface)),
2828
dumping(std::move(dumping)),
2929
logger(pluginInterface->newNamedLogger(INMEMORY_LOGGER_NAME)),
3030
inMemResultsLogger(pluginInterface->newNamedLogger(INMEMORY_LOGGER_NAME))
@@ -131,7 +131,7 @@ namespace InMemoryScanner
131131
// The semaphore protects the yara rules from being accessed more than YR_MAX_THREADS (32 atm.) times in
132132
// parallel.
133133
semaphore.wait();
134-
auto results = yaraEngine->scanMemory(*mappedRegions);
134+
auto results = yaraInterface->scanMemory(*mappedRegions);
135135
semaphore.notify();
136136

137137
logger->debug("End scanMemory");

plugins/inmemoryscanner/src/lib/Scanner.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33
#include "Config.h"
44
#include "Dumping.h"
5+
#include "IYaraInterface.h"
56
#include "OutputXML.h"
67
#include "Semaphore.h"
7-
#include "YaraInterface.h"
88
#include <condition_variable>
99
#include <memory>
1010
#include <vmicore/plugins/PluginInterface.h>
@@ -17,7 +17,7 @@ namespace InMemoryScanner
1717
public:
1818
Scanner(VmiCore::Plugin::PluginInterface* pluginInterface,
1919
std::shared_ptr<IConfig> configuration,
20-
std::unique_ptr<YaraInterface> yaraEngine,
20+
std::unique_ptr<IYaraInterface> yaraInterface,
2121
std::unique_ptr<IDumping> dumping);
2222

2323
[[nodiscard]] static std::unique_ptr<std::string> getFilenameFromPath(const std::string& path);
@@ -31,7 +31,7 @@ namespace InMemoryScanner
3131
private:
3232
VmiCore::Plugin::PluginInterface* pluginInterface;
3333
std::shared_ptr<IConfig> configuration;
34-
std::unique_ptr<YaraInterface> yaraEngine;
34+
std::unique_ptr<IYaraInterface> yaraInterface;
3535
OutputXML outputXml{};
3636
std::unique_ptr<IDumping> dumping;
3737
std::unique_ptr<VmiCore::ILogger> logger;

plugins/inmemoryscanner/src/lib/Yara.cpp

Lines changed: 0 additions & 94 deletions
This file was deleted.

plugins/inmemoryscanner/src/lib/Yara.h

Lines changed: 0 additions & 25 deletions
This file was deleted.
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
#include "YaraInterface.h"
2+
#include <fmt/core.h>
3+
4+
using VmiCore::MappedRegion;
5+
using BlockIteratorPair = std::pair<std::vector<YR_MEMORY_BLOCK>::iterator, std::vector<YR_MEMORY_BLOCK>::iterator>;
6+
7+
namespace InMemoryScanner
8+
{
9+
YaraInterface::YaraInterface(const std::string& rulesFile)
10+
{
11+
auto err = yr_initialize();
12+
if (err != ERROR_SUCCESS)
13+
{
14+
throw YaraException("Cannot initialize Yara. Error code: " + std::to_string(err));
15+
}
16+
17+
err = yr_rules_load(rulesFile.c_str(), &rules);
18+
if (err != ERROR_SUCCESS)
19+
{
20+
throw YaraException("Cannot load rules. Error code: " + std::to_string(err));
21+
}
22+
}
23+
24+
YaraInterface::~YaraInterface()
25+
{
26+
yr_rules_destroy(rules);
27+
yr_finalize();
28+
}
29+
30+
YR_MEMORY_BLOCK* get_next_block(YR_MEMORY_BLOCK_ITERATOR* iterator)
31+
{
32+
auto* blockVectorIterators = reinterpret_cast<BlockIteratorPair*>(iterator->context);
33+
blockVectorIterators->first++;
34+
35+
if (blockVectorIterators->first == blockVectorIterators->second)
36+
{
37+
return nullptr;
38+
}
39+
40+
return &*blockVectorIterators->first;
41+
}
42+
43+
YR_MEMORY_BLOCK* get_first_block(YR_MEMORY_BLOCK_ITERATOR* iterator)
44+
{
45+
return &*reinterpret_cast<BlockIteratorPair*>(iterator->context)->first;
46+
}
47+
48+
const uint8_t* fetch_block_data(YR_MEMORY_BLOCK* block)
49+
{
50+
return reinterpret_cast<const uint8_t*>(block->context);
51+
}
52+
53+
std::unique_ptr<std::vector<Rule>> YaraInterface::scanMemory(const std::vector<MappedRegion>& mappedRegions)
54+
{
55+
auto results = std::make_unique<std::vector<Rule>>();
56+
57+
std::vector<YR_MEMORY_BLOCK> blocks;
58+
blocks.reserve(mappedRegions.size());
59+
for (const auto& mappedRegion : mappedRegions)
60+
{
61+
blocks.emplace_back(mappedRegion.mapping.size(),
62+
mappedRegion.guestBaseVA - mappedRegions.front().guestBaseVA,
63+
reinterpret_cast<void*>(mappedRegion.mapping.data()),
64+
&fetch_block_data);
65+
}
66+
auto blockIterators = std::make_pair(blocks.begin(), blocks.end());
67+
#ifdef LIBYARA_4_1
68+
YR_MEMORY_BLOCK_ITERATOR iterator{.context = &blockIterators,
69+
.first = &get_first_block,
70+
.next = &get_next_block,
71+
.file_size = nullptr,
72+
.last_error = ERROR_SUCCESS};
73+
#else
74+
YR_MEMORY_BLOCK_ITERATOR iterator{
75+
.context = &blockIterators, .first = &get_first_block, .next = &get_next_block};
76+
#endif
77+
78+
auto err = yr_rules_scan_mem_blocks(rules, &iterator, 0, yaraCallback, results.get(), 0);
79+
if (err != ERROR_SUCCESS)
80+
{
81+
throw YaraException("Error scanning memory. Error code: " + std::to_string(err));
82+
}
83+
84+
return results;
85+
}
86+
87+
int YaraInterface::yaraCallback(YR_SCAN_CONTEXT* context, int message, void* message_data, void* user_data)
88+
{
89+
int ret = 0;
90+
switch (message)
91+
{
92+
case CALLBACK_MSG_RULE_MATCHING:
93+
ret = handleRuleMatch(
94+
context, static_cast<YR_RULE*>(message_data), static_cast<std::vector<Rule>*>(user_data));
95+
break;
96+
case CALLBACK_MSG_RULE_NOT_MATCHING:
97+
[[fallthrough]];
98+
case CALLBACK_MSG_SCAN_FINISHED:
99+
ret = CALLBACK_CONTINUE;
100+
break;
101+
default:
102+
ret = CALLBACK_ERROR;
103+
break;
104+
}
105+
106+
return ret;
107+
}
108+
109+
int YaraInterface::handleRuleMatch(YR_SCAN_CONTEXT* context, YR_RULE* rule, std::vector<Rule>* results)
110+
{
111+
YR_STRING* string = nullptr;
112+
YR_MATCH* match = nullptr;
113+
114+
Rule tmpRule;
115+
tmpRule.ruleName = rule->identifier;
116+
tmpRule.ruleNamespace = rule->ns->name;
117+
118+
yr_rule_strings_foreach(rule, string)
119+
{
120+
yr_string_matches_foreach(context, string, match) // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
121+
{
122+
Match tmpMatch;
123+
tmpMatch.matchName = string->identifier;
124+
tmpMatch.position = match->base + match->offset;
125+
126+
tmpRule.matches.push_back(tmpMatch);
127+
}
128+
}
129+
130+
results->push_back(tmpRule);
131+
132+
return CALLBACK_CONTINUE;
133+
}
134+
}

0 commit comments

Comments
 (0)