api: Refactored providers to allow for loading interfaces and config views

This commit is contained in:
WerWolv
2021-12-12 00:41:44 +01:00
parent 2e90abd2c5
commit 3e736b36b6
23 changed files with 272 additions and 145 deletions

View File

@@ -130,7 +130,7 @@ namespace hex::plugin::builtin::prv {
::close(handle);
#endif
this->open(this->m_path);
this->open();
}
size_t FileProvider::getActualSize() const {
@@ -156,8 +156,11 @@ namespace hex::plugin::builtin::prv {
return result;
}
void FileProvider::open(const std::string &path) {
void FileProvider::setPath(const std::string &path) {
this->m_path = path;
}
bool FileProvider::open() {
this->m_fileStatsValid = stat(this->m_path.data(), &this->m_fileStats) == 0;
this->m_readable = true;
@@ -194,12 +197,12 @@ namespace hex::plugin::builtin::prv {
};
if (this->m_file == nullptr || this->m_file == INVALID_HANDLE_VALUE) {
return;
return false;
}
this->m_mapping = CreateFileMapping(this->m_file, nullptr, PAGE_READWRITE, fileSize.HighPart, fileSize.LowPart, nullptr);
if (this->m_mapping == nullptr || this->m_mapping == INVALID_HANDLE_VALUE) {
return;
return false;
}
auto mappingCleanup = SCOPE_GUARD {
@@ -211,7 +214,7 @@ namespace hex::plugin::builtin::prv {
this->m_mappedFile = MapViewOfFile(this->m_mapping, FILE_MAP_ALL_ACCESS, 0, 0, this->m_fileSize);
if (this->m_mappedFile == nullptr) {
this->m_readable = false;
return;
return false;
}
fileCleanup.release();
@@ -220,7 +223,7 @@ namespace hex::plugin::builtin::prv {
ProjectFile::setFilePath(this->m_path);
#else
this->m_file = ::open(this->m_path.data(), O_RDWR);
this->m_file = ::open(this->m_path.data(), O_RDWR);
if (this->m_file == -1) {
this->m_file = ::open(this->m_path.data(), O_RDONLY);
this->m_writable = false;
@@ -228,14 +231,21 @@ namespace hex::plugin::builtin::prv {
if (this->m_file == -1) {
this->m_readable = false;
return;
return false;
}
this->m_fileSize = this->m_fileStats.st_size;
this->m_mappedFile = mmap(nullptr, this->m_fileSize, PROT_READ | PROT_WRITE, MAP_PRIVATE, this->m_file, 0);
this->m_mappedFile = ::mmap(nullptr, this->m_fileSize, PROT_READ | PROT_WRITE, MAP_PRIVATE, this->m_file, 0);
if (this->m_mappedFile == nullptr) {
::close(this->m_file);
this->m_file = -1;
#endif
return false;
}
#endif
return true;
}
void FileProvider::close() {

View File

@@ -77,6 +77,19 @@ namespace hex::plugin::builtin::prv {
return data;
}
void writeMemory(Socket &socket, u64 address, const void *buffer, size_t size) {
std::vector<u8> bytes(size);
std::memcpy(bytes.data(), buffer, size);
std::string byteString = crypt::encode16(bytes);
std::string packet = createPacket(hex::format("M{:X},{:X}:{}", address, size, byteString));
socket.writeString(packet);
auto receivedPacket = socket.readString(3);
}
bool enableNoAckMode(Socket &socket) {
socket.writeString(createPacket("QStartNoAckMode"));
@@ -104,7 +117,7 @@ namespace hex::plugin::builtin::prv {
}
GDBProvider::~GDBProvider() {
this->disconnect();
this->close();
}
@@ -135,9 +148,9 @@ namespace hex::plugin::builtin::prv {
offset -= this->getBaseAddress();
u64 alignedOffset = offset - (offset & 0xFFF);
u64 alignedOffset = offset - (offset % CacheLineSize);
{
if (size <= CacheLineSize) {
std::scoped_lock lock(this->m_cacheLock);
const auto &cacheLine = std::find_if(this->m_cache.begin(), this->m_cache.end(), [&](auto &line){
@@ -154,7 +167,15 @@ namespace hex::plugin::builtin::prv {
}
if (cacheLine != this->m_cache.end())
std::memcpy(buffer, &cacheLine->data[0] + (offset & 0xFFF), size);
std::memcpy(buffer, &cacheLine->data[0] + (offset % CacheLineSize), size);
} else {
while (size > 0) {
size_t readSize = std::min(size, CacheLineSize);
this->readRaw(offset, buffer, readSize);
size -= readSize;
offset += readSize;
}
}
for (u64 i = 0; i < size; i++)
@@ -166,7 +187,12 @@ namespace hex::plugin::builtin::prv {
}
void GDBProvider::write(u64 offset, const void *buffer, size_t size) {
if ((offset - this->getBaseAddress()) > (this->getActualSize() - size) || buffer == nullptr || size == 0)
return;
offset -= this->getBaseAddress();
gdb::writeMemory(this->m_socket, offset, buffer, size);
}
void GDBProvider::readRaw(u64 offset, void *buffer, size_t size) {
@@ -178,7 +204,10 @@ namespace hex::plugin::builtin::prv {
}
void GDBProvider::writeRaw(u64 offset, const void *buffer, size_t size) {
if ((offset - this->getBaseAddress()) > (this->getActualSize() - size) || buffer == nullptr || size == 0)
return;
gdb::writeMemory(this->m_socket, offset, buffer, size);
}
void GDBProvider::save() {
@@ -200,7 +229,7 @@ namespace hex::plugin::builtin::prv {
std::string GDBProvider::getName() const {
std::string address, port;
if (this->m_ipAddress.empty()) {
if (!this->isConnected()) {
address = "-";
port = "-";
} else {
@@ -212,19 +241,18 @@ namespace hex::plugin::builtin::prv {
}
std::vector<std::pair<std::string, std::string>> GDBProvider::getDataInformation() const {
return { };
return {
{ "hex.builtin.provider.gdb.server"_lang, hex::format("{}:{}", this->m_ipAddress, this->m_port) },
};
}
void GDBProvider::connect(const std::string &address, u16 port) {
this->m_socket.connect(address, port);
bool GDBProvider::open() {
this->m_socket.connect(this->m_ipAddress, this->m_port);
if (!gdb::enableNoAckMode(this->m_socket)) {
this->m_socket.disconnect();
return;
return false;
}
this->m_ipAddress = address;
this->m_port = port;
if (this->m_socket.isConnected()) {
this->m_cacheUpdateThread = std::thread([this]() {
auto cacheLine = this->m_cache.begin();
@@ -250,10 +278,14 @@ namespace hex::plugin::builtin::prv {
std::this_thread::sleep_for(100ms);
}
});
return true;
} else {
return false;
}
}
void GDBProvider::disconnect() {
void GDBProvider::close() {
this->m_socket.disconnect();
if (this->m_cacheUpdateThread.joinable()) {
@@ -261,8 +293,20 @@ namespace hex::plugin::builtin::prv {
}
}
bool GDBProvider::isConnected() {
bool GDBProvider::isConnected() const {
return this->m_socket.isConnected();
}
void GDBProvider::drawLoadInterface() {
ImGui::InputText("hex.builtin.view.gdb.ip"_lang, this->m_ipAddress.data(), this->m_ipAddress.capacity(), ImGuiInputTextFlags_CallbackEdit, ImGui::UpdateStringSizeCallback, &this->m_ipAddress);
ImGui::InputInt("hex.builtin.view.gdb.port"_lang, &this->m_port, 0, 0);
if (this->m_port < 0)
this->m_port = 0;
else if (this->m_port > 0xFFFF)
this->m_port = 0xFFFF;
}
}

View File

@@ -17,7 +17,7 @@
#include "content/views/view_constants.hpp"
#include "content/views/view_store.hpp"
#include "content/views/view_diff.hpp"
#include "content/views/view_gdb.hpp"
#include "content/views/view_provider_settings.hpp"
namespace hex::plugin::builtin {
@@ -41,7 +41,7 @@ namespace hex::plugin::builtin {
ContentRegistry::Views::add<ViewConstants>();
ContentRegistry::Views::add<ViewStore>();
ContentRegistry::Views::add<ViewDiff>();
ContentRegistry::Views::add<ViewGDB>();
ContentRegistry::Views::add<ViewProviderSettings>();
}
}

View File

@@ -1,51 +0,0 @@
#include "content/views/view_gdb.hpp"
#include "content/providers/gdb_provider.hpp"
namespace hex::plugin::builtin {
ViewGDB::ViewGDB() : hex::View("hex.builtin.view.gdb.name") {
this->m_address.reserve(3 * 4 + 4);
this->m_port = 0;
}
void ViewGDB::drawContent() {
if (ImGui::Begin(View::toWindowName("hex.builtin.view.gdb.name").c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
ImGui::Header("hex.builtin.view.gdb.settings"_lang);
ImGui::InputText("hex.builtin.view.gdb.ip"_lang, this->m_address.data(), this->m_address.capacity(), ImGuiInputTextFlags_CallbackEdit, ImGui::UpdateStringSizeCallback, &this->m_address);
ImGui::InputInt("hex.builtin.view.gdb.port"_lang, &this->m_port, 0, 0);
if (this->m_port < 0)
this->m_port = 0;
else if (this->m_port > 0xFFFF)
this->m_port = 0xFFFF;
ImGui::NewLine();
auto provider = dynamic_cast<prv::GDBProvider*>(hex::ImHexApi::Provider::get());
if (provider != nullptr) {
if (!provider->isConnected()) {
if (ImGui::Button("hex.builtin.view.gdb.connect"_lang)) {
provider->connect(this->m_address, this->m_port);
}
} else {
if (ImGui::Button("hex.builtin.view.gdb.disconnect"_lang)) {
provider->disconnect();
}
}
}
}
ImGui::End();
}
bool ViewGDB::hasViewMenuItemEntry() {
return this->isAvailable();
}
bool ViewGDB::isAvailable() {
auto provider = hex::ImHexApi::Provider::get();
return provider != nullptr && dynamic_cast<prv::GDBProvider*>(provider) != nullptr;
}
}

View File

@@ -713,8 +713,13 @@ namespace hex::plugin::builtin {
hex::prv::Provider *provider = nullptr;
EventManager::post<RequestCreateProvider>("hex.builtin.provider.file", &provider);
if (auto fileProvider = dynamic_cast<prv::FileProvider*>(provider))
fileProvider->open(path);
if (auto fileProvider = dynamic_cast<prv::FileProvider*>(provider)) {
fileProvider->setPath(path);
if (!fileProvider->open()) {
View::showErrorPopup("hex.builtin.view.hexeditor.error.open"_lang);
return;
}
}
if (!provider->isWritable()) {
this->m_memoryEditor.ReadOnly = true;

View File

@@ -51,23 +51,18 @@ namespace hex::plugin::builtin {
EventManager::unsubscribe<EventFileUnloaded>(this);
}
static float calculateEntropy(std::array<ImU64, 256> &valueCounts, size_t numBytes) {
static float calculateEntropy(std::array<ImU64, 256> &valueCounts, size_t blockSize) {
float entropy = 0;
if (numBytes == 0)
return 0.0F;
for (auto count : valueCounts) {
if (count == 0) continue;
std::array<float, 256> floatValueCounts{ 0 };
std::copy(valueCounts.begin(), valueCounts.end(), floatValueCounts.begin());
float probability = static_cast<float>(count) / blockSize;
for (u16 i = 0; i < 256; i++) {
floatValueCounts[i] /= float(numBytes);
if (floatValueCounts[i] > 0)
entropy -= (floatValueCounts[i] * std::log2(floatValueCounts[i]));
entropy += probability * std::log2(probability);
}
return entropy / 8;
return (-entropy) / 8; // log2(256) = 8
}
void ViewInformation::analyze() {
@@ -93,7 +88,10 @@ namespace hex::plugin::builtin {
blockValueCounts[buffer[j]]++;
this->m_valueCounts[buffer[j]]++;
}
this->m_blockEntropy.push_back(calculateEntropy(blockValueCounts, this->m_blockSize));
this->m_bytesAnalyzed = i;
}
this->m_averageEntropy = calculateEntropy(this->m_valueCounts, provider->getSize());
@@ -128,20 +126,23 @@ namespace hex::plugin::builtin {
}, this->m_analyzing);
if (this->m_analyzing) {
ImGui::SameLine();
ImGui::TextSpinner("hex.builtin.view.information.analyzing"_lang);
ImGui::ProgressBar(this->m_bytesAnalyzed / static_cast<double>(provider->getSize()));
} else {
ImGui::NewLine();
ImGui::NewLine();
}
ImGui::NewLine();
ImGui::TextUnformatted("hex.builtin.view.information.region"_lang);
ImGui::Separator();
for (auto &[name, value] : provider->getDataInformation()) {
ImGui::LabelText(name.c_str(), "%s", value.c_str());
}
if (this->m_dataValid) {
ImGui::NewLine();
ImGui::TextUnformatted("hex.builtin.view.information.region"_lang);
ImGui::Separator();
for (auto &[name, value] : provider->getDataInformation()) {
ImGui::LabelText(name.c_str(), "%s", value.c_str());
}
ImGui::LabelText("hex.builtin.view.information.region"_lang, "0x%llx - 0x%llx", this->m_analyzedRegion.first, this->m_analyzedRegion.second);
ImGui::NewLine();
@@ -169,8 +170,8 @@ namespace hex::plugin::builtin {
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImGui::GetColorU32(ImGuiCol_WindowBg));
ImGui::TextUnformatted("hex.builtin.view.information.distribution"_lang);
ImPlot::SetNextPlotLimits(0, 256, 0, float(*std::max_element(this->m_valueCounts.begin(), this->m_valueCounts.end())) * 1.1F, ImGuiCond_Always);
if (ImPlot::BeginPlot("##distribution", "Address", "Count", ImVec2(-1,0), ImPlotFlags_NoLegend | ImPlotFlags_NoMenus | ImPlotFlags_NoBoxSelect, ImPlotAxisFlags_Lock, ImPlotAxisFlags_Lock)) {
ImPlot::SetNextPlotLimits(0, 256, 0.5, float(*std::max_element(this->m_valueCounts.begin(), this->m_valueCounts.end())) * 1.1F, ImGuiCond_Always);
if (ImPlot::BeginPlot("##distribution", "Address", "Count", ImVec2(-1,0), ImPlotFlags_NoLegend | ImPlotFlags_NoMenus | ImPlotFlags_NoBoxSelect, ImPlotAxisFlags_Lock, ImPlotAxisFlags_Lock | ImPlotAxisFlags_LogScale)) {
static auto x = []{
std::array<ImU64, 256> result{ 0 };
std::iota(result.begin(), result.end(), 0);
@@ -188,7 +189,7 @@ namespace hex::plugin::builtin {
ImGui::TextUnformatted("hex.builtin.view.information.entropy"_lang);
ImPlot::SetNextPlotLimits(0, this->m_blockEntropy.size(), -0.1, 1.1, ImGuiCond_Always);
if (ImPlot::BeginPlot("##entropy", "Address", "Entropy", ImVec2(-1,0), ImPlotFlags_CanvasOnly, ImPlotAxisFlags_Lock | ImPlotAxisFlags_NoTickLabels, ImPlotAxisFlags_Lock)) {
if (ImPlot::BeginPlot("##entropy", "Address", "Entropy", ImVec2(-1,0), ImPlotFlags_CanvasOnly | ImPlotFlags_AntiAliased, ImPlotAxisFlags_Lock | ImPlotAxisFlags_NoTickLabels, ImPlotAxisFlags_Lock)) {
ImPlot::PlotLine("##entropy_line", this->m_blockEntropy.data(), this->m_blockEntropy.size());
if (ImPlot::DragLineX("Position", &this->m_entropyHandlePosition, false)) {

View File

@@ -0,0 +1,65 @@
#include "content/views/view_provider_settings.hpp"
namespace hex::plugin::builtin {
ViewProviderSettings::ViewProviderSettings() : hex::View("hex.builtin.view.provider_settings.name") {
EventManager::subscribe<EventProviderCreated>(this, [](hex::prv::Provider *provider){
if (provider->hasLoadInterface())
EventManager::post<RequestOpenPopup>(View::toWindowName("hex.builtin.view.provider_settings.load_popup"));
});
}
ViewProviderSettings::~ViewProviderSettings() {
EventManager::unsubscribe<EventProviderCreated>(this);
}
void ViewProviderSettings::drawContent() {
if (ImGui::Begin(View::toWindowName("hex.builtin.view.provider_settings.name").c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
auto provider = hex::ImHexApi::Provider::get();
if (provider != nullptr)
provider->drawInterface();
}
ImGui::End();
}
void ViewProviderSettings::drawAlwaysVisible() {
ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_Appearing, ImVec2(0.5F, 0.5F));
if (ImGui::BeginPopupModal(View::toWindowName("hex.builtin.view.provider_settings.load_popup").c_str(), nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
auto provider = hex::ImHexApi::Provider::get();
if (provider != nullptr) {
provider->drawLoadInterface();
ImGui::NewLine();
ImGui::Separator();
if (ImGui::Button("hex.common.open"_lang)) {
if (provider->open())
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if (ImGui::Button("hex.common.cancel"_lang)) {
ImHexApi::Provider::remove(provider);
ImGui::CloseCurrentPopup();
}
}
ImGui::EndPopup();
}
}
bool ViewProviderSettings::hasViewMenuItemEntry() {
return this->isAvailable();
}
bool ViewProviderSettings::isAvailable() {
auto provider = hex::ImHexApi::Provider::get();
return provider != nullptr && provider->hasInterface();
}
}

View File

@@ -678,6 +678,7 @@ namespace hex::plugin::builtin {
{ "hex.builtin.provider.file", "Datei Provider" },
{ "hex.builtin.provider.gdb", "GDB Server Provider" },
{ "hex.builtin.provider.gdb.name", "GDB Server <{0}:{1}>" },
{ "hex.builtin.provider.gdb.server", "Server" },
});
}

View File

@@ -681,6 +681,7 @@ namespace hex::plugin::builtin {
{ "hex.builtin.provider.file", "File Provider" },
{ "hex.builtin.provider.gdb", "GDB Server Provider" },
{ "hex.builtin.provider.gdb.name", "GDB Server <{0}:{1}>" },
{ "hex.builtin.provider.gdb.server", "Server" },
});
}

View File

@@ -675,6 +675,7 @@ namespace hex::plugin::builtin {
//{ "hex.builtin.provider.file", "File Provider" },
//{ "hex.builtin.provider.gdb", "GDB Server Provider" },
//{ "hex.builtin.provider.gdb.name", "GDB Server <{0}:{1}>" },
//{ "hex.builtin.provider.gdb.server", "Server" },
});
}

View File

@@ -677,6 +677,7 @@ namespace hex::plugin::builtin {
//{ "hex.builtin.provider.file", "File Provider" },
//{ "hex.builtin.provider.gdb", "GDB Server Provider" },
//{ "hex.builtin.provider.gdb.name", "GDB Server <{0}:{1}>" },
//{ "hex.builtin.provider.gdb.server", "Server" },
});
}