feat: Added basic windows process memory provider
This commit is contained in:
parent
4cd6646cca
commit
90df4413c3
@ -133,7 +133,7 @@ namespace hex::prv {
|
|||||||
|
|
||||||
|
|
||||||
u32 Provider::getPageCount() const {
|
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 {
|
u32 Provider::getCurrentPage() const {
|
||||||
|
@ -45,7 +45,7 @@ namespace hex::plugin::builtin {
|
|||||||
|
|
||||||
std::string sourceCode;
|
std::string sourceCode;
|
||||||
std::mutex runtimeMutex;
|
std::mutex runtimeMutex;
|
||||||
std::unique_ptr<pl::PatternLanguage> runtime;
|
std::unique_ptr<pl::PatternLanguage> runtime = std::make_unique<pl::PatternLanguage>();
|
||||||
std::vector<std::pair<pl::core::LogConsole::Level, std::string>> console;
|
std::vector<std::pair<pl::core::LogConsole::Level, std::string>> console;
|
||||||
bool executionDone = true;
|
bool executionDone = true;
|
||||||
|
|
||||||
|
@ -23,8 +23,8 @@ namespace hex::plugin::builtin {
|
|||||||
|
|
||||||
class FileProvider : public hex::prv::Provider {
|
class FileProvider : public hex::prv::Provider {
|
||||||
public:
|
public:
|
||||||
FileProvider() = default;;
|
FileProvider() = default;
|
||||||
~FileProvider() override = default;;
|
~FileProvider() override = default;
|
||||||
|
|
||||||
[[nodiscard]] bool isAvailable() const override;
|
[[nodiscard]] bool isAvailable() const override;
|
||||||
[[nodiscard]] bool isReadable() const override;
|
[[nodiscard]] bool isReadable() const override;
|
||||||
|
@ -13,7 +13,7 @@ namespace hex::plugin::builtin {
|
|||||||
class GDBProvider : public hex::prv::Provider {
|
class GDBProvider : public hex::prv::Provider {
|
||||||
public:
|
public:
|
||||||
GDBProvider();
|
GDBProvider();
|
||||||
~GDBProvider() override = default;;
|
~GDBProvider() override = default;
|
||||||
|
|
||||||
[[nodiscard]] bool isAvailable() const override;
|
[[nodiscard]] bool isAvailable() const override;
|
||||||
[[nodiscard]] bool isReadable() const override;
|
[[nodiscard]] bool isReadable() const override;
|
||||||
|
@ -140,7 +140,7 @@ namespace hex::plugin::builtin {
|
|||||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1);
|
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1);
|
||||||
|
|
||||||
auto &runtime = ProviderExtraData::getCurrent().patternLanguage.runtime;
|
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)))
|
if (ImGui::IconButton(ICON_VS_DEBUG_STOP, ImGui::GetCustomColorVec4(ImGuiCustomCol_ToolbarRed)))
|
||||||
runtime->abort();
|
runtime->abort();
|
||||||
} else {
|
} else {
|
||||||
|
@ -715,7 +715,7 @@ namespace hex::plugin::builtin::ui {
|
|||||||
|
|
||||||
ImGui::BeginDisabled(pageCount <= 1);
|
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);
|
this->m_provider->setCurrentPage(page - 1);
|
||||||
}
|
}
|
||||||
ImGui::EndDisabled();
|
ImGui::EndDisabled();
|
||||||
|
@ -13,6 +13,9 @@ if (WIN32)
|
|||||||
|
|
||||||
source/content/ui_items.cpp
|
source/content/ui_items.cpp
|
||||||
source/content/settings_entries.cpp
|
source/content/settings_entries.cpp
|
||||||
|
source/content/providers.cpp
|
||||||
|
|
||||||
|
source/content/providers/process_memory_provider.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
# Add additional include directories here #
|
# Add additional include directories here #
|
||||||
|
@ -0,0 +1,82 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <hex/providers/provider.hpp>
|
||||||
|
#include <hex/api/localization.hpp>
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <mutex>
|
||||||
|
#include <string_view>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
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<std::pair<std::string, std::string>> 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<Region, bool> getRegionValidity(u64) const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Process {
|
||||||
|
u32 id;
|
||||||
|
std::string name;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Module {
|
||||||
|
Region region;
|
||||||
|
std::string name;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<Process> m_processes;
|
||||||
|
std::optional<Process> m_selectedProcess;
|
||||||
|
|
||||||
|
std::vector<Module> m_modules;
|
||||||
|
|
||||||
|
HANDLE m_processHandle = nullptr;
|
||||||
|
|
||||||
|
bool m_enumerationFailed = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -3,6 +3,10 @@
|
|||||||
"country": "United States",
|
"country": "United States",
|
||||||
"language": "English",
|
"language": "English",
|
||||||
"translations": {
|
"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.builtin.setting.general.context_menu_entry": "Windows context menu entry",
|
||||||
"hex.windows.title_bar_button.debug_build": "Debug build",
|
"hex.windows.title_bar_button.debug_build": "Debug build",
|
||||||
"hex.windows.title_bar_button.feedback": "Leave Feedback",
|
"hex.windows.title_bar_button.feedback": "Leave Feedback",
|
||||||
|
11
plugins/windows/source/content/providers.cpp
Normal file
11
plugins/windows/source/content/providers.cpp
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#include <hex/api/content_registry.hpp>
|
||||||
|
|
||||||
|
#include <content/providers/process_memory_provider.hpp>
|
||||||
|
|
||||||
|
namespace hex::plugin::windows {
|
||||||
|
|
||||||
|
void registerProviders() {
|
||||||
|
ContentRegistry::Provider::add<ProcessMemoryProvider>();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,148 @@
|
|||||||
|
#include <content/providers/process_memory_provider.hpp>
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#include <psapi.h>
|
||||||
|
|
||||||
|
#include <imgui.h>
|
||||||
|
#include <hex/helpers/utils.hpp>
|
||||||
|
|
||||||
|
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<HMODULE> 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<Region, bool> 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<DWORD> 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -16,6 +16,7 @@ namespace hex::plugin::windows {
|
|||||||
void addFooterItems();
|
void addFooterItems();
|
||||||
void addTitleBarButtons();
|
void addTitleBarButtons();
|
||||||
void registerSettings();
|
void registerSettings();
|
||||||
|
void registerProviders();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void detectSystemTheme() {
|
static void detectSystemTheme() {
|
||||||
@ -68,6 +69,7 @@ IMHEX_PLUGIN_SETUP("Windows", "WerWolv", "Windows-only features") {
|
|||||||
addFooterItems();
|
addFooterItems();
|
||||||
addTitleBarButtons();
|
addTitleBarButtons();
|
||||||
registerSettings();
|
registerSettings();
|
||||||
|
registerProviders();
|
||||||
|
|
||||||
detectSystemTheme();
|
detectSystemTheme();
|
||||||
checkBorderlessWindowOverride();
|
checkBorderlessWindowOverride();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user