From 31e5ec7bc3c9bba53c2d211f647c29a50ede07d1 Mon Sep 17 00:00:00 2001 From: WerWolv Date: Fri, 26 Feb 2021 13:35:19 +0100 Subject: [PATCH] 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 --- .gitmodules | 4 + .idea/vcs.xml | 1 + CMakeLists.txt | 8 +- external/yara/CMakeLists.txt | 125 +++++++++++++ external/yara/crypto_mbedtls.h | 143 +++++++++++++++ external/yara/yara | 1 + include/views/view_yara.hpp | 36 ++++ plugins/builtin/source/lang/en_US.cpp | 10 ++ source/main.cpp | 2 + source/views/view_yara.cpp | 243 ++++++++++++++++++++++++++ 10 files changed, 570 insertions(+), 3 deletions(-) create mode 100644 external/yara/CMakeLists.txt create mode 100644 external/yara/crypto_mbedtls.h create mode 160000 external/yara/yara create mode 100644 include/views/view_yara.hpp create mode 100644 source/views/view_yara.cpp diff --git a/.gitmodules b/.gitmodules index 991a4c291..e3ab41e22 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,7 @@ [submodule "external/nativefiledialog"] path = external/nativefiledialog url = https://github.com/btzy/nativefiledialog-extended +[submodule "external/yara/yara"] + path = external/yara/yara + url = https://github.com/VirusTotal/yara + ignore = dirty diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 94a25f7f4..9860228c3 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -2,5 +2,6 @@ + \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 649d208c3..5fefa9728 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,6 +26,7 @@ detectArch() # Add bundled dependencies add_subdirectory(external/llvm) +add_subdirectory(external/yara) add_subdirectory(plugins/libimhex) # Add include directories @@ -61,6 +62,7 @@ add_executable(imhex ${application_type} source/views/view_command_palette.cpp source/views/view_settings.cpp source/views/view_data_processor.cpp + source/views/view_yara.cpp ${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}) 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) - 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() -createPackage() \ No newline at end of file +createPackage() diff --git a/external/yara/CMakeLists.txt b/external/yara/CMakeLists.txt new file mode 100644 index 000000000..3673f1dfc --- /dev/null +++ b/external/yara/CMakeLists.txt @@ -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 $ $ + 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) \ No newline at end of file diff --git a/external/yara/crypto_mbedtls.h b/external/yara/crypto_mbedtls.h new file mode 100644 index 000000000..19c16296c --- /dev/null +++ b/external/yara/crypto_mbedtls.h @@ -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 +#include +#include + +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 + +#include + +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 + +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 +#include +#include + +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 diff --git a/external/yara/yara b/external/yara/yara new file mode 160000 index 000000000..24350d834 --- /dev/null +++ b/external/yara/yara @@ -0,0 +1 @@ +Subproject commit 24350d8346b994146578753fbc0e9f8c198cad64 diff --git a/include/views/view_yara.hpp b/include/views/view_yara.hpp new file mode 100644 index 000000000..924759510 --- /dev/null +++ b/include/views/view_yara.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include + +#include +#include + +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 m_rules; + std::vector m_matches; + u32 m_selectedRule = 0; + bool m_matching = false; + std::vector m_errorMessage; + + void reloadRules(); + void applyRules(); + }; + +} \ No newline at end of file diff --git a/plugins/builtin/source/lang/en_US.cpp b/plugins/builtin/source/lang/en_US.cpp index 7303b0677..9733eba5c 100644 --- a/plugins/builtin/source/lang/en_US.cpp +++ b/plugins/builtin/source/lang/en_US.cpp @@ -253,6 +253,16 @@ namespace hex::plugin::builtin { { "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 */ { "hex.builtin.command.calc.desc", "Calculator" }, diff --git a/source/main.cpp b/source/main.cpp index f630a0c86..07dc11df9 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -22,6 +22,7 @@ #include "views/view_command_palette.hpp" #include "views/view_settings.hpp" #include "views/view_data_processor.hpp" +#include "views/view_yara.hpp" #include @@ -49,6 +50,7 @@ int main(int argc, char **argv) { ContentRegistry::Views::add(); ContentRegistry::Views::add(); ContentRegistry::Views::add(); + ContentRegistry::Views::add(); if (argc > 1) View::postEvent(Events::FileDropped, argv[1]); diff --git a/source/views/view_yara.cpp b/source/views/view_yara.cpp new file mode 100644 index 000000000..a9dfa8df6 --- /dev/null +++ b/source/views/view_yara.cpp @@ -0,0 +1,243 @@ +#include "views/view_yara.hpp" + +#include + +#include +#include +#include + +#include + +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("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 newMatches; + + YR_MEMORY_BLOCK_ITERATOR iterator; + + struct ScanContext { + std::vector buffer; + YR_MEMORY_BLOCK currBlock; + }; + + ScanContext context; + context.currBlock.base = 0; + context.currBlock.fetch_data = [](auto *block) -> const u8* { + auto &context = *static_cast(block->context); + + auto &provider = SharedData::currentProvider; + + context.buffer.resize(std::min(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(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(iterator->context); + + u64 address = context.currBlock.base + context.currBlock.size; + + iterator->last_error = ERROR_SUCCESS; + context.currBlock.base = address; + context.currBlock.size = std::min(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*>(userData); + auto rule = static_cast(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(); + + } + +} \ No newline at end of file