From 90df4413c3252806b90f9bdfdef5b2a3aa109e8b Mon Sep 17 00:00:00 2001 From: WerWolv Date: Sun, 1 Jan 2023 02:29:38 +0100 Subject: [PATCH] feat: Added basic windows process memory provider --- lib/libimhex/source/providers/provider.cpp | 2 +- .../content/helpers/provider_extra_data.hpp | 2 +- .../content/providers/file_provider.hpp | 4 +- .../content/providers/gdb_provider.hpp | 2 +- .../content/views/view_pattern_editor.cpp | 2 +- plugins/builtin/source/ui/hex_editor.cpp | 2 +- plugins/windows/CMakeLists.txt | 3 + .../providers/process_memory_provider.hpp | 82 ++++++++++ plugins/windows/romfs/lang/en_US.json | 4 + plugins/windows/source/content/providers.cpp | 11 ++ .../providers/process_memory_provider.cpp | 148 ++++++++++++++++++ plugins/windows/source/plugin_windows.cpp | 2 + 12 files changed, 257 insertions(+), 7 deletions(-) create mode 100644 plugins/windows/include/content/providers/process_memory_provider.hpp create mode 100644 plugins/windows/source/content/providers.cpp create mode 100644 plugins/windows/source/content/providers/process_memory_provider.cpp diff --git a/lib/libimhex/source/providers/provider.cpp b/lib/libimhex/source/providers/provider.cpp index 2ee377fbc..0c51e20b2 100644 --- a/lib/libimhex/source/providers/provider.cpp +++ b/lib/libimhex/source/providers/provider.cpp @@ -133,7 +133,7 @@ namespace hex::prv { u32 Provider::getPageCount() const { - return std::max(1.0, std::ceil(this->getActualSize() / double(PageSize))); + return (this->getActualSize() / PageSize) + (this->getActualSize() % PageSize != 0 ? 1 : 0); } u32 Provider::getCurrentPage() const { diff --git a/plugins/builtin/include/content/helpers/provider_extra_data.hpp b/plugins/builtin/include/content/helpers/provider_extra_data.hpp index e90064382..30d28455d 100644 --- a/plugins/builtin/include/content/helpers/provider_extra_data.hpp +++ b/plugins/builtin/include/content/helpers/provider_extra_data.hpp @@ -45,7 +45,7 @@ namespace hex::plugin::builtin { std::string sourceCode; std::mutex runtimeMutex; - std::unique_ptr runtime; + std::unique_ptr runtime = std::make_unique(); std::vector> console; bool executionDone = true; diff --git a/plugins/builtin/include/content/providers/file_provider.hpp b/plugins/builtin/include/content/providers/file_provider.hpp index c8f206704..a64f300c6 100644 --- a/plugins/builtin/include/content/providers/file_provider.hpp +++ b/plugins/builtin/include/content/providers/file_provider.hpp @@ -23,8 +23,8 @@ namespace hex::plugin::builtin { class FileProvider : public hex::prv::Provider { public: - FileProvider() = default;; - ~FileProvider() override = default;; + FileProvider() = default; + ~FileProvider() override = default; [[nodiscard]] bool isAvailable() const override; [[nodiscard]] bool isReadable() const override; diff --git a/plugins/builtin/include/content/providers/gdb_provider.hpp b/plugins/builtin/include/content/providers/gdb_provider.hpp index 9118dd907..78ee6f209 100644 --- a/plugins/builtin/include/content/providers/gdb_provider.hpp +++ b/plugins/builtin/include/content/providers/gdb_provider.hpp @@ -13,7 +13,7 @@ namespace hex::plugin::builtin { class GDBProvider : public hex::prv::Provider { public: GDBProvider(); - ~GDBProvider() override = default;; + ~GDBProvider() override = default; [[nodiscard]] bool isAvailable() const override; [[nodiscard]] bool isReadable() const override; diff --git a/plugins/builtin/source/content/views/view_pattern_editor.cpp b/plugins/builtin/source/content/views/view_pattern_editor.cpp index b6318fd1a..e38b20d65 100644 --- a/plugins/builtin/source/content/views/view_pattern_editor.cpp +++ b/plugins/builtin/source/content/views/view_pattern_editor.cpp @@ -140,7 +140,7 @@ namespace hex::plugin::builtin { ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1); auto &runtime = ProviderExtraData::getCurrent().patternLanguage.runtime; - if (runtime->isRunning()) { + if (runtime != nullptr && runtime->isRunning()) { if (ImGui::IconButton(ICON_VS_DEBUG_STOP, ImGui::GetCustomColorVec4(ImGuiCustomCol_ToolbarRed))) runtime->abort(); } else { diff --git a/plugins/builtin/source/ui/hex_editor.cpp b/plugins/builtin/source/ui/hex_editor.cpp index 4649ad914..f05bc295f 100644 --- a/plugins/builtin/source/ui/hex_editor.cpp +++ b/plugins/builtin/source/ui/hex_editor.cpp @@ -715,7 +715,7 @@ namespace hex::plugin::builtin::ui { ImGui::BeginDisabled(pageCount <= 1); { - if (ImGui::SliderScalar("##page_selection", ImGuiDataType_U32, &page, &MinPage, &pageCount, hex::format("%d / {}", pageCount).c_str())) + if (ImGui::SliderScalar("##page_selection", ImGuiDataType_U32, &page, &MinPage, &pageCount, hex::format("0x%llX / 0x{:02X}", pageCount).c_str())) this->m_provider->setCurrentPage(page - 1); } ImGui::EndDisabled(); diff --git a/plugins/windows/CMakeLists.txt b/plugins/windows/CMakeLists.txt index 59ce978c1..819178768 100644 --- a/plugins/windows/CMakeLists.txt +++ b/plugins/windows/CMakeLists.txt @@ -13,6 +13,9 @@ if (WIN32) source/content/ui_items.cpp source/content/settings_entries.cpp + source/content/providers.cpp + + source/content/providers/process_memory_provider.cpp ) # Add additional include directories here # diff --git a/plugins/windows/include/content/providers/process_memory_provider.hpp b/plugins/windows/include/content/providers/process_memory_provider.hpp new file mode 100644 index 000000000..406166108 --- /dev/null +++ b/plugins/windows/include/content/providers/process_memory_provider.hpp @@ -0,0 +1,82 @@ +#pragma once + +#include +#include + +#include + +#include +#include +#include +#include + +namespace hex::plugin::windows { + + class ProcessMemoryProvider : public hex::prv::Provider { + public: + ProcessMemoryProvider() = default; + ~ProcessMemoryProvider() override = default; + + [[nodiscard]] bool isAvailable() const override { return this->m_processHandle != nullptr; } + [[nodiscard]] bool isReadable() const override { return true; } + [[nodiscard]] bool isWritable() const override { return true; } + [[nodiscard]] bool isResizable() const override { return false; } + [[nodiscard]] bool isSavable() const override { return false; } + + void read(u64 address, void *buffer, size_t size, bool) override { this->readRaw(address, buffer, size); } + void write(u64 address, const void *buffer, size_t size) override { this->writeRaw(address, buffer, size); } + + void readRaw(u64 address, void *buffer, size_t size) override; + void writeRaw(u64 address, const void *buffer, size_t size) override; + [[nodiscard]] size_t getActualSize() const override { return 0xFFFF'FFFF'FFFF; } + + void save() override {} + void saveAs(const std::fs::path &) override {} + + [[nodiscard]] std::string getName() const override { return "hex.windows.provider.process_memory"_lang; } + [[nodiscard]] std::vector> getDataInformation() const override { + return { + { "hex.windows.provider.process_memory.process_name"_lang, this->m_selectedProcess->name }, + { "hex.windows.provider.process_memory.process_id"_lang, std::to_string(this->m_selectedProcess->id) } + }; + } + + [[nodiscard]] bool open() override; + void close() override; + + [[nodiscard]] bool hasLoadInterface() const override { return true; } + [[nodiscard]] bool hasInterface() const override { return true; } + void drawLoadInterface() override; + void drawInterface() override; + + void loadSettings(const nlohmann::json &) override {} + [[nodiscard]] nlohmann::json storeSettings(nlohmann::json) const override { return { }; } + + [[nodiscard]] std::string getTypeName() const override { + return "hex.windows.provider.process_memory"; + } + + [[nodiscard]] std::pair getRegionValidity(u64) const override; + + private: + struct Process { + u32 id; + std::string name; + }; + + struct Module { + Region region; + std::string name; + }; + + std::vector m_processes; + std::optional m_selectedProcess; + + std::vector m_modules; + + HANDLE m_processHandle = nullptr; + + bool m_enumerationFailed = false; + }; + +} \ No newline at end of file diff --git a/plugins/windows/romfs/lang/en_US.json b/plugins/windows/romfs/lang/en_US.json index 5cc097192..5014eac50 100644 --- a/plugins/windows/romfs/lang/en_US.json +++ b/plugins/windows/romfs/lang/en_US.json @@ -3,6 +3,10 @@ "country": "United States", "language": "English", "translations": { + "hex.windows.provider.process_memory": "Process Memory Provider", + "hex.windows.provider.process_memory.enumeration_failed": "Failed to enumerate processes", + "hex.windows.provider.process_memory.process_name": "Process Name", + "hex.windows.provider.process_memory.process_id": "Process ID", "hex.builtin.setting.general.context_menu_entry": "Windows context menu entry", "hex.windows.title_bar_button.debug_build": "Debug build", "hex.windows.title_bar_button.feedback": "Leave Feedback", diff --git a/plugins/windows/source/content/providers.cpp b/plugins/windows/source/content/providers.cpp new file mode 100644 index 000000000..997fe0d55 --- /dev/null +++ b/plugins/windows/source/content/providers.cpp @@ -0,0 +1,11 @@ +#include + +#include + +namespace hex::plugin::windows { + + void registerProviders() { + ContentRegistry::Provider::add(); + } + +} \ No newline at end of file diff --git a/plugins/windows/source/content/providers/process_memory_provider.cpp b/plugins/windows/source/content/providers/process_memory_provider.cpp new file mode 100644 index 000000000..dde17d88d --- /dev/null +++ b/plugins/windows/source/content/providers/process_memory_provider.cpp @@ -0,0 +1,148 @@ +#include + +#include +#include + +#include +#include + +namespace hex::plugin::windows { + + bool ProcessMemoryProvider::open() { + this->m_processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, this->m_selectedProcess->id); + if (this->m_processHandle == nullptr) + return false; + + DWORD numModules = 0; + std::vector modules; + + do { + modules.resize(modules.size() + 1024); + if (EnumProcessModules(this->m_processHandle, modules.data(), modules.size() * sizeof(HMODULE), &numModules) == FALSE) { + modules.clear(); + break; + } + } while (numModules == modules.size() * sizeof(HMODULE)); + + modules.resize(numModules / sizeof(HMODULE)); + + for (auto &module : modules) { + MODULEINFO moduleInfo; + if (GetModuleInformation(this->m_processHandle, module, &moduleInfo, sizeof(MODULEINFO)) == FALSE) + continue; + + char moduleName[MAX_PATH]; + if (GetModuleFileNameExA(this->m_processHandle, module, moduleName, MAX_PATH) == FALSE) + continue; + + this->m_modules.push_back({ { (u64)moduleInfo.lpBaseOfDll, (u64)moduleInfo.lpBaseOfDll + moduleInfo.SizeOfImage }, std::fs::path(moduleName).filename().string() }); + } + + return true; + } + + void ProcessMemoryProvider::close() { + CloseHandle(this->m_processHandle); + this->m_processHandle = nullptr; + } + + void ProcessMemoryProvider::readRaw(u64 address, void *buffer, size_t size) { + ReadProcessMemory(this->m_processHandle, (LPCVOID)address, buffer, size, nullptr); + } + void ProcessMemoryProvider::writeRaw(u64 address, const void *buffer, size_t size) { + WriteProcessMemory(this->m_processHandle, (LPVOID)address, buffer, size, nullptr); + } + + std::pair ProcessMemoryProvider::getRegionValidity(u64 address) const { + for (const auto &module : this->m_modules) { + if (module.region.overlaps({ address, 1 })) + return { module.region, true }; + } + + return { Region::Invalid(), false }; + } + + void ProcessMemoryProvider::drawLoadInterface() { + if (this->m_processes.empty() && !this->m_enumerationFailed) { + DWORD numProcesses = 0; + std::vector processIds; + + do { + processIds.resize(processIds.size() + 1024); + if (EnumProcesses(processIds.data(), processIds.size() * sizeof(DWORD), &numProcesses) == FALSE) { + processIds.clear(); + this->m_enumerationFailed = true; + break; + } + } while (numProcesses == processIds.size() * sizeof(DWORD)); + + processIds.resize(numProcesses / sizeof(DWORD)); + + for (auto processId : processIds) { + HANDLE processHandle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processId); + if (processHandle == nullptr) + continue; + + ON_SCOPE_EXIT { CloseHandle(processHandle); }; + + char processName[MAX_PATH]; + if (GetModuleBaseNameA(processHandle, nullptr, processName, MAX_PATH) == 0) + continue; + + this->m_processes.push_back({ processId, processName }); + } + } + + if (this->m_enumerationFailed) { + ImGui::TextUnformatted("hex.windows.provider.process_memory.enumeration_failed"_lang); + } else { + if (ImGui::BeginTable("##process_table", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollY, ImVec2(0, 500))) { + ImGui::TableSetupColumn("ID"); + ImGui::TableSetupColumn("Name"); + ImGui::TableHeadersRow(); + + for (auto &process : this->m_processes) { + ImGui::PushID(process.id); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("%d", process.id); + + ImGui::TableNextColumn(); + if (ImGui::Selectable(process.name.c_str(), this->m_selectedProcess.has_value() && process.id == this->m_selectedProcess->id, ImGuiSelectableFlags_SpanAllColumns)) + this->m_selectedProcess = process; + + ImGui::PopID(); + } + + ImGui::EndTable(); + } + + } + } + + void ProcessMemoryProvider::drawInterface() { + if (ImGui::BeginTable("##module_table", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollY)) { + ImGui::TableSetupColumn("Base"); + ImGui::TableSetupColumn("Name"); + ImGui::TableHeadersRow(); + + for (auto &module : this->m_modules) { + ImGui::PushID(module.region.getStartAddress()); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("0x%016llX - 0x%016llX", module.region.getStartAddress(), module.region.getEndAddress()); + + ImGui::TableNextColumn(); + if (ImGui::Selectable(module.name.c_str(), false, ImGuiSelectableFlags_SpanAllColumns)) + ImHexApi::HexEditor::setSelection(module.region); + + ImGui::PopID(); + } + + ImGui::EndTable(); + } + } + +} \ No newline at end of file diff --git a/plugins/windows/source/plugin_windows.cpp b/plugins/windows/source/plugin_windows.cpp index 7a140dce2..acccd6b80 100644 --- a/plugins/windows/source/plugin_windows.cpp +++ b/plugins/windows/source/plugin_windows.cpp @@ -16,6 +16,7 @@ namespace hex::plugin::windows { void addFooterItems(); void addTitleBarButtons(); void registerSettings(); + void registerProviders(); } static void detectSystemTheme() { @@ -68,6 +69,7 @@ IMHEX_PLUGIN_SETUP("Windows", "WerWolv", "Windows-only features") { addFooterItems(); addTitleBarButtons(); registerSettings(); + registerProviders(); detectSystemTheme(); checkBorderlessWindowOverride();