1
0
mirror of synced 2025-01-25 15:53:43 +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/provider.hpp>
#include <hex/providers/buffered_reader.hpp> #include <hex/providers/buffered_reader.hpp>
#include <hex/ui/imgui_imhex_extensions.h>
#include <hex/helpers/utils.hpp> #include <hex/helpers/utils.hpp>
#include <imgui_internal.h> #include <imgui_internal.h>
#include <atomic> #include <atomic>
#include <implot_internal.h>
#include <random> #include <random>
#include <hex/ui/imgui_imhex_extensions.h> #include <ranges>
namespace hex { namespace hex {
namespace impl { 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) { inline int IntegerAxisFormatter(double value, char* buffer, int size, void *userData) {
u64 integer = static_cast<u64>(value); u64 integer = static_cast<u64>(value);
return snprintf(buffer, size, static_cast<const char*>(userData), integer); return snprintf(buffer, size, static_cast<const char*>(userData), integer);
@ -380,6 +393,39 @@ namespace hex {
// Draw the plot // Draw the plot
ImPlot::PlotLine("##ChunkBasedAnalysisLine", m_xBlockEntropy.data(), m_yBlockEntropySampled.data(), m_xBlockEntropy.size()); 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 // 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. // but just a set of bytes, we won't be able to use the drag bar correctly.
if (updateHandle) { if (updateHandle) {
@ -563,6 +609,10 @@ namespace hex {
m_handlePosition = filePosition; m_handlePosition = filePosition;
} }
void enableAnnotations(bool enabled) {
m_showAnnotations = enabled;
}
private: private:
// Private method used to factorize the process public method // Private method used to factorize the process public method
void processImpl(const std::vector<u8> &bytes) { void processImpl(const std::vector<u8> &bytes) {
@ -610,6 +660,41 @@ namespace hex {
for (u64 i = 0; i < m_blockCount; ++i) for (u64 i = 0; i < m_blockCount; ++i)
m_xBlockEntropy[i] = ((m_startAddress / m_blockSize) + stride * i) * m_blockSize; m_xBlockEntropy[i] = ((m_startAddress / m_blockSize) + stride * i) * m_blockSize;
m_xBlockEntropy.push_back(m_endAddress); 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: private:
@ -650,6 +735,11 @@ namespace hex {
size_t m_sampleSize = 0; size_t m_sampleSize = 0;
std::atomic<bool> m_processing = false; std::atomic<bool> m_processing = false;
std::vector<impl::AnnotationRegion> m_annotationRegions;
std::vector<impl::Tag> m_tags;
bool m_showAnnotations = true;
}; };
class DiagramByteDistribution { class DiagramByteDistribution {
@ -748,20 +838,6 @@ namespace hex {
}; };
class DiagramByteTypesDistribution { class DiagramByteTypesDistribution {
private:
struct AnnotationRegion {
UnlocalizedString unlocalizedName;
Region region;
ImColor color;
};
struct Tag {
UnlocalizedString unlocalizedName;
ImU64 value;
ImAxis axis;
ImGuiCol color;
};
public: public:
explicit DiagramByteTypesDistribution(u64 blockSize = 256, size_t sampleSize = 0x1000) : m_blockSize(blockSize), m_sampleSize(sampleSize){ } 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); m_xBlockTypeDistributions.push_back(m_endAddress);
} }
void addRegion(const UnlocalizedString &name, Region region, ImColor color) { void addRegion(const UnlocalizedString &unlocalizedName, Region region, ImColor color) {
const auto existingRegion = std::ranges::find_if(m_annotationRegions, [this, &region](const AnnotationRegion &annotation) { const auto existingRegion = std::ranges::find_if(m_annotationRegions, [this, &region](const impl::AnnotationRegion &annotation) {
auto difference = i64(region.getEndAddress()) - i64(annotation.region.getEndAddress()); auto difference = i64(region.getEndAddress()) - i64(annotation.region.getEndAddress());
return difference > 0 && difference < i64(m_blockSize * 32); return difference > 0 && difference < i64(m_blockSize * 32);
}); });
@ -1058,7 +1134,7 @@ namespace hex {
if (existingRegion != m_annotationRegions.end()) { if (existingRegion != m_annotationRegions.end()) {
existingRegion->region.size += region.size; existingRegion->region.size += region.size;
} else { } 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::array<std::vector<float>, 12> m_yBlockTypeDistributions, m_yBlockTypeDistributionsSampled;
std::atomic<bool> m_processing = false; std::atomic<bool> m_processing = false;
std::vector<AnnotationRegion> m_annotationRegions; std::vector<impl::AnnotationRegion> m_annotationRegions;
std::vector<Tag> m_tags; std::vector<impl::Tag> m_tags;
bool m_showAnnotations = true; bool m_showAnnotations = true;
}; };

View File

@ -180,6 +180,7 @@ namespace hex::plugin::builtin {
m_chunkBasedEntropy.reset(m_inputChunkSize, region.getStartAddress(), region.getEndAddress(), m_chunkBasedEntropy.reset(m_inputChunkSize, region.getStartAddress(), region.getEndAddress(),
provider->getBaseAddress(), provider->getActualSize()); provider->getBaseAddress(), provider->getActualSize());
m_chunkBasedEntropy.enableAnnotations(m_showAnnotations);
m_byteTypesDistribution.enableAnnotations(m_showAnnotations); m_byteTypesDistribution.enableAnnotations(m_showAnnotations);
// Create a handle to the file // 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.unknown": "Unknown visualizer",
"hex.ui.pattern_drawer.visualizer.invalid_parameter_count": "Invalid parameter count", "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.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"
} }
} }