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:
parent
372981920e
commit
31e5ec7bc3
4
.gitmodules
vendored
4
.gitmodules
vendored
@ -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
|
||||||
|
@ -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>
|
@ -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
125
external/yara/CMakeLists.txt
vendored
Normal 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
143
external/yara/crypto_mbedtls.h
vendored
Normal 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
1
external/yara/yara
vendored
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 24350d8346b994146578753fbc0e9f8c198cad64
|
36
include/views/view_yara.hpp
Normal file
36
include/views/view_yara.hpp
Normal 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();
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -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" },
|
||||||
|
@ -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
243
source/views/view_yara.cpp
Normal 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();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user