From 8a36897fd939d9d2d71763710e172c21cf354898 Mon Sep 17 00:00:00 2001 From: WerWolv Date: Sun, 12 Dec 2021 00:42:12 +0100 Subject: [PATCH] provider: Added raw disk provider --- plugins/builtin/CMakeLists.txt | 1 + .../content/providers/disk_provider.hpp | 62 ++++ plugins/builtin/source/content/providers.cpp | 13 + .../content/providers/disk_provider.cpp | 308 ++++++++++++++++++ plugins/builtin/source/lang/de_DE.cpp | 5 + plugins/builtin/source/lang/en_US.cpp | 5 + plugins/builtin/source/lang/it_IT.cpp | 5 + plugins/builtin/source/lang/zh_CN.cpp | 5 + 8 files changed, 404 insertions(+) create mode 100644 plugins/builtin/include/content/providers/disk_provider.hpp create mode 100644 plugins/builtin/source/content/providers/disk_provider.cpp diff --git a/plugins/builtin/CMakeLists.txt b/plugins/builtin/CMakeLists.txt index 91fa5e900..4af2e159e 100644 --- a/plugins/builtin/CMakeLists.txt +++ b/plugins/builtin/CMakeLists.txt @@ -18,6 +18,7 @@ add_library(${PROJECT_NAME} SHARED source/content/providers/file_provider.cpp source/content/providers/gdb_provider.cpp + source/content/providers/disk_provider.cpp source/content/views/view_hexeditor.cpp source/content/views/view_pattern_editor.cpp diff --git a/plugins/builtin/include/content/providers/disk_provider.hpp b/plugins/builtin/include/content/providers/disk_provider.hpp new file mode 100644 index 000000000..c5113c651 --- /dev/null +++ b/plugins/builtin/include/content/providers/disk_provider.hpp @@ -0,0 +1,62 @@ +#pragma once + +#include + +#include "content/providers/file_provider.hpp" + +#include +#include +#include + +#if defined(OS_WINDOWS) + #include +#endif + +namespace hex::plugin::builtin::prv { + + class DiskProvider : public hex::prv::Provider { + public: + DiskProvider(); + ~DiskProvider() override; + + [[nodiscard]] bool isAvailable() const override; + [[nodiscard]] bool isReadable() const override; + [[nodiscard]] bool isWritable() const override; + [[nodiscard]] bool isResizable() const override; + [[nodiscard]] bool isSavable() const override; + + void readRaw(u64 offset, void *buffer, size_t size) override; + void writeRaw(u64 offset, const void *buffer, size_t size) override; + [[nodiscard]] size_t getActualSize() const override; + + void setPath(const std::string &path); + + [[nodiscard]] bool open() override; + void close() override; + + [[nodiscard]] virtual std::string getName() const; + [[nodiscard]] virtual std::vector> getDataInformation() const; + + [[nodiscard]] bool hasLoadInterface() const override { return true; } + void drawLoadInterface() override; + protected: + std::set m_availableDrives; + std::string m_path; + + #if defined(OS_WINDOWS) + HANDLE m_diskHandle = INVALID_HANDLE_VALUE; + #else + int m_diskHandle = -1; + #endif + + size_t m_diskSize; + size_t m_sectorSize; + + u64 m_sectorBufferAddress; + std::vector m_sectorBuffer; + + bool m_readable = false; + bool m_writable = false; + }; + +} \ No newline at end of file diff --git a/plugins/builtin/source/content/providers.cpp b/plugins/builtin/source/content/providers.cpp index 0ef2b717b..eb70d6aca 100644 --- a/plugins/builtin/source/content/providers.cpp +++ b/plugins/builtin/source/content/providers.cpp @@ -3,11 +3,13 @@ #include "content/providers/gdb_provider.hpp" #include "content/providers/file_provider.hpp" +#include "content/providers/disk_provider.hpp" namespace hex::plugin::builtin { void registerProviders() { ContentRegistry::Provider::add("hex.builtin.provider.gdb"); + ContentRegistry::Provider::add("hex.builtin.provider.disk"); (void) EventManager::subscribe([](const std::string &unlocalizedName, hex::prv::Provider **provider){ if (unlocalizedName != "hex.builtin.provider.file") return; @@ -30,6 +32,17 @@ namespace hex::plugin::builtin { if (provider != nullptr) *provider = newProvider; }); + + (void) EventManager::subscribe([](const std::string &unlocalizedName, hex::prv::Provider **provider){ + if (unlocalizedName != "hex.builtin.provider.disk") return; + + auto newProvider = new prv::DiskProvider(); + + hex::ImHexApi::Provider::add(newProvider); + + if (provider != nullptr) + *provider = newProvider; + }); } } \ No newline at end of file diff --git a/plugins/builtin/source/content/providers/disk_provider.cpp b/plugins/builtin/source/content/providers/disk_provider.cpp new file mode 100644 index 000000000..bbcd6475b --- /dev/null +++ b/plugins/builtin/source/content/providers/disk_provider.cpp @@ -0,0 +1,308 @@ +#include "content/providers/disk_provider.hpp" + +#include + +#include +#include + +#if defined (OS_LINUX) || defined (OS_MACOS) + #include + #include +#endif + +namespace hex::plugin::builtin::prv { + + DiskProvider::DiskProvider() : Provider() { + + } + + DiskProvider::~DiskProvider() { + this->close(); + } + + bool DiskProvider::isAvailable() const { + #if defined (OS_WINDOWS) + return this->m_diskHandle != INVALID_HANDLE_VALUE; + #else + return this->m_diskHandle != -1; + #endif + } + + bool DiskProvider::isReadable() const { + return this->m_readable; + } + + bool DiskProvider::isWritable() const { + return this->m_writable; + } + + bool DiskProvider::isResizable() const { + return false; + } + + bool DiskProvider::isSavable() const { + return false; + } + + + void DiskProvider::setPath(const std::string &path) { + this->m_path = path; + } + + bool DiskProvider::open() { + this->m_readable = true; + this->m_writable = true; + + #if defined (OS_WINDOWS) + + std::wstring widePath; + { + auto length = this->m_path.length() + 1; + auto wideLength = MultiByteToWideChar(CP_UTF8, 0, this->m_path.data(), length, 0, 0); + auto buffer = new wchar_t[wideLength]; + MultiByteToWideChar(CP_UTF8, 0, this->m_path.data(), length, buffer, wideLength); + widePath = buffer; + delete[] buffer; + } + + this->m_diskHandle = reinterpret_cast(CreateFileW(widePath.data(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr)); + if (this->m_diskHandle == INVALID_HANDLE_VALUE) { + this->m_diskHandle = reinterpret_cast(CreateFileW(widePath.data(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr)); + this->m_writable = false; + + if (this->m_diskHandle == INVALID_HANDLE_VALUE) + return false; + } + + { + DISK_GEOMETRY_EX diskGeometry = { 0 }; + DWORD bytesRead = 0; + if (DeviceIoControl( + this->m_diskHandle, + IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, + nullptr, 0, + &diskGeometry, sizeof(DISK_GEOMETRY_EX), + &bytesRead, + nullptr)) + { + this->m_diskSize = diskGeometry.DiskSize.QuadPart; + this->m_sectorSize = diskGeometry.Geometry.BytesPerSector; + this->m_sectorBuffer.resize(this->m_sectorSize); + } + } + + if (this->m_diskHandle == nullptr || this->m_diskHandle == INVALID_HANDLE_VALUE) { + this->m_readable = false; + this->m_diskHandle = nullptr; + CloseHandle(this->m_diskHandle); + + return false; + } + + + + #else + struct stat driveStat; + + ::stat(this->m_path.data(), &driveStat) == 0; + this->m_diskSize = driveStat.st_size; + this->m_sectorSize = 0; + + this->m_diskHandle = ::open(this->m_path.data(), O_RDWR); + if (this->m_diskHandle == -1) { + this->m_diskHandle = ::open(this->m_path.data(), O_RDONLY); + this->m_writable = false; + } + + if (this->m_diskHandle == -1) { + this->m_readable = false; + return false; + } + + #endif + + return true; + } + + void DiskProvider::close() { + #if defined (OS_WINDOWS) + if (this->m_diskHandle != INVALID_HANDLE_VALUE) + ::CloseHandle(this->m_diskHandle); + + this->m_diskHandle = INVALID_HANDLE_VALUE; + #else + if (this->m_diskHandle != -1) + ::close(this->m_diskHandle); + + this->m_diskHandle = -1; + #endif + } + + void DiskProvider::readRaw(u64 offset, void *buffer, size_t size) { + #if defined (OS_WINDOWS) + DWORD bytesRead = 0; + + u64 startOffset = offset; + + while (size > 0) { + LARGE_INTEGER seekPosition; + seekPosition.LowPart = (offset & 0xFFFF'FFFF) - (offset % this->m_sectorSize); + seekPosition.HighPart = offset >> 32; + + if (this->m_sectorBufferAddress != seekPosition.QuadPart) { + ::SetFilePointer(this->m_diskHandle, seekPosition.LowPart, &seekPosition.HighPart, FILE_BEGIN); + ::ReadFile(this->m_diskHandle, this->m_sectorBuffer.data(), this->m_sectorBuffer.size(), &bytesRead, nullptr); + this->m_sectorBufferAddress = seekPosition.QuadPart; + } + + std::memcpy(reinterpret_cast(buffer) + (offset - startOffset), this->m_sectorBuffer.data() + (offset & (this->m_sectorSize - 1)), std::min(this->m_sectorSize, size)); + + size = std::max(static_cast(size) - this->m_sectorSize, 0); + offset += this->m_sectorSize; + } + #else + u64 startOffset = offset; + + while (size > 0) { + u64 seekPosition = offset - (offset % this->m_sectorSize); + + if (this->m_sectorBufferAddress != seekPosition) { + ::lseek64(this->m_diskHandle, seekPosition, SEEK_SET); + ::read(this->m_diskHandle, buffer, size); + this->m_sectorBufferAddress = seekPosition; + } + + std::memcpy(reinterpret_cast(buffer) + (offset - startOffset), this->m_sectorBuffer.data() + (offset & (this->m_sectorSize - 1)), std::min(this->m_sectorSize, size)); + + size = std::max(static_cast(size) - this->m_sectorSize, 0); + offset += this->m_sectorSize; + } + #endif + } + + void DiskProvider::writeRaw(u64 offset, const void *buffer, size_t size) { + #if defined (OS_WINDOWS) + DWORD bytesWritten = 0; + + u64 startOffset = offset; + + std::vector modifiedSectorBuffer; + modifiedSectorBuffer.resize(this->m_sectorSize); + + while (size > 0) { + u64 sectorBase = offset - (offset % this->m_sectorSize); + size_t currSize = std::min(size, this->m_sectorSize); + + this->readRaw(sectorBase, modifiedSectorBuffer.data(), modifiedSectorBuffer.size()); + std::memcpy(modifiedSectorBuffer.data() + ((offset - sectorBase) % this->m_sectorSize), reinterpret_cast(buffer) + (startOffset - offset), currSize); + + LARGE_INTEGER seekPosition; + seekPosition.LowPart = (offset & 0xFFFF'FFFF) - (offset % this->m_sectorSize); + seekPosition.HighPart = offset >> 32; + + ::SetFilePointer(this->m_diskHandle, seekPosition.LowPart, &seekPosition.HighPart, FILE_BEGIN); + ::WriteFile(this->m_diskHandle, modifiedSectorBuffer.data(), modifiedSectorBuffer.size(), &bytesWritten, nullptr); + + offset += currSize; + size -= currSize; + } + + #else + + u64 startOffset = offset; + + std::vector modifiedSectorBuffer; + modifiedSectorBuffer.resize(this->m_sectorSize); + + while (size > 0) { + u64 sectorBase = offset - (offset % this->m_sectorSize); + size_t currSize = std::min(size, this->m_sectorSize); + + this->readRaw(sectorBase, modifiedSectorBuffer.data(), modifiedSectorBuffer.size()); + std::memcpy(modifiedSectorBuffer.data() + ((offset - sectorBase) % this->m_sectorSize), reinterpret_cast(buffer) + (startOffset - offset), currSize); + + ::lseek64(this->m_diskHandle, sectorBase, SEEK_SET); + ::write(this->m_diskHandle, modifiedSectorBuffer.data(), modifiedSectorBuffer.size()); + + offset += currSize; + size -= currSize; + } + + #endif + } + + size_t DiskProvider::getActualSize() const { + return this->m_diskSize; + } + + std::string DiskProvider::getName() const { + return this->m_path; + } + + std::vector> DiskProvider::getDataInformation() const { + return { + { "hex.builtin.provider.disk.selected_disk"_lang, this->m_path }, + { "hex.builtin.provider.disk.disk_size"_lang, hex::toByteString(this->m_diskSize) }, + { "hex.builtin.provider.disk.sector_size"_lang, hex::toByteString(this->m_sectorSize) } + }; + } + + + + void DiskProvider::drawLoadInterface() { + #if defined (OS_WINDOWS) + if (ImGui::BeginListBox("hex.builtin.provider.disk.selected_disk"_lang)) { + + for (const auto &drive : this->m_availableDrives) { + if (ImGui::Selectable(drive.c_str(), this->m_path == drive)) + this->m_path = drive; + } + + ImGui::EndListBox(); + } + + ImGui::SameLine(); + + if (ImGui::Button("hex.builtin.provider.disk.reload"_lang)) { + this->m_availableDrives.clear(); + std::bitset<32> drives = ::GetLogicalDrives(); + for (char i = 0; i < 26; i++) { + if (drives[i]) + this->m_availableDrives.insert(hex::format(R"(\\.\{:c}:)", 'A' + i)); + } + + auto logicalDrives = this->m_availableDrives; + for (const auto &drive : logicalDrives) { + auto handle = reinterpret_cast(::CreateFile(drive.data(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr)); + + if (handle == INVALID_HANDLE_VALUE) continue; + + VOLUME_DISK_EXTENTS diskExtents = { 0 }; + DWORD bytesRead = 0; + auto result = ::DeviceIoControl( + handle, + IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, + nullptr, + 0, + &diskExtents, + sizeof(VOLUME_DISK_EXTENTS), + &bytesRead, + nullptr); + + if (result) { + auto diskPath = hex::format(R"(\\.\PhysicalDrive{})", diskExtents.Extents[0].DiskNumber); + this->m_availableDrives.insert(diskPath); + } + + ::CloseHandle(handle); + } + } + + #else + + ImGui::InputText("hex.builtin.provider.disk.selected_disk"_lang, this->m_path.data(), this->m_path.capacity(), ImGuiInputTextFlags_CallbackResize, ImGui::UpdateStringSizeCallback, &this->m_path); + + #endif + } + +} \ No newline at end of file diff --git a/plugins/builtin/source/lang/de_DE.cpp b/plugins/builtin/source/lang/de_DE.cpp index d813b4220..63573f30b 100644 --- a/plugins/builtin/source/lang/de_DE.cpp +++ b/plugins/builtin/source/lang/de_DE.cpp @@ -679,6 +679,11 @@ namespace hex::plugin::builtin { { "hex.builtin.provider.gdb", "GDB Server Provider" }, { "hex.builtin.provider.gdb.name", "GDB Server <{0}:{1}>" }, { "hex.builtin.provider.gdb.server", "Server" }, + { "hex.builtin.provider.disk", "Datenträger Provider" }, + { "hex.builtin.provider.disk.selected_disk", "Datenträger" }, + { "hex.builtin.provider.disk.disk_size", "Datenträgergrösse" }, + { "hex.builtin.provider.disk.sector_size", "Sektorgrösse" }, + { "hex.builtin.provider.disk.reload", "Neu laden" }, }); } diff --git a/plugins/builtin/source/lang/en_US.cpp b/plugins/builtin/source/lang/en_US.cpp index 629753566..364a0c5a9 100644 --- a/plugins/builtin/source/lang/en_US.cpp +++ b/plugins/builtin/source/lang/en_US.cpp @@ -682,6 +682,11 @@ namespace hex::plugin::builtin { { "hex.builtin.provider.gdb", "GDB Server Provider" }, { "hex.builtin.provider.gdb.name", "GDB Server <{0}:{1}>" }, { "hex.builtin.provider.gdb.server", "Server" }, + { "hex.builtin.provider.disk", "Raw Disk Provider" }, + { "hex.builtin.provider.disk.selected_disk", "Disk" }, + { "hex.builtin.provider.disk.disk_size", "Disk Size" }, + { "hex.builtin.provider.disk.sector_size", "Sector Size" }, + { "hex.builtin.provider.disk.reload", "Reload" }, }); } diff --git a/plugins/builtin/source/lang/it_IT.cpp b/plugins/builtin/source/lang/it_IT.cpp index b370cd7ca..a0c727897 100644 --- a/plugins/builtin/source/lang/it_IT.cpp +++ b/plugins/builtin/source/lang/it_IT.cpp @@ -676,6 +676,11 @@ namespace hex::plugin::builtin { //{ "hex.builtin.provider.gdb", "GDB Server Provider" }, //{ "hex.builtin.provider.gdb.name", "GDB Server <{0}:{1}>" }, //{ "hex.builtin.provider.gdb.server", "Server" }, + //{ "hex.builtin.provider.disk", "Raw Disk Provider" }, + //{ "hex.builtin.provider.disk.selected_disk", "Disk" }, + //{ "hex.builtin.provider.disk.disk_size", "Disk Size" }, + //{ "hex.builtin.provider.disk.sector_size", "Sector Size" }, + //{ "hex.builtin.provider.disk.reload", "Reload" }, }); } diff --git a/plugins/builtin/source/lang/zh_CN.cpp b/plugins/builtin/source/lang/zh_CN.cpp index 94d1a2ab7..8bd4173aa 100644 --- a/plugins/builtin/source/lang/zh_CN.cpp +++ b/plugins/builtin/source/lang/zh_CN.cpp @@ -678,6 +678,11 @@ namespace hex::plugin::builtin { //{ "hex.builtin.provider.gdb", "GDB Server Provider" }, //{ "hex.builtin.provider.gdb.name", "GDB Server <{0}:{1}>" }, //{ "hex.builtin.provider.gdb.server", "Server" }, + //{ "hex.builtin.provider.disk", "Raw Disk Provider" }, + //{ "hex.builtin.provider.disk.selected_disk", "Disk" }, + //{ "hex.builtin.provider.disk.disk_size", "Disk Size" }, + //{ "hex.builtin.provider.disk.sector_size", "Sector Size" }, + //{ "hex.builtin.provider.disk.reload", "Reload" }, }); }