From 4b4054675016ece4b874706b60cfe83f816f2d80 Mon Sep 17 00:00:00 2001 From: WerWolv Date: Fri, 3 Sep 2021 02:34:40 +0200 Subject: [PATCH] views: Add simple pattern, library and magics store --- CMakeLists.txt | 1 + include/views/view_store.hpp | 39 +++- .../libimhex/include/hex/helpers/crypto.hpp | 9 + plugins/libimhex/include/hex/helpers/net.hpp | 6 + plugins/libimhex/source/helpers/crypto.cpp | 128 ++++++++++ plugins/libimhex/source/helpers/net.cpp | 30 +++ source/init/tasks.cpp | 2 + source/views/view_store.cpp | 218 ++++++++++++++++-- 8 files changed, 415 insertions(+), 18 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 01ce4be02..33a343175 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -90,6 +90,7 @@ add_executable(imhex ${application_type} source/views/view_data_processor.cpp source/views/view_yara.cpp source/views/view_constants.cpp + source/views/view_store.cpp ${imhex_icon} ) diff --git a/include/views/view_store.hpp b/include/views/view_store.hpp index c202c3ac9..cb23ea401 100644 --- a/include/views/view_store.hpp +++ b/include/views/view_store.hpp @@ -2,24 +2,53 @@ #include -#include #include +#include +#include #include +#include #include namespace hex { - namespace prv { class Provider; } + struct StoreEntry { + std::string name; + std::string description; + std::string fileName; + std::string link; + std::string hash; - class ViewTools : public View { + bool downloading; + bool installed; + bool hasUpdate; + }; + + class ViewStore : public View { public: - ViewTools(); - ~ViewTools() override; + ViewStore(); + ~ViewStore() override; void drawContent() override; void drawMenu() override; + bool isAvailable() override { return true; } + bool hasViewMenuItemEntry() override { return false; } + + private: + Net m_net; + std::future> m_apiRequest; + std::future> m_download; + + std::vector m_patterns, m_magics, m_includes; + + void drawStore(); + + void refresh(); + void parseResponse(); + + void download(ImHexPath pathType, const std::string &fileName, const std::string &url, bool update); + void remove(ImHexPath pathType, const std::string &fileName); }; } \ No newline at end of file diff --git a/plugins/libimhex/include/hex/helpers/crypto.hpp b/plugins/libimhex/include/hex/helpers/crypto.hpp index 2698312aa..7faa74688 100644 --- a/plugins/libimhex/include/hex/helpers/crypto.hpp +++ b/plugins/libimhex/include/hex/helpers/crypto.hpp @@ -24,8 +24,17 @@ namespace hex::crypt { std::array sha384(prv::Provider* &data, u64 offset, size_t size); std::array sha512(prv::Provider* &data, u64 offset, size_t size); + std::array md5(const std::vector &data); + std::array sha1(const std::vector &data); + std::array sha224(const std::vector &data); + std::array sha256(const std::vector &data); + std::array sha384(const std::vector &data); + std::array sha512(const std::vector &data); + std::vector decode64(const std::vector &input); std::vector encode64(const std::vector &input); + std::vector decode16(const std::string &input); + std::string encode16(const std::vector &input); enum class AESMode : u8 { ECB = 0, diff --git a/plugins/libimhex/include/hex/helpers/net.hpp b/plugins/libimhex/include/hex/helpers/net.hpp index d66cbfe54..796c39fc3 100644 --- a/plugins/libimhex/include/hex/helpers/net.hpp +++ b/plugins/libimhex/include/hex/helpers/net.hpp @@ -21,6 +21,11 @@ namespace hex { T body; }; + template<> + struct Response { + s32 code; + }; + class Net { public: Net(); @@ -30,6 +35,7 @@ namespace hex { std::future> getJson(const std::string &url); std::future> uploadFile(const std::string &url, const std::filesystem::path &filePath); + std::future> downloadFile(const std::string &url, const std::filesystem::path &filePath); [[nodiscard]] std::string encode(const std::string &input) { diff --git a/plugins/libimhex/source/helpers/crypto.cpp b/plugins/libimhex/source/helpers/crypto.cpp index 0cb3624bf..072ebffba 100644 --- a/plugins/libimhex/source/helpers/crypto.cpp +++ b/plugins/libimhex/source/helpers/crypto.cpp @@ -1,9 +1,11 @@ #include #include +#include #include #include +#include #include #include #include @@ -131,6 +133,21 @@ namespace hex::crypt { return result; } + std::array md5(const std::vector &data) { + std::array result = { 0 }; + + mbedtls_md5_context ctx; + mbedtls_md5_init(&ctx); + + mbedtls_md5_starts(&ctx); + mbedtls_md5_update(&ctx, data.data(), data.size()); + mbedtls_md5_finish(&ctx, result.data()); + + mbedtls_md5_free(&ctx); + + return result; + } + std::array sha1(prv::Provider* &data, u64 offset, size_t size) { std::array result = { 0 }; @@ -153,6 +170,21 @@ namespace hex::crypt { return result; } + std::array sha1(const std::vector &data) { + std::array result = { 0 }; + + mbedtls_sha1_context ctx; + mbedtls_sha1_init(&ctx); + + mbedtls_sha1_starts(&ctx); + mbedtls_sha1_update(&ctx, data.data(), data.size()); + mbedtls_sha1_finish(&ctx, result.data()); + + mbedtls_sha1_free(&ctx); + + return result; + } + std::array sha224(prv::Provider* &data, u64 offset, size_t size) { std::array result = { 0 }; @@ -175,6 +207,21 @@ namespace hex::crypt { return result; } + std::array sha224(const std::vector &data) { + std::array result = { 0 }; + + mbedtls_sha256_context ctx; + mbedtls_sha256_init(&ctx); + + mbedtls_sha256_starts(&ctx, true); + mbedtls_sha256_update(&ctx, data.data(), data.size()); + mbedtls_sha256_finish(&ctx, result.data()); + + mbedtls_sha256_free(&ctx); + + return result; + } + std::array sha256(prv::Provider* &data, u64 offset, size_t size) { std::array result = { 0 }; @@ -197,6 +244,21 @@ namespace hex::crypt { return result; } + std::array sha256(const std::vector &data) { + std::array result = { 0 }; + + mbedtls_sha256_context ctx; + mbedtls_sha256_init(&ctx); + + mbedtls_sha256_starts(&ctx, false); + mbedtls_sha256_update(&ctx, data.data(), data.size()); + mbedtls_sha256_finish(&ctx, result.data()); + + mbedtls_sha256_free(&ctx); + + return result; + } + std::array sha384(prv::Provider* &data, u64 offset, size_t size) { std::array result = { 0 }; @@ -219,6 +281,21 @@ namespace hex::crypt { return result; } + std::array sha384(const std::vector &data) { + std::array result = { 0 }; + + mbedtls_sha512_context ctx; + mbedtls_sha512_init(&ctx); + + mbedtls_sha512_starts(&ctx, true); + mbedtls_sha512_update(&ctx, data.data(), data.size()); + mbedtls_sha512_finish(&ctx, result.data()); + + mbedtls_sha512_free(&ctx); + + return result; + } + std::array sha512(prv::Provider* &data, u64 offset, size_t size) { std::array result = { 0 }; @@ -241,6 +318,22 @@ namespace hex::crypt { return result; } + std::array sha512(const std::vector &data) { + std::array result = { 0 }; + + mbedtls_sha512_context ctx; + mbedtls_sha512_init(&ctx); + + mbedtls_sha512_starts(&ctx, false); + mbedtls_sha512_update(&ctx, data.data(), data.size()); + mbedtls_sha512_finish(&ctx, result.data()); + + mbedtls_sha512_free(&ctx); + + return result; + } + + std::vector decode64(const std::vector &input) { size_t outputSize = (3 * input.size()) / 4; std::vector output(outputSize + 1, 0x00); @@ -263,6 +356,41 @@ namespace hex::crypt { return output; } + std::vector decode16(const std::string &input) { + std::vector output(input.length() / 2, 0x00); + + mbedtls_mpi ctx; + mbedtls_mpi_init(&ctx); + + ON_SCOPE_EXIT { mbedtls_mpi_free(&ctx); }; + + if (mbedtls_mpi_read_string(&ctx, 16, input.c_str())) + return { }; + + if (mbedtls_mpi_write_binary(&ctx, output.data(), output.size())) + return { }; + + return output; + } + + std::string encode16(const std::vector &input) { + std::string output(input.size() * 2 + 1, 0x00); + + mbedtls_mpi ctx; + mbedtls_mpi_init(&ctx); + + ON_SCOPE_EXIT { mbedtls_mpi_free(&ctx); }; + + if (mbedtls_mpi_read_binary(&ctx, input.data(), input.size())) + return { }; + + size_t written = 0; + if (mbedtls_mpi_write_string(&ctx, 16, output.data(), output.size(), &written)) + return { }; + + return output; + } + static std::vector aes(mbedtls_cipher_type_t type, mbedtls_operation_t operation, const std::vector &key, std::array nonce, std::array iv, const std::vector &input) { std::vector output; diff --git a/plugins/libimhex/source/helpers/net.cpp b/plugins/libimhex/source/helpers/net.cpp index e7900e950..dd2b09283 100644 --- a/plugins/libimhex/source/helpers/net.cpp +++ b/plugins/libimhex/source/helpers/net.cpp @@ -42,6 +42,12 @@ namespace hex { return fread(contents, size, nmemb, file); } + static size_t writeToFile(void *contents, size_t size, size_t nmemb, void *userdata) { + FILE *file = static_cast(userdata); + + return fwrite(contents, size, nmemb, file); + } + static CURLcode sslCtxFunction(CURL *ctx, void *sslctx, void *userdata) { auto* cfg = static_cast(sslctx); @@ -203,4 +209,28 @@ namespace hex { }); } + std::future> Net::downloadFile(const std::string &url, const std::filesystem::path &filePath) { + this->m_transmissionActive.lock(); + + return std::async(std::launch::async, [=, this]{ + std::string response; + + ON_SCOPE_EXIT { this->m_transmissionActive.unlock(); }; + + FILE *file = fopen(filePath.string().c_str(), "wb"); + if (file == nullptr) + return Response { 400 }; + + ON_SCOPE_EXIT { fclose(file); }; + + setCommonSettings(response, url, {}); + curl_easy_setopt(this->m_ctx, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(this->m_ctx, CURLOPT_WRITEFUNCTION, writeToFile); + curl_easy_setopt(this->m_ctx, CURLOPT_WRITEDATA, file); + auto responseCode = execute(); + + return Response { responseCode.value_or(0) }; + }); + } + } \ No newline at end of file diff --git a/source/init/tasks.cpp b/source/init/tasks.cpp index 733cce77c..ca8b50421 100644 --- a/source/init/tasks.cpp +++ b/source/init/tasks.cpp @@ -27,6 +27,7 @@ #include "views/view_data_processor.hpp" #include "views/view_yara.hpp" #include "views/view_constants.hpp" +#include "views/view_store.hpp" #include "helpers/plugin_manager.hpp" @@ -188,6 +189,7 @@ namespace hex::init { ContentRegistry::Views::add(); ContentRegistry::Views::add(); ContentRegistry::Views::add(); + ContentRegistry::Views::add(); return true; } diff --git a/source/views/view_store.cpp b/source/views/view_store.cpp index e62129b55..2e77698d6 100644 --- a/source/views/view_store.cpp +++ b/source/views/view_store.cpp @@ -1,26 +1,218 @@ -#include "views/view_tools.hpp" +#include "views/view_store.hpp" -#include +#include +#define IMGUI_DEFINE_MATH_OPERATORS +#include + +#include +#include +#include + +#include +#include +#include namespace hex { - ViewTools::ViewTools() : View("hex.view.tools.name") { } + using namespace std::literals::string_literals; + using namespace std::literals::chrono_literals; + + namespace fs = std::filesystem; - ViewTools::~ViewTools() { } + ViewStore::ViewStore() : View("hex.view.store.name") { + this->refresh(); + } - void ViewTools::drawContent() { - if (ImGui::Begin(View::toWindowName("hex.view.tools.name").c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) { - for (const auto& [name, function] : ContentRegistry::Tools::getEntries()) { - if (ImGui::CollapsingHeader(LangEntry(name))) { - function(); + ViewStore::~ViewStore() { } + + void ViewStore::drawStore() { + ImGui::Header("ImHex content store", true); + + if (ImGui::Button("Reload")) { + this->refresh(); + } + + auto drawTab = [this](auto title, ImHexPath pathType, auto &content) { + if (ImGui::BeginTabItem(title)) { + if (ImGui::BeginTable("##patterns", 3, ImGuiTableFlags_ScrollY | ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingStretchProp)) { + ImGui::TableSetupScrollFreeze(0, 1); + ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_None, 1.0); + ImGui::TableSetupColumn("Description", ImGuiTableColumnFlags_None, 3.0); + ImGui::TableSetupColumn("", ImGuiTableColumnFlags_None, 1.0); + + ImGui::TableHeadersRow(); + + for (auto &entry : content) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::TextUnformatted(entry.name.c_str()); + ImGui::TableNextColumn(); + ImGui::TextUnformatted(entry.description.c_str()); + ImGui::TableNextColumn(); + + ImGui::BeginDisabled(this->m_download.valid() && this->m_download.wait_for(0s) != std::future_status::ready); + { + if (entry.downloading) { + ImGui::TextSpinner("Downloading..."); + + if (this->m_download.valid() && this->m_download.wait_for(0s) == std::future_status::ready) { + entry.downloading = false; + + auto response = this->m_download.get(); + if (response.code == 200) { + entry.installed = true; + entry.hasUpdate = false; + } else + log::error("Download failed!"); + + + this->m_download = { }; + } + + } else if (entry.hasUpdate) { + if (ImGui::Button("Update")) { + this->download(pathType, entry.fileName, entry.link, true); + entry.downloading = true; + } + } else if (!entry.installed) { + if (ImGui::Button("Download")) { + this->download(pathType, entry.fileName, entry.link, false); + entry.downloading = true; + } + } else { + if (ImGui::Button("Remove")) { + this->remove(pathType, entry.fileName); + entry.installed = false; + } + } + } + ImGui::EndDisabled(); + } + + ImGui::EndTable(); + } + ImGui::EndTabItem(); + } + }; + + if (ImGui::BeginTabBar("storeTabs")) { + drawTab("Patterns", ImHexPath::Patterns, this->m_patterns); + drawTab("Libraries", ImHexPath::PatternsInclude, this->m_includes); + drawTab("Magic", ImHexPath::Magic, this->m_magics); + + ImGui::EndTabBar(); + } + } + + void ViewStore::refresh() { + this->m_patterns.clear(); + this->m_includes.clear(); + this->m_magics.clear(); + + this->m_apiRequest = this->m_net.getString(ImHexApiURL + "/store"s); + } + + void ViewStore::parseResponse() { + auto response = this->m_apiRequest.get(); + if (response.code != 200) + ImGui::TextUnformatted("Invalid response from store API"); + else { + auto json = nlohmann::json::parse(response.body); + + auto parseStoreEntries = [](auto storeJson, const std::string &name, ImHexPath pathType, std::vector &results) { + // Check if the response handles the type of files + if (storeJson.contains(name)) { + + for (auto &entry : storeJson[name]) { + + // Check if entry is valid + if (entry.contains("name") && entry.contains("desc") && entry.contains("file") && entry.contains("url") && entry.contains("hash")) { + + // Parse entry + StoreEntry storeEntry = { entry["name"], entry["desc"], entry["file"], entry["url"], entry["hash"], false, false, false }; + + // Check if file is installed already or has an update available + for (auto folder : hex::getPath(pathType)) { + + auto path = folder / fs::path(storeEntry.fileName); + + if (fs::exists(path)) { + storeEntry.installed = true; + + std::ifstream file(path, std::ios::in | std::ios::binary); + std::vector data(fs::file_size(path), 0x00); + file.read(reinterpret_cast(data.data()), data.size()); + + auto fileHash = crypt::sha256(data); + + // Compare installed file hash with hash of repo file + if (std::vector(fileHash.begin(), fileHash.end()) != crypt::decode16(storeEntry.hash)) + storeEntry.hasUpdate = true; + } + } + + results.push_back(storeEntry); + } + } + } + }; + + parseStoreEntries(json, "patterns", ImHexPath::Patterns, this->m_patterns); + parseStoreEntries(json, "includes", ImHexPath::PatternsInclude, this->m_includes); + parseStoreEntries(json, "magic", ImHexPath::Magic, this->m_magics); + + + this->m_apiRequest = { }; + } + + } + + void ViewStore::drawContent() { + ImGui::SetNextWindowSizeConstraints(ImVec2(600, 400) * SharedData::globalScale, ImVec2(FLT_MAX, FLT_MAX)); + if (ImGui::BeginPopupModal(View::toWindowName("hex.view.store.name").c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_AlwaysAutoResize)) { + if (this->m_apiRequest.valid()) { + if (this->m_apiRequest.wait_for(0s) != std::future_status::ready) + ImGui::TextSpinner("Loading store content..."); + else + this->parseResponse(); + } + + this->drawStore(); + + ImGui::EndPopup(); + } else { + this->getWindowOpenState() = false; + } + } + + void ViewStore::drawMenu() { + if (ImGui::BeginMenu("hex.menu.help"_lang)) { + if (ImGui::MenuItem("hex.view.store.name"_lang)) { + View::doLater([]{ ImGui::OpenPopup(View::toWindowName("hex.view.store.name").c_str()); }); + this->getWindowOpenState() = true; + } + + ImGui::EndMenu(); + } + } + + + void ViewStore::download(ImHexPath pathType, const std::string &fileName, const std::string &url, bool update) { + if (!update) { + this->m_download = this->m_net.downloadFile(url, hex::getPath(pathType).front() / fs::path(fileName)); + } else { + for (const auto &path : hex::getPath(pathType)) { + auto fullPath = path / fs::path(fileName); + if (fs::exists(fullPath)) { + this->m_download = this->m_net.downloadFile(url, fullPath); } } } - ImGui::End(); } - - void ViewTools::drawMenu() { - + + void ViewStore::remove(ImHexPath pathType, const std::string &fileName) { + for (const auto &path : hex::getPath(pathType)) + fs::remove(path / fs::path(fileName)); } } \ No newline at end of file