1
0
mirror of synced 2025-01-10 21:41:53 +01:00

feat: Added entropy drop/spike annotations to the entropy graph

This commit is contained in:
WerWolv 2024-12-01 22:27:04 +01:00
parent 485ce887ea
commit a7fe384a31
3 changed files with 101 additions and 22 deletions

View File

@ -10,20 +10,33 @@
#include <hex/providers/provider.hpp>
#include <hex/providers/buffered_reader.hpp>
#include <hex/ui/imgui_imhex_extensions.h>
#include <hex/helpers/utils.hpp>
#include <imgui_internal.h>
#include <atomic>
#include <implot_internal.h>
#include <random>
#include <hex/ui/imgui_imhex_extensions.h>
#include <ranges>
namespace hex {
namespace impl {
struct AnnotationRegion {
UnlocalizedString unlocalizedName;
Region region;
ImColor color;
};
struct Tag {
UnlocalizedString unlocalizedName;
ImU64 value;
ImAxis axis;
ImGuiCol color;
};
inline int IntegerAxisFormatter(double value, char* buffer, int size, void *userData) {
u64 integer = static_cast<u64>(value);
return snprintf(buffer, size, static_cast<const char*>(userData), integer);
@ -380,6 +393,39 @@ namespace hex {
// Draw the plot
ImPlot::PlotLine("##ChunkBasedAnalysisLine", m_xBlockEntropy.data(), m_yBlockEntropySampled.data(), m_xBlockEntropy.size());
if (m_showAnnotations) {
u32 id = 1;
for (const auto &annotation : m_annotationRegions) {
const auto &region = annotation.region;
double xMin = region.getStartAddress();
double xMax = region.getEndAddress();
double yMin = 0.0F;
double yMax = 100.0F;
ImPlot::DragRect(id, &xMin, &yMin, &xMax, &yMax, annotation.color, ImPlotDragToolFlags_NoFit | ImPlotDragToolFlags_NoInputs);
const auto min = ImFloor(ImPlot::PlotToPixels(xMin, yMax));
const auto max = ImFloor(ImPlot::PlotToPixels(xMax, yMin)) + scaled({ 1, 1 });
const auto mousePos = ImPlot::PixelsToPlot(ImGui::GetMousePos());
if (ImGui::IsMouseHoveringRect(min, max)) {
ImPlot::Annotation(xMin + (xMax - xMin) / 2, mousePos.y, annotation.color, ImVec2(), false, "%s", Lang(annotation.unlocalizedName).get());
if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
ImHexApi::HexEditor::setSelection(annotation.region);
}
}
id += 1;
}
for (const auto &tag : m_tags) {
if (tag.axis == ImAxis_X1)
ImPlot::TagX(tag.value, ImGui::GetStyleColorVec4(tag.color), "%s", Lang(tag.unlocalizedName).get());
else if (tag.axis == ImAxis_Y1)
ImPlot::TagY(tag.value, ImGui::GetStyleColorVec4(tag.color), "%s", Lang(tag.unlocalizedName).get());
}
}
// The parameter updateHandle is used when using the pattern language since we don't have a provider
// but just a set of bytes, we won't be able to use the drag bar correctly.
if (updateHandle) {
@ -563,6 +609,10 @@ namespace hex {
m_handlePosition = filePosition;
}
void enableAnnotations(bool enabled) {
m_showAnnotations = enabled;
}
private:
// Private method used to factorize the process public method
void processImpl(const std::vector<u8> &bytes) {
@ -610,6 +660,41 @@ namespace hex {
for (u64 i = 0; i < m_blockCount; ++i)
m_xBlockEntropy[i] = ((m_startAddress / m_blockSize) + stride * i) * m_blockSize;
m_xBlockEntropy.push_back(m_endAddress);
u64 index = 0;
for (const auto [first, second] : std::views::adjacent<2>(m_yBlockEntropySampled)) {
const auto relativeDifference = std::abs(first - second) / std::max(first, second);
if (relativeDifference > 0.5 && index + 1 < m_xBlockEntropy.size()) {
const u64 start = u64(m_xBlockEntropy[index + 1]);
const u64 size = u64(m_xBlockEntropy[index + 1] - m_xBlockEntropy[index + 0]);
Region region = { start, size };
if (first > second) {
this->addRegion("hex.ui.diagram.entropy_analysis.entropy_drop", region, 0x60FF2020);
} else {
this->addRegion("hex.ui.diagram.entropy_analysis.entropy_spike", region, 0x602020FF);
}
}
index += 1;
}
}
void addRegion(const UnlocalizedString &unlocalizedName, Region region, ImColor color) {
const auto existingRegion = std::ranges::find_if(m_annotationRegions, [this, &region](const impl::AnnotationRegion &annotation) {
auto difference = i64(region.getEndAddress()) - i64(annotation.region.getEndAddress());
return difference > 0 && difference < i64(m_blockSize * 32);
});
if (existingRegion != m_annotationRegions.end()) {
existingRegion->region.size += region.size;
} else {
m_annotationRegions.push_back({ unlocalizedName, region, color });
}
}
void addTag(const UnlocalizedString &name, u64 value, ImAxis axis, ImGuiCol color) {
m_tags.push_back({ name, value, axis, color });
}
private:
@ -650,6 +735,11 @@ namespace hex {
size_t m_sampleSize = 0;
std::atomic<bool> m_processing = false;
std::vector<impl::AnnotationRegion> m_annotationRegions;
std::vector<impl::Tag> m_tags;
bool m_showAnnotations = true;
};
class DiagramByteDistribution {
@ -748,20 +838,6 @@ namespace hex {
};
class DiagramByteTypesDistribution {
private:
struct AnnotationRegion {
UnlocalizedString unlocalizedName;
Region region;
ImColor color;
};
struct Tag {
UnlocalizedString unlocalizedName;
ImU64 value;
ImAxis axis;
ImGuiCol color;
};
public:
explicit DiagramByteTypesDistribution(u64 blockSize = 256, size_t sampleSize = 0x1000) : m_blockSize(blockSize), m_sampleSize(sampleSize){ }
@ -1049,8 +1125,8 @@ namespace hex {
m_xBlockTypeDistributions.push_back(m_endAddress);
}
void addRegion(const UnlocalizedString &name, Region region, ImColor color) {
const auto existingRegion = std::ranges::find_if(m_annotationRegions, [this, &region](const AnnotationRegion &annotation) {
void addRegion(const UnlocalizedString &unlocalizedName, Region region, ImColor color) {
const auto existingRegion = std::ranges::find_if(m_annotationRegions, [this, &region](const impl::AnnotationRegion &annotation) {
auto difference = i64(region.getEndAddress()) - i64(annotation.region.getEndAddress());
return difference > 0 && difference < i64(m_blockSize * 32);
});
@ -1058,7 +1134,7 @@ namespace hex {
if (existingRegion != m_annotationRegions.end()) {
existingRegion->region.size += region.size;
} else {
m_annotationRegions.push_back({ name, region, color });
m_annotationRegions.push_back({ unlocalizedName, region, color });
}
}
@ -1103,8 +1179,8 @@ namespace hex {
std::array<std::vector<float>, 12> m_yBlockTypeDistributions, m_yBlockTypeDistributionsSampled;
std::atomic<bool> m_processing = false;
std::vector<AnnotationRegion> m_annotationRegions;
std::vector<Tag> m_tags;
std::vector<impl::AnnotationRegion> m_annotationRegions;
std::vector<impl::Tag> m_tags;
bool m_showAnnotations = true;
};

View File

@ -180,6 +180,7 @@ namespace hex::plugin::builtin {
m_chunkBasedEntropy.reset(m_inputChunkSize, region.getStartAddress(), region.getEndAddress(),
provider->getBaseAddress(), provider->getActualSize());
m_chunkBasedEntropy.enableAnnotations(m_showAnnotations);
m_byteTypesDistribution.enableAnnotations(m_showAnnotations);
// Create a handle to the file

View File

@ -122,6 +122,8 @@
"hex.ui.pattern_drawer.visualizer.unknown": "Unknown visualizer",
"hex.ui.pattern_drawer.visualizer.invalid_parameter_count": "Invalid parameter count",
"hex.ui.diagram.byte_type_distribution.plain_text": "Plain Text",
"hex.ui.diagram.byte_type_distribution.similar_bytes": "Similar Bytes"
"hex.ui.diagram.byte_type_distribution.similar_bytes": "Similar Bytes",
"hex.ui.diagram.entropy_analysis.entropy_drop": "Large Entropy Drop",
"hex.ui.diagram.entropy_analysis.entropy_spike": "Large Entropy Spike"
}
}