1
0
mirror of synced 2024-11-12 02:00:52 +01:00

Add Yara rule matching interface (#178)

* build: Added YARA as submodule

* ui: Added basic yara rules matching interface

* build: Make libyara link libpthread on Unix

* ui: Add jump-to feature to yara matches list

* yara: Add more modules and patch yara to support mbedtls crypto

* yara: Started to fix scanning of bigger data

* yara: Fixed implementation

* ui: Improved yara matcher interface and added localization

* build: Ignore changed files in yara submodule

* yara: Fixed rules matching agianst entire file

* yara: Properly handle compiler errors
This commit is contained in:
WerWolv 2021-02-26 13:35:19 +01:00 committed by GitHub
parent 372981920e
commit 31e5ec7bc3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 570 additions and 3 deletions

4
.gitmodules vendored
View File

@ -1,3 +1,7 @@
[submodule "external/nativefiledialog"] [submodule "external/nativefiledialog"]
path = external/nativefiledialog path = external/nativefiledialog
url = https://github.com/btzy/nativefiledialog-extended url = https://github.com/btzy/nativefiledialog-extended
[submodule "external/yara/yara"]
path = external/yara/yara
url = https://github.com/VirusTotal/yara
ignore = dirty

View File

@ -2,5 +2,6 @@
<project version="4"> <project version="4">
<component name="VcsDirectoryMappings"> <component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" /> <mapping directory="$PROJECT_DIR$" vcs="Git" />
<mapping directory="$PROJECT_DIR$/external/yara/yara" vcs="Git" />
</component> </component>
</project> </project>

View File

@ -26,6 +26,7 @@ detectArch()
# Add bundled dependencies # Add bundled dependencies
add_subdirectory(external/llvm) add_subdirectory(external/llvm)
add_subdirectory(external/yara)
add_subdirectory(plugins/libimhex) add_subdirectory(plugins/libimhex)
# Add include directories # Add include directories
@ -61,6 +62,7 @@ add_executable(imhex ${application_type}
source/views/view_command_palette.cpp source/views/view_command_palette.cpp
source/views/view_settings.cpp source/views/view_settings.cpp
source/views/view_data_processor.cpp source/views/view_data_processor.cpp
source/views/view_yara.cpp
${imhex_icon} ${imhex_icon}
) )
@ -69,9 +71,9 @@ set_target_properties(imhex PROPERTIES CXX_VISIBILITY_PRESET hidden)
target_link_directories(imhex PRIVATE ${CAPSTONE_LIBRARY_DIRS} ${MAGIC_LIBRARY_DIRS}) target_link_directories(imhex PRIVATE ${CAPSTONE_LIBRARY_DIRS} ${MAGIC_LIBRARY_DIRS})
if (WIN32) if (WIN32)
target_link_libraries(imhex libdl.a libmagic.a libgnurx.a libtre.a libintl.a libiconv.a libshlwapi.a libcapstone.a LLVMDemangle libimhex ${Python_LIBRARIES} wsock32 ws2_32) target_link_libraries(imhex libdl.a libmagic.a libgnurx.a libtre.a libintl.a libiconv.a libshlwapi.a libcapstone.a LLVMDemangle libimhex ${Python_LIBRARIES} wsock32 ws2_32 libyara)
elseif (UNIX) elseif (UNIX)
target_link_libraries(imhex magic ${CMAKE_DL_LIBS} capstone LLVMDemangle libimhex ${Python_LIBRARIES} dl pthread) target_link_libraries(imhex magic ${CMAKE_DL_LIBS} capstone LLVMDemangle libimhex ${Python_LIBRARIES} dl pthread libyara)
endif() endif()
createPackage() createPackage()

125
external/yara/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,125 @@
cmake_minimum_required(VERSION 3.10)
set(LIBYARA_SOURCE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/yara/libyara")
set(LIBYARA_INCLUDES
${LIBYARA_SOURCE_PATH}/include/yara/ahocorasick.h
${LIBYARA_SOURCE_PATH}/include/yara/arena.h
${LIBYARA_SOURCE_PATH}/include/yara/atoms.h
${LIBYARA_SOURCE_PATH}/include/yara/bitmask.h
${LIBYARA_SOURCE_PATH}/include/yara/compiler.h
${LIBYARA_SOURCE_PATH}/include/yara/error.h
${LIBYARA_SOURCE_PATH}/include/yara/exec.h
${LIBYARA_SOURCE_PATH}/include/yara/exefiles.h
${LIBYARA_SOURCE_PATH}/include/yara/filemap.h
${LIBYARA_SOURCE_PATH}/include/yara/hash.h
${LIBYARA_SOURCE_PATH}/include/yara/integers.h
${LIBYARA_SOURCE_PATH}/include/yara/libyara.h
${LIBYARA_SOURCE_PATH}/include/yara/limits.h
${LIBYARA_SOURCE_PATH}/include/yara/mem.h
${LIBYARA_SOURCE_PATH}/include/yara/modules.h
${LIBYARA_SOURCE_PATH}/include/yara/object.h
${LIBYARA_SOURCE_PATH}/include/yara/parser.h
${LIBYARA_SOURCE_PATH}/include/yara/proc.h
${LIBYARA_SOURCE_PATH}/include/yara/re.h
${LIBYARA_SOURCE_PATH}/include/yara/rules.h
${LIBYARA_SOURCE_PATH}/include/yara/scan.h
${LIBYARA_SOURCE_PATH}/include/yara/scanner.h
${LIBYARA_SOURCE_PATH}/include/yara/sizedstr.h
${LIBYARA_SOURCE_PATH}/include/yara/stack.h
${LIBYARA_SOURCE_PATH}/include/yara/stopwatch.h
${LIBYARA_SOURCE_PATH}/include/yara/stream.h
${LIBYARA_SOURCE_PATH}/include/yara/strutils.h
${LIBYARA_SOURCE_PATH}/include/yara/threading.h
${LIBYARA_SOURCE_PATH}/include/yara/types.h
${LIBYARA_SOURCE_PATH}/include/yara/utils.h
${LIBYARA_SOURCE_PATH}/crypto.h
)
set(LIBYARA_SOURCE
${LIBYARA_SOURCE_PATH}/grammar.y
${LIBYARA_SOURCE_PATH}/ahocorasick.c
${LIBYARA_SOURCE_PATH}/arena.c
${LIBYARA_SOURCE_PATH}/atoms.c
${LIBYARA_SOURCE_PATH}/base64.c
${LIBYARA_SOURCE_PATH}/bitmask.c
${LIBYARA_SOURCE_PATH}/compiler.c
${LIBYARA_SOURCE_PATH}/endian.c
${LIBYARA_SOURCE_PATH}/exec.c
${LIBYARA_SOURCE_PATH}/exefiles.c
${LIBYARA_SOURCE_PATH}/filemap.c
${LIBYARA_SOURCE_PATH}/hash.c
${LIBYARA_SOURCE_PATH}/hex_grammar.y
${LIBYARA_SOURCE_PATH}/hex_lexer.l
${LIBYARA_SOURCE_PATH}/lexer.l
${LIBYARA_SOURCE_PATH}/libyara.c
${LIBYARA_SOURCE_PATH}/mem.c
${LIBYARA_SOURCE_PATH}/modules.c
${LIBYARA_SOURCE_PATH}/notebook.c
${LIBYARA_SOURCE_PATH}/object.c
${LIBYARA_SOURCE_PATH}/parser.c
${LIBYARA_SOURCE_PATH}/proc.c
${LIBYARA_SOURCE_PATH}/re.c
${LIBYARA_SOURCE_PATH}/re_grammar.y
${LIBYARA_SOURCE_PATH}/re_lexer.l
${LIBYARA_SOURCE_PATH}/rules.c
${LIBYARA_SOURCE_PATH}/scan.c
${LIBYARA_SOURCE_PATH}/scanner.c
${LIBYARA_SOURCE_PATH}/sizedstr.c
${LIBYARA_SOURCE_PATH}/stack.c
${LIBYARA_SOURCE_PATH}/stopwatch.c
${LIBYARA_SOURCE_PATH}/strutils.c
${LIBYARA_SOURCE_PATH}/stream.c
${LIBYARA_SOURCE_PATH}/threading.c
${LIBYARA_SOURCE_PATH}/lexer.c
${LIBYARA_SOURCE_PATH}/hex_lexer.c
${LIBYARA_SOURCE_PATH}/grammar.c
${LIBYARA_SOURCE_PATH}/re_lexer.c
${LIBYARA_SOURCE_PATH}/hex_grammar.c
${LIBYARA_SOURCE_PATH}/re_grammar.c
${LIBYARA_SOURCE_PATH}/proc/none.c
)
set(LIBYARA_MODULES
${LIBYARA_SOURCE_PATH}/modules/tests/tests.c
${LIBYARA_SOURCE_PATH}/modules/pe/pe.c
${LIBYARA_SOURCE_PATH}/modules/pe/pe_utils.c
${LIBYARA_SOURCE_PATH}/modules/elf/elf.c
${LIBYARA_SOURCE_PATH}/modules/math/math.c
${LIBYARA_SOURCE_PATH}/modules/time/time.c
${LIBYARA_SOURCE_PATH}/modules/macho/macho.c
${LIBYARA_SOURCE_PATH}/modules/hash/hash.c
${LIBYARA_SOURCE_PATH}/modules/dex/dex.c
${LIBYARA_SOURCE_PATH}/modules/dotnet/dotnet.c
${LIBYARA_SOURCE_PATH}/modules/magic/magic.c)
# Add mbedtls crypto wrappers
file(READ crypto_mbedtls.h MBEDTLS_CRYPTO_H)
file(WRITE ${LIBYARA_SOURCE_PATH}/crypto.h "${MBEDTLS_CRYPTO_H}")
add_compile_definitions("HAVE_MBEDTLS")
add_compile_definitions("USE_NO_PROC")
add_compile_definitions("HASH_MODULE")
add_compile_definitions("DOTNET_MODULE")
add_compile_definitions("MAGIC_MODULE")
add_compile_definitions("MACHO_MODULE")
add_compile_definitions("DEX_MODULE")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-shift-count-overflow")
add_library(libyara STATIC ${LIBYARA_SOURCE} ${LIBYARA_INCLUDES} ${LIBYARA_MODULES})
target_include_directories(
libyara
PUBLIC $<BUILD_INTERFACE:${LIBYARA_SOURCE_PATH}/include> $<INSTALL_INTERFACE:include>
PRIVATE ${LIBYARA_SOURCE_PATH}
)
if (UNIX)
target_link_libraries(libyara pthread)
endif ()
include(GNUInstallDirs)
configure_file(${LIBYARA_SOURCE_PATH}/yara.pc.in
${LIBYARA_SOURCE_PATH}/yara.pc @ONLY)

143
external/yara/crypto_mbedtls.h vendored Normal file
View File

@ -0,0 +1,143 @@
/*
Copyright (c) 2017. The YARA Authors. All Rights Reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef YR_CRYPTO_H
#define YR_CRYPTO_H
#define YR_MD5_LEN 16
#define YR_SHA1_LEN 20
#define YR_SHA256_LEN 32
#if defined(HAVE_LIBCRYPTO)
#include <openssl/crypto.h>
#include <openssl/md5.h>
#include <openssl/sha.h>
typedef MD5_CTX yr_md5_ctx;
typedef SHA_CTX yr_sha1_ctx;
typedef SHA256_CTX yr_sha256_ctx;
#define yr_md5_init(ctx) MD5_Init(ctx)
#define yr_md5_update(ctx, data, len) MD5_Update(ctx, data, len)
#define yr_md5_final(digest, ctx) MD5_Final(digest, ctx)
#define yr_sha1_init(ctx) SHA1_Init(ctx)
#define yr_sha1_update(ctx, data, len) SHA1_Update(ctx, data, len)
#define yr_sha1_final(digest, ctx) SHA1_Final(digest, ctx)
#define yr_sha256_init(ctx) SHA256_Init(ctx)
#define yr_sha256_update(ctx, data, len) SHA256_Update(ctx, data, len)
#define yr_sha256_final(digest, ctx) SHA256_Final(digest, ctx)
#elif defined(HAVE_WINCRYPT_H)
#include <windows.h>
#include <wincrypt.h>
extern HCRYPTPROV yr_cryptprov;
typedef HCRYPTHASH yr_md5_ctx;
typedef HCRYPTHASH yr_sha1_ctx;
typedef HCRYPTHASH yr_sha256_ctx;
#define yr_md5_init(ctx) CryptCreateHash(yr_cryptprov, CALG_MD5, 0, 0, ctx)
#define yr_md5_update(ctx, data, len) \
CryptHashData(*ctx, (const BYTE*) data, len, 0)
#define yr_md5_final(digest, ctx) \
{ \
DWORD len = YR_MD5_LEN; \
CryptGetHashParam(*ctx, HP_HASHVAL, digest, &len, 0); \
CryptDestroyHash(*ctx); \
}
#define yr_sha1_init(ctx) CryptCreateHash(yr_cryptprov, CALG_SHA1, 0, 0, ctx)
#define yr_sha1_update(ctx, data, len) \
CryptHashData(*ctx, (const BYTE*) data, len, 0)
#define yr_sha1_final(digest, ctx) \
{ \
DWORD len = YR_SHA1_LEN; \
CryptGetHashParam(*ctx, HP_HASHVAL, digest, &len, 0); \
CryptDestroyHash(*ctx); \
}
#define yr_sha256_init(ctx) \
CryptCreateHash(yr_cryptprov, CALG_SHA_256, 0, 0, ctx)
#define yr_sha256_update(ctx, data, len) \
CryptHashData(*ctx, (const BYTE*) data, len, 0)
#define yr_sha256_final(digest, ctx) \
{ \
DWORD len = YR_SHA256_LEN; \
CryptGetHashParam(*ctx, HP_HASHVAL, digest, &len, 0); \
CryptDestroyHash(*ctx); \
}
#elif defined(HAVE_COMMONCRYPTO_COMMONCRYPTO_H)
#include <CommonCrypto/CommonDigest.h>
typedef CC_MD5_CTX yr_md5_ctx;
typedef CC_SHA1_CTX yr_sha1_ctx;
typedef CC_SHA256_CTX yr_sha256_ctx;
#define yr_md5_init(ctx) CC_MD5_Init(ctx)
#define yr_md5_update(ctx, data, len) CC_MD5_Update(ctx, data, len)
#define yr_md5_final(digest, ctx) CC_MD5_Final(digest, ctx)
#define yr_sha1_init(ctx) CC_SHA1_Init(ctx)
#define yr_sha1_update(ctx, data, len) CC_SHA1_Update(ctx, data, len)
#define yr_sha1_final(digest, ctx) CC_SHA1_Final(digest, ctx)
#define yr_sha256_init(ctx) CC_SHA256_Init(ctx)
#define yr_sha256_update(ctx, data, len) CC_SHA256_Update(ctx, data, len)
#define yr_sha256_final(digest, ctx) CC_SHA256_Final(digest, ctx)
#elif defined(HAVE_MBEDTLS)
#include <mbedtls/md5.h>
#include <mbedtls/sha1.h>
#include <mbedtls/sha256.h>
typedef mbedtls_md5_context yr_md5_ctx;
typedef mbedtls_sha1_context yr_sha1_ctx;
typedef mbedtls_sha256_context yr_sha256_ctx;
#define yr_md5_init(ctx) { mbedtls_md5_init(ctx); mbedtls_md5_starts_ret(ctx); }
#define yr_md5_update(ctx, data, len) mbedtls_md5_update_ret(ctx, data, len)
#define yr_md5_final(digest, ctx) { mbedtls_md5_finish_ret(ctx, digest); mbedtls_md5_free(ctx); }
#define yr_sha1_init(ctx) { mbedtls_sha1_init(ctx); mbedtls_sha1_starts_ret(ctx); }
#define yr_sha1_update(ctx, data, len) mbedtls_sha1_update_ret(ctx, data, len)
#define yr_sha1_final(digest, ctx) { mbedtls_sha1_finish_ret(ctx, digest); mbedtls_sha1_free(ctx); }
#define yr_sha256_init(ctx) { mbedtls_sha256_init(ctx); mbedtls_sha256_starts_ret(ctx, false); }
#define yr_sha256_update(ctx, data, len) mbedtls_sha256_update_ret(ctx, data, len)
#define yr_sha256_final(digest, ctx) { mbedtls_sha256_finish_ret(ctx, digest); mbedtls_sha256_free(ctx); }
#define HAVE_COMMONCRYPTO_COMMONCRYPTO_H
#endif
#endif

1
external/yara/yara vendored Submodule

@ -0,0 +1 @@
Subproject commit 24350d8346b994146578753fbc0e9f8c198cad64

View File

@ -0,0 +1,36 @@
#pragma once
#include <hex.hpp>
#include <imgui.h>
#include <hex/views/view.hpp>
namespace hex {
class ViewYara : public View {
public:
ViewYara();
~ViewYara() override;
void drawContent() override;
void drawMenu() override;
private:
struct YaraMatch {
std::string identifier;
s64 address;
s32 size;
bool wholeDataMatch;
};
std::vector<std::string> m_rules;
std::vector<YaraMatch> m_matches;
u32 m_selectedRule = 0;
bool m_matching = false;
std::vector<char> m_errorMessage;
void reloadRules();
void applyRules();
};
}

View File

@ -253,6 +253,16 @@ namespace hex::plugin::builtin {
{ "hex.view.tools.name", "Tools" }, { "hex.view.tools.name", "Tools" },
{ "hex.view.yara.name", "Yara Rules" },
{ "hex.view.yara.header.rules", "Rules" },
{ "hex.view.yara.reload", "Reload" },
{ "hex.view.yara.match", "Match Rules" },
{ "hex.view.yara.matching", "Matching..." },
{ "hex.view.yara.error", "Yara Compiler error: " },
{ "hex.view.yara.header.matches", "Matches" },
{ "hex.view.yara.matches.identifier", "Identifier" },
{ "hex.view.yara.whole_data", "Whole file matches!" },
/* Builtin plugin features */ /* Builtin plugin features */
{ "hex.builtin.command.calc.desc", "Calculator" }, { "hex.builtin.command.calc.desc", "Calculator" },

View File

@ -22,6 +22,7 @@
#include "views/view_command_palette.hpp" #include "views/view_command_palette.hpp"
#include "views/view_settings.hpp" #include "views/view_settings.hpp"
#include "views/view_data_processor.hpp" #include "views/view_data_processor.hpp"
#include "views/view_yara.hpp"
#include <vector> #include <vector>
@ -49,6 +50,7 @@ int main(int argc, char **argv) {
ContentRegistry::Views::add<ViewHelp>(); ContentRegistry::Views::add<ViewHelp>();
ContentRegistry::Views::add<ViewSettings>(); ContentRegistry::Views::add<ViewSettings>();
ContentRegistry::Views::add<ViewDataProcessor>(); ContentRegistry::Views::add<ViewDataProcessor>();
ContentRegistry::Views::add<ViewYara>();
if (argc > 1) if (argc > 1)
View::postEvent(Events::FileDropped, argv[1]); View::postEvent(Events::FileDropped, argv[1]);

243
source/views/view_yara.cpp Normal file
View File

@ -0,0 +1,243 @@
#include "views/view_yara.hpp"
#include <hex/providers/provider.hpp>
#include <yara.h>
#include <filesystem>
#include <thread>
#include <imgui_imhex_extensions.h>
namespace hex {
ViewYara::ViewYara() : View("hex.view.yara.name"_lang) {
yr_initialize();
this->reloadRules();
}
ViewYara::~ViewYara() {
yr_finalize();
}
void ViewYara::drawContent() {
if (ImGui::Begin("hex.view.yara.name"_lang, &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
if (!this->m_matching && !this->m_errorMessage.empty()) {
View::showErrorPopup("hex.view.yara.error"_lang + this->m_errorMessage.data());
this->m_errorMessage.clear();
}
ImGui::TextUnformatted("hex.view.yara.header.rules"_lang);
ImGui::Separator();
if (this->m_rules.empty()) {
ImGui::TextColored(ImVec4(0.92F, 0.25F, 0.2F, 1.0F), "No YARA rules found. Put them in the 'yara' folder next to the ImHex executable");
if (ImGui::Button("hex.view.yara.reload"_lang)) this->reloadRules();
} else {
ImGui::Disabled([this]{
if (ImGui::BeginCombo("hex.view.yara.header.rules"_lang, this->m_rules[this->m_selectedRule].c_str())) {
for (u32 i = 0; i < this->m_rules.size(); i++) {
const bool selected = (this->m_selectedRule == i);
if (ImGui::Selectable(this->m_rules[i].c_str(), selected))
this->m_selectedRule = i;
if (selected)
ImGui::SetItemDefaultFocus();
}
ImGui::EndCombo();
}
ImGui::SameLine();
if (ImGui::Button("hex.view.yara.reload"_lang)) this->reloadRules();
if (ImGui::Button("hex.view.yara.match"_lang)) this->applyRules();
}, this->m_matching);
if (this->m_matching) {
ImGui::SameLine();
ImGui::TextSpinner("hex.view.yara.matching"_lang);
}
}
ImGui::NewLine();
ImGui::TextUnformatted("hex.view.yara.header.matches"_lang);
ImGui::Separator();
if (ImGui::BeginTable("matches", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Sortable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY)) {
ImGui::TableSetupScrollFreeze(0, 1);
ImGui::TableSetupColumn("hex.view.yara.matches.identifier"_lang);
ImGui::TableSetupColumn("hex.common.address"_lang);
ImGui::TableSetupColumn("hex.common.size"_lang);
ImGui::TableHeadersRow();
ImGuiListClipper clipper;
clipper.Begin(this->m_matches.size());
while (clipper.Step()) {
for (u32 i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) {
auto &[identifier, address, size, wholeDataMatch] = this->m_matches[i];
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::PushID(i);
if (ImGui::Selectable("match", false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowItemOverlap)) {
Region selectRegion = { u64(address), size_t(size) };
View::postEvent(Events::SelectionChangeRequest, selectRegion);
}
ImGui::PopID();
ImGui::SameLine();
ImGui::TextUnformatted(identifier.c_str());
if (!wholeDataMatch) {
ImGui::TableNextColumn();
ImGui::Text("0x%llX : 0x%llX", address, address + size - 1);
ImGui::TableNextColumn();
ImGui::Text("0x%lX", size);
} else {
ImGui::TableNextColumn();
ImGui::TextColored(ImVec4(0.92F, 0.25F, 0.2F, 1.0F), "%s", static_cast<const char*>("hex.view.yara.whole_data"_lang));
ImGui::TableNextColumn();
ImGui::TextUnformatted("");
}
}
}
clipper.End();
ImGui::EndTable();
}
}
ImGui::End();
}
void ViewYara::drawMenu() {
}
void ViewYara::reloadRules() {
this->m_rules.clear();
if (!std::filesystem::exists("./yara"))
return;
for (const auto &entry : std::filesystem::directory_iterator("yara")) {
if (entry.is_regular_file())
this->m_rules.push_back(entry.path().string());
}
}
void ViewYara::applyRules() {
this->m_matches.clear();
this->m_errorMessage.clear();
this->m_matching = true;
std::thread([this] {
YR_COMPILER *compiler = nullptr;
yr_compiler_create(&compiler);
FILE *file = fopen(this->m_rules[this->m_selectedRule].c_str(), "r");
if (file == nullptr) return;
SCOPE_EXIT( fclose(file); );
if (yr_compiler_add_file(compiler, file, nullptr, nullptr) != 0) {
this->m_errorMessage.resize(0xFFFF);
yr_compiler_get_error_message(compiler, this->m_errorMessage.data(), this->m_errorMessage.size());
this->m_matching = false;
return;
}
YR_RULES *rules;
yr_compiler_get_rules(compiler, &rules);
auto &provider = SharedData::currentProvider;
std::vector<YaraMatch> newMatches;
YR_MEMORY_BLOCK_ITERATOR iterator;
struct ScanContext {
std::vector<u8> buffer;
YR_MEMORY_BLOCK currBlock;
};
ScanContext context;
context.currBlock.base = 0;
context.currBlock.fetch_data = [](auto *block) -> const u8* {
auto &context = *static_cast<ScanContext*>(block->context);
auto &provider = SharedData::currentProvider;
context.buffer.resize(std::min<u64>(0xF'FFFF, provider->getSize() - context.currBlock.base));
if (context.buffer.empty()) return nullptr;
provider->read(context.currBlock.base, context.buffer.data(), context.buffer.size());
return context.buffer.data();
};
iterator.file_size = [](auto *iterator) -> u64 {
return SharedData::currentProvider->getSize();
};
iterator.context = &context;
iterator.first = [](YR_MEMORY_BLOCK_ITERATOR* iterator) -> YR_MEMORY_BLOCK* {
auto &context = *static_cast<ScanContext*>(iterator->context);
context.currBlock.base = 0;
context.currBlock.size = 0;
context.buffer.clear();
iterator->last_error = ERROR_SUCCESS;
return iterator->next(iterator);
};
iterator.next = [](YR_MEMORY_BLOCK_ITERATOR* iterator) -> YR_MEMORY_BLOCK* {
auto &context = *static_cast<ScanContext*>(iterator->context);
u64 address = context.currBlock.base + context.currBlock.size;
iterator->last_error = ERROR_SUCCESS;
context.currBlock.base = address;
context.currBlock.size = std::min<u64>(0xF'FFFF, SharedData::currentProvider->getSize() - address);
context.currBlock.context = &context;
if (context.currBlock.size == 0) return nullptr;
return &context.currBlock;
};
yr_rules_scan_mem_blocks(rules, &iterator, 0, [](YR_SCAN_CONTEXT* context, int message, void *data, void *userData) -> int {
if (message == CALLBACK_MSG_RULE_MATCHING) {
auto &newMatches = *static_cast<std::vector<YaraMatch>*>(userData);
auto rule = static_cast<YR_RULE*>(data);
YR_STRING *string;
YR_MATCH *match;
if (rule->strings != nullptr) {
yr_rule_strings_foreach(rule, string) {
yr_string_matches_foreach(context, string, match) {
newMatches.push_back({ rule->identifier, match->offset, match->match_length, false });
}
}
} else {
newMatches.push_back({ rule->identifier, 0, 0, true });
}
}
return CALLBACK_CONTINUE;
}, &newMatches, 0);
std::copy(newMatches.begin(), newMatches.end(), std::back_inserter(this->m_matches));
yr_compiler_destroy(compiler);
this->m_matching = false;
}).detach();
}
}