1 #include "views/view_information.hpp"
2 
3 #include <hex/providers/provider.hpp>
4 #include <hex/helpers/utils.hpp>
5 
6 #include <cstring>
7 #include <cmath>
8 #include <filesystem>
9 #include <span>
10 #include <vector>
11 
12 #include <magic.h>
13 
14 namespace hex {
15 
ViewInformation()16     ViewInformation::ViewInformation() : View("hex.view.information.title"_lang) {
17         View::subscribeEvent(Events::DataChanged, [this](auto) {
18             this->m_dataValid = false;
19             this->m_highestBlockEntropy = 0;
20             this->m_blockEntropy.clear();
21             this->m_averageEntropy = 0;
22             this->m_blockSize = 0;
23             this->m_valueCounts.fill(0x00);
24             this->m_mimeType = "";
25             this->m_fileDescription = "";
26             this->m_analyzedRegion = { 0, 0 };
27         });
28     }
29 
~ViewInformation()30     ViewInformation::~ViewInformation() {
31         View::unsubscribeEvent(Events::DataChanged);
32     }
33 
calculateEntropy(std::array<float,256> & valueCounts,size_t numBytes)34     static float calculateEntropy(std::array<float, 256> &valueCounts, size_t numBytes) {
35         float entropy = 0;
36 
37         for (u16 i = 0; i < 256; i++) {
38             valueCounts[i] /= numBytes;
39 
40             if (valueCounts[i] > 0)
41                 entropy -= (valueCounts[i] * std::log2(valueCounts[i]));
42         }
43 
44         return entropy / 8;
45     }
46 
drawContent()47     void ViewInformation::drawContent() {
48         if (ImGui::Begin("hex.view.information.title"_lang, &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
49             ImGui::BeginChild("##scrolling", ImVec2(0, 0), false, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav);
50 
51             auto provider = SharedData::currentProvider;
52 
53             if (provider != nullptr && provider->isReadable()) {
54                 if (this->m_shouldInvalidate) {
55 
56                     this->m_analyzedRegion = { provider->getBaseAddress(), provider->getBaseAddress() + provider->getSize() };
57 
58                     {
59                         this->m_blockSize = std::ceil(provider->getSize() / 2048.0F);
60                         std::vector<u8> buffer(this->m_blockSize, 0x00);
61                         std::memset(this->m_valueCounts.data(), 0x00, this->m_valueCounts.size() * sizeof(u32));
62                         this->m_blockEntropy.clear();
63 
64                         for (u64 i = 0; i < provider->getSize(); i += this->m_blockSize) {
65                             std::array<float, 256> blockValueCounts = { 0 };
66                             provider->read(i, buffer.data(), std::min(u64(this->m_blockSize), provider->getSize() - i));
67 
68                             for (size_t j = 0; j < this->m_blockSize; j++) {
69                                 blockValueCounts[buffer[j]]++;
70                                 this->m_valueCounts[buffer[j]]++;
71                             }
72                             this->m_blockEntropy.push_back(calculateEntropy(blockValueCounts, this->m_blockSize));
73                         }
74 
75                         this->m_averageEntropy = calculateEntropy(this->m_valueCounts, provider->getSize());
76                         this->m_highestBlockEntropy = *std::max_element(this->m_blockEntropy.begin(), this->m_blockEntropy.end());
77                     }
78 
79                     {
80                         std::vector<u8> buffer(provider->getSize(), 0x00);
81                         provider->read(0x00, buffer.data(), buffer.size());
82 
83                         this->m_fileDescription.clear();
84                         this->m_mimeType.clear();
85 
86                         std::string magicFiles;
87 
88                         std::error_code error;
89                         for (const auto &entry : std::filesystem::directory_iterator("magic", error)) {
90                             if (entry.is_regular_file() && entry.path().extension() == ".mgc")
91                                 magicFiles += entry.path().string() + MAGIC_PATH_SEPARATOR;
92                         }
93 
94                         if (!error) {
95                             magicFiles.pop_back();
96 
97                             {
98                                 magic_t cookie = magic_open(MAGIC_NONE);
99                                 if (magic_load(cookie, magicFiles.c_str()) != -1)
100                                     this->m_fileDescription = magic_buffer(cookie, buffer.data(), buffer.size());
101                                 else
102                                     this->m_fileDescription = "";
103 
104                                 magic_close(cookie);
105                             }
106 
107 
108                             {
109                                 magic_t cookie = magic_open(MAGIC_MIME);
110                                 if (magic_load(cookie, magicFiles.c_str()) != -1)
111                                     this->m_mimeType = magic_buffer(cookie, buffer.data(), buffer.size());
112                                 else
113                                     this->m_mimeType = "";
114 
115                                 magic_close(cookie);
116                             }
117 
118                         }
119 
120 
121                         this->m_shouldInvalidate = false;
122                         this->m_dataValid = true;
123                     }
124                 }
125 
126                 ImGui::NewLine();
127 
128                 if (ImGui::Button("hex.view.information.analyze"_lang))
129                     this->m_shouldInvalidate = true;
130 
131                 ImGui::NewLine();
132                 ImGui::Separator();
133                 ImGui::NewLine();
134 
135                 if (this->m_dataValid) {
136 
137                     for (auto &[name, value] : (SharedData::currentProvider)->getDataInformation()) {
138                         ImGui::LabelText(name.c_str(), "%s", value.c_str());
139                     }
140 
141                     ImGui::LabelText("hex.view.information.region"_lang, "0x%llx - 0x%llx", this->m_analyzedRegion.first, this->m_analyzedRegion.second);
142 
143                     ImGui::NewLine();
144                     ImGui::Separator();
145                     ImGui::NewLine();
146 
147                     if (!this->m_fileDescription.empty()) {
148                         ImGui::TextUnformatted("hex.view.information.description"_lang);
149                         ImGui::TextWrapped("%s", this->m_fileDescription.c_str());
150                         ImGui::NewLine();
151                     }
152 
153                     if (!this->m_mimeType.empty()) {
154                         ImGui::TextUnformatted("hex.view.information.mime"_lang);
155                         ImGui::TextWrapped("%s", this->m_mimeType.c_str());
156                         ImGui::NewLine();
157                     }
158 
159                     ImGui::Separator();
160                     ImGui::NewLine();
161 
162                     ImGui::Text("hex.view.information.distribution"_lang);
163                     ImGui::PlotHistogram("##nolabel", this->m_valueCounts.data(), 256, 0, nullptr, FLT_MAX, FLT_MAX,ImVec2(0, 100));
164 
165                     ImGui::NewLine();
166                     ImGui::Separator();
167                     ImGui::NewLine();
168 
169                     ImGui::Text("hex.view.information.entropy"_lang);
170                     ImGui::PlotLines("##nolabel", this->m_blockEntropy.data(), this->m_blockEntropy.size(), 0, nullptr, FLT_MAX, FLT_MAX, ImVec2(0, 100));
171 
172                     ImGui::NewLine();
173 
174                     ImGui::LabelText("hex.view.information.block_size"_lang, "hex.view.information.block_size.desc"_lang, this->m_blockSize);
175                     ImGui::LabelText("hex.view.information.file_entropy"_lang, "%.8f", this->m_averageEntropy);
176                     ImGui::LabelText("hex.view.information.highest_entropy"_lang, "%.8f", this->m_highestBlockEntropy);
177 
178                     if (this->m_averageEntropy > 0.83 && this->m_highestBlockEntropy > 0.9) {
179                         ImGui::NewLine();
180                         ImGui::TextColored(ImVec4(0.92F, 0.25F, 0.2F, 1.0F),"hex.view.information.encrypted"_lang);
181                     }
182 
183                 }
184             }
185 
186             ImGui::EndChild();
187         }
188         ImGui::End();
189     }
190 
drawMenu()191     void ViewInformation::drawMenu() {
192 
193     }
194 
195 }