1
0
mirror of synced 2024-11-28 01:20:51 +01:00

ui: Make use of ImPlot to drastically improve information view

This commit is contained in:
WerWolv 2021-03-02 13:49:45 +01:00
parent 2e4eaf8d33
commit de327cf3a4
5 changed files with 55 additions and 15 deletions

View File

@ -15,6 +15,8 @@ namespace ImGui {
void Disabled(const std::function<void()> &widgets, bool disabled);
void TextSpinner(const char* label);
void Header(const char *label, bool firstEntry = false);
enum ImGuiCustomCol {
ImGuiCustomCol_DescButton,
ImGuiCustomCol_DescButtonHovered,

View File

@ -157,6 +157,13 @@ namespace ImGui {
ImGui::Text("[%c] %s", "|/-\\"[ImU32(ImGui::GetTime() * 20) % 4], label);
}
void Header(const char *label, bool firstEntry) {
if (!firstEntry)
ImGui::NewLine();
ImGui::TextUnformatted(label);
ImGui::Separator();
}
ImU32 GetCustomColorU32(ImGuiCustomCol idx, float alpha_mul) {
auto& customData = *static_cast<ImHexCustomData*>(GImGui->IO.UserData);
ImVec4 c = customData.Colors[idx];

View File

@ -26,7 +26,7 @@ namespace hex {
float m_highestBlockEntropy = 0;
std::vector<float> m_blockEntropy;
std::array<float, 256> m_valueCounts = { 0 };
std::array<u64, 256> m_valueCounts = { 0 };
bool m_analyzing = false;
std::pair<u64, u64> m_analyzedRegion = { 0, 0 };

View File

@ -207,7 +207,7 @@ namespace hex::plugin::builtin {
{ "hex.view.information.distribution", "Byte distribution" },
{ "hex.view.information.entropy", "Entropy" },
{ "hex.view.information.block_size", "Block size" },
{ "hex.view.information.block_size.desc", "2046 blocks of %lu bytes" },
{ "hex.view.information.block_size.desc", "%lu blocks of %lu bytes" },
{ "hex.view.information.file_entropy", "File entropy" },
{ "hex.view.information.highest_entropy", "Highest entropy block" },
{ "hex.view.information.encrypted", "This data is most likely encrypted or compressed!" },

View File

@ -13,6 +13,8 @@
#include <magic.h>
#include <imgui_imhex_extensions.h>
#include <implot.h>
#include <implot_internal.h>
namespace hex {
@ -34,14 +36,20 @@ namespace hex {
View::unsubscribeEvent(Events::DataChanged);
}
static float calculateEntropy(std::array<float, 256> &valueCounts, size_t numBytes) {
static float calculateEntropy(std::array<u64, 256> &valueCounts, size_t numBytes) {
float entropy = 0;
for (u16 i = 0; i < 256; i++) {
valueCounts[i] /= numBytes;
if (numBytes == 0)
return 0.0F;
if (valueCounts[i] > 0)
entropy -= (valueCounts[i] * std::log2(valueCounts[i]));
std::array<float, 256> floatValueCounts{ 0 };
std::copy(valueCounts.begin(), valueCounts.end(), floatValueCounts.begin());
for (u16 i = 0; i < 256; i++) {
floatValueCounts[i] /= float(numBytes);
if (floatValueCounts[i] > 0)
entropy -= (floatValueCounts[i] * std::log2(floatValueCounts[i]));
}
return entropy / 8;
@ -56,13 +64,13 @@ namespace hex {
this->m_analyzedRegion = { provider->getBaseAddress(), provider->getBaseAddress() + provider->getSize() };
{
this->m_blockSize = std::ceil(provider->getSize() / 2048.0F);
this->m_blockSize = std::max<u32>(std::ceil(provider->getSize() / 2048.0F), 256);
std::vector<u8> buffer(this->m_blockSize, 0x00);
std::memset(this->m_valueCounts.data(), 0x00, this->m_valueCounts.size() * sizeof(u32));
this->m_blockEntropy.clear();
for (u64 i = 0; i < provider->getSize(); i += this->m_blockSize) {
std::array<float, 256> blockValueCounts = { 0 };
std::array<u64, 256> blockValueCounts = { 0 };
provider->read(i, buffer.data(), std::min(u64(this->m_blockSize), provider->getSize() - i));
for (size_t j = 0; j < this->m_blockSize; j++) {
@ -177,23 +185,46 @@ namespace hex {
ImGui::TextUnformatted("hex.view.information.info_analysis"_lang);
ImGui::Separator();
ImGui::Text("hex.view.information.distribution"_lang);
ImGui::PlotHistogram("##nolabel", this->m_valueCounts.data(), 256, 0, nullptr, FLT_MAX, FLT_MAX,ImVec2(0, 100));
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImGui::GetColorU32(ImGuiCol_WindowBg));
ImGui::TextUnformatted("hex.view.information.distribution"_lang);
ImPlot::SetNextPlotLimits(0, 256, 0, float(*std::max_element(this->m_valueCounts.begin(), this->m_valueCounts.end())) * 1.1F, ImGuiCond_Always);
if (ImPlot::BeginPlot("##distribution", "Address", "Count", ImVec2(-1,0), ImPlotFlags_NoLegend | ImPlotFlags_NoMenus | ImPlotFlags_NoBoxSelect, ImPlotAxisFlags_Lock, ImPlotAxisFlags_Lock)) {
constexpr static auto x = []{
std::array<u64, 256> result{ 0 };
std::iota(result.begin(), result.end(), 0);
return result;
}();
ImPlot::PlotBars("##bytes", x.data(), this->m_valueCounts.data(), x.size(), 0.67);
ImPlot::EndPlot();
}
ImGui::NewLine();
ImGui::Text("hex.view.information.entropy"_lang);
ImGui::PlotLines("##nolabel", this->m_blockEntropy.data(), this->m_blockEntropy.size(), 0, nullptr, FLT_MAX, FLT_MAX, ImVec2(0, 100));
ImGui::TextUnformatted("hex.view.information.entropy"_lang);
ImPlot::SetNextPlotLimits(0, this->m_blockEntropy.size(), -0.1, 1.1, ImGuiCond_Always);
if (ImPlot::BeginPlot("##entropy", "Address", "Entropy", ImVec2(-1,0), ImPlotFlags_CanvasOnly, ImPlotAxisFlags_Lock | ImPlotAxisFlags_NoTickLabels, ImPlotAxisFlags_Lock)) {
ImPlot::PlotLine("##entropy_line", this->m_blockEntropy.data(), this->m_blockEntropy.size());
if (ImGui::IsItemClicked())
View::postEvent(Events::SelectionChangeRequest, Region{ u64(ImPlot::GetPlotMousePos().x) * this->m_blockSize, 1 });
ImPlot::EndPlot();
}
ImGui::PopStyleColor();
ImGui::NewLine();
ImGui::LabelText("hex.view.information.block_size"_lang, "hex.view.information.block_size.desc"_lang, this->m_blockSize);
ImGui::LabelText("hex.view.information.block_size"_lang, "hex.view.information.block_size.desc"_lang, this->m_blockEntropy.size(), this->m_blockSize);
ImGui::LabelText("hex.view.information.file_entropy"_lang, "%.8f", this->m_averageEntropy);
ImGui::LabelText("hex.view.information.highest_entropy"_lang, "%.8f", this->m_highestBlockEntropy);
if (this->m_averageEntropy > 0.83 && this->m_highestBlockEntropy > 0.9) {
ImGui::NewLine();
ImGui::TextColored(ImVec4(0.92F, 0.25F, 0.2F, 1.0F), "hex.view.information.encrypted"_lang);
ImGui::TextColored(ImVec4(0.92F, 0.25F, 0.2F, 1.0F), "%s", static_cast<const char*>("hex.view.information.encrypted"_lang));
}
}