diff --git a/AppCUI b/AppCUI index fb683857..6a075a25 160000 --- a/AppCUI +++ b/AppCUI @@ -1 +1 @@ -Subproject commit fb6838578a749adaf10e7a8ad65d6c8cca308692 +Subproject commit 6a075a257f8506633688cad1eace45b2036354f5 diff --git a/GViewCore/include/GView.hpp b/GViewCore/include/GView.hpp index 727457b0..316cae5d 100644 --- a/GViewCore/include/GView.hpp +++ b/GViewCore/include/GView.hpp @@ -1,7 +1,7 @@ #pragma once // Version MUST be in the following format .. -#define GVIEW_VERSION "0.351.0" +#define GVIEW_VERSION "0.352.0" #include @@ -34,20 +34,21 @@ struct CORE_EXPORT KeyboardControl { const char* Explanation; uint32 CommandId; }; -struct CORE_EXPORT KeyboardControlsInterface -{ +struct CORE_EXPORT KeyboardControlsInterface { virtual bool RegisterKey(KeyboardControl* key) = 0; - virtual ~KeyboardControlsInterface() = default; + virtual ~KeyboardControlsInterface() = default; }; class CORE_EXPORT Object; struct CORE_EXPORT TypeInterface { Object* obj{ nullptr }; - virtual std::string_view GetTypeName() = 0; - virtual void RunCommand(std::string_view commandName) = 0; + virtual std::string_view GetTypeName() = 0; + virtual void RunCommand(std::string_view commandName) = 0; virtual bool UpdateKeys(KeyboardControlsInterface* interface) = 0; - virtual ~TypeInterface(){} + virtual ~TypeInterface() + { + } struct SelectionZone { uint64 start, end; @@ -180,7 +181,7 @@ namespace Utils Zone(uint64 low, uint64 high, ColorPair cp, std::string_view name) : interval{ low, high }, color(cp), name(name) { } - Zone() : interval{ INVALID_OFFSET, INVALID_OFFSET }, color(NoColorPair), name(){}; + Zone() : interval{ INVALID_OFFSET, INVALID_OFFSET }, color(NoColorPair), name() {}; }; class CORE_EXPORT ZonesList @@ -605,46 +606,68 @@ namespace Golang CORE_EXPORT const char* GetNameForGoMagic(GoMagic magic); } // namespace Golang -namespace ZLIB +namespace Decoding { - CORE_EXPORT bool Decompress(const Buffer& input, uint64 inputSize, Buffer& output, uint64 outputSize); -} + namespace Base64 + { + CORE_EXPORT void Encode(BufferView view, Buffer& output); + CORE_EXPORT bool Decode(BufferView view, Buffer& output, bool& hasWarning, String& warningMessage); + CORE_EXPORT bool Decode(BufferView view, Buffer& output); + } // namespace Base64 -namespace ZIP -{ - enum class EntryType { Unknown = 0, Directory = 1, Symlink = 2, File = 3 }; + namespace LZXPRESS::Huffman + { + CORE_EXPORT bool Decompress(const BufferView& compressed, Buffer& uncompressed); + } // namespace LZXPRESS::Huffman - struct CORE_EXPORT Entry { - void* context{ nullptr }; + namespace QuotedPrintable + { + CORE_EXPORT void Encode(BufferView view, Buffer& output); + CORE_EXPORT bool Decode(BufferView view, Buffer& output); + } // namespace QuotedPrintable - std::u8string_view GetFilename() const; - uint16 GetFlags() const; - std::string GetFlagNames() const; - int64 GetCompressedSize() const; - int64 GetUncompressedSize() const; - int64 GetCompressionMethod() const; - std::string GetCompressionMethodName() const; - uint32 GetDiskNumber() const; - int64 GetDiskOffset() const; - EntryType GetType() const; - std::string_view GetTypeName() const; - bool IsEncrypted() const; - }; + namespace ZLIB + { + CORE_EXPORT bool Decompress(const Buffer& input, uint64 inputSize, Buffer& output, uint64 outputSize); + CORE_EXPORT bool DecompressStream(const BufferView& input, Buffer& output, String& message, uint64& sizeConsumed); + } // namespace ZLIB - struct CORE_EXPORT Info { - void* context{ nullptr }; + namespace ZIP + { + enum class EntryType { Unknown = 0, Directory = 1, Symlink = 2, File = 3 }; + + struct CORE_EXPORT Entry { + void* context{ nullptr }; + + std::u8string_view GetFilename() const; + uint16 GetFlags() const; + std::string GetFlagNames() const; + int64 GetCompressedSize() const; + int64 GetUncompressedSize() const; + int64 GetCompressionMethod() const; + std::string GetCompressionMethodName() const; + uint32 GetDiskNumber() const; + int64 GetDiskOffset() const; + EntryType GetType() const; + std::string_view GetTypeName() const; + bool IsEncrypted() const; + }; - uint32 GetCount() const; - bool GetEntry(uint32 index, Entry& entry) const; - bool Decompress(Buffer& output, uint32 index, const std::string& password) const; - bool Decompress(const BufferView& input, Buffer& output, uint32 index, const std::string& password) const; + struct CORE_EXPORT Info { + void* context{ nullptr }; - Info(); - ~Info(); - }; - CORE_EXPORT bool GetInfo(std::u16string_view path, Info& info); - CORE_EXPORT bool GetInfo(Utils::DataCache& cache, Info& info); -} // namespace ZIP + uint32 GetCount() const; + bool GetEntry(uint32 index, Entry& entry) const; + bool Decompress(Buffer& output, uint32 index, const std::string& password) const; + bool Decompress(const BufferView& input, Buffer& output, uint32 index, const std::string& password) const; + + Info(); + ~Info(); + }; + CORE_EXPORT bool GetInfo(std::u16string_view path, Info& info); + CORE_EXPORT bool GetInfo(Utils::DataCache& cache, Info& info); + } // namespace ZIP +} // namespace Decoding namespace Dissasembly { @@ -720,11 +743,6 @@ namespace Dissasembly }; } // namespace Dissasembly -namespace Compression::LZXPRESS::Huffman -{ - CORE_EXPORT bool Decompress(const BufferView& compressed, Buffer& uncompressed); -} // namespace Compression::LZXPRESS::Huffman - namespace SQLite3 { class CORE_EXPORT Column @@ -787,6 +805,7 @@ namespace Regex namespace Entropy { CORE_EXPORT double ShannonEntropy(const BufferView& buffer); + CORE_EXPORT double RenyiEntropy(const BufferView& buffer, double alpha); } // namespace Entropy /* @@ -886,7 +905,7 @@ namespace View virtual bool ShowGoToDialog() = 0; virtual bool ShowFindDialog() = 0; virtual bool ShowCopyDialog() = 0; - virtual bool UpdateKeys(KeyboardControlsInterface* interface) + virtual bool UpdateKeys(KeyboardControlsInterface*) { return true; } @@ -1504,21 +1523,6 @@ namespace App uint32 CORE_EXPORT GetTypePluginsCount(); }; // namespace App - -namespace Unpack -{ - namespace Base64 - { - CORE_EXPORT void Encode(BufferView view, Buffer& output); - CORE_EXPORT bool Decode(BufferView view, Buffer& output, bool& hasWarning, String& warningMessage); - CORE_EXPORT bool Decode(BufferView view, Buffer& output); - } // namespace Base64 - namespace QuotedPrintable - { - CORE_EXPORT void Encode(BufferView view, Buffer& output); - CORE_EXPORT bool Decode(BufferView view, Buffer& output); - } // namespace QuotedPrintable -} // namespace Unpack }; // namespace GView ADD_FLAG_OPERATORS(GView::View::LexicalViewer::StringFormat, AppCUI::uint32); diff --git a/GViewCore/src/CMakeLists.txt b/GViewCore/src/CMakeLists.txt index d39edd30..3ef3af0c 100644 --- a/GViewCore/src/CMakeLists.txt +++ b/GViewCore/src/CMakeLists.txt @@ -5,14 +5,11 @@ add_subdirectory(Utils) add_subdirectory(View) add_subdirectory(Hashes) add_subdirectory(DigitalSignature) -add_subdirectory(Compression) +add_subdirectory(Decoding) add_subdirectory(Go) -add_subdirectory(ZLIB) add_subdirectory(Dissasembly) -add_subdirectory(ZIP) add_subdirectory(SQLite3) add_subdirectory(Regex) -add_subdirectory(Unpack) add_subdirectory(Entropy) if(NOT DEFINED CMAKE_TESTING_ENABLED) diff --git a/GViewCore/src/Compression/CMakeLists.txt b/GViewCore/src/Compression/CMakeLists.txt deleted file mode 100644 index 0cf698f7..00000000 --- a/GViewCore/src/Compression/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -target_sources(GViewCore PRIVATE - LZXPRESS.cpp -) diff --git a/GViewCore/src/Unpack/Base64.cpp b/GViewCore/src/Decoding/Base64.cpp similarity index 97% rename from GViewCore/src/Unpack/Base64.cpp rename to GViewCore/src/Decoding/Base64.cpp index 6c9a083c..6860b96b 100644 --- a/GViewCore/src/Unpack/Base64.cpp +++ b/GViewCore/src/Decoding/Base64.cpp @@ -10,9 +10,8 @@ constexpr char BASE64_DECODE_TABLE[] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 }; -namespace GView::Unpack::Base64 +namespace GView::Decoding::Base64 { - void Encode(BufferView view, Buffer& output) { uint32 sequence = 0; @@ -62,7 +61,7 @@ bool Decode(BufferView view, Buffer& output, bool& hasWarning, String& warningMe } if (lastEncoded == '=' && sequenceIndex == 0) { - hasWarning = true; + hasWarning = true; warningMessage = "Ignoring extra bytes after the end of buffer"; break; } @@ -110,4 +109,4 @@ bool Decode(BufferView view, Buffer& output) return Decode(view, output, tempHasWarning, tempWarningMessage); } -} // namespace GView::Unpack::Base64 +} // namespace GView::Decoding::Base64 diff --git a/GViewCore/src/Unpack/CMakeLists.txt b/GViewCore/src/Decoding/CMakeLists.txt similarity index 60% rename from GViewCore/src/Unpack/CMakeLists.txt rename to GViewCore/src/Decoding/CMakeLists.txt index c087a3b9..e9065dee 100644 --- a/GViewCore/src/Unpack/CMakeLists.txt +++ b/GViewCore/src/Decoding/CMakeLists.txt @@ -1,4 +1,7 @@ target_sources(GViewCore PRIVATE Base64.cpp + LZXPRESS.cpp QuotedPrintable.cpp + zip.cpp + zlib.cpp ) diff --git a/GViewCore/src/Compression/LZXPRESS.cpp b/GViewCore/src/Decoding/LZXPRESS.cpp similarity index 99% rename from GViewCore/src/Compression/LZXPRESS.cpp rename to GViewCore/src/Decoding/LZXPRESS.cpp index 4792e00d..2530269a 100644 --- a/GViewCore/src/Compression/LZXPRESS.cpp +++ b/GViewCore/src/Decoding/LZXPRESS.cpp @@ -9,7 +9,7 @@ # undef GetObject #endif -namespace GView::Compression::LZXPRESS::Huffman +namespace GView::Decoding::LZXPRESS::Huffman { constexpr uint32 MAX_BITS_COUNT = 32U; constexpr uint32 UINT32_BITS_COUNT = 32U; diff --git a/GViewCore/src/Unpack/QuotedPrintable.cpp b/GViewCore/src/Decoding/QuotedPrintable.cpp similarity index 92% rename from GViewCore/src/Unpack/QuotedPrintable.cpp rename to GViewCore/src/Decoding/QuotedPrintable.cpp index c72dc888..70429519 100644 --- a/GViewCore/src/Unpack/QuotedPrintable.cpp +++ b/GViewCore/src/Decoding/QuotedPrintable.cpp @@ -1,7 +1,7 @@ #include "Internal.hpp" //TODO: THIS WAS NOT TESTED! -void GView::Unpack::QuotedPrintable::Encode(BufferView view, Buffer& output) +void GView::Decoding::QuotedPrintable::Encode(BufferView view, Buffer& output) { // Iterate over each character in the input buffer for (size_t i = 0; i < view.GetLength(); i++) { @@ -32,8 +32,12 @@ void GView::Unpack::QuotedPrintable::Encode(BufferView view, Buffer& output) } //TODO: Consider more testing! -bool GView::Unpack::QuotedPrintable::Decode(BufferView view, Buffer& output) +bool GView::Decoding::QuotedPrintable::Decode(BufferView view, Buffer& output) { + CHECK(view.IsValid(), false, ""); + CHECK(view.GetLength() >= 3, false, ""); + CHECK(view.GetData()[0] == '=', false, ""); + char temp_buffer[2] = {}; // Iterate over each character in the input buffer for (size_t i = 0; i < view.GetLength(); i++) { diff --git a/GViewCore/src/ZIP/zip.cpp b/GViewCore/src/Decoding/zip.cpp similarity index 99% rename from GViewCore/src/ZIP/zip.cpp rename to GViewCore/src/Decoding/zip.cpp index 1acde3f7..029d3861 100644 --- a/GViewCore/src/ZIP/zip.cpp +++ b/GViewCore/src/Decoding/zip.cpp @@ -11,7 +11,7 @@ #include #include -namespace GView::ZIP +namespace GView::Decoding::ZIP { using mz_zip_reader_create_ptr = struct Reader { diff --git a/GViewCore/src/Decoding/zlib.cpp b/GViewCore/src/Decoding/zlib.cpp new file mode 100644 index 00000000..e823e342 --- /dev/null +++ b/GViewCore/src/Decoding/zlib.cpp @@ -0,0 +1,69 @@ +#include "../include/GView.hpp" +#include + +namespace GView::Decoding::ZLIB +{ +bool Decompress(const Buffer& input, uint64 inputSize, Buffer& output, uint64 outputSize) +{ + CHECK(input.IsValid(), false, ""); + CHECK(inputSize > 0, false, ""); + CHECK(outputSize > inputSize, false, ""); + + output.Resize(outputSize); + + uint64 outputSizeCopy = outputSize; + int32 ret = uncompress(output.GetData(), (uLongf*) &outputSizeCopy, input.GetData(), static_cast(inputSize)); + CHECK(outputSize == outputSizeCopy, false, "ZLIB error: %d!", ret); + CHECK(ret == Z_OK, false, "ZLIB error: %d!", ret); + + return true; +} + +bool DecompressStream(const BufferView& input, Buffer& output, String& message, uint64& sizeConsumed) +{ + CHECK(input.IsValid(), false, ""); + CHECK(input.GetLength() > 0, false, ""); + output.Resize(input.GetLength()); + sizeConsumed = 0; + + z_stream stream; + memset(&stream, Z_NULL, sizeof(stream)); + + stream.avail_in = static_cast(input.GetLength()); + stream.next_in = const_cast(input.GetData()); + stream.avail_out = static_cast(input.GetLength()); + stream.next_out = reinterpret_cast(output.GetData()); + + int ret = inflateInit(&stream); + CHECK(ret == Z_OK, false, ""); + + struct ZWrapper { + z_stream& z; + + ZWrapper(z_stream& z) : z(z) + { + } + ~ZWrapper() + { + inflateEnd(&z); + } + } zWrapper(stream); + + while (ret == Z_OK || ret == Z_BUF_ERROR) { + ret = inflate(&stream, Z_NO_FLUSH); + if (ret == Z_BUF_ERROR) { + output.Resize(stream.total_out * 2); + stream.avail_out = static_cast(stream.total_out); + stream.next_out = reinterpret_cast(output.GetData() + stream.total_out); + } + } + + output.Resize(stream.total_out); + sizeConsumed = stream.total_in; + message.Format("Return code: %d with msg: %s", ret, stream.msg); + + CHECK(ret == Z_OK || ret == Z_STREAM_END, false, ""); + + return true; +} +} // namespace GView::ZLIB diff --git a/GViewCore/src/Entropy/Entropy.cpp b/GViewCore/src/Entropy/Entropy.cpp index 31c6b12c..9eef51c2 100644 --- a/GViewCore/src/Entropy/Entropy.cpp +++ b/GViewCore/src/Entropy/Entropy.cpp @@ -1,29 +1,80 @@ #include "Internal.hpp" #include +#include + +constexpr uint32 MAX_NUMBER_OF_BYTES = 256; namespace GView::Entropy { -double ShannonEntropy(const BufferView& buffer) +void SetFrequencies(const BufferView& buffer, std::array& frequency) { - char frequency[256]{}; - // Count frequency of each byte in the buffer for (uint32 i = 0; i < buffer.GetLength(); i++) { const auto c = buffer[i]; frequency[c]++; } +} + +/* + In physics, the word entropy has important physical implications as the amount of "disorder" of a system. + In mathematics, a more abstract definition is used. - // Calculate entropy + The (Shannon) entropy of a variable X is defined as + H(X) congruent - sum_x P(x) log_2[P(x)] + bits, where P(x) is the probability that X is in the state x, and P log_2 P is defined as 0 if P = 0. + + The joint entropy of variables X_1, ..., X_n is then defined by + H(X_1, ..., X_n) congruent - sum_(x_1) ... sum_(x_n) P(x_1, ..., x_n) log_2[P(x_1, ..., x_n)]. +*/ +double ShannonEntropy_private(const BufferView& buffer, std::array& frequency) +{ double entropy = 0.0; - for (const auto& value : frequency) { - if (value == 0) { + for (auto f : frequency) { + if (f == 0) { continue; } - double probability = static_cast(value) / buffer.GetLength(); + double probability = static_cast(f) / buffer.GetLength(); entropy -= probability * log2(probability); } - return entropy; // max log2(n) = 8 + return entropy; // max log2(n) = 8 (the entire sum) +} + +double ShannonEntropy(const BufferView& buffer) +{ + std::array frequency{}; + SetFrequencies(buffer, frequency); + return ShannonEntropy_private(buffer, frequency); +} + +/* + Rényi entropy is defined as: + H_α(p_1, p_2, ..., p_n) = 1/(1 - α) ln( sum_(i = 1)^n p_i^α), where α>0, α!=1. + As α->1, H_α(p_1, p_2, ..., p_n) converges to H(p_1, p_2, ..., p_n), which is Shannon's measure of entropy. + Rényi's measure satisfies + H_α(p_1, p_2, ..., p_n)<=H_α'(p_1, p_2, ..., p_n) + for α<=α'. +*/ +double RenyiEntropy(const BufferView& buffer, double alpha) +{ + std::array frequency{}; + SetFrequencies(buffer, frequency); + + if (alpha == 1.0) { + return ShannonEntropy_private(buffer, frequency); + } + + double sum = 0.0; + for (auto f : frequency) { + if (f > 0) { + const double probability = static_cast(f) / buffer.GetLength(); + sum += pow(probability, alpha); + } + } + + // Convert to bits if using log base e + // return std::max(((1.0 / (1.0 - alpha)) * log(sum)) / log(2), 0.0); + return ((1.0 / (1.0 - alpha)) * log(sum)) / log(2); } } // namespace GView::Entropy diff --git a/GViewCore/src/View/BufferViewer/BufferViewer.hpp b/GViewCore/src/View/BufferViewer/BufferViewer.hpp index c7553e57..abaf0fd7 100644 --- a/GViewCore/src/View/BufferViewer/BufferViewer.hpp +++ b/GViewCore/src/View/BufferViewer/BufferViewer.hpp @@ -390,12 +390,12 @@ class Instance : public View::ViewControl, public GView::Utils::SelectionZoneInt return GView::TypeInterface::SelectionZone{ .start = selection.GetSelectionStart(index), .end = selection.GetSelectionEnd(index) }; } - virtual uint32 GetObjectsZonesCount() const + virtual uint32 GetObjectsZonesCount() const override { return static_cast(this->settings->zListObjects.GetCount()); } - virtual std::optional GetObjectsZone(uint32 index) const + virtual std::optional GetObjectsZone(uint32 index) const override { return this->settings->zListObjects.GetZone(index); } diff --git a/GViewCore/src/View/BufferViewer/DissasmDialog.cpp b/GViewCore/src/View/BufferViewer/DissasmDialog.cpp index 05bc9517..7a76b1af 100644 --- a/GViewCore/src/View/BufferViewer/DissasmDialog.cpp +++ b/GViewCore/src/View/BufferViewer/DissasmDialog.cpp @@ -26,8 +26,8 @@ DissasmDialog::DissasmDialog(Reference instance) : Window("Dissasm", " list->SetFocus(); architecture = Factory::Label::Create(this, "Architecture", "x:90%,y:2,w:11%,h:1"); - x64 = Factory::RadioBox::Create(this, "x&64", "x:90%,y:3,w:11%,h:1", GROUPD_ID_ARCHITECTURE_TYPE, RADIOBOX_ID_ARCHITECTURE_X86); - x86 = Factory::RadioBox::Create(this, "x&86", "x:90%,y:4,w:11%,h:1", GROUPD_ID_ARCHITECTURE_TYPE, RADIOBOX_ID_ARCHITECTURE_X64); + x64 = Factory::RadioBox::Create(this, "x&64", "x:90%,y:3,w:11%,h:1", GROUPD_ID_ARCHITECTURE_TYPE, RADIOBOX_ID_ARCHITECTURE_X64); + x86 = Factory::RadioBox::Create(this, "x&86", "x:90%,y:4,w:11%,h:1", GROUPD_ID_ARCHITECTURE_TYPE, RADIOBOX_ID_ARCHITECTURE_X86); x64->Handlers()->OnCheck = this; x86->Handlers()->OnCheck = this; diff --git a/GViewCore/src/View/BufferViewer/SelectionEditor.cpp b/GViewCore/src/View/BufferViewer/SelectionEditor.cpp index c4788d1c..690c8595 100644 --- a/GViewCore/src/View/BufferViewer/SelectionEditor.cpp +++ b/GViewCore/src/View/BufferViewer/SelectionEditor.cpp @@ -9,7 +9,7 @@ constexpr int32 BTN_ID_RELOAD = 3; constexpr int32 BTN_ID_CANCEL = 4; SelectionEditor::SelectionEditor(Reference _selection, uint32 index, Reference _settings, uint64 sz) - : Window("Selection Editor", "d:c,w:61,h:10", WindowFlags::None), selection(_selection), zoneIndex(index), settings(_settings), + : Window("Selection Editor", "d:c,w:61,h:10", WindowFlags::None), selection(_selection), settings(_settings), zoneIndex(index), maxSize(sz) { LocalString<128> tmp; diff --git a/GViewCore/src/ZIP/CMakeLists.txt b/GViewCore/src/ZIP/CMakeLists.txt deleted file mode 100644 index e0f6c788..00000000 --- a/GViewCore/src/ZIP/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -target_sources(GViewCore PRIVATE - zip.cpp -) diff --git a/GViewCore/src/ZLIB/CMakeLists.txt b/GViewCore/src/ZLIB/CMakeLists.txt deleted file mode 100644 index 3d2d3c1e..00000000 --- a/GViewCore/src/ZLIB/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -target_sources(GViewCore PRIVATE - zlib.cpp -) diff --git a/GViewCore/src/ZLIB/zlib.cpp b/GViewCore/src/ZLIB/zlib.cpp deleted file mode 100644 index cebe44a1..00000000 --- a/GViewCore/src/ZLIB/zlib.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include "../include/GView.hpp" -#include - -namespace GView::ZLIB -{ -bool Decompress(const Buffer& input, uint64 inputSize, Buffer& output, uint64 outputSize) -{ - CHECK(input.IsValid(), false, ""); - CHECK(inputSize > 0, false, ""); - CHECK(outputSize > inputSize, false, ""); - - output.Resize(outputSize); - - uint64 outputSizeCopy = outputSize; - int32 ret = uncompress(output.GetData(), (uLongf*) &outputSizeCopy, input.GetData(), static_cast(inputSize)); - CHECK(outputSize == outputSizeCopy, false, "ZLIB error: %d!", ret); - CHECK(ret == Z_OK, false, "ZLIB error: %d!", ret); - - return true; -} -} // namespace GView::ZLIB diff --git a/GenericPlugins/EntropyVisualizer/include/EntropyVisualizer.hpp b/GenericPlugins/EntropyVisualizer/include/EntropyVisualizer.hpp index 559b8e06..a8722fd0 100644 --- a/GenericPlugins/EntropyVisualizer/include/EntropyVisualizer.hpp +++ b/GenericPlugins/EntropyVisualizer/include/EntropyVisualizer.hpp @@ -11,6 +11,7 @@ static const uint32 SHANNON_ENTROPY_DATA_TYPE_MAX_VALUE = 2; static const uint32 SHANNON_ENTROPY_LEGEND_DATA_TYPE_HEIGHT = 10; static const uint32 SHANNON_ENTROPY_LEGEND_HEIGHT = 13; static const std::string_view SHANNON_ENTROPY_OPTION_NAME = "Shannon Entropy"; +static const std::string_view RENYI_ENTROPY_OPTION_NAME = "Renyi Entropy"; static const std::string_view SHANNON_ENTROPY_DATA_TYPE_OPTION_NAME = "Shannon Entropy Data Type"; static const uint32 EMBEDDED_OBJECTS_MAX_VALUE = 6; static const uint32 EMBEDDED_OBJECTS_LEGEND_HEIGHT = 12 + 8; @@ -18,8 +19,15 @@ static const std::string_view EMBEDDED_OBJECTS_OPTION_NAME = "Embedded static const uint32 MINIMUM_BLOCK_SIZE = 4; static const uint32 COMBO_BOX_ITEM_SHANNON_ENTROPY = 0; -static const uint32 COMBO_BOX_ITEM_SHANNON_ENTROPY_DATA_TYPE = 1; -static const uint32 COMBO_BOX_ITEM_EMBEDDED_OBJECTS = 2; +static const uint32 COMBO_BOX_ITEM_RENYI_ENTROPY = 1; +static const uint32 COMBO_BOX_ITEM_SHANNON_ENTROPY_DATA_TYPE = 2; +static const uint32 COMBO_BOX_ITEM_EMBEDDED_OBJECTS = 3; + +enum class EntropyType { + Shannon = 0, + ShannonDataType = 1, + Renyi = 2 +}; class Plugin : public Window { @@ -29,11 +37,14 @@ class Plugin : public Window private: Reference object; Reference entropyComboBox; - Reference blockSizeComboBox; + Reference blockSizeSelector; Reference canvasEntropy; Reference canvasLegend; - uint32 blockSize = MINIMUM_BLOCK_SIZE; + Reference alphaSelector; + + uint32 blockSize = MINIMUM_BLOCK_SIZE; + double renyiAlpha = 0.5; private: void ResizeLegendCanvas(); @@ -47,8 +58,8 @@ class Plugin : public Window public: Plugin(Reference object); - bool DrawShannonEntropy(bool dataType); - bool DrawShannonEntropyLegend(bool dataType); + bool DrawEntropy(EntropyType type); + bool DrawEntropyLegend(EntropyType type); bool DrawEmbeddedObjects(); bool DrawEmbeddedObjectsLegend(); diff --git a/GenericPlugins/EntropyVisualizer/src/CMakeLists.txt b/GenericPlugins/EntropyVisualizer/src/CMakeLists.txt index 00cd1b47..178240f9 100644 --- a/GenericPlugins/EntropyVisualizer/src/CMakeLists.txt +++ b/GenericPlugins/EntropyVisualizer/src/CMakeLists.txt @@ -1 +1 @@ -target_sources(EntropyVisualizer PRIVATE EntropyVisualizer.cpp) \ No newline at end of file +target_sources(EntropyVisualizer PRIVATE Plugin.cpp EntropyVisualizer.cpp) diff --git a/GenericPlugins/EntropyVisualizer/src/EntropyVisualizer.cpp b/GenericPlugins/EntropyVisualizer/src/EntropyVisualizer.cpp index 89e354a4..29510251 100644 --- a/GenericPlugins/EntropyVisualizer/src/EntropyVisualizer.cpp +++ b/GenericPlugins/EntropyVisualizer/src/EntropyVisualizer.cpp @@ -1,7 +1,5 @@ #include "EntropyVisualizer.hpp" -#include - namespace GView::GenericPlugins::EntropyVisualizer { extern "C" { @@ -20,446 +18,4 @@ PLUGIN_EXPORT void UpdateSettings(IniSection sect) sect["Command.EntropyVisualizer"] = Input::Key::F12; } } - -Color Plugin::ShannonEntropyValueToColor(int32 value) -{ - switch (value) { - case 0: - return Color::White; - case 1: - return Color::Silver; - case 2: - return Color::Gray; - case 3: - return Color::Olive; - case 4: - return Color::Yellow; - case 5: - return Color::DarkGreen; - case 6: - return Color::DarkRed; - case 7: - return Color::Magenta; - case 8: - return Color::Red; - default: - return Color::Black; - } -} - -Color Plugin::ShannonEntropyDataTypeValueToColor(double value, double epsilon) -{ - if (value >= 8.0 - epsilon && value < 8.0 ) { - return Color::Green; - } - - if (value > 6.0 && value < 8.0 - epsilon) { - return Color::Red; - } - - return Color::Gray; -} - -Color Plugin::ShannonEntropyDataTypeValueToColorName(std::string_view name) -{ - if (name == "Plain") { - return Color::Gray; - } else if (name == "Binary") { - return Color::Red; - } else if (name == "Encrypted") { - return Color::Green; - } - return Color::Black; -} - -double Plugin::ComputeEpsilon(uint64 sample_size) -{ - return 2.0 - (log2(sample_size) - 2.0) / 10; -} - -// TODO: configurable colors using color picker -Color Plugin::EmbeddedObjectValueToColor(std::string_view name) -{ - if (name == "Archive") { - return Color::White; - } else if (name == "Cryptographic") { - return Color::Silver; - } else if (name == "Executable") { - return Color::Red; - } else if (name == "HTML Object") { - return Color::Olive; - } else if (name == "Image") { - return Color::Yellow; - } else if (name == "Multimedia") { - return Color::DarkGreen; - } else if (name == "Special Strings") { - return Color::Aqua; - } else { - return Color::Gray; - } -} - -Plugin::Plugin(Reference object) : Window("EntropyVisualizer", "d:c,w:95%,h:95%", WindowFlags::FixedPosition) -{ - auto desktop = AppCUI::Application::GetDesktop(); - this->parent = desktop->GetFocusedChild(); - this->object = object; - { - Factory::Label::Create(this, "Entropy type", "x:1, y:0,w:12,h:1"); - this->entropyComboBox = Factory::ComboBox::Create(this, "x:14, y:0,w:25,h:1", ""); - entropyComboBox->SetHotKey('E'); - entropyComboBox->AddItem(SHANNON_ENTROPY_OPTION_NAME, COMBO_BOX_ITEM_SHANNON_ENTROPY); - entropyComboBox->AddSeparator(); - entropyComboBox->AddItem(SHANNON_ENTROPY_DATA_TYPE_OPTION_NAME, COMBO_BOX_ITEM_SHANNON_ENTROPY_DATA_TYPE); - entropyComboBox->AddItem(EMBEDDED_OBJECTS_OPTION_NAME, COMBO_BOX_ITEM_EMBEDDED_OBJECTS); - // TODO: add the rest - entropyComboBox->SetCurentItemIndex(0); - } - { - Factory::Label::Create(this, "Block size", "x:40,y:0,w:10,h:1"); - this->blockSizeComboBox = Factory::ComboBox::Create(this, "x:51,y:0,w:12,h:1", ""); - blockSizeComboBox->SetHotKey('B'); - } - { - this->canvasEntropy = Factory::CanvasViewer::Create(this, "d:lb,w:85%,h:99%", this->GetWidth(), this->GetHeight(), Controls::ViewerFlags::Border); - auto canvas = this->canvasEntropy->GetCanvas(); - canvas->Resize(this->canvasEntropy->GetWidth(), this->canvasEntropy->GetHeight()); - canvas->SetCursor(0, 0); - } - { - this->canvasLegend = Factory::CanvasViewer::Create(this, "d:tr,w:15%,h:20%", this->GetWidth(), this->GetHeight(), Controls::ViewerFlags::Border); - auto canvas = this->canvasLegend->GetCanvas(); - canvas->Resize(this->canvasLegend->GetWidth(), this->canvasLegend->GetHeight()); - } - - this->InitializeBlocksForCanvas(); - // raise events after all children are initialized - this->entropyComboBox->RaiseEvent(Event::ComboBoxClosed); - - this->canvasEntropy->SetFocus(); -} - -bool Plugin::DrawShannonEntropy(bool dataType) -{ - CHECK(this->canvasEntropy.IsValid(), false, ""); - auto canvas = this->canvasEntropy->GetCanvas(); - - auto& cache = object->GetData(); - const auto size = cache.GetSize(); - const auto epsilon = ComputeEpsilon(this->blockSize); - const uint32 blocksCount = static_cast(size / this->blockSize + 1); - - uint32 x = 0; - uint32 y = 0; - uint32 maxX = canvas->GetWidth(); - uint32 maxY = std::max(blocksCount / maxX + 1 + 1, canvas->GetHeight()); - const auto color = ColorPair{ Color::White, this->GetConfig()->Window.Background.Normal }; - canvas->Resize(maxX, maxY, 'X', color); - canvas->ClearEntireSurface('X', color); - - for (uint32 i = 0; i < blocksCount; i++) { - auto bf = cache.Get(i * static_cast(this->blockSize), this->blockSize, false); - const auto value = GView::Entropy::ShannonEntropy(bf); - const auto roundedValue = static_cast(std::llround(value)); - const auto fColor = dataType ? ShannonEntropyDataTypeValueToColor(value, epsilon) : ShannonEntropyValueToColor(roundedValue); - - canvas->WriteSpecialCharacter(x++, y, BLOCK_SPECIAL_CHARACTER, ColorPair{ fColor, CANVAS_ENTROPY_BACKGROUND }); - if (x == maxX) { - x = 0; - y++; - } - } - - return true; -} - -bool Plugin::DrawShannonEntropyLegend(bool dataType) -{ - CHECK(this->canvasLegend.IsValid(), false, ""); - ResizeLegendCanvas(); - - auto canvas = this->canvasLegend->GetCanvas(); - - const auto color = ColorPair{ Color::White, this->GetConfig()->Window.Background.Normal }; - - uint32 x = 0; - uint32 y = 0; - canvas->Clear(' ', color); - - std::string_view name = dataType ? "Shanon Data Type Legend" : "Shanon Legend [0-8]"; - - canvas->WriteSingleLineText(x, y++, name, color); - canvas->FillHorizontalLineWithSpecialChar(x, y++, canvas->GetWidth(), SpecialChars::BoxHorizontalSingleLine, color); - x = 0; - - if (dataType) { - static std::vector SHANNON_ENTROPY_DATA_TYPES{ "Plain", "Binary", "Encrypted" }; - - for (uint32 i = 0; i <= SHANNON_ENTROPY_DATA_TYPE_MAX_VALUE; i++) { - const auto& name = SHANNON_ENTROPY_DATA_TYPES.at(i); - canvas->WriteSingleLineText(x, y++, name, color); - - while (x < canvas->GetWidth()) { - canvas->WriteSpecialCharacter( - x++, y, BLOCK_SPECIAL_CHARACTER, ColorPair{ ShannonEntropyDataTypeValueToColorName(name), CANVAS_ENTROPY_BACKGROUND }); - } - - y++; - x = 0; - } - } else { - for (uint32 i = 0; i <= SHANNON_ENTROPY_MAX_VALUE; i++) { - canvas->WriteCharacter(x++, y, i + '0', color); - canvas->WriteCharacter(x++, y, ' ', color); - canvas->WriteCharacter(x++, y, '=', color); - canvas->WriteCharacter(x++, y, '>', color); - canvas->WriteCharacter(x++, y, ' ', color); - - while (x < canvas->GetWidth()) { - canvas->WriteSpecialCharacter(x++, y, BLOCK_SPECIAL_CHARACTER, ColorPair{ ShannonEntropyValueToColor(i), CANVAS_ENTROPY_BACKGROUND }); - } - y++; - x = 0; - } - } - - return true; -} - -bool Plugin::DrawEmbeddedObjects() -{ - constexpr std::string_view VIEW_NAME{ "Buffer View" }; - - auto interface = this->parent.ToObjectRef(); - - auto currentView = interface->GetCurrentView(); - const auto currentViewName = currentView->GetName(); - - CHECK(currentViewName == VIEW_NAME, true, ""); - - const auto& zones = currentView->GetObjectsHighlightingZonesList(); - - CHECK(this->canvasEntropy.IsValid(), false, ""); - auto canvas = this->canvasEntropy->GetCanvas(); - - auto& cache = object->GetData(); - const auto size = cache.GetSize(); - const uint32 blocksCount = static_cast(size / this->blockSize + 1); - - uint32 x = 0; - uint32 y = 0; - uint32 maxX = canvas->GetWidth(); - uint32 maxY = std::max(blocksCount / maxX + 1 + 1, canvas->GetHeight()); - const auto color = ColorPair{ Color::White, this->GetConfig()->Window.Background.Normal }; - canvas->Resize(maxX, maxY, 'X', color); - canvas->ClearEntireSurface('X', color); - - for (uint32 i = 0; i < blocksCount; i++) { - const auto fColor = EmbeddedObjectValueToColor(""); - canvas->WriteCharacter(x++, y, ' ', ColorPair{ fColor, fColor }); - if (x == maxX) { - x = 0; - y++; - } - } - x = 0; - y = 0; - - const auto zonesNo = zones.GetCount(); - for (uint32 i = 0; i < zonesNo; i++) { - const auto& zone = zones.GetZone(i); - if (zone.has_value()) { - const auto blockStart = zone->interval.low / this->blockSize; - const auto blockEnd = zone->interval.high / this->blockSize; - const auto deltaBlocks = blockEnd - blockStart; - - x = blockStart % maxX; - y = blockStart / maxX; - y = blockStart / maxX; - - // bad.. TODO: change - Color c = EmbeddedObjectValueToColor("Executable"); - const auto zn = std::string_view{ zone->name }; - if (zn == "Email Address") { - c = EmbeddedObjectValueToColor("Special Strings"); - } else if (zn == "Filepath") { - c = EmbeddedObjectValueToColor("Special Strings"); - } else if (zn == "IFrame") { - c = EmbeddedObjectValueToColor("HTML Object"); - } else if (zn == "IP Address") { - c = EmbeddedObjectValueToColor("Special Strings"); - } else if (zn == "MZPE") { - c = EmbeddedObjectValueToColor("Executable"); - } else if (zn == "PHP") { - c = EmbeddedObjectValueToColor("HTML Object"); - } else if (zn == "PNG") { - c = EmbeddedObjectValueToColor("Image"); - } else if (zn == "Registry") { - c = EmbeddedObjectValueToColor("Special Strings"); - } else if (zn == "Script") { - c = EmbeddedObjectValueToColor("HTML Object"); - } else if (zn == "URL") { - c = EmbeddedObjectValueToColor("Special Strings"); - } else if (zn == "Wallet") { - c = EmbeddedObjectValueToColor("Special Strings"); - } else if (zn == "XML") { - c = EmbeddedObjectValueToColor("HTML Object"); - } - - for (uint32 j = 0; j <= deltaBlocks; j++) { - canvas->WriteSpecialCharacter(x++, y, BLOCK_SPECIAL_CHARACTER, ColorPair{ c, CANVAS_ENTROPY_BACKGROUND }); - if (x == maxX) { - x = 0; - y++; - } - } - } - } - - return true; -} - -bool Plugin::DrawEmbeddedObjectsLegend() -{ - CHECK(this->canvasLegend.IsValid(), false, ""); - ResizeLegendCanvas(); - - auto canvas = this->canvasLegend->GetCanvas(); - - const auto color = ColorPair{ Color::White, this->GetConfig()->Window.Background.Normal }; - - uint32 x = 0; - uint32 y = 0; - canvas->Clear(' ', color); - - canvas->WriteSingleLineText(x, y++, "Embedded objects", color); - canvas->FillHorizontalLineWithSpecialChar(x, y++, canvas->GetWidth(), SpecialChars::BoxHorizontalSingleLine, color); - x = 0; - - static std::vector EMBEDDED_OBJECTS{ "Archive", "Cryptographic", "Executable", "HTML Object", "Image", "Multimedia", "Special Strings" }; - - for (uint32 i = 0; i <= EMBEDDED_OBJECTS_MAX_VALUE; i++) { - const auto& name = EMBEDDED_OBJECTS.at(i); - canvas->WriteSingleLineText(x, y++, name, color); - - while (x < canvas->GetWidth()) { - canvas->WriteSpecialCharacter(x++, y, BLOCK_SPECIAL_CHARACTER, ColorPair{ EmbeddedObjectValueToColor(name), CANVAS_ENTROPY_BACKGROUND }); - } - - y++; - x = 0; - } - canvas->WriteSingleLineText(x, y++, "Unknown", color); - while (x < canvas->GetWidth()) { - canvas->WriteSpecialCharacter(x++, y, BLOCK_SPECIAL_CHARACTER, ColorPair{ EmbeddedObjectValueToColor(""), CANVAS_ENTROPY_BACKGROUND }); - } - - return true; -} - -std::optional Plugin::IsOffsetInZone(const GView::Utils::ZonesList& zones, uint64 offset) const -{ - const auto zonesNo = zones.GetCount(); - for (uint32 i = 0; i < zonesNo; i++) { - const auto zone = zones.GetZone(i); - if (zone.has_value()) { - if (offset >= zone->interval.low && offset <= zone->interval.high) { - return zone; - } - } - } - - return std::nullopt; -} - -void Plugin::OnAfterResize(int newWidth, int newHeight) -{ - ResizeLegendCanvas(); - - // this->MoveTo(this->parent->GetX() + 10, this->parent->GetY() + 10); - // this->Resize(this->parent->GetWidth() - 10, this->parent->GetHeight() - 10); -} - -void Plugin::ResizeLegendCanvas() -{ - CHECKRET(canvasLegend.IsValid(), ""); - CHECKRET(canvasEntropy.IsValid(), ""); - this->canvasLegend->MoveTo(this->canvasLegend->GetX(), this->canvasEntropy->GetY()); - - uint32 newHeight = this->canvasLegend->GetHeight(); - const auto entropy = this->entropyComboBox->GetCurrentItemUserData(-1); - if (entropy == COMBO_BOX_ITEM_SHANNON_ENTROPY) { - newHeight = SHANNON_ENTROPY_LEGEND_HEIGHT; - } else if (entropy == COMBO_BOX_ITEM_SHANNON_ENTROPY_DATA_TYPE) { - newHeight = SHANNON_ENTROPY_LEGEND_DATA_TYPE_HEIGHT; - } else if (entropy == COMBO_BOX_ITEM_EMBEDDED_OBJECTS) { - newHeight = EMBEDDED_OBJECTS_LEGEND_HEIGHT; - } - - this->canvasLegend->Resize(this->canvasLegend->GetWidth(), newHeight); - auto canvas = this->canvasLegend->GetCanvas(); - canvas->Resize(this->canvasLegend->GetWidth(), newHeight); -} - -bool Plugin::OnEvent(Reference sender, Event eventType, int controlID) -{ - if (Window::OnEvent(sender, eventType, controlID)) { - return true; - } - - switch (eventType) { - case AppCUI::Controls::Event::ComboBoxSelectedItemChanged: - /* nothing, it is to costly computing entropy each time / on the fly */ - break; - case AppCUI::Controls::Event::ComboBoxClosed: - if (sender == this->entropyComboBox.ToBase()) { - const auto entropy = this->entropyComboBox->GetCurrentItemUserData(-1); - if (entropy == COMBO_BOX_ITEM_SHANNON_ENTROPY) { - this->DrawShannonEntropy(false); - this->DrawShannonEntropyLegend(false); - } else if (entropy == COMBO_BOX_ITEM_SHANNON_ENTROPY_DATA_TYPE) { - this->DrawShannonEntropy(true); - this->DrawShannonEntropyLegend(true); - } else if (entropy == COMBO_BOX_ITEM_EMBEDDED_OBJECTS) { - this->DrawEmbeddedObjects(); - this->DrawEmbeddedObjectsLegend(); - } - return true; - } - if (sender == this->blockSizeComboBox.ToBase()) { - this->blockSize = static_cast(this->blockSizeComboBox->GetCurrentItemUserData(-1)); - this->entropyComboBox->RaiseEvent(Event::ComboBoxClosed); - return true; - } - break; - default: - break; - } - - return false; -} - -bool Plugin::InitializeBlocksForCanvas() -{ - CHECK(this->object.IsValid(), false, ""); - CHECK(this->canvasEntropy.IsValid(), false, ""); - - const auto size = this->object->GetData().GetSize(); - auto canvas = this->canvasEntropy->GetCanvas(); - const auto canvasWidth = canvas->GetWidth(); - const auto canvasHeight = canvas->GetHeight(); - - uint32 blocksRows = 0; - do { - uint32 blocksCount = static_cast(size / this->blockSize); - blocksRows = blocksCount / canvasWidth + 1; - blockSizeComboBox->AddItem(std::to_string(this->blockSize), this->blockSize); - this->blockSize *= 2; - } while (blocksRows > canvasHeight); - this->blockSize /= 2; - - blockSizeComboBox->SetCurentItemIndex(blockSizeComboBox->GetItemsCount() - 1); - - return true; -} } // namespace GView::GenericPlugins::EntropyVisualizer diff --git a/GenericPlugins/EntropyVisualizer/src/Plugin.cpp b/GenericPlugins/EntropyVisualizer/src/Plugin.cpp new file mode 100644 index 00000000..370a5e3e --- /dev/null +++ b/GenericPlugins/EntropyVisualizer/src/Plugin.cpp @@ -0,0 +1,511 @@ +#include "EntropyVisualizer.hpp" + +#include + +namespace GView::GenericPlugins::EntropyVisualizer +{ +Color Plugin::ShannonEntropyValueToColor(int32 value) +{ + switch (value) { + case 0: + return Color::White; + case 1: + return Color::Silver; + case 2: + return Color::Gray; + case 3: + return Color::Olive; + case 4: + return Color::Yellow; + case 5: + return Color::DarkGreen; + case 6: + return Color::DarkRed; + case 7: + return Color::Magenta; + case 8: + return Color::Red; + default: + if (value < 0) { + return Color::Black; + } else { + return Color::Pink; + } + } +} + +Color Plugin::ShannonEntropyDataTypeValueToColor(double value, double epsilon) +{ + if (value >= 8.0 - epsilon && value < 8.0) { + return Color::Green; + } + + if (value > 6.0 && value < 8.0 - epsilon) { + return Color::Red; + } + + return Color::Gray; +} + +Color Plugin::ShannonEntropyDataTypeValueToColorName(std::string_view name) +{ + if (name == "Plain") { + return Color::Gray; + } else if (name == "Binary") { + return Color::Red; + } else if (name == "Encrypted") { + return Color::Green; + } + return Color::Black; +} + +double Plugin::ComputeEpsilon(uint64 sample_size) +{ + return 2.0 - (log2(sample_size) - 2.0) / 10; +} + +// TODO: configurable colors using color picker +Color Plugin::EmbeddedObjectValueToColor(std::string_view name) +{ + if (name == "Archive") { + return Color::White; + } else if (name == "Cryptographic") { + return Color::Silver; + } else if (name == "Executable") { + return Color::Red; + } else if (name == "HTML Object") { + return Color::Olive; + } else if (name == "Image") { + return Color::Yellow; + } else if (name == "Multimedia") { + return Color::DarkGreen; + } else if (name == "Special Strings") { + return Color::Aqua; + } else { + return Color::Gray; + } +} + +Plugin::Plugin(Reference object) : Window("Entropy Visualizer", "d:c,w:100%,h:98%", WindowFlags::FixedPosition) +{ + auto desktop = AppCUI::Application::GetDesktop(); + this->parent = desktop->GetFocusedChild(); + this->object = object; + { + this->canvasEntropy = Factory::CanvasViewer::Create(this, "d:lb,w:80%,h:100%", this->GetWidth(), this->GetHeight(), Controls::ViewerFlags::Border); + auto canvas = this->canvasEntropy->GetCanvas(); + canvas->Resize(this->canvasEntropy->GetWidth(), this->canvasEntropy->GetHeight()); + canvas->SetCursor(0, 0); + } + { + Factory::Label::Create(this, "Entropy type", "x:81%, y:0,w:12,h:1"); + this->entropyComboBox = Factory::ComboBox::Create(this, "x:81%,y:1,w:19%,h:1", ""); + entropyComboBox->SetHotKey('E'); + entropyComboBox->AddItem(SHANNON_ENTROPY_OPTION_NAME, COMBO_BOX_ITEM_SHANNON_ENTROPY); + entropyComboBox->AddItem(RENYI_ENTROPY_OPTION_NAME, COMBO_BOX_ITEM_RENYI_ENTROPY); + entropyComboBox->AddSeparator(); + entropyComboBox->AddItem(SHANNON_ENTROPY_DATA_TYPE_OPTION_NAME, COMBO_BOX_ITEM_SHANNON_ENTROPY_DATA_TYPE); + entropyComboBox->AddItem(EMBEDDED_OBJECTS_OPTION_NAME, COMBO_BOX_ITEM_EMBEDDED_OBJECTS); + // TODO: add the rest + entropyComboBox->SetCurentItemIndex(0); + } + { + Factory::Label::Create(this, "Block size", "x:81%,y:3,w:19%,h:1"); + this->blockSizeSelector = Factory::NumericSelector::Create(this, MINIMUM_BLOCK_SIZE, object->GetData().GetSize(), 32, "x:81%,y:4,w:19%,h:1"); + blockSizeSelector->SetHotKey('B'); + } + { + Factory::Label::Create(this, "Alpha (Renyi) /10", "x:81%,y:6,w:19%,h:1"); + this->alphaSelector = Factory::NumericSelector::Create(this, -99, 99, 0, "x:81%,y:7,w:19%,h:1"); + } + { + this->canvasLegend = Factory::CanvasViewer::Create( + this, "x:81%,y:9,w:19%,h:20%", this->GetWidth(), this->GetHeight(), Controls::ViewerFlags::Border | Controls::ViewerFlags::HideScrollBar); + auto canvas = this->canvasLegend->GetCanvas(); + canvas->Resize(this->canvasLegend->GetWidth(), this->canvasLegend->GetHeight()); + } + + this->InitializeBlocksForCanvas(); + // raise events after all children are initialized + this->entropyComboBox->RaiseEvent(Event::ComboBoxClosed); + + this->canvasEntropy->SetFocus(); +} + +bool Plugin::DrawEntropy(EntropyType type) +{ + CHECK(this->canvasEntropy.IsValid(), false, ""); + auto canvas = this->canvasEntropy->GetCanvas(); + + auto& cache = object->GetData(); + const auto size = cache.GetSize(); + const auto epsilon = ComputeEpsilon(this->blockSize); + const uint32 blocksCount = static_cast(size / this->blockSize + 1); + + uint32 x = 0; + uint32 y = 0; + uint32 maxX = canvas->GetWidth(); + uint32 maxY = std::max(blocksCount / maxX + 1 + 1, canvas->GetHeight()); + const auto color = ColorPair{ Color::White, this->GetConfig()->Window.Background.Normal }; + canvas->Resize(maxX, maxY, 'X', color); + canvas->ClearEntireSurface('X', color); + + for (uint32 i = 0; i < blocksCount; i++) { + auto bf = cache.Get(i * static_cast(this->blockSize), this->blockSize, false); + auto value = 0.0; + switch (type) { + case EntropyType::Shannon: + case EntropyType::ShannonDataType: + value = GView::Entropy::ShannonEntropy(bf); + break; + case EntropyType::Renyi: + value = GView::Entropy::RenyiEntropy(bf, this->renyiAlpha); + default: + break; + } + + auto fColor = Color::Black; + switch (type) { + case EntropyType::Shannon: + case EntropyType::Renyi: + fColor = ShannonEntropyValueToColor(static_cast(std::llround(value))); + break; + case EntropyType::ShannonDataType: + fColor = ShannonEntropyDataTypeValueToColor(value, epsilon); + default: + break; + } + + canvas->WriteSpecialCharacter(x++, y, BLOCK_SPECIAL_CHARACTER, ColorPair{ fColor, CANVAS_ENTROPY_BACKGROUND }); + if (x == maxX) { + x = 0; + y++; + } + } + + return true; +} + +bool Plugin::DrawEntropyLegend(EntropyType type) +{ + CHECK(this->canvasLegend.IsValid(), false, ""); + ResizeLegendCanvas(); + + auto canvas = this->canvasLegend->GetCanvas(); + const auto color = ColorPair{ Color::White, this->GetConfig()->Window.Background.Normal }; + canvas->Clear(' ', color); + + std::string_view name = ""; + switch (type) { + case EntropyType::Shannon: + name = "Shanon Legend [0-8]"; + break; + case EntropyType::ShannonDataType: + name = "Shanon Data Type Legend"; + break; + case EntropyType::Renyi: + name = "Renyi Legend [0-8]"; + break; + default: + break; + } + + uint32 x = 0; + uint32 y = 0; + canvas->WriteSingleLineText(x, y++, name, color); + canvas->FillHorizontalLineWithSpecialChar(x, y++, canvas->GetWidth(), SpecialChars::BoxHorizontalSingleLine, color); + x = 0; + + switch (type) { + case EntropyType::Shannon: + case EntropyType::Renyi: + for (uint32 i = 0; i <= SHANNON_ENTROPY_MAX_VALUE; i++) { + canvas->WriteCharacter(x++, y, i + '0', color); + canvas->WriteCharacter(x++, y, ' ', color); + canvas->WriteCharacter(x++, y, '=', color); + canvas->WriteCharacter(x++, y, '>', color); + canvas->WriteCharacter(x++, y, ' ', color); + + while (x < canvas->GetWidth()) { + canvas->WriteSpecialCharacter(x++, y, BLOCK_SPECIAL_CHARACTER, ColorPair{ ShannonEntropyValueToColor(i), CANVAS_ENTROPY_BACKGROUND }); + } + y++; + x = 0; + } + break; + case EntropyType::ShannonDataType: { + static std::vector SHANNON_ENTROPY_DATA_TYPES{ "Plain", "Binary", "Encrypted" }; + + for (uint32 i = 0; i <= SHANNON_ENTROPY_DATA_TYPE_MAX_VALUE; i++) { + const auto& name = SHANNON_ENTROPY_DATA_TYPES.at(i); + canvas->WriteSingleLineText(x, y++, name, color); + + while (x < canvas->GetWidth()) { + canvas->WriteSpecialCharacter( + x++, y, BLOCK_SPECIAL_CHARACTER, ColorPair{ ShannonEntropyDataTypeValueToColorName(name), CANVAS_ENTROPY_BACKGROUND }); + } + + y++; + x = 0; + } + } break; + default: + break; + } + + return true; +} + +bool Plugin::DrawEmbeddedObjects() +{ + constexpr std::string_view VIEW_NAME{ "Buffer View" }; + + auto interface = this->parent.ToObjectRef(); + + auto currentView = interface->GetCurrentView(); + const auto currentViewName = currentView->GetName(); + + CHECK(currentViewName == VIEW_NAME, true, ""); + + const auto& zones = currentView->GetObjectsHighlightingZonesList(); + + CHECK(this->canvasEntropy.IsValid(), false, ""); + auto canvas = this->canvasEntropy->GetCanvas(); + + auto& cache = object->GetData(); + const auto size = cache.GetSize(); + const uint32 blocksCount = static_cast(size / this->blockSize + 1); + + uint32 x = 0; + uint32 y = 0; + uint32 maxX = canvas->GetWidth(); + uint32 maxY = std::max(blocksCount / maxX + 1 + 1, canvas->GetHeight()); + const auto color = ColorPair{ Color::White, this->GetConfig()->Window.Background.Normal }; + canvas->Resize(maxX, maxY, 'X', color); + canvas->ClearEntireSurface('X', color); + + for (uint32 i = 0; i < blocksCount; i++) { + const auto fColor = EmbeddedObjectValueToColor(""); + canvas->WriteCharacter(x++, y, ' ', ColorPair{ fColor, fColor }); + if (x == maxX) { + x = 0; + y++; + } + } + x = 0; + y = 0; + + const auto zonesNo = zones.GetCount(); + for (uint32 i = 0; i < zonesNo; i++) { + const auto& zone = zones.GetZone(i); + if (zone.has_value()) { + const auto blockStart = zone->interval.low / this->blockSize; + const auto blockEnd = zone->interval.high / this->blockSize; + const auto deltaBlocks = blockEnd - blockStart; + + x = blockStart % maxX; + y = blockStart / maxX; + y = blockStart / maxX; + + // bad.. TODO: change + Color c = EmbeddedObjectValueToColor("Executable"); + const auto zn = std::string_view{ zone->name }; + if (zn == "Email Address") { + c = EmbeddedObjectValueToColor("Special Strings"); + } else if (zn == "Filepath") { + c = EmbeddedObjectValueToColor("Special Strings"); + } else if (zn == "IFrame") { + c = EmbeddedObjectValueToColor("HTML Object"); + } else if (zn == "IP Address") { + c = EmbeddedObjectValueToColor("Special Strings"); + } else if (zn == "MZPE") { + c = EmbeddedObjectValueToColor("Executable"); + } else if (zn == "PHP") { + c = EmbeddedObjectValueToColor("HTML Object"); + } else if (zn == "PNG") { + c = EmbeddedObjectValueToColor("Image"); + } else if (zn == "Registry") { + c = EmbeddedObjectValueToColor("Special Strings"); + } else if (zn == "Script") { + c = EmbeddedObjectValueToColor("HTML Object"); + } else if (zn == "URL") { + c = EmbeddedObjectValueToColor("Special Strings"); + } else if (zn == "Wallet") { + c = EmbeddedObjectValueToColor("Special Strings"); + } else if (zn == "XML") { + c = EmbeddedObjectValueToColor("HTML Object"); + } + + for (uint32 j = 0; j <= deltaBlocks; j++) { + canvas->WriteSpecialCharacter(x++, y, BLOCK_SPECIAL_CHARACTER, ColorPair{ c, CANVAS_ENTROPY_BACKGROUND }); + if (x == maxX) { + x = 0; + y++; + } + } + } + } + + return true; +} + +bool Plugin::DrawEmbeddedObjectsLegend() +{ + CHECK(this->canvasLegend.IsValid(), false, ""); + ResizeLegendCanvas(); + + auto canvas = this->canvasLegend->GetCanvas(); + + const auto color = ColorPair{ Color::White, this->GetConfig()->Window.Background.Normal }; + + uint32 x = 0; + uint32 y = 0; + canvas->Clear(' ', color); + + canvas->WriteSingleLineText(x, y++, "Embedded objects", color); + canvas->FillHorizontalLineWithSpecialChar(x, y++, canvas->GetWidth(), SpecialChars::BoxHorizontalSingleLine, color); + x = 0; + + static std::vector EMBEDDED_OBJECTS{ "Archive", "Cryptographic", "Executable", "HTML Object", "Image", "Multimedia", "Special Strings" }; + + for (uint32 i = 0; i <= EMBEDDED_OBJECTS_MAX_VALUE; i++) { + const auto& name = EMBEDDED_OBJECTS.at(i); + canvas->WriteSingleLineText(x, y++, name, color); + + while (x < canvas->GetWidth()) { + canvas->WriteSpecialCharacter(x++, y, BLOCK_SPECIAL_CHARACTER, ColorPair{ EmbeddedObjectValueToColor(name), CANVAS_ENTROPY_BACKGROUND }); + } + + y++; + x = 0; + } + canvas->WriteSingleLineText(x, y++, "Unknown", color); + while (x < canvas->GetWidth()) { + canvas->WriteSpecialCharacter(x++, y, BLOCK_SPECIAL_CHARACTER, ColorPair{ EmbeddedObjectValueToColor(""), CANVAS_ENTROPY_BACKGROUND }); + } + + return true; +} + +std::optional Plugin::IsOffsetInZone(const GView::Utils::ZonesList& zones, uint64 offset) const +{ + const auto zonesNo = zones.GetCount(); + for (uint32 i = 0; i < zonesNo; i++) { + const auto zone = zones.GetZone(i); + if (zone.has_value()) { + if (offset >= zone->interval.low && offset <= zone->interval.high) { + return zone; + } + } + } + + return std::nullopt; +} + +void Plugin::OnAfterResize(int, int) +{ + ResizeLegendCanvas(); + + // this->MoveTo(this->parent->GetX() + 10, this->parent->GetY() + 10); + // this->Resize(this->parent->GetWidth() - 10, this->parent->GetHeight() - 10); +} + +void Plugin::ResizeLegendCanvas() +{ + CHECKRET(canvasLegend.IsValid(), ""); + CHECKRET(canvasEntropy.IsValid(), ""); + // this->canvasLegend->MoveTo(this->canvasLegend->GetX(), this->canvasEntropy->GetY()); + + uint32 newHeight = this->canvasLegend->GetHeight(); + const auto entropy = this->entropyComboBox->GetCurrentItemUserData(-1); + if (entropy == COMBO_BOX_ITEM_SHANNON_ENTROPY) { + newHeight = SHANNON_ENTROPY_LEGEND_HEIGHT; + } else if (entropy == COMBO_BOX_ITEM_SHANNON_ENTROPY_DATA_TYPE) { + newHeight = SHANNON_ENTROPY_LEGEND_DATA_TYPE_HEIGHT; + } else if (entropy == COMBO_BOX_ITEM_EMBEDDED_OBJECTS) { + newHeight = EMBEDDED_OBJECTS_LEGEND_HEIGHT; + } + + this->canvasLegend->Resize(this->canvasLegend->GetWidth(), newHeight); + auto canvas = this->canvasLegend->GetCanvas(); + canvas->Resize(this->canvasLegend->GetWidth(), newHeight); +} + +bool Plugin::OnEvent(Reference sender, Event eventType, int controlID) +{ + if (Window::OnEvent(sender, eventType, controlID)) { + return true; + } + + const auto drawSelectedEntropyType = [this]() -> bool { + const auto entropy = this->entropyComboBox->GetCurrentItemUserData(-1); + switch (entropy) { + case COMBO_BOX_ITEM_SHANNON_ENTROPY: + this->DrawEntropy(EntropyType::Shannon); + this->DrawEntropyLegend(EntropyType::Shannon); + break; + case COMBO_BOX_ITEM_RENYI_ENTROPY: + this->DrawEntropy(EntropyType::Renyi); + this->DrawEntropyLegend(EntropyType::Renyi); + break; + case COMBO_BOX_ITEM_SHANNON_ENTROPY_DATA_TYPE: + this->DrawEntropy(EntropyType::ShannonDataType); + this->DrawEntropyLegend(EntropyType::ShannonDataType); + break; + case COMBO_BOX_ITEM_EMBEDDED_OBJECTS: + this->DrawEmbeddedObjects(); + this->DrawEmbeddedObjectsLegend(); + break; + default: + break; + } + return true; + }; + + switch (eventType) { + case AppCUI::Controls::Event::ComboBoxSelectedItemChanged: + /* nothing, it is to costly computing entropy each time / on the fly */ + break; + case AppCUI::Controls::Event::ComboBoxClosed: + if (sender == this->entropyComboBox.ToBase()) { + return drawSelectedEntropyType(); + } + break; + case AppCUI::Controls::Event::NumericSelectorValueChanged: + if (sender == this->blockSizeSelector.ToBase()) { + this->blockSize = this->blockSizeSelector->GetValue(); + return drawSelectedEntropyType(); + } else if (sender == this->alphaSelector.ToBase()) { + this->renyiAlpha = this->alphaSelector->GetValue() / 10; + return drawSelectedEntropyType(); + } + break; + default: + break; + } + + return false; +} + +bool Plugin::InitializeBlocksForCanvas() +{ + CHECK(this->object.IsValid(), false, ""); + CHECK(this->canvasEntropy.IsValid(), false, ""); + + const auto size = this->object->GetData().GetSize(); + auto canvas = this->canvasEntropy->GetCanvas(); + const auto canvasWidth = canvas->GetWidth(); + const auto canvasHeight = canvas->GetHeight(); + + uint32 blocksRows = 0; + do { + uint32 blocksCount = static_cast(size / this->blockSize); + blocksRows = blocksCount / canvasWidth + 1; + this->blockSize *= 2; + } while (blocksRows > canvasHeight); + this->blockSize /= 2; + this->blockSizeSelector->SetValue(this->blockSize); + + return true; +} +} // namespace GView::GenericPlugins::EntropyVisualizer diff --git a/GenericPlugins/Unpacker/include/Unpacker.hpp b/GenericPlugins/Unpacker/include/Unpacker.hpp index 443c67a0..3822bddf 100644 --- a/GenericPlugins/Unpacker/include/Unpacker.hpp +++ b/GenericPlugins/Unpacker/include/Unpacker.hpp @@ -1,25 +1,35 @@ #pragma once #include "GView.hpp" -#include -namespace GView::GenericPlugins::Unpackers +namespace GView::GenericPlugins::Unpacker { using namespace AppCUI::Graphics; using namespace GView::View; class Plugin : public Window, public Handlers::OnButtonPressedInterface { + private: + Reference object; + Reference parent; + Reference list; - Reference sync; + Reference