1
0
mirror of synced 2024-12-15 01:01:15 +01:00
ImHex/plugins/yara_rules/source/content/views/view_yara.cpp

315 lines
13 KiB
C++
Raw Normal View History

2021-12-07 22:47:41 +01:00
#include "content/views/view_yara.hpp"
#include <hex/api/content_registry.hpp>
#include <hex/api/project_file_manager.hpp>
#include <hex/helpers/fs.hpp>
#include <toasts/toast_notification.hpp>
#include <popups/popup_file_chooser.hpp>
#include <filesystem>
#include <wolv/io/fs.hpp>
2023-03-16 13:35:09 +01:00
#include <wolv/literals.hpp>
namespace hex::plugin::yara {
2023-03-16 13:35:09 +01:00
using namespace wolv::literals;
2024-01-08 21:51:48 +01:00
ViewYara::ViewYara() : View::Window("hex.yara_rules.view.yara.name", ICON_VS_BUG) {
2024-02-21 22:08:26 +01:00
YaraRule::init();
ContentRegistry::FileHandler::add({ ".yar", ".yara" }, [](const auto &path) {
for (const auto &destPath : fs::getDefaultPaths(fs::ImHexPath::Yara)) {
if (wolv::io::fs::copyFile(path, destPath / path.filename(), std::fs::copy_options::overwrite_existing)) {
ui::ToastInfo::open("hex.yara_rules.view.yara.rule_added"_lang);
return true;
}
}
return false;
});
ProjectFile::registerPerProviderHandler({
.basePath = "yara.json",
.required = false,
2023-12-27 16:33:49 +01:00
.load = [this](prv::Provider *provider, const std::fs::path &basePath, const Tar &tar) -> bool {
auto fileContent = tar.readString(basePath);
if (fileContent.empty())
return true;
auto data = nlohmann::json::parse(fileContent.begin(), fileContent.end());
if (!data.contains("rules"))
return false;
auto &rules = data["rules"];
if (!rules.is_array())
return false;
2023-12-19 13:10:25 +01:00
m_matches.get(provider).clear();
for (auto &rule : rules) {
if (!rule.contains("name") || !rule.contains("path"))
return false;
auto &name = rule["name"];
auto &path = rule["path"];
if (!name.is_string() || !path.is_string())
return false;
2024-02-21 22:08:26 +01:00
m_rulePaths.get(provider).emplace_back(std::fs::path(name.get<std::string>()), std::fs::path(path.get<std::string>()));
}
return true;
},
2023-12-27 16:33:49 +01:00
.store = [this](prv::Provider *provider, const std::fs::path &basePath, const Tar &tar) -> bool {
nlohmann::json data;
data["rules"] = nlohmann::json::array();
2024-02-21 22:08:26 +01:00
for (auto &[name, path] : m_rulePaths.get(provider)) {
data["rules"].push_back({
2023-03-12 18:43:05 +01:00
{ "name", wolv::util::toUTF8String(name) },
{ "path", wolv::util::toUTF8String(path) }
});
}
tar.writeString(basePath, data.dump(4));
return true;
}
});
}
ViewYara::~ViewYara() {
2024-02-21 22:08:26 +01:00
YaraRule::cleanup();
}
void ViewYara::drawContent() {
ImGuiExt::Header("hex.yara_rules.view.yara.header.rules"_lang, true);
if (ImGui::BeginListBox("##rules", ImVec2(-FLT_MIN, ImGui::GetTextLineHeightWithSpacing() * 5))) {
2024-02-21 22:08:26 +01:00
for (u32 i = 0; i < m_rulePaths->size(); i++) {
const bool selected = (*m_selectedRule == i);
if (ImGui::Selectable(wolv::util::toUTF8String((*m_rulePaths)[i].first).c_str(), selected)) {
*m_selectedRule = i;
}
}
ImGui::EndListBox();
}
if (ImGuiExt::IconButton(ICON_VS_ADD, ImGui::GetStyleColorVec4(ImGuiCol_Text))) {
const auto basePaths = fs::getDefaultPaths(fs::ImHexPath::Yara);
std::vector<std::fs::path> paths;
for (const auto &path : basePaths) {
std::error_code error;
for (const auto &entry : std::fs::recursive_directory_iterator(path, error)) {
if (!entry.is_regular_file()) continue;
if (entry.path().extension() != ".yara" && entry.path().extension() != ".yar") continue;
paths.push_back(entry);
}
}
ui::PopupFileChooser::open(basePaths, paths, std::vector<hex::fs::ItemFilter>{ { "Yara File", "yara" }, { "Yara File", "yar" } }, true,
[&](const auto &path) {
2024-02-21 22:08:26 +01:00
m_rulePaths->push_back({ path.filename(), path });
});
}
ImGui::SameLine();
if (ImGuiExt::IconButton(ICON_VS_REMOVE, ImGui::GetStyleColorVec4(ImGuiCol_Text))) {
2024-02-21 22:08:26 +01:00
if (*m_selectedRule < m_rulePaths->size()) {
m_rulePaths->erase(m_rulePaths->begin() + *m_selectedRule);
m_selectedRule = std::min(*m_selectedRule, u32(m_rulePaths->size() - 1));
}
}
ImGui::NewLine();
if (ImGui::Button("hex.yara_rules.view.yara.match"_lang)) this->applyRules();
ImGui::SameLine();
2023-12-19 13:10:25 +01:00
if (m_matcherTask.isRunning()) {
ImGui::SameLine();
ImGuiExt::TextSpinner("hex.yara_rules.view.yara.matching"_lang);
}
ImGuiExt::Header("hex.yara_rules.view.yara.header.matches"_lang);
auto matchesTableSize = ImGui::GetContentRegionAvail();
matchesTableSize.y *= 3.75 / 5.0;
matchesTableSize.y -= ImGui::GetTextLineHeightWithSpacing();
if (ImGui::BeginTable("matches", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Sortable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY, matchesTableSize)) {
ImGui::TableSetupScrollFreeze(0, 1);
ImGui::TableSetupColumn("hex.yara_rules.view.yara.matches.identifier"_lang, ImGuiTableColumnFlags_PreferSortAscending, 0, ImGui::GetID("identifier"));
ImGui::TableSetupColumn("hex.yara_rules.view.yara.matches.variable"_lang, ImGuiTableColumnFlags_PreferSortAscending, 0, ImGui::GetID("variable"));
ImGui::TableSetupColumn("hex.ui.common.address"_lang, ImGuiTableColumnFlags_PreferSortAscending, 0, ImGui::GetID("address"));
ImGui::TableSetupColumn("hex.ui.common.size"_lang, ImGuiTableColumnFlags_PreferSortAscending, 0, ImGui::GetID("size"));
ImGui::TableHeadersRow();
auto sortSpecs = ImGui::TableGetSortSpecs();
2023-12-19 13:10:25 +01:00
if (!m_matches->empty() && (sortSpecs->SpecsDirty || m_sortedMatches->empty())) {
m_sortedMatches->clear();
std::transform(m_matches->begin(), m_matches->end(), std::back_inserter(*m_sortedMatches), [](auto &match) {
return &match;
});
2023-12-19 13:10:25 +01:00
std::sort(m_sortedMatches->begin(), m_sortedMatches->end(), [&sortSpecs](const YaraMatch *left, const YaraMatch *right) -> bool {
if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("identifier"))
2024-02-21 22:08:26 +01:00
return left->match.identifier < right->match.identifier;
else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("variable"))
2024-02-21 22:08:26 +01:00
return left->match.variable < right->match.variable;
else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("address"))
2024-02-21 22:08:26 +01:00
return left->match.region.getStartAddress() < right->match.region.getStartAddress();
else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("size"))
2024-02-21 22:08:26 +01:00
return left->match.region.getSize() < right->match.region.getSize();
else
return false;
});
2023-03-16 13:35:09 +01:00
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Descending)
2023-12-19 13:10:25 +01:00
std::reverse(m_sortedMatches->begin(), m_sortedMatches->end());
2023-03-16 13:35:09 +01:00
sortSpecs->SpecsDirty = false;
}
2023-03-16 13:35:09 +01:00
2023-12-19 13:10:25 +01:00
if (!m_matcherTask.isRunning()) {
ImGuiListClipper clipper;
2023-12-19 13:10:25 +01:00
clipper.Begin(m_sortedMatches->size());
2023-03-16 13:35:09 +01:00
while (clipper.Step()) {
for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) {
2024-02-21 22:08:26 +01:00
auto &[match, highlightId, tooltipId] = *(*m_sortedMatches)[i];
auto &[identifier, variableName, region, wholeDataMatch] = match;
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::PushID(i);
if (ImGui::Selectable("match", false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap)) {
2024-02-21 22:08:26 +01:00
ImHexApi::HexEditor::setSelection(region.getStartAddress(), region.getSize());
}
ImGui::PopID();
ImGui::SameLine();
ImGui::TextUnformatted(identifier.c_str());
ImGui::TableNextColumn();
ImGui::TextUnformatted(variableName.c_str());
if (!wholeDataMatch) {
ImGui::TableNextColumn();
2024-02-21 22:08:26 +01:00
ImGuiExt::TextFormatted("0x{0:X} : 0x{1:X}", region.getStartAddress(), region.getEndAddress());
ImGui::TableNextColumn();
2024-02-21 22:08:26 +01:00
ImGuiExt::TextFormatted("0x{0:X}", region.getSize());
} else {
ImGui::TableNextColumn();
ImGuiExt::TextFormattedColored(ImVec4(0.92F, 0.25F, 0.2F, 1.0F), "{}", "hex.yara_rules.view.yara.whole_data"_lang);
ImGui::TableNextColumn();
ImGui::TextUnformatted("");
}
}
}
clipper.End();
}
ImGui::EndTable();
}
auto consoleSize = ImGui::GetContentRegionAvail();
if (ImGui::BeginChild("##console", consoleSize, true, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_HorizontalScrollbar)) {
ImGuiListClipper clipper;
2024-02-21 22:08:26 +01:00
clipper.Begin(m_consoleMessages->size());
2023-12-27 16:33:49 +01:00
while (clipper.Step()) {
for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) {
2024-02-21 22:08:26 +01:00
const auto &message = m_consoleMessages->at(i);
if (ImGui::Selectable(message.c_str()))
ImGui::SetClipboardText(message.c_str());
}
2023-12-27 16:33:49 +01:00
}
}
ImGui::EndChild();
}
void ViewYara::clearResult() {
2023-12-19 13:10:25 +01:00
for (const auto &match : *m_matches) {
ui/ux: Rewrite of the entire hex editor view to make it more flexible (#512) * ui/ux: Initial recreation of the hex editor view * ui/ux: Added back support for editing cells * ux: Make scrolling and selecting bytes feel nice again * ui/ux: Improved byte selecting, added footer * sys: Make math evaluator more generic to support integer only calculations * patterns: Moved value formatting into pattern language * ui/ux: Added Goto and Search popups, improved selection * ui: Added better tooltips for bookmarks and patterns * sys: Use worse hex search algorithm on macOS Sadly it still doesn't support `std::boyer_moore_horsepool_searcher` * ui: Added back missing events, menu items and shortcuts * fix: Bookmark highlighting being rendered off by one * fix: Various macOS build errors * fix: size_t is not u64 on macos * fix: std::fmod and std::pow not working with integer types on macos * fix: Missing semicolons * sys: Added proper integer pow function * ui: Added back support for custom encodings * fix: Editor not jumping to selection when selection gets changed * ui: Turn Hexii setting into a data visualizer * sys: Added back remaining shortcuts * sys: Remove old hex editor files * sys: Moved more legacy things away from the hex editor view, updated localization * fix: Hex editor scrolling behaving weirdly and inconsistently * sys: Cleaned up Hex editor code * sys: Added selection color setting, localized all new settings * fix: Search feature not working correctly * ui: Replace custom ImGui::Disabled function with native ImGui ones * ui: Fix bookmark tooltip rendering issues * fix: Another size_t not being 64 bit issue on MacOS
2022-05-27 20:42:07 +02:00
ImHexApi::HexEditor::removeBackgroundHighlight(match.highlightId);
ImHexApi::HexEditor::removeTooltip(match.tooltipId);
}
2023-12-19 13:10:25 +01:00
m_matches->clear();
2024-02-21 22:08:26 +01:00
m_consoleMessages->clear();
}
void ViewYara::applyRules() {
this->clearResult();
2024-02-21 22:08:26 +01:00
auto provider = ImHexApi::Provider::get();
if (provider == nullptr)
return;
2024-02-21 22:08:26 +01:00
m_matcherTask = TaskManager::createTask("hex.yara_rules.view.yara.matching", 0, [this, provider](auto &task) {
u32 progress = 0;
2024-02-21 22:08:26 +01:00
std::vector<YaraRule::Result> results;
for (const auto &[fileName, filePath] : *m_rulePaths) {
YaraRule rule(filePath);
2024-02-21 22:08:26 +01:00
task.setInterruptCallback([&rule] {
rule.interrupt();
});
2024-02-21 23:17:12 +01:00
auto result = rule.match(provider, { provider->getBaseAddress(), provider->getSize() });
2024-02-21 22:08:26 +01:00
if (!result.has_value()) {
TaskManager::doLater([this, error = result.error()] {
m_consoleMessages->emplace_back(error.message);
});
2024-02-21 22:08:26 +01:00
break;
}
2024-02-21 22:08:26 +01:00
results.emplace_back(result.value());
2022-02-02 21:08:46 +01:00
2024-02-21 22:08:26 +01:00
task.update(progress);
progress += 1;
}
2024-02-21 22:08:26 +01:00
TaskManager::doLater([this, results = std::move(results)] {
2023-12-19 13:10:25 +01:00
for (const auto &match : *m_matches) {
ImHexApi::HexEditor::removeBackgroundHighlight(match.highlightId);
ImHexApi::HexEditor::removeTooltip(match.tooltipId);
}
2024-02-21 22:08:26 +01:00
for (auto &result : results) {
for (auto &match : result.matches) {
m_matches->emplace_back(YaraMatch { std::move(match), 0, 0 });
}
2024-02-21 22:08:26 +01:00
m_consoleMessages->append_range(result.consoleMessages);
}
2024-02-21 22:08:26 +01:00
auto uniques = std::set(m_matches->begin(), m_matches->end(), [](const auto &leftMatch, const auto &rightMatch) {
const auto &l = leftMatch.match;
const auto &r = rightMatch.match;
return std::tie(l.region.address, l.wholeDataMatch, l.identifier, l.variable) <
std::tie(r.region.address, r.wholeDataMatch, r.identifier, r.variable);
});
2023-12-19 13:10:25 +01:00
m_matches->clear();
std::move(uniques.begin(), uniques.end(), std::back_inserter(*m_matches));
ui/ux: Rewrite of the entire hex editor view to make it more flexible (#512) * ui/ux: Initial recreation of the hex editor view * ui/ux: Added back support for editing cells * ux: Make scrolling and selecting bytes feel nice again * ui/ux: Improved byte selecting, added footer * sys: Make math evaluator more generic to support integer only calculations * patterns: Moved value formatting into pattern language * ui/ux: Added Goto and Search popups, improved selection * ui: Added better tooltips for bookmarks and patterns * sys: Use worse hex search algorithm on macOS Sadly it still doesn't support `std::boyer_moore_horsepool_searcher` * ui: Added back missing events, menu items and shortcuts * fix: Bookmark highlighting being rendered off by one * fix: Various macOS build errors * fix: size_t is not u64 on macos * fix: std::fmod and std::pow not working with integer types on macos * fix: Missing semicolons * sys: Added proper integer pow function * ui: Added back support for custom encodings * fix: Editor not jumping to selection when selection gets changed * ui: Turn Hexii setting into a data visualizer * sys: Added back remaining shortcuts * sys: Remove old hex editor files * sys: Moved more legacy things away from the hex editor view, updated localization * fix: Hex editor scrolling behaving weirdly and inconsistently * sys: Cleaned up Hex editor code * sys: Added selection color setting, localized all new settings * fix: Search feature not working correctly * ui: Replace custom ImGui::Disabled function with native ImGui ones * ui: Fix bookmark tooltip rendering issues * fix: Another size_t not being 64 bit issue on MacOS
2022-05-27 20:42:07 +02:00
constexpr static color_t YaraColor = 0x70B4771F;
2024-02-21 22:08:26 +01:00
for (const YaraMatch &yaraMatch : uniques) {
yaraMatch.highlightId = ImHexApi::HexEditor::addBackgroundHighlight(yaraMatch.match.region, YaraColor);
yaraMatch.tooltipId = ImHexApi::HexEditor::addTooltip(yaraMatch.match.region, hex::format("{0} [{1}]", yaraMatch.match.identifier, yaraMatch.match.variable), YaraColor);
}
});
});
}
}