Added event system and make use of it for data invalidation

This commit is contained in:
WerWolv
2020-11-12 09:38:52 +01:00
parent e3df658b4a
commit 3a6d19eca4
13 changed files with 355 additions and 179 deletions

View File

@@ -5,7 +5,7 @@
#include "views/view_pattern.hpp"
#include "views/view_pattern_data.hpp"
#include "views/view_hashes.hpp"
#include "views/view_entropy.hpp"
#include "views/view_information.hpp"
#include "providers/provider.hpp"
@@ -23,12 +23,9 @@ int main() {
window.addView<hex::ViewPattern>(highlights);
window.addView<hex::ViewPatternData>(dataProvider, highlights);
window.addView<hex::ViewHashes>(dataProvider);
window.addView<hex::ViewEntropy>(dataProvider);
window.addView<hex::ViewInformation>(dataProvider);
window.loop();
if (dataProvider != nullptr)
delete dataProvider;
return 0;
}

View File

@@ -1,85 +0,0 @@
#include "views/view_entropy.hpp"
#include "providers/provider.hpp"
#include "utils.hpp"
#include <vector>
#include <cstring>
#include <cmath>
namespace hex {
ViewEntropy::ViewEntropy(prv::Provider* &dataProvider) : View(), m_dataProvider(dataProvider) {
}
ViewEntropy::~ViewEntropy() {
}
void ViewEntropy::createView() {
if (!this->m_windowOpen)
return;
if (ImGui::Begin("Entropy", &this->m_windowOpen)) {
ImGui::BeginChild("##scrolling", ImVec2(0, 0), false, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav);
if (this->m_dataProvider != nullptr && this->m_dataProvider->isReadable()) {
if (this->m_shouldInvalidate) {
std::vector<u8> buffer(512, 0x00);
std::memset(this->m_valueCounts, 0x00, sizeof(this->m_valueCounts));
for (u64 i = 0; i < this->m_dataProvider->getSize(); i += 512) {
this->m_dataProvider->read(i, buffer.data(), std::min(512ULL, this->m_dataProvider->getSize() - i));
for (u16 j = 0; j < 512; j++)
this->m_valueCounts[buffer[j]]++;
}
size_t numBytes = this->m_dataProvider->getSize();
this->m_entropy = 0;
for (u16 i = 0; i < 256; i++) {
this->m_valueCounts[i] /= numBytes;
if (this->m_valueCounts[i] > 0)
this->m_entropy -= this->m_valueCounts[i] * std::log2(this->m_valueCounts[i]);
}
this->m_entropy /= 8;
this->m_shouldInvalidate = false;
}
ImGui::LabelText("Entropy", "%.8f", this->m_entropy);
if (this->m_entropy > 0.99)
ImGui::TextUnformatted("This file is most likely encrypted or compressed!");
ImGui::NewLine();
ImGui::Separator();
ImGui::NewLine();
ImGui::PlotHistogram("Byte Distribution", this->m_valueCounts, 256, 0, nullptr, FLT_MAX, FLT_MAX, ImVec2(0, 100));
ImGui::NewLine();
ImGui::Separator();
ImGui::NewLine();
if (ImGui::Button("Invalidate"))
this->m_shouldInvalidate = true;
}
ImGui::EndChild();
}
ImGui::End();
}
void ViewEntropy::createMenu() {
if (ImGui::BeginMenu("View")) {
ImGui::MenuItem("Entropy View", "", &this->m_windowOpen);
ImGui::EndMenu();
}
}
}

View File

@@ -9,17 +9,17 @@
namespace hex {
ViewHashes::ViewHashes(prv::Provider* &dataProvider) : View(), m_dataProvider(dataProvider) {
View::subscribeEvent(Events::DataChanged, [this](void*){
this->m_shouldInvalidate = true;
});
}
ViewHashes::~ViewHashes() {
View::unsubscribeEvent(Events::DataChanged);
}
void ViewHashes::createView() {
static bool invalidate = false;
if (!this->m_windowOpen)
return;
@@ -29,16 +29,18 @@ namespace hex {
if (this->m_dataProvider != nullptr && this->m_dataProvider->isAvailable()) {
ImGui::Combo("Hash Function", &this->m_currHashFunction, HashFunctionNames,
sizeof(HashFunctionNames) / sizeof(const char *));
ImGui::Combo("Hash Function", &this->m_currHashFunction, HashFunctionNames,sizeof(HashFunctionNames) / sizeof(const char *));
if (ImGui::IsItemEdited()) this->m_shouldInvalidate = true;
ImGui::NewLine();
ImGui::Separator();
ImGui::NewLine();
ImGui::InputInt("Begin", &this->m_hashStart, 0, 0, ImGuiInputTextFlags_CharsHexadecimal);
if (ImGui::IsItemEdited()) this->m_shouldInvalidate = true;
ImGui::InputInt("End", &this->m_hashEnd, 0, 0, ImGuiInputTextFlags_CharsHexadecimal);
if (ImGui::IsItemEdited()) this->m_shouldInvalidate = true;
size_t dataSize = this->m_dataProvider->getSize();
if (this->m_hashEnd >= dataSize)
@@ -52,7 +54,7 @@ namespace hex {
std::vector<u8> buffer;
if (invalidate) {
if (this->m_shouldInvalidate) {
buffer = std::vector<u8>(this->m_hashEnd - this->m_hashStart + 1, 0x00);
this->m_dataProvider->read(this->m_hashStart, buffer.data(), buffer.size());
}
@@ -63,7 +65,10 @@ namespace hex {
static int polynomial = 0, init = 0;
ImGui::InputInt("Initial Value", &init, 0, 0, ImGuiInputTextFlags_CharsHexadecimal);
if (ImGui::IsItemEdited()) this->m_shouldInvalidate = true;
ImGui::InputInt("Polynomial", &polynomial, 0, 0, ImGuiInputTextFlags_CharsHexadecimal);
if (ImGui::IsItemEdited()) this->m_shouldInvalidate = true;
ImGui::NewLine();
ImGui::Separator();
@@ -71,7 +76,7 @@ namespace hex {
static u16 result = 0;
if (invalidate)
if (this->m_shouldInvalidate)
result = crc16(buffer.data(), buffer.size(), polynomial, init);
ImGui::LabelText("##nolabel", "%X", result);
@@ -82,7 +87,10 @@ namespace hex {
static int polynomial = 0, init = 0;
ImGui::InputInt("Initial Value", &init, 0, 0, ImGuiInputTextFlags_CharsHexadecimal);
if (ImGui::IsItemEdited()) this->m_shouldInvalidate = true;
ImGui::InputInt("Polynomial", &polynomial, 0, 0, ImGuiInputTextFlags_CharsHexadecimal);
if (ImGui::IsItemEdited()) this->m_shouldInvalidate = true;
ImGui::NewLine();
ImGui::Separator();
@@ -90,7 +98,7 @@ namespace hex {
static u32 result = 0;
if (invalidate)
if (this->m_shouldInvalidate)
result = crc32(buffer.data(), buffer.size(), polynomial, init);
ImGui::LabelText("##nolabel", "%X", result);
@@ -100,7 +108,7 @@ namespace hex {
{
static std::array<u32, 4> result;
if (invalidate)
if (this->m_shouldInvalidate)
result = md5(buffer.data(), buffer.size());
ImGui::LabelText("##nolabel", "%08X%08X%08X%08X",
@@ -114,12 +122,7 @@ namespace hex {
}
invalidate = false;
ImGui::SameLine();
if (ImGui::Button("Hash"))
invalidate = true;
this->m_shouldInvalidate = false;
}
ImGui::EndChild();
}

View File

@@ -29,6 +29,7 @@ namespace hex {
return;
_this->m_dataProvider->write(off, &d, sizeof(ImU8));
_this->postEvent(Events::DataChanged);
};
this->m_memoryEditor.HighlightFn = [](const ImU8 *data, size_t off, bool next) -> bool {
@@ -50,9 +51,13 @@ namespace hex {
};
}
ViewHexEditor::~ViewHexEditor() {}
ViewHexEditor::~ViewHexEditor() {
if (this->m_dataProvider != nullptr)
delete this->m_dataProvider;
this->m_dataProvider = nullptr;
}
auto findString(prv::Provider* &provider, std::string string) {
static auto findString(prv::Provider* &provider, std::string string) {
std::vector<std::pair<u64, u64>> results;
u32 foundCharacters = 0;
@@ -76,24 +81,76 @@ namespace hex {
}
}
return results;
}
void ViewHexEditor::createView() {
this->m_memoryEditor.DrawWindow("Hex Editor", this, (this->m_dataProvider == nullptr || !this->m_dataProvider->isReadable()) ? 0x00 : this->m_dataProvider->getSize());
void ViewHexEditor::createView() {
if (!this->m_memoryEditor.Open)
return;
size_t dataSize = (this->m_dataProvider == nullptr || !this->m_dataProvider->isReadable()) ? 0x00 : this->m_dataProvider->getSize();
this->m_memoryEditor.DrawWindow("Hex Editor", this, dataSize);
if (dataSize != 0x00) {
this->drawSearchPopup();
this->drawGotoPopup();
}
}
void ViewHexEditor::createMenu() {
if (ImGui::BeginMenu("File")) {
if (ImGui::MenuItem("Open File...")) {
auto filePath = openFileDialog();
if (filePath.has_value()) {
if (this->m_dataProvider != nullptr)
delete this->m_dataProvider;
this->m_dataProvider = new prv::FileProvider(filePath.value());
View::postEvent(Events::DataChanged);
}
}
ImGui::EndMenu();
}
if (ImGui::BeginMenu("View")) {
ImGui::MenuItem("Hex View", "", &this->m_memoryEditor.Open);
ImGui::EndMenu();
}
}
bool ViewHexEditor::handleShortcut(int key, int mods) {
if (mods & GLFW_MOD_CONTROL && key == GLFW_KEY_F) {
ImGui::OpenPopup("Search");
return true;
} else if (mods & GLFW_MOD_CONTROL && key == GLFW_KEY_G) {
ImGui::OpenPopup("Goto");
return true;
}
return false;
}
void ViewHexEditor::drawSearchPopup() {
if (ImGui::BeginPopup("Search")) {
ImGui::TextUnformatted("Search");
ImGui::InputText("##nolabel", this->m_searchBuffer, 0xFFFF, ImGuiInputTextFlags_CallbackEdit,
[](ImGuiInputTextCallbackData* data) -> int {
auto lastSearch = static_cast<std::vector<std::pair<u64, u64>>*>(data->UserData);
ImGui::InputText("##nolabel", this->m_searchBuffer, 0xFFFF, ImGuiInputTextFlags_CallbackCompletion,
[](ImGuiInputTextCallbackData* data) -> int {
auto _this = static_cast<ViewHexEditor*>(data->UserData);
lastSearch->clear();
_this->m_lastSearch = findString(_this->m_dataProvider, _this->m_searchBuffer);
_this->m_lastSearchIndex = 0;
return 0;
}, &this->m_lastSearch);
if (_this->m_lastSearch.size() > 0)
_this->m_memoryEditor.GotoAddrAndHighlight(_this->m_lastSearch[0].first, _this->m_lastSearch[0].second);
return 0;
}, this);
if (ImGui::Button("Find")) {
@@ -135,35 +192,20 @@ namespace hex {
}
}
void ViewHexEditor::createMenu() {
if (ImGui::BeginMenu("File")) {
if (ImGui::MenuItem("Open File...")) {
auto filePath = openFileDialog();
if (filePath.has_value()) {
if (this->m_dataProvider != nullptr)
delete this->m_dataProvider;
void ViewHexEditor::drawGotoPopup() {
if (ImGui::BeginPopup("Goto")) {
ImGui::TextUnformatted("Goto");
ImGui::InputScalar("##nolabel", ImGuiDataType_U64, &this->m_gotoAddress, nullptr, nullptr, "%llx", ImGuiInputTextFlags_CharsHexadecimal);
this->m_dataProvider = new prv::FileProvider(filePath.value());
}
if (this->m_gotoAddress >= this->m_dataProvider->getSize())
this->m_gotoAddress = this->m_dataProvider->getSize() - 1;
if (ImGui::Button("Goto")) {
this->m_memoryEditor.GotoAddr = this->m_gotoAddress;
}
ImGui::EndMenu();
ImGui::EndPopup();
}
if (ImGui::BeginMenu("View")) {
ImGui::MenuItem("Hex View", "", &this->m_memoryEditor.Open);
ImGui::EndMenu();
}
}
bool ViewHexEditor::handleShortcut(int key, int mods) {
if (mods & GLFW_MOD_CONTROL && key == GLFW_KEY_F) {
ImGui::OpenPopup("Search");
return true;
}
return false;
}
}

View File

@@ -0,0 +1,139 @@
#include "views/view_information.hpp"
#include "providers/provider.hpp"
#include "utils.hpp"
#include <algorithm>
#include <vector>
#include <cstring>
#include <cmath>
#include <magic.h>
#include <span>
namespace hex {
ViewInformation::ViewInformation(prv::Provider* &dataProvider) : View(), m_dataProvider(dataProvider) {
View::subscribeEvent(Events::DataChanged, [this](void*) {
this->m_shouldInvalidate = true;
});
}
ViewInformation::~ViewInformation() {
View::unsubscribeEvent(Events::DataChanged);
}
static float calculateEntropy(std::array<float, 256> &valueCounts, size_t numBytes) {
float entropy = 0;
for (u16 i = 0; i < 256; i++) {
valueCounts[i] /= numBytes;
if (valueCounts[i] > 0)
entropy -= (valueCounts[i] * std::log2(valueCounts[i]));
}
return entropy / 8;
}
void ViewInformation::createView() {
if (!this->m_windowOpen)
return;
if (ImGui::Begin("File Information", &this->m_windowOpen)) {
ImGui::BeginChild("##scrolling", ImVec2(0, 0), false, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav);
if (this->m_dataProvider != nullptr && this->m_dataProvider->isReadable()) {
if (this->m_shouldInvalidate) {
{
std::vector<u8> buffer(512, 0x00);
std::memset(this->m_valueCounts.data(), 0x00, this->m_valueCounts.size() * sizeof(u32));
this->m_blockEntropy.clear();
for (u64 i = 0; i < this->m_dataProvider->getSize(); i += 512) {
std::array<float, 256> blockValueCounts = { 0 };
this->m_dataProvider->read(i, buffer.data(), std::min(512ULL, this->m_dataProvider->getSize() - i));
for (u16 j = 0; j < 512; j++) {
blockValueCounts[buffer[j]]++;
this->m_valueCounts[buffer[j]]++;
}
this->m_blockEntropy.push_back(calculateEntropy(blockValueCounts, 512));
}
this->m_averageEntropy = calculateEntropy(this->m_valueCounts, this->m_dataProvider->getSize());
this->m_highestBlockEntropy = *std::max_element(this->m_blockEntropy.begin(), this->m_blockEntropy.end());
}
{
std::vector<u8> buffer(this->m_dataProvider->getSize(), 0x00);
this->m_dataProvider->read(0x00, buffer.data(), buffer.size());
{
magic_t cookie = magic_open(MAGIC_NONE);
magic_load(cookie, "magic");
this->m_fileDescription = magic_buffer(cookie, buffer.data(), buffer.size());
magic_close(cookie);
}
{
magic_t cookie = magic_open(MAGIC_MIME);
magic_load(cookie, "magic");
this->m_mimeType = magic_buffer(cookie, buffer.data(), buffer.size());
magic_close(cookie);
}
this->m_shouldInvalidate = false;
}
}
ImGui::NewLine();
ImGui::Text("Byte Distribution");
ImGui::PlotHistogram("##nolabel", this->m_valueCounts.data(), 256, 0, nullptr, FLT_MAX, FLT_MAX, ImVec2(0, 100));
ImGui::NewLine();
ImGui::Separator();
ImGui::NewLine();
ImGui::Text("Entropy");
ImGui::PlotLines("##nolabel", this->m_blockEntropy.data(), this->m_blockEntropy.size(), 0, nullptr, FLT_MAX, FLT_MAX, ImVec2(0, 100));
ImGui::NewLine();
ImGui::LabelText("Average entropy", "%.8f", this->m_averageEntropy);
ImGui::LabelText("Highest entropy block", "%.8f", this->m_highestBlockEntropy);
ImGui::NewLine();
if (this->m_averageEntropy > 0.83 && this->m_highestBlockEntropy > 0.9)
ImGui::TextColored(ImVec4(0.92F, 0.25F, 0.2F, 1.0F), "This data is most likely encrypted or compressed!");
ImGui::NewLine();
ImGui::Separator();
ImGui::NewLine();
ImGui::TextUnformatted("Description:");
ImGui::TextWrapped("%s", this->m_fileDescription.c_str());
ImGui::NewLine();
ImGui::TextUnformatted("MIME Type:");
ImGui::TextWrapped("%s", this->m_mimeType.c_str());
ImGui::NewLine();
}
ImGui::EndChild();
}
ImGui::End();
}
void ViewInformation::createMenu() {
if (ImGui::BeginMenu("View")) {
ImGui::MenuItem("Entropy View", "", &this->m_windowOpen);
ImGui::EndMenu();
}
}
}