feat: Added Digram and Layered Distribution plots to information view

This commit is contained in:
WerWolv
2022-12-27 22:50:37 +01:00
parent f1aeec309e
commit 4807ca0057
5 changed files with 283 additions and 164 deletions

View File

@@ -8,6 +8,7 @@
#include <hex/providers/provider.hpp>
#include <content/helpers/provider_extra_data.hpp>
#include <content/helpers/diagrams.hpp>
#include <cctype>
#include <random>
@@ -951,91 +952,21 @@ namespace hex::plugin::builtin {
NodeVisualizerDigram() : Node("hex.builtin.nodes.visualizer.digram.header", { dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Buffer, "hex.builtin.nodes.common.input") }) { }
void drawNode() override {
drawDigram(scaled({ 200, 200 }));
this->m_digram.draw(scaled({ 200, 200 }));
if (ImGui::IsItemHovered() && ImGui::IsKeyDown(ImGuiKey_LeftShift)) {
ImGui::BeginTooltip();
drawDigram(scaled({ 600, 600 }));
this->m_digram.draw(scaled({ 600, 600 }));
ImGui::EndTooltip();
}
}
void drawDigram(const ImVec2 &viewSize) {
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImU32(ImColor(0, 0, 0)));
if (ImGui::BeginChild("##visualizer", viewSize, true)) {
auto drawList = ImGui::GetWindowDrawList();
float xStep = (viewSize.x * 0.95F) / 0xFF;
float yStep = (viewSize.y * 0.95F) / 0xFF;
for (size_t i = 0; i < (this->m_buffer.empty() ? 0 : this->m_buffer.size() - 1); i++) {
const auto &[x, y] = std::pair { this->m_buffer[i] * xStep, this->m_buffer[i + 1] * yStep };
auto color = ImLerp(ImColor(0xFF, 0x6D, 0x01).Value, ImColor(0x01, 0x93, 0xFF).Value, float(i) / this->m_buffer.size());
color.w = this->m_opacityBuffer[i];
auto pos = ImGui::GetWindowPos() + ImVec2(viewSize.x * 0.025F, viewSize.y * 0.025F) + ImVec2(x, y);
drawList->AddRectFilled(pos, pos + ImVec2(xStep, yStep), ImColor(color));
}
}
ImGui::EndChild();
ImGui::PopStyleColor();
}
void process() override {
constexpr static auto SampleSize = 0x9000;
const static size_t SequenceCount = std::ceil(std::sqrt(SampleSize));
this->m_buffer.clear();
auto buffer = this->getBufferOnInput(0);
if (buffer.size() < SampleSize)
this->m_buffer = buffer;
else {
std::random_device randomDevice;
std::mt19937_64 random(randomDevice());
std::map<u64, std::vector<u8>> orderedData;
for (u32 i = 0; i < SequenceCount; i++) {
ssize_t offset = random() % buffer.size();
std::vector<u8> sequence;
sequence.reserve(SampleSize);
std::copy(buffer.begin() + offset, buffer.begin() + offset + std::min<size_t>(SequenceCount, buffer.size() - offset), std::back_inserter(sequence));
orderedData.insert({ offset, sequence });
}
this->m_buffer.reserve(SampleSize);
u64 lastEnd = 0x00;
for (const auto &[offset, sequence] : orderedData) {
if (offset < lastEnd)
this->m_buffer.resize(this->m_buffer.size() - (lastEnd - offset));
std::copy(sequence.begin(), sequence.end(), std::back_inserter(this->m_buffer));
lastEnd = offset + sequence.size();
}
}
this->m_opacityBuffer.resize(this->m_buffer.size());
std::map<u64, size_t> heatMap;
for (size_t i = 0; i < (this->m_buffer.empty() ? 0 : this->m_buffer.size() - 1); i++) {
auto count = ++heatMap[this->m_buffer[i] << 8 | heatMap[i + 1]];
this->m_highestCount = std::max(this->m_highestCount, count);
}
for (size_t i = 0; i < (this->m_buffer.empty() ? 0 : this->m_buffer.size() - 1); i++) {
this->m_opacityBuffer[i] = std::min(0.2F + (float(heatMap[this->m_buffer[i] << 8 | this->m_buffer[i + 1]]) / float(this->m_highestCount / 1000)), 1.0F);
}
this->m_digram.process(this->getBufferOnInput(0));
}
private:
std::vector<u8> m_buffer;
std::vector<float> m_opacityBuffer;
size_t m_highestCount = 0;
DiagramDigram m_digram;
};
class NodeVisualizerLayeredDistribution : public dp::Node {
@@ -1043,90 +974,20 @@ namespace hex::plugin::builtin {
NodeVisualizerLayeredDistribution() : Node("hex.builtin.nodes.visualizer.layered_dist.header", { dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Buffer, "hex.builtin.nodes.common.input") }) { }
void drawNode() override {
drawLayeredDistribution(scaled({ 200, 200 }));
this->m_layeredDistribution.draw(scaled({ 200, 200 }));
if (ImGui::IsItemHovered() && ImGui::IsKeyDown(ImGuiKey_LeftShift)) {
ImGui::BeginTooltip();
drawLayeredDistribution(scaled({ 600, 600 }));
this->m_layeredDistribution.draw(scaled({ 600, 600 }));
ImGui::EndTooltip();
}
}
void drawLayeredDistribution(const ImVec2 &viewSize) {
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImU32(ImColor(0, 0, 0)));
if (ImGui::BeginChild("##visualizer", viewSize, true)) {
auto drawList = ImGui::GetWindowDrawList();
float xStep = (viewSize.x * 0.95F) / 0xFF;
float yStep = (viewSize.y * 0.95F) / 0xFF;
for (size_t i = 0; i < (this->m_buffer.empty() ? 0 : this->m_buffer.size()); i++) {
const auto &[x, y] = std::pair { this->m_buffer[i] * xStep, yStep * ((float(i) / this->m_buffer.size()) * 0xFF) };
auto color = ImLerp(ImColor(0xFF, 0x6D, 0x01).Value, ImColor(0x01, 0x93, 0xFF).Value, float(i) / this->m_buffer.size());
color.w = this->m_opacityBuffer[i];
auto pos = ImGui::GetWindowPos() + ImVec2(viewSize.x * 0.025F, viewSize.y * 0.025F) + ImVec2(x, y);
drawList->AddRectFilled(pos, pos + ImVec2(xStep, yStep), ImColor(color));
}
}
ImGui::EndChild();
ImGui::PopStyleColor();
}
void process() override {
constexpr static auto SampleSize = 0x9000;
const static size_t SequenceCount = std::ceil(std::sqrt(SampleSize));
this->m_buffer.clear();
auto buffer = this->getBufferOnInput(0);
if (buffer.size() < SampleSize)
this->m_buffer = buffer;
else {
std::random_device randomDevice;
std::mt19937_64 random(randomDevice());
std::map<u64, std::vector<u8>> orderedData;
for (u32 i = 0; i < SequenceCount; i++) {
ssize_t offset = random() % buffer.size();
std::vector<u8> sequence;
sequence.reserve(SampleSize);
std::copy(buffer.begin() + offset, buffer.begin() + offset + std::min<size_t>(SequenceCount, buffer.size() - offset), std::back_inserter(sequence));
orderedData.insert({ offset, sequence });
}
this->m_buffer.reserve(SampleSize);
u64 lastEnd = 0x00;
for (const auto &[offset, sequence] : orderedData) {
if (offset < lastEnd)
this->m_buffer.resize(this->m_buffer.size() - (lastEnd - offset));
std::copy(sequence.begin(), sequence.end(), std::back_inserter(this->m_buffer));
lastEnd = offset + sequence.size();
}
}
this->m_opacityBuffer.resize(this->m_buffer.size());
std::map<u64, size_t> heatMap;
for (size_t i = 0; i < (this->m_buffer.empty() ? 0 : this->m_buffer.size() - 1); i++) {
auto count = ++heatMap[this->m_buffer[i] << 8 | heatMap[i + 1]];
this->m_highestCount = std::max(this->m_highestCount, count);
}
for (size_t i = 0; i < (this->m_buffer.empty() ? 0 : this->m_buffer.size() - 1); i++) {
this->m_opacityBuffer[i] = std::min(0.2F + (float(heatMap[this->m_buffer[i] << 8 | this->m_buffer[i + 1]]) / float(this->m_highestCount / 1000)), 1.0F);
}
this->m_layeredDistribution.process(this->getBufferOnInput(0));
}
private:
std::vector<u8> m_buffer;
std::vector<float> m_opacityBuffer;
size_t m_highestCount = 0;
DiagramLayeredDistribution m_layeredDistribution;
};
class NodeVisualizerImage : public dp::Node {

View File

@@ -35,7 +35,7 @@ namespace hex::plugin::builtin {
EventManager::subscribe<EventRegionSelected>(this, [this](Region region) {
if (this->m_blockSize != 0)
this->m_entropyHandlePosition = region.getStartAddress() / double(this->m_blockSize);
this->m_diagramHandlePosition = region.getStartAddress() / double(this->m_blockSize);
});
EventManager::subscribe<EventProviderDeleted>(this, [this](const auto*) {
@@ -139,11 +139,19 @@ namespace hex::plugin::builtin {
std::array<ImU64, 256> blockValueCounts = { 0 };
const auto blockCount = provider->getSize() / this->m_blockSize;
this->m_blockTypeDistributions.fill({});
this->m_blockEntropy.clear();
this->m_blockEntropy.resize(provider->getSize() / this->m_blockSize);
this->m_blockEntropy.resize(blockCount);
for (auto &blockDistribution : this->m_blockTypeDistributions)
blockDistribution.resize(blockCount);
this->m_valueCounts.fill(0);
this->m_blockEntropyProcessedCount = 0;
this->m_processedBlockCount = 0;
this->m_digram.process(provider, this->m_analyzedRegion.getStartAddress(), this->m_analyzedRegion.getSize());
this->m_layeredDistribution.process(provider, this->m_analyzedRegion.getStartAddress(), this->m_analyzedRegion.getSize());
auto reader = prv::BufferedReader(provider);
reader.setEndAddress(provider->getBaseAddress() + provider->getSize());
@@ -155,15 +163,15 @@ namespace hex::plugin::builtin {
count++;
if ((count % this->m_blockSize) == 0) [[unlikely]] {
this->m_blockEntropy[this->m_blockEntropyProcessedCount] = calculateEntropy(blockValueCounts, this->m_blockSize);
this->m_blockEntropy[this->m_processedBlockCount] = calculateEntropy(blockValueCounts, this->m_blockSize);
{
auto typeDist = calculateTypeDistribution(blockValueCounts, this->m_blockSize);
for (u8 i = 0; i < typeDist.size(); i++)
this->m_blockTypeDistributions[i].push_back(typeDist[i]);
this->m_blockTypeDistributions[i][this->m_processedBlockCount] = typeDist[i];
}
this->m_blockEntropyProcessedCount += 1;
this->m_processedBlockCount += 1;
blockValueCounts = { 0 };
task.update(count);
}
@@ -286,11 +294,11 @@ namespace hex::plugin::builtin {
constexpr static std::array Names = { "iscntrl", "isprint", "isspace", "isblank", "isgraph", "ispunct", "isalnum", "isalpha", "isupper", "islower", "isdigit", "isxdigit" };
for (u32 i = 0; i < 12; i++) {
ImPlot::PlotLine(Names[i], this->m_blockTypeDistributions[i].data(), this->m_blockTypeDistributions[i].size());
ImPlot::PlotLine(Names[i], this->m_blockTypeDistributions[i].data(), this->m_processedBlockCount);
}
if (ImPlot::DragLineX(1, &this->m_entropyHandlePosition, ImGui::GetStyleColorVec4(ImGuiCol_Text))) {
u64 address = u64(std::max<double>(this->m_entropyHandlePosition, 0) * this->m_blockSize) + provider->getBaseAddress();
if (ImPlot::DragLineX(1, &this->m_diagramHandlePosition, ImGui::GetStyleColorVec4(ImGuiCol_Text))) {
u64 address = u64(std::max<double>(this->m_diagramHandlePosition, 0) * this->m_blockSize) + provider->getBaseAddress();
address = std::min(address, provider->getBaseAddress() + provider->getSize() - 1);
ImHexApi::HexEditor::setSelection(address, 1);
}
@@ -306,21 +314,25 @@ namespace hex::plugin::builtin {
ImPlot::SetupAxes("hex.builtin.common.address"_lang, "hex.builtin.view.information.entropy"_lang, ImPlotAxisFlags_Lock, ImPlotAxisFlags_Lock);
ImPlot::SetupAxesLimits(0, this->m_blockEntropy.size(), -0.1F, 1.1F, ImGuiCond_Always);
ImPlot::PlotLine("##entropy_line", this->m_blockEntropy.data(), this->m_blockEntropyProcessedCount);
ImPlot::PlotLine("##entropy_line", this->m_blockEntropy.data(), this->m_processedBlockCount);
if (ImPlot::DragLineX(1, &this->m_entropyHandlePosition, ImGui::GetStyleColorVec4(ImGuiCol_Text))) {
u64 address = u64(std::max<double>(this->m_entropyHandlePosition, 0) * this->m_blockSize) + provider->getBaseAddress();
if (ImPlot::DragLineX(1, &this->m_diagramHandlePosition, ImGui::GetStyleColorVec4(ImGuiCol_Text))) {
u64 address = u64(std::max<double>(this->m_diagramHandlePosition, 0) * this->m_blockSize) + provider->getBaseAddress();
address = std::min(address, provider->getBaseAddress() + provider->getSize() - 1);
ImHexApi::HexEditor::setSelection(address, 1);
}
ImPlot::EndPlot();
}
ImPlot::PopStyleColor();
ImGui::PopStyleColor();
ImGui::NewLine();
this->m_diagramHandlePosition = std::clamp<double>(
this->m_diagramHandlePosition,
this->m_analyzedRegion.getStartAddress() / double(this->m_blockSize),
this->m_analyzedRegion.getEndAddress() / double(this->m_blockSize));
}
// Entropy information
@@ -352,7 +364,22 @@ namespace hex::plugin::builtin {
ImGui::NewLine();
ImGui::TextFormattedColored(ImVec4(0.92F, 0.25F, 0.2F, 1.0F), "{}", "hex.builtin.view.information.encrypted"_lang);
}
}
ImGui::BeginGroup();
{
ImGui::Header("hex.builtin.view.information.digram"_lang);
this->m_digram.draw(ImVec2(300, 300));
}
ImGui::EndGroup();
ImGui::SameLine();
ImGui::BeginGroup();
{
ImGui::Header("hex.builtin.view.information.layered_distribution"_lang);
this->m_layeredDistribution.draw(ImVec2(300, 300));
}
ImGui::EndGroup(); }
}
}
ImGui::EndChild();