views: Add simple pattern, library and magics store
This commit is contained in:
parent
fcfaaacdcc
commit
4b40546750
@ -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}
|
||||
)
|
||||
|
@ -2,24 +2,53 @@
|
||||
|
||||
#include <hex.hpp>
|
||||
|
||||
#include <imgui.h>
|
||||
#include <hex/views/view.hpp>
|
||||
#include <hex/helpers/net.hpp>
|
||||
#include <hex/helpers/paths.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <future>
|
||||
#include <string>
|
||||
|
||||
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<Response<std::string>> m_apiRequest;
|
||||
std::future<Response<void>> m_download;
|
||||
|
||||
std::vector<StoreEntry> 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);
|
||||
};
|
||||
|
||||
}
|
@ -24,8 +24,17 @@ namespace hex::crypt {
|
||||
std::array<u8, 48> sha384(prv::Provider* &data, u64 offset, size_t size);
|
||||
std::array<u8, 64> sha512(prv::Provider* &data, u64 offset, size_t size);
|
||||
|
||||
std::array<u8, 16> md5(const std::vector<u8> &data);
|
||||
std::array<u8, 20> sha1(const std::vector<u8> &data);
|
||||
std::array<u8, 28> sha224(const std::vector<u8> &data);
|
||||
std::array<u8, 32> sha256(const std::vector<u8> &data);
|
||||
std::array<u8, 48> sha384(const std::vector<u8> &data);
|
||||
std::array<u8, 64> sha512(const std::vector<u8> &data);
|
||||
|
||||
std::vector<u8> decode64(const std::vector<u8> &input);
|
||||
std::vector<u8> encode64(const std::vector<u8> &input);
|
||||
std::vector<u8> decode16(const std::string &input);
|
||||
std::string encode16(const std::vector<u8> &input);
|
||||
|
||||
enum class AESMode : u8 {
|
||||
ECB = 0,
|
||||
|
@ -21,6 +21,11 @@ namespace hex {
|
||||
T body;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Response<void> {
|
||||
s32 code;
|
||||
};
|
||||
|
||||
class Net {
|
||||
public:
|
||||
Net();
|
||||
@ -30,6 +35,7 @@ namespace hex {
|
||||
std::future<Response<nlohmann::json>> getJson(const std::string &url);
|
||||
|
||||
std::future<Response<std::string>> uploadFile(const std::string &url, const std::filesystem::path &filePath);
|
||||
std::future<Response<void>> downloadFile(const std::string &url, const std::filesystem::path &filePath);
|
||||
|
||||
[[nodiscard]]
|
||||
std::string encode(const std::string &input) {
|
||||
|
@ -1,9 +1,11 @@
|
||||
#include <hex/helpers/crypto.hpp>
|
||||
|
||||
#include <hex/providers/provider.hpp>
|
||||
#include <hex/helpers/utils.hpp>
|
||||
|
||||
#include <mbedtls/version.h>
|
||||
#include <mbedtls/base64.h>
|
||||
#include <mbedtls/bignum.h>
|
||||
#include <mbedtls/md5.h>
|
||||
#include <mbedtls/sha1.h>
|
||||
#include <mbedtls/sha256.h>
|
||||
@ -131,6 +133,21 @@ namespace hex::crypt {
|
||||
return result;
|
||||
}
|
||||
|
||||
std::array<u8, 16> md5(const std::vector<u8> &data) {
|
||||
std::array<u8, 16> 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<u8, 20> sha1(prv::Provider* &data, u64 offset, size_t size) {
|
||||
std::array<u8, 20> result = { 0 };
|
||||
|
||||
@ -153,6 +170,21 @@ namespace hex::crypt {
|
||||
return result;
|
||||
}
|
||||
|
||||
std::array<u8, 20> sha1(const std::vector<u8> &data) {
|
||||
std::array<u8, 20> 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<u8, 28> sha224(prv::Provider* &data, u64 offset, size_t size) {
|
||||
std::array<u8, 28> result = { 0 };
|
||||
|
||||
@ -175,6 +207,21 @@ namespace hex::crypt {
|
||||
return result;
|
||||
}
|
||||
|
||||
std::array<u8, 28> sha224(const std::vector<u8> &data) {
|
||||
std::array<u8, 28> 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<u8, 32> sha256(prv::Provider* &data, u64 offset, size_t size) {
|
||||
std::array<u8, 32> result = { 0 };
|
||||
|
||||
@ -197,6 +244,21 @@ namespace hex::crypt {
|
||||
return result;
|
||||
}
|
||||
|
||||
std::array<u8, 32> sha256(const std::vector<u8> &data) {
|
||||
std::array<u8, 32> 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<u8, 48> sha384(prv::Provider* &data, u64 offset, size_t size) {
|
||||
std::array<u8, 48> result = { 0 };
|
||||
|
||||
@ -219,6 +281,21 @@ namespace hex::crypt {
|
||||
return result;
|
||||
}
|
||||
|
||||
std::array<u8, 48> sha384(const std::vector<u8> &data) {
|
||||
std::array<u8, 48> 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<u8, 64> sha512(prv::Provider* &data, u64 offset, size_t size) {
|
||||
std::array<u8, 64> result = { 0 };
|
||||
|
||||
@ -241,6 +318,22 @@ namespace hex::crypt {
|
||||
return result;
|
||||
}
|
||||
|
||||
std::array<u8, 64> sha512(const std::vector<u8> &data) {
|
||||
std::array<u8, 64> 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<u8> decode64(const std::vector<u8> &input) {
|
||||
size_t outputSize = (3 * input.size()) / 4;
|
||||
std::vector<u8> output(outputSize + 1, 0x00);
|
||||
@ -263,6 +356,41 @@ namespace hex::crypt {
|
||||
return output;
|
||||
}
|
||||
|
||||
std::vector<u8> decode16(const std::string &input) {
|
||||
std::vector<u8> 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<u8> &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<u8> aes(mbedtls_cipher_type_t type, mbedtls_operation_t operation, const std::vector<u8> &key, std::array<u8, 8> nonce, std::array<u8, 8> iv, const std::vector<u8> &input) {
|
||||
std::vector<u8> output;
|
||||
|
||||
|
@ -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<FILE*>(userdata);
|
||||
|
||||
return fwrite(contents, size, nmemb, file);
|
||||
}
|
||||
|
||||
static CURLcode sslCtxFunction(CURL *ctx, void *sslctx, void *userdata) {
|
||||
auto* cfg = static_cast<mbedtls_ssl_config*>(sslctx);
|
||||
|
||||
@ -203,4 +209,28 @@ namespace hex {
|
||||
});
|
||||
}
|
||||
|
||||
std::future<Response<void>> 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<void> { 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<void> { responseCode.value_or(0) };
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@ -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<ViewDataProcessor>();
|
||||
ContentRegistry::Views::add<ViewYara>();
|
||||
ContentRegistry::Views::add<ViewConstants>();
|
||||
ContentRegistry::Views::add<ViewStore>();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -1,26 +1,218 @@
|
||||
#include "views/view_tools.hpp"
|
||||
#include "views/view_store.hpp"
|
||||
|
||||
#include <hex/providers/provider.hpp>
|
||||
#include <imgui.h>
|
||||
#define IMGUI_DEFINE_MATH_OPERATORS
|
||||
#include <imgui_internal.h>
|
||||
|
||||
#include <hex/helpers/utils.hpp>
|
||||
#include <hex/helpers/crypto.hpp>
|
||||
#include <hex/helpers/logger.hpp>
|
||||
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
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<StoreEntry> &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<u8> data(fs::file_size(path), 0x00);
|
||||
file.read(reinterpret_cast<char*>(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));
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user