mirror of https://github.com/WerWolv/ImHex.git
feat: Added Linux support to the Process Memory Provider (#1331)
<!-- Please provide as much information as possible about what your PR aims to do. PRs with no description will most likely be closed until more information is provided. If you're planing on changing fundamental behaviour or add big new features, please open a GitHub Issue first before starting to work on it. If it's not something big and you still want to contact us about it, feel free to do so ! --> ### Problem description <!-- Describe the bug that you fixed/feature request that you implemented, or link to an existing issue describing it --> Implement a Linux backend for the ProcessMemoryProvider plugin. ### Implementation description <!-- Explain what you did to correct the problem --> Most of the provider code is the same between Windows and Linux. The primary differences are: - enumerate PIDs in `/proc/` to get the process list - use `/proc/<PID>/cmdline` as the process name - parse `/proc/<PID>/maps` to get the module list - reading/writing from memory is done using `process_vm_readv`/`process_vm_writev` NOTE: `sudo setcap CAP_SYS_PTRACE=+eip build/imhex` must be run to give the binary permission to read another process' memory. Running as root user should also work but I would not recommend it. ### Additional things The existing translations keys no longer match since I moved the plugin from `windows` to `builtin`. I'm not well versed in C++ so I attempted to keep my changes rather simple. Feedback is very welcome. --------- Co-authored-by: WerWolv <werwolv98@gmail.com>
This commit is contained in:
parent
8ab85a2af1
commit
5c84ef5f72
|
@ -1 +1 @@
|
|||
Subproject commit e4891c89b6df5e3dd7ac976f12fbcc8d850b3c8d
|
||||
Subproject commit bc29a4f31743f4a05c2c23aff1168fd0b4d359b9
|
|
@ -60,6 +60,7 @@ add_imhex_plugin(
|
|||
source/content/providers/intel_hex_provider.cpp
|
||||
source/content/providers/motorola_srec_provider.cpp
|
||||
source/content/providers/memory_file_provider.cpp
|
||||
source/content/providers/process_memory_provider.cpp
|
||||
|
||||
source/content/tools/ascii_table.cpp
|
||||
source/content/tools/base_converter.cpp
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#if defined(OS_WINDOWS) || defined (OS_LINUX)
|
||||
|
||||
#include <hex/providers/provider.hpp>
|
||||
#include <hex/api/localization_manager.hpp>
|
||||
|
||||
|
@ -8,15 +10,30 @@
|
|||
#include <hex/helpers/utils.hpp>
|
||||
|
||||
#include <set>
|
||||
#include <thread>
|
||||
|
||||
namespace hex::plugin::windows {
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#if defined(OS_WINDOWS)
|
||||
#include <windows.h>
|
||||
#elif defined(OS_LINUX)
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
class ProcessMemoryProvider : public hex::prv::Provider {
|
||||
public:
|
||||
ProcessMemoryProvider() = default;
|
||||
~ProcessMemoryProvider() override = default;
|
||||
|
||||
[[nodiscard]] bool isAvailable() const override { return this->m_processHandle != nullptr; }
|
||||
[[nodiscard]] bool isAvailable() const override {
|
||||
#ifdef _WIN32
|
||||
return this->m_processHandle != nullptr;
|
||||
#elif __linux__
|
||||
return this->m_processId != -1;
|
||||
#endif
|
||||
}
|
||||
[[nodiscard]] bool isReadable() const override { return true; }
|
||||
[[nodiscard]] bool isWritable() const override { return true; }
|
||||
[[nodiscard]] bool isResizable() const override { return false; }
|
||||
|
@ -25,19 +42,16 @@ namespace hex::plugin::windows {
|
|||
|
||||
void readRaw(u64 address, void *buffer, size_t size) override;
|
||||
void writeRaw(u64 address, const void *buffer, size_t size) override;
|
||||
[[nodiscard]] u64 getActualSize() const override { return std::numeric_limits<u64>::max(); }
|
||||
[[nodiscard]] u64 getActualSize() const override { return 0xFFFF'FFFF'FFFF; }
|
||||
|
||||
void save() override {}
|
||||
|
||||
[[nodiscard]] std::string getName() const override { return hex::format("hex.windows.provider.process_memory.name"_lang, this->m_selectedProcess != nullptr ? this->m_selectedProcess->name : ""); }
|
||||
[[nodiscard]] std::string getName() const override { return hex::format("hex.builtin.provider.process_memory.name"_lang, this->m_selectedProcess != nullptr ? this->m_selectedProcess->name : ""); }
|
||||
[[nodiscard]] std::vector<Description> getDataDescription() const override {
|
||||
if (this->m_selectedProcess == nullptr)
|
||||
return {};
|
||||
else
|
||||
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) }
|
||||
};
|
||||
return {
|
||||
{ "hex.builtin.provider.process_memory.process_name"_lang, this->m_selectedProcess->name },
|
||||
{ "hex.builtin.provider.process_memory.process_id"_lang, std::to_string(this->m_selectedProcess->id) }
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] bool open() override;
|
||||
|
@ -48,11 +62,11 @@ namespace hex::plugin::windows {
|
|||
bool drawLoadInterface() override;
|
||||
void drawInterface() override;
|
||||
|
||||
void loadSettings(const nlohmann::json &) override;
|
||||
[[nodiscard]] nlohmann::json storeSettings(nlohmann::json) const 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";
|
||||
return "hex.builtin.provider.process_memory";
|
||||
}
|
||||
|
||||
[[nodiscard]] std::pair<Region, bool> getRegionValidity(u64) const override;
|
||||
|
@ -88,9 +102,15 @@ namespace hex::plugin::windows {
|
|||
return hex::containsIgnoreCase(memoryRegion.name, search);
|
||||
});
|
||||
|
||||
void* m_processHandle = reinterpret_cast<void*>(-1);
|
||||
#ifdef _WIN32
|
||||
HANDLE m_processHandle = nullptr;
|
||||
#elif __linux__
|
||||
pid_t m_processId = -1;
|
||||
#endif
|
||||
|
||||
bool m_enumerationFailed = false;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -538,6 +538,20 @@
|
|||
"hex.builtin.provider.mem_file.rename.desc": "Enter a name for this memory file.",
|
||||
"hex.builtin.provider.motorola_srec": "Motorola SREC Provider",
|
||||
"hex.builtin.provider.motorola_srec.name": "Motorola SREC {0}",
|
||||
"hex.builtin.provider.process_memory": "Process Memory Provider",
|
||||
"hex.builtin.provider.process_memory.enumeration_failed": "Failed to enumerate processes",
|
||||
"hex.builtin.provider.process_memory.memory_regions": "Memory Regions",
|
||||
"hex.builtin.provider.process_memory.name": "'{0}' Process Memory",
|
||||
"hex.builtin.provider.process_memory.process_name": "Process Name",
|
||||
"hex.builtin.provider.process_memory.process_id": "PID",
|
||||
"hex.builtin.provider.process_memory.region.commit": "Commit",
|
||||
"hex.builtin.provider.process_memory.region.reserve": "Reserved",
|
||||
"hex.builtin.provider.process_memory.region.private": "Private",
|
||||
"hex.builtin.provider.process_memory.region.mapped": "Mapped",
|
||||
"hex.builtin.provider.process_memory.utils": "Utils",
|
||||
"hex.builtin.provider.process_memory.utils.inject_dll": "Inject DLL",
|
||||
"hex.builtin.provider.process_memory.utils.inject_dll.success": "Successfully injected DLL '{0}'!",
|
||||
"hex.builtin.provider.process_memory.utils.inject_dll.failure": "Failed to inject DLL '{0}'!",
|
||||
"hex.builtin.provider.view": "View",
|
||||
"hex.builtin.setting.experiments": "Experiments",
|
||||
"hex.builtin.setting.experiments.description": "Experiments are features that are still in development and may not work correctly yet.\n\nFeel free to try them out and report any issues you encounter!",
|
||||
|
|
|
@ -504,6 +504,20 @@
|
|||
"hex.builtin.provider.mem_file.rename.desc": "이 메모리 파일의 이름을 입력합니다.",
|
||||
"hex.builtin.provider.motorola_srec": "모토로라 SREC 공급자",
|
||||
"hex.builtin.provider.motorola_srec.name": "모토로라 SREC {0}",
|
||||
"hex.builtin.provider.process_memory": "프로세스 메모리 공급자",
|
||||
"hex.builtin.provider.process_memory.enumeration_failed": "프로세스 열거 실패",
|
||||
"hex.builtin.provider.process_memory.memory_regions": "메모리 영역",
|
||||
"hex.builtin.provider.process_memory.name": "'{0}' 프로세스 메모리",
|
||||
"hex.builtin.provider.process_memory.process_id": "PID",
|
||||
"hex.builtin.provider.process_memory.process_name": "프로세스 이름",
|
||||
"hex.builtin.provider.process_memory.region.commit": "커밋",
|
||||
"hex.builtin.provider.process_memory.region.mapped": "맵",
|
||||
"hex.builtin.provider.process_memory.region.private": "프라이빗",
|
||||
"hex.builtin.provider.process_memory.region.reserve": "예약됨",
|
||||
"hex.builtin.provider.process_memory.utils": "도구",
|
||||
"hex.builtin.provider.process_memory.utils.inject_dll": "DLL 삽입",
|
||||
"hex.builtin.provider.process_memory.utils.inject_dll.failure": "DLL '{0}'을(를) 삽입하지 못했습니다!",
|
||||
"hex.builtin.provider.process_memory.utils.inject_dll.success": "DLL '{0}'을(를) 성공적으로 삽입했습니다!",
|
||||
"hex.builtin.provider.view": "보기",
|
||||
"hex.builtin.setting.folders": "폴더",
|
||||
"hex.builtin.setting.folders.add_folder": "새 폴더 추가",
|
||||
|
|
|
@ -504,6 +504,20 @@
|
|||
"hex.builtin.provider.mem_file.rename.desc": "输入此内存文件的名称。",
|
||||
"hex.builtin.provider.motorola_srec": "Motorola SREC",
|
||||
"hex.builtin.provider.motorola_srec.name": "Motorola SREC {0}",
|
||||
"hex.builtin.provider.process_memory": "进程内存提供器",
|
||||
"hex.builtin.provider.process_memory.enumeration_failed": "无法枚举进程",
|
||||
"hex.builtin.provider.process_memory.memory_regions": "内存区域",
|
||||
"hex.builtin.provider.process_memory.name": "'{0}' 进程内存",
|
||||
"hex.builtin.provider.process_memory.process_id": "PID",
|
||||
"hex.builtin.provider.process_memory.process_name": "进程名",
|
||||
"hex.builtin.provider.process_memory.region.commit": "提交",
|
||||
"hex.builtin.provider.process_memory.region.mapped": "映射",
|
||||
"hex.builtin.provider.process_memory.region.private": "私有",
|
||||
"hex.builtin.provider.process_memory.region.reserve": "保留",
|
||||
"hex.builtin.provider.process_memory.utils": "工具",
|
||||
"hex.builtin.provider.process_memory.utils.inject_dll": "注入DLL",
|
||||
"hex.builtin.provider.process_memory.utils.inject_dll.failure": "无法注入DLL '{0}'!",
|
||||
"hex.builtin.provider.process_memory.utils.inject_dll.success": "成功注入DLL '{0}'!",
|
||||
"hex.builtin.provider.view": "独立查看",
|
||||
"hex.builtin.setting.folders": "扩展搜索路径",
|
||||
"hex.builtin.setting.folders.add_folder": "添加新的目录",
|
||||
|
|
|
@ -504,6 +504,20 @@
|
|||
"hex.builtin.provider.mem_file.rename.desc": "Enter a name for this memory file.",
|
||||
"hex.builtin.provider.motorola_srec": "Motorola SREC 提供者",
|
||||
"hex.builtin.provider.motorola_srec.name": "Motorola SREC {0}",
|
||||
"hex.builtin.provider.process_memory": "處理序記憶體提供者",
|
||||
"hex.builtin.provider.process_memory.enumeration_failed": "無法列舉處理序",
|
||||
"hex.builtin.provider.process_memory.memory_regions": "記憶體區域",
|
||||
"hex.builtin.provider.process_memory.name": "'{0}' 處理序記憶體",
|
||||
"hex.builtin.provider.process_memory.process_id": "處理序名稱",
|
||||
"hex.builtin.provider.process_memory.process_name": "PID",
|
||||
"hex.builtin.provider.process_memory.region.commit": "已認可",
|
||||
"hex.builtin.provider.process_memory.region.mapped": "已對應",
|
||||
"hex.builtin.provider.process_memory.region.private": "私人",
|
||||
"hex.builtin.provider.process_memory.region.reserve": "受保留",
|
||||
"hex.builtin.provider.process_memory.utils": "工具",
|
||||
"hex.builtin.provider.process_memory.utils.inject_dll": "注入 DLL",
|
||||
"hex.builtin.provider.process_memory.utils.inject_dll.failure": "無法注入 DLL '{0}'!",
|
||||
"hex.builtin.provider.process_memory.utils.inject_dll.success": "已成功注入 DLL '{0}'!",
|
||||
"hex.builtin.provider.view": "View",
|
||||
"hex.builtin.setting.folders": "資料夾",
|
||||
"hex.builtin.setting.folders.add_folder": "新增資料夾",
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "content/providers/motorola_srec_provider.hpp"
|
||||
#include "content/providers/memory_file_provider.hpp"
|
||||
#include "content/providers/view_provider.hpp"
|
||||
#include <content/providers/process_memory_provider.hpp>
|
||||
#include "content/popups/popup_notification.hpp"
|
||||
#include "content/helpers/notification.hpp"
|
||||
|
||||
|
@ -34,6 +35,10 @@ namespace hex::plugin::builtin {
|
|||
ContentRegistry::Provider::add<MemoryFileProvider>(false);
|
||||
ContentRegistry::Provider::add<ViewProvider>(false);
|
||||
|
||||
#if defined(OS_WINDOWS) ||defined (OS_LINUX)
|
||||
ContentRegistry::Provider::add<ProcessMemoryProvider>();
|
||||
#endif
|
||||
|
||||
ProjectFile::registerHandler({
|
||||
.basePath = "providers",
|
||||
.required = true,
|
||||
|
@ -133,4 +138,4 @@ namespace hex::plugin::builtin {
|
|||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,407 @@
|
|||
#if defined(OS_WINDOWS) || defined (OS_LINUX)
|
||||
|
||||
#include <content/providers/process_memory_provider.hpp>
|
||||
|
||||
#if defined(OS_WINDOWS)
|
||||
#include <windows.h>
|
||||
#include <psapi.h>
|
||||
#include <shellapi.h>
|
||||
#elif defined(OS_LINUX)
|
||||
#include <sys/uio.h>
|
||||
#endif
|
||||
|
||||
#include <imgui.h>
|
||||
#include <hex/ui/imgui_imhex_extensions.h>
|
||||
#include <hex/helpers/utils.hpp>
|
||||
#include <hex/helpers/fmt.hpp>
|
||||
#include <hex/ui/view.hpp>
|
||||
|
||||
#include <wolv/io/fs.hpp>
|
||||
#include <wolv/io/file.hpp>
|
||||
#include <wolv/utils/guards.hpp>
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
bool ProcessMemoryProvider::open() {
|
||||
#if defined(OS_WINDOWS)
|
||||
this->m_processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, this->m_selectedProcess->id);
|
||||
if (this->m_processHandle == nullptr)
|
||||
return false;
|
||||
#elif defined(OS_LINUX)
|
||||
this->m_processId = pid_t(this->m_selectedProcess->id);
|
||||
#endif
|
||||
|
||||
this->reloadProcessModules();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ProcessMemoryProvider::close() {
|
||||
#if defined(OS_WINDOWS)
|
||||
CloseHandle(this->m_processHandle);
|
||||
this->m_processHandle = nullptr;
|
||||
#elif defined(OS_LINUX)
|
||||
this->m_processId = -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
void ProcessMemoryProvider::readRaw(u64 address, void *buffer, size_t size) {
|
||||
#if defined(OS_WINDOWS)
|
||||
ReadProcessMemory(this->m_processHandle, reinterpret_cast<LPCVOID>(address), buffer, size, nullptr);
|
||||
#elif defined(OS_LINUX)
|
||||
const iovec local {
|
||||
.iov_base = buffer,
|
||||
.iov_len = size,
|
||||
};
|
||||
const iovec remote = {
|
||||
.iov_base = reinterpret_cast<void*>(address),
|
||||
.iov_len = size,
|
||||
};
|
||||
|
||||
auto read = process_vm_readv(this->m_processId, &local, 1, &remote, 1, 0);
|
||||
|
||||
if (read == -1) {
|
||||
// TODO error handling strerror(errno)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
void ProcessMemoryProvider::writeRaw(u64 address, const void *buffer, size_t size) {
|
||||
#if defined(OS_WINDOWS)
|
||||
WriteProcessMemory(this->m_processHandle, reinterpret_cast<LPVOID>(address), buffer, size, nullptr);
|
||||
#elif defined(OS_LINUX)
|
||||
const iovec local {
|
||||
.iov_base = const_cast<void*>(buffer),
|
||||
.iov_len = size,
|
||||
};
|
||||
const iovec remote = {
|
||||
.iov_base = reinterpret_cast<void*>(address),
|
||||
.iov_len = size,
|
||||
};
|
||||
|
||||
auto read = process_vm_writev(this->m_processId, &local, 1, &remote, 1, 0);
|
||||
if (read == -1) {
|
||||
// TODO error handling strerror(errno)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
std::pair<Region, bool> ProcessMemoryProvider::getRegionValidity(u64 address) const {
|
||||
for (const auto &memoryRegion : this->m_memoryRegions) {
|
||||
if (memoryRegion.region.overlaps({ address, 1 }))
|
||||
return { memoryRegion.region, true };
|
||||
}
|
||||
|
||||
Region lastRegion = Region::Invalid();
|
||||
for (const auto &memoryRegion : this->m_memoryRegions) {
|
||||
|
||||
if (address < memoryRegion.region.getStartAddress())
|
||||
return { Region { lastRegion.getEndAddress() + 1, memoryRegion.region.getStartAddress() - lastRegion.getEndAddress() }, false };
|
||||
|
||||
lastRegion = memoryRegion.region;
|
||||
}
|
||||
|
||||
return { Region::Invalid(), false };
|
||||
}
|
||||
|
||||
bool ProcessMemoryProvider::drawLoadInterface() {
|
||||
if (this->m_processes.empty() && !this->m_enumerationFailed) {
|
||||
#if defined(OS_WINDOWS)
|
||||
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));
|
||||
|
||||
auto dc = GetDC(nullptr);
|
||||
ON_SCOPE_EXIT { ReleaseDC(nullptr, dc); };
|
||||
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;
|
||||
|
||||
ImGuiExt::Texture texture;
|
||||
{
|
||||
HMODULE moduleHandle = nullptr;
|
||||
DWORD numModules = 0;
|
||||
if (EnumProcessModules(processHandle, &moduleHandle, sizeof(HMODULE), &numModules) != FALSE) {
|
||||
char modulePath[MAX_PATH];
|
||||
if (GetModuleFileNameExA(processHandle, moduleHandle, modulePath, MAX_PATH) != FALSE) {
|
||||
SHFILEINFOA fileInfo;
|
||||
if (SHGetFileInfoA(modulePath, 0, &fileInfo, sizeof(SHFILEINFOA), SHGFI_ICON | SHGFI_SMALLICON) > 0) {
|
||||
ON_SCOPE_EXIT { DestroyIcon(fileInfo.hIcon); };
|
||||
|
||||
ICONINFO iconInfo;
|
||||
if (GetIconInfo(fileInfo.hIcon, &iconInfo) != FALSE) {
|
||||
ON_SCOPE_EXIT { DeleteObject(iconInfo.hbmColor); DeleteObject(iconInfo.hbmMask); };
|
||||
|
||||
BITMAP bitmap;
|
||||
if (GetObject(iconInfo.hbmColor, sizeof(BITMAP), &bitmap) > 0) {
|
||||
BITMAPINFO bitmapInfo = { };
|
||||
bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
||||
bitmapInfo.bmiHeader.biWidth = bitmap.bmWidth;
|
||||
bitmapInfo.bmiHeader.biHeight = -bitmap.bmHeight;
|
||||
bitmapInfo.bmiHeader.biPlanes = 1;
|
||||
bitmapInfo.bmiHeader.biBitCount = 32;
|
||||
bitmapInfo.bmiHeader.biCompression = BI_RGB;
|
||||
|
||||
std::vector<u32> pixels(bitmap.bmWidth * bitmap.bmHeight * 4);
|
||||
if (GetDIBits(dc, iconInfo.hbmColor, 0, bitmap.bmHeight, pixels.data(), &bitmapInfo, DIB_RGB_COLORS) > 0) {
|
||||
for (auto &pixel : pixels)
|
||||
pixel = (pixel & 0xFF00FF00) | ((pixel & 0xFF) << 16) | ((pixel & 0xFF0000) >> 16);
|
||||
|
||||
texture = ImGuiExt::Texture(reinterpret_cast<const u8*>(pixels.data()), pixels.size(), bitmap.bmWidth, bitmap.bmHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this->m_processes.push_back({ u32(processId), processName, std::move(texture) });
|
||||
}
|
||||
#elif defined(OS_LINUX)
|
||||
for (const auto& entry : std::fs::directory_iterator("/proc")) {
|
||||
if (!std::fs::is_directory(entry)) continue;
|
||||
|
||||
const auto &path = entry.path();
|
||||
u32 processId = 0;
|
||||
try {
|
||||
processId = std::stoi(path.filename());
|
||||
} catch (...) {
|
||||
continue; // not a PID
|
||||
}
|
||||
|
||||
wolv::io::File file(path /"cmdline", wolv::io::File::Mode::Read);
|
||||
if (!file.isValid())
|
||||
continue;
|
||||
|
||||
std::string processName = file.readString(0xF'FFFF);
|
||||
|
||||
this->m_processes.emplace_back(processId, processName, ImGuiExt::Texture());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (this->m_enumerationFailed) {
|
||||
ImGui::TextUnformatted("hex.builtin.provider.process_memory.enumeration_failed"_lang);
|
||||
} else {
|
||||
ImGui::PushItemWidth(500_scaled);
|
||||
const auto &filtered = this->m_processSearchWidget.draw(this->m_processes);
|
||||
ImGui::PopItemWidth();
|
||||
if (ImGui::BeginTable("##process_table", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollY, ImVec2(500_scaled, 500_scaled))) {
|
||||
ImGui::TableSetupColumn("##icon");
|
||||
ImGui::TableSetupColumn("hex.builtin.provider.process_memory.process_id"_lang);
|
||||
ImGui::TableSetupColumn("hex.builtin.provider.process_memory.process_name"_lang);
|
||||
ImGui::TableSetupScrollFreeze(0, 1);
|
||||
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
for (auto &process : filtered) {
|
||||
ImGui::PushID(process);
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Image(process->icon, process->icon.getSize());
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%d", process->id);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Selectable(process->name.c_str(), this->m_selectedProcess != nullptr && process->id == this->m_selectedProcess->id, ImGuiSelectableFlags_SpanAllColumns, ImVec2(0, process->icon.getSize().y)))
|
||||
this->m_selectedProcess = process;
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return this->m_selectedProcess != nullptr;
|
||||
}
|
||||
|
||||
void ProcessMemoryProvider::drawInterface() {
|
||||
ImGuiExt::Header("hex.builtin.provider.process_memory.memory_regions"_lang, true);
|
||||
|
||||
auto availableX = ImGui::GetContentRegionAvail().x;
|
||||
ImGui::PushItemWidth(availableX);
|
||||
const auto &filtered = this->m_regionSearchWidget.draw(this->m_memoryRegions);
|
||||
ImGui::PopItemWidth();
|
||||
|
||||
#if defined(OS_WINDOWS)
|
||||
auto availableY = 400_scaled;
|
||||
#else
|
||||
// Take up full height on Linux since there are no DLL injection controls
|
||||
auto availableY = ImGui::GetContentRegionAvail().y;
|
||||
#endif
|
||||
|
||||
if (ImGui::BeginTable("##module_table", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollY, ImVec2(availableX, availableY))) {
|
||||
ImGui::TableSetupColumn("hex.builtin.common.region"_lang);
|
||||
ImGui::TableSetupColumn("hex.builtin.common.size"_lang);
|
||||
ImGui::TableSetupColumn("hex.builtin.common.name"_lang);
|
||||
ImGui::TableSetupScrollFreeze(0, 1);
|
||||
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
for (const auto &memoryRegion : filtered) {
|
||||
ImGui::PushID(&memoryRegion);
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGuiExt::TextFormatted("0x{0:016X} - 0x{1:016X}", memoryRegion->region.getStartAddress(), memoryRegion->region.getEndAddress());
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(hex::toByteString(memoryRegion->region.getSize()).c_str());
|
||||
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Selectable(memoryRegion->name.c_str(), false, ImGuiSelectableFlags_SpanAllColumns))
|
||||
ImHexApi::HexEditor::setSelection(memoryRegion->region);
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
#if defined(OS_WINDOWS)
|
||||
ImGuiExt::Header("hex.builtin.provider.process_memory.utils"_lang);
|
||||
|
||||
if (ImGui::Button("hex.builtin.provider.process_memory.utils.inject_dll"_lang)) {
|
||||
hex::fs::openFileBrowser(fs::DialogMode::Open, { { "DLL File", "dll" } }, [this](const std::fs::path &path) {
|
||||
const auto &dllPath = path.native();
|
||||
const auto dllPathLength = (dllPath.length() + 1) * sizeof(std::fs::path::value_type);
|
||||
|
||||
if (auto pathAddress = VirtualAllocEx(this->m_processHandle, nullptr, dllPathLength, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); pathAddress != nullptr) {
|
||||
if (WriteProcessMemory(this->m_processHandle, pathAddress, dllPath.c_str(), dllPathLength, nullptr) != FALSE) {
|
||||
auto loadLibraryW = reinterpret_cast<LPTHREAD_START_ROUTINE>(reinterpret_cast<void*>(GetProcAddress(GetModuleHandleA("kernel32.dll"), "LoadLibraryW")));
|
||||
if (loadLibraryW != nullptr) {
|
||||
if (auto threadHandle = CreateRemoteThread(this->m_processHandle, nullptr, 0, loadLibraryW, pathAddress, 0, nullptr); threadHandle != nullptr) {
|
||||
WaitForSingleObject(threadHandle, INFINITE);
|
||||
EventManager::post<RequestOpenErrorPopup>(hex::format("hex.builtin.provider.process_memory.utils.inject_dll.success"_lang, path.filename().string()));
|
||||
this->reloadProcessModules();
|
||||
CloseHandle(threadHandle);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EventManager::post<RequestOpenErrorPopup>(hex::format("hex.builtin.provider.process_memory.utils.inject_dll.failure"_lang, path.filename().string()));
|
||||
});
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void ProcessMemoryProvider::reloadProcessModules() {
|
||||
this->m_memoryRegions.clear();
|
||||
|
||||
#if defined(OS_WINDOWS)
|
||||
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_memoryRegions.insert({ { u64(moduleInfo.lpBaseOfDll), size_t(moduleInfo.SizeOfImage) }, std::fs::path(moduleName).filename().string() });
|
||||
}
|
||||
|
||||
MEMORY_BASIC_INFORMATION memoryInfo;
|
||||
for (u64 address = 0; address < this->getActualSize(); address += memoryInfo.RegionSize) {
|
||||
if (VirtualQueryEx(this->m_processHandle, reinterpret_cast<LPCVOID>(address), &memoryInfo, sizeof(MEMORY_BASIC_INFORMATION)) == 0)
|
||||
break;
|
||||
|
||||
std::string name;
|
||||
if (memoryInfo.State & MEM_IMAGE) continue;
|
||||
if (memoryInfo.State & MEM_FREE) continue;
|
||||
if (memoryInfo.State & MEM_COMMIT) name += hex::format("{} ", "hex.builtin.provider.process_memory.region.commit"_lang);
|
||||
if (memoryInfo.State & MEM_RESERVE) name += hex::format("{} ", "hex.builtin.provider.process_memory.region.reserve"_lang);
|
||||
if (memoryInfo.State & MEM_PRIVATE) name += hex::format("{} ", "hex.builtin.provider.process_memory.region.private"_lang);
|
||||
if (memoryInfo.State & MEM_MAPPED) name += hex::format("{} ", "hex.builtin.provider.process_memory.region.mapped"_lang);
|
||||
|
||||
this->m_memoryRegions.insert({ { reinterpret_cast<u64>(memoryInfo.BaseAddress), reinterpret_cast<u64>(memoryInfo.BaseAddress) + memoryInfo.RegionSize }, name });
|
||||
}
|
||||
|
||||
#elif defined(OS_LINUX)
|
||||
|
||||
wolv::io::File file(std::fs::path("/proc") / std::to_string(this->m_processId) / "maps", wolv::io::File::Mode::Read);
|
||||
|
||||
if (!file.isValid())
|
||||
return;
|
||||
|
||||
for (const auto &line : wolv::util::splitString(file.readString(0xF'FFFF), "\n")) {
|
||||
const auto &split = wolv::util::splitString(line, " ");
|
||||
if (split.size() < 6)
|
||||
continue;
|
||||
|
||||
const u64 start = std::stoull(split[0].substr(0, split[0].find('-')), nullptr, 16);
|
||||
const u64 end = std::stoull(split[0].substr(split[0].find('-') + 1), nullptr, 16);
|
||||
const auto &name = split[5];
|
||||
|
||||
this->m_memoryRegions.insert({ { start, end - start }, name });
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
std::variant<std::string, i128> ProcessMemoryProvider::queryInformation(const std::string &category, const std::string &argument) {
|
||||
auto findRegionByName = [this](const std::string &name) {
|
||||
return std::find_if(this->m_memoryRegions.begin(), this->m_memoryRegions.end(),
|
||||
[name](const auto ®ion) {
|
||||
return region.name == name;
|
||||
});
|
||||
};
|
||||
|
||||
if (category == "region_address") {
|
||||
if (auto iter = findRegionByName(argument); iter != this->m_memoryRegions.end())
|
||||
return iter->region.getStartAddress();
|
||||
else
|
||||
return 0;
|
||||
} else if (category == "region_size") {
|
||||
if (auto iter = findRegionByName(argument); iter != this->m_memoryRegions.end())
|
||||
return iter->region.getSize();
|
||||
else
|
||||
return 0;
|
||||
} else if (category == "process_id") {
|
||||
return this->m_selectedProcess->id;
|
||||
} else if (category == "process_name") {
|
||||
return this->m_selectedProcess->name;
|
||||
} else
|
||||
return Provider::queryInformation(category, argument);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -14,8 +14,6 @@ if (WIN32)
|
|||
source/content/ui_items.cpp
|
||||
source/content/settings_entries.cpp
|
||||
source/content/providers.cpp
|
||||
|
||||
source/content/providers/process_memory_provider.cpp
|
||||
INCLUDES
|
||||
include
|
||||
)
|
||||
|
|
|
@ -4,20 +4,6 @@
|
|||
"language": "German",
|
||||
"translations": {
|
||||
"hex.builtin.setting.general.context_menu_entry": "Windows Kontextmenu-Eintrag",
|
||||
"hex.windows.provider.process_memory": "",
|
||||
"hex.windows.provider.process_memory.enumeration_failed": "",
|
||||
"hex.windows.provider.process_memory.memory_regions": "",
|
||||
"hex.windows.provider.process_memory.name": "",
|
||||
"hex.windows.provider.process_memory.process_id": "",
|
||||
"hex.windows.provider.process_memory.process_name": "",
|
||||
"hex.windows.provider.process_memory.region.commit": "",
|
||||
"hex.windows.provider.process_memory.region.mapped": "",
|
||||
"hex.windows.provider.process_memory.region.private": "",
|
||||
"hex.windows.provider.process_memory.region.reserve": "",
|
||||
"hex.windows.provider.process_memory.utils": "",
|
||||
"hex.windows.provider.process_memory.utils.inject_dll": "",
|
||||
"hex.windows.provider.process_memory.utils.inject_dll.failure": "",
|
||||
"hex.windows.provider.process_memory.utils.inject_dll.success": "",
|
||||
"hex.windows.view.tty_console.auto_scroll": "Auto scroll",
|
||||
"hex.windows.view.tty_console.baud": "Baudrate",
|
||||
"hex.windows.view.tty_console.clear": "Löschen",
|
||||
|
|
|
@ -3,20 +3,6 @@
|
|||
"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.memory_regions": "Memory Regions",
|
||||
"hex.windows.provider.process_memory.name": "'{0}' Process Memory",
|
||||
"hex.windows.provider.process_memory.process_name": "Process Name",
|
||||
"hex.windows.provider.process_memory.process_id": "PID",
|
||||
"hex.windows.provider.process_memory.region.commit": "Commit",
|
||||
"hex.windows.provider.process_memory.region.reserve": "Reserved",
|
||||
"hex.windows.provider.process_memory.region.private": "Private",
|
||||
"hex.windows.provider.process_memory.region.mapped": "Mapped",
|
||||
"hex.windows.provider.process_memory.utils": "Utils",
|
||||
"hex.windows.provider.process_memory.utils.inject_dll": "Inject DLL",
|
||||
"hex.windows.provider.process_memory.utils.inject_dll.success": "Successfully injected DLL '{0}'!",
|
||||
"hex.windows.provider.process_memory.utils.inject_dll.failure": "Failed to inject DLL '{0}'!",
|
||||
"hex.builtin.setting.general.context_menu_entry": "Windows context menu entry",
|
||||
"hex.builtin.setting.interface.show_resource_usage": "Show resource usage in footer",
|
||||
"hex.windows.view.tty_console.auto_scroll": "Auto scroll",
|
||||
|
|
|
@ -4,20 +4,6 @@
|
|||
"language": "Korean",
|
||||
"translations": {
|
||||
"hex.builtin.setting.general.context_menu_entry": "Windows 컨텍스트 메뉴 항목",
|
||||
"hex.windows.provider.process_memory": "프로세스 메모리 공급자",
|
||||
"hex.windows.provider.process_memory.enumeration_failed": "프로세스 열거 실패",
|
||||
"hex.windows.provider.process_memory.memory_regions": "메모리 영역",
|
||||
"hex.windows.provider.process_memory.name": "'{0}' 프로세스 메모리",
|
||||
"hex.windows.provider.process_memory.process_id": "PID",
|
||||
"hex.windows.provider.process_memory.process_name": "프로세스 이름",
|
||||
"hex.windows.provider.process_memory.region.commit": "커밋",
|
||||
"hex.windows.provider.process_memory.region.mapped": "맵",
|
||||
"hex.windows.provider.process_memory.region.private": "프라이빗",
|
||||
"hex.windows.provider.process_memory.region.reserve": "예약됨",
|
||||
"hex.windows.provider.process_memory.utils": "도구",
|
||||
"hex.windows.provider.process_memory.utils.inject_dll": "DLL 삽입",
|
||||
"hex.windows.provider.process_memory.utils.inject_dll.failure": "DLL '{0}'을(를) 삽입하지 못했습니다!",
|
||||
"hex.windows.provider.process_memory.utils.inject_dll.success": "DLL '{0}'을(를) 성공적으로 삽입했습니다!",
|
||||
"hex.windows.view.tty_console.auto_scroll": "자동 스크롤",
|
||||
"hex.windows.view.tty_console.baud": "보 레이트",
|
||||
"hex.windows.view.tty_console.clear": "지우기",
|
||||
|
|
|
@ -4,20 +4,6 @@
|
|||
"language": "Portuguese",
|
||||
"translations": {
|
||||
"hex.builtin.setting.general.context_menu_entry": "Entrada do menu de contexto do Windows",
|
||||
"hex.windows.provider.process_memory": "",
|
||||
"hex.windows.provider.process_memory.enumeration_failed": "",
|
||||
"hex.windows.provider.process_memory.memory_regions": "",
|
||||
"hex.windows.provider.process_memory.name": "",
|
||||
"hex.windows.provider.process_memory.process_id": "",
|
||||
"hex.windows.provider.process_memory.process_name": "",
|
||||
"hex.windows.provider.process_memory.region.commit": "",
|
||||
"hex.windows.provider.process_memory.region.mapped": "",
|
||||
"hex.windows.provider.process_memory.region.private": "",
|
||||
"hex.windows.provider.process_memory.region.reserve": "",
|
||||
"hex.windows.provider.process_memory.utils": "",
|
||||
"hex.windows.provider.process_memory.utils.inject_dll": "",
|
||||
"hex.windows.provider.process_memory.utils.inject_dll.failure": "",
|
||||
"hex.windows.provider.process_memory.utils.inject_dll.success": "",
|
||||
"hex.windows.view.tty_console.auto_scroll": "Auto rolagem",
|
||||
"hex.windows.view.tty_console.baud": "Baud rate",
|
||||
"hex.windows.view.tty_console.clear": "Limpar",
|
||||
|
|
|
@ -4,20 +4,6 @@
|
|||
"language": "Chinese (Simplified)",
|
||||
"translations": {
|
||||
"hex.builtin.setting.general.context_menu_entry": "窗口上下文菜单项",
|
||||
"hex.windows.provider.process_memory": "进程内存提供器",
|
||||
"hex.windows.provider.process_memory.enumeration_failed": "无法枚举进程",
|
||||
"hex.windows.provider.process_memory.memory_regions": "内存区域",
|
||||
"hex.windows.provider.process_memory.name": "'{0}' 进程内存",
|
||||
"hex.windows.provider.process_memory.process_id": "PID",
|
||||
"hex.windows.provider.process_memory.process_name": "进程名",
|
||||
"hex.windows.provider.process_memory.region.commit": "提交",
|
||||
"hex.windows.provider.process_memory.region.mapped": "映射",
|
||||
"hex.windows.provider.process_memory.region.private": "私有",
|
||||
"hex.windows.provider.process_memory.region.reserve": "保留",
|
||||
"hex.windows.provider.process_memory.utils": "工具",
|
||||
"hex.windows.provider.process_memory.utils.inject_dll": "注入DLL",
|
||||
"hex.windows.provider.process_memory.utils.inject_dll.failure": "无法注入DLL '{0}'!",
|
||||
"hex.windows.provider.process_memory.utils.inject_dll.success": "成功注入DLL '{0}'!",
|
||||
"hex.windows.view.tty_console.auto_scroll": "自动滚动",
|
||||
"hex.windows.view.tty_console.baud": "波特率",
|
||||
"hex.windows.view.tty_console.clear": "清除",
|
||||
|
|
|
@ -4,20 +4,6 @@
|
|||
"language": "Chinese (Traditional)",
|
||||
"translations": {
|
||||
"hex.builtin.setting.general.context_menu_entry": "視窗內容功能表項目",
|
||||
"hex.windows.provider.process_memory": "處理序記憶體提供者",
|
||||
"hex.windows.provider.process_memory.enumeration_failed": "無法列舉處理序",
|
||||
"hex.windows.provider.process_memory.memory_regions": "記憶體區域",
|
||||
"hex.windows.provider.process_memory.name": "'{0}' 處理序記憶體",
|
||||
"hex.windows.provider.process_memory.process_id": "處理序名稱",
|
||||
"hex.windows.provider.process_memory.process_name": "PID",
|
||||
"hex.windows.provider.process_memory.region.commit": "已認可",
|
||||
"hex.windows.provider.process_memory.region.mapped": "已對應",
|
||||
"hex.windows.provider.process_memory.region.private": "私人",
|
||||
"hex.windows.provider.process_memory.region.reserve": "受保留",
|
||||
"hex.windows.provider.process_memory.utils": "工具",
|
||||
"hex.windows.provider.process_memory.utils.inject_dll": "注入 DLL",
|
||||
"hex.windows.provider.process_memory.utils.inject_dll.failure": "無法注入 DLL '{0}'!",
|
||||
"hex.windows.provider.process_memory.utils.inject_dll.success": "已成功注入 DLL '{0}'!",
|
||||
"hex.windows.view.tty_console.auto_scroll": "自動捲動",
|
||||
"hex.windows.view.tty_console.baud": "鮑率",
|
||||
"hex.windows.view.tty_console.clear": "清除",
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
#include <hex/api/content_registry.hpp>
|
||||
|
||||
#include <content/providers/process_memory_provider.hpp>
|
||||
|
||||
namespace hex::plugin::windows {
|
||||
|
||||
void registerProviders() {
|
||||
ContentRegistry::Provider::add<ProcessMemoryProvider>();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,315 +0,0 @@
|
|||
#include <content/providers/process_memory_provider.hpp>
|
||||
|
||||
#include <windows.h>
|
||||
#include <psapi.h>
|
||||
#include <shellapi.h>
|
||||
|
||||
#include <imgui.h>
|
||||
#include <hex/ui/imgui_imhex_extensions.h>
|
||||
#include <hex/helpers/utils.hpp>
|
||||
#include <hex/helpers/fmt.hpp>
|
||||
#include <hex/ui/view.hpp>
|
||||
|
||||
#include <wolv/utils/guards.hpp>
|
||||
|
||||
#include <nlohmann/json.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;
|
||||
|
||||
this->reloadProcessModules();
|
||||
|
||||
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, reinterpret_cast<LPCVOID>(address), buffer, size, nullptr);
|
||||
}
|
||||
void ProcessMemoryProvider::writeRaw(u64 address, const void *buffer, size_t size) {
|
||||
WriteProcessMemory(this->m_processHandle, reinterpret_cast<LPVOID>(address), buffer, size, nullptr);
|
||||
}
|
||||
|
||||
std::pair<Region, bool> ProcessMemoryProvider::getRegionValidity(u64 address) const {
|
||||
for (const auto &memoryRegion : this->m_memoryRegions) {
|
||||
if (memoryRegion.region.overlaps({ address, 1 }))
|
||||
return { memoryRegion.region, true };
|
||||
}
|
||||
|
||||
Region lastRegion = Region::Invalid();
|
||||
for (const auto &memoryRegion : this->m_memoryRegions) {
|
||||
|
||||
if (address < memoryRegion.region.getStartAddress())
|
||||
return { Region { lastRegion.getEndAddress() + 1, memoryRegion.region.getStartAddress() - lastRegion.getEndAddress() }, false };
|
||||
|
||||
lastRegion = memoryRegion.region;
|
||||
}
|
||||
|
||||
return { Region::Invalid(), false };
|
||||
}
|
||||
|
||||
bool 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));
|
||||
|
||||
auto dc = GetDC(nullptr);
|
||||
ON_SCOPE_EXIT { ReleaseDC(nullptr, dc); };
|
||||
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;
|
||||
|
||||
ImGuiExt::Texture texture;
|
||||
{
|
||||
HMODULE moduleHandle = nullptr;
|
||||
DWORD numModules = 0;
|
||||
if (EnumProcessModules(processHandle, &moduleHandle, sizeof(HMODULE), &numModules) != FALSE) {
|
||||
char modulePath[MAX_PATH];
|
||||
if (GetModuleFileNameExA(processHandle, moduleHandle, modulePath, MAX_PATH) != FALSE) {
|
||||
SHFILEINFOA fileInfo;
|
||||
if (SHGetFileInfoA(modulePath, 0, &fileInfo, sizeof(SHFILEINFOA), SHGFI_ICON | SHGFI_SMALLICON) > 0) {
|
||||
ON_SCOPE_EXIT { DestroyIcon(fileInfo.hIcon); };
|
||||
|
||||
ICONINFO iconInfo;
|
||||
if (GetIconInfo(fileInfo.hIcon, &iconInfo) != FALSE) {
|
||||
ON_SCOPE_EXIT { DeleteObject(iconInfo.hbmColor); DeleteObject(iconInfo.hbmMask); };
|
||||
|
||||
BITMAP bitmap;
|
||||
if (GetObject(iconInfo.hbmColor, sizeof(BITMAP), &bitmap) > 0) {
|
||||
BITMAPINFO bitmapInfo = { };
|
||||
bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
||||
bitmapInfo.bmiHeader.biWidth = bitmap.bmWidth;
|
||||
bitmapInfo.bmiHeader.biHeight = -bitmap.bmHeight;
|
||||
bitmapInfo.bmiHeader.biPlanes = 1;
|
||||
bitmapInfo.bmiHeader.biBitCount = 32;
|
||||
bitmapInfo.bmiHeader.biCompression = BI_RGB;
|
||||
|
||||
std::vector<u32> pixels(bitmap.bmWidth * bitmap.bmHeight * 4);
|
||||
if (GetDIBits(dc, iconInfo.hbmColor, 0, bitmap.bmHeight, pixels.data(), &bitmapInfo, DIB_RGB_COLORS) > 0) {
|
||||
for (auto &pixel : pixels)
|
||||
pixel = (pixel & 0xFF00FF00) | ((pixel & 0xFF) << 16) | ((pixel & 0xFF0000) >> 16);
|
||||
|
||||
texture = ImGuiExt::Texture(reinterpret_cast<u8*>(pixels.data()), pixels.size(), bitmap.bmWidth, bitmap.bmHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this->m_processes.push_back(Process { u32(processId), processName, std::move(texture) });
|
||||
}
|
||||
}
|
||||
|
||||
if (this->m_enumerationFailed) {
|
||||
ImGui::TextUnformatted("hex.windows.provider.process_memory.enumeration_failed"_lang);
|
||||
} else {
|
||||
ImGui::PushItemWidth(350_scaled);
|
||||
const auto &filtered = this->m_processSearchWidget.draw(this->m_processes);
|
||||
ImGui::PopItemWidth();
|
||||
if (ImGui::BeginTable("##process_table", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollY, ImVec2(350_scaled, 500_scaled))) {
|
||||
ImGui::TableSetupColumn("##icon");
|
||||
ImGui::TableSetupColumn("hex.windows.provider.process_memory.process_id"_lang);
|
||||
ImGui::TableSetupColumn("hex.windows.provider.process_memory.process_name"_lang);
|
||||
ImGui::TableSetupScrollFreeze(0, 1);
|
||||
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
for (auto &process : filtered) {
|
||||
ImGui::PushID(process);
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Image(process->icon, process->icon.getSize());
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%d", process->id);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Selectable(process->name.c_str(), this->m_selectedProcess != nullptr && process->id == this->m_selectedProcess->id, ImGuiSelectableFlags_SpanAllColumns, ImVec2(0, process->icon.getSize().y)))
|
||||
this->m_selectedProcess = process;
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return this->m_selectedProcess != nullptr;
|
||||
}
|
||||
|
||||
void ProcessMemoryProvider::drawInterface() {
|
||||
ImGuiExt::Header("hex.windows.provider.process_memory.memory_regions"_lang, true);
|
||||
|
||||
auto availableX = ImGui::GetContentRegionAvail().x;
|
||||
ImGui::PushItemWidth(availableX);
|
||||
const auto &filtered = this->m_regionSearchWidget.draw(this->m_memoryRegions);
|
||||
ImGui::PopItemWidth();
|
||||
if (ImGui::BeginTable("##module_table", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollY, ImVec2(availableX, 400_scaled))) {
|
||||
ImGui::TableSetupColumn("hex.builtin.common.region"_lang);
|
||||
ImGui::TableSetupColumn("hex.builtin.common.size"_lang);
|
||||
ImGui::TableSetupColumn("hex.builtin.common.name"_lang);
|
||||
ImGui::TableSetupScrollFreeze(0, 1);
|
||||
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
for (auto &memoryRegion : filtered) {
|
||||
ImGui::PushID(memoryRegion->region.getStartAddress());
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%016llX - 0x%016llX", memoryRegion->region.getStartAddress(), memoryRegion->region.getEndAddress());
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(hex::toByteString(memoryRegion->region.getSize()).c_str());
|
||||
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Selectable(memoryRegion->name.c_str(), false, ImGuiSelectableFlags_SpanAllColumns))
|
||||
ImHexApi::HexEditor::setSelection(memoryRegion->region);
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
ImGuiExt::Header("hex.windows.provider.process_memory.utils"_lang);
|
||||
|
||||
if (ImGui::Button("hex.windows.provider.process_memory.utils.inject_dll"_lang)) {
|
||||
hex::fs::openFileBrowser(fs::DialogMode::Open, { { "DLL File", "dll" } }, [this](const std::fs::path &path) {
|
||||
const auto &dllPath = path.native();
|
||||
const auto dllPathLength = (dllPath.length() + 1) * sizeof(std::fs::path::value_type);
|
||||
|
||||
if (auto pathAddress = VirtualAllocEx(this->m_processHandle, nullptr, dllPathLength, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); pathAddress != nullptr) {
|
||||
if (WriteProcessMemory(this->m_processHandle, pathAddress, dllPath.c_str(), dllPathLength, nullptr) != FALSE) {
|
||||
auto loadLibraryW = reinterpret_cast<LPTHREAD_START_ROUTINE>(reinterpret_cast<void*>(GetProcAddress(GetModuleHandleA("kernel32.dll"), "LoadLibraryW")));
|
||||
if (loadLibraryW != nullptr) {
|
||||
if (auto threadHandle = CreateRemoteThread(this->m_processHandle, nullptr, 0, loadLibraryW, pathAddress, 0, nullptr); threadHandle != nullptr) {
|
||||
WaitForSingleObject(threadHandle, INFINITE);
|
||||
EventManager::post<RequestOpenErrorPopup>(hex::format("hex.windows.provider.process_memory.utils.inject_dll.success"_lang, path.filename().string()));
|
||||
this->reloadProcessModules();
|
||||
CloseHandle(threadHandle);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EventManager::post<RequestOpenErrorPopup>(hex::format("hex.windows.provider.process_memory.utils.inject_dll.failure"_lang, path.filename().string()));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessMemoryProvider::reloadProcessModules() {
|
||||
this->m_memoryRegions.clear();
|
||||
|
||||
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_memoryRegions.insert({ { u64(moduleInfo.lpBaseOfDll), size_t(moduleInfo.SizeOfImage) }, std::fs::path(moduleName).filename().string() });
|
||||
}
|
||||
|
||||
MEMORY_BASIC_INFORMATION memoryInfo;
|
||||
for (u64 address = 0; address < this->getActualSize(); address += memoryInfo.RegionSize) {
|
||||
if (VirtualQueryEx(this->m_processHandle, reinterpret_cast<LPCVOID>(address), &memoryInfo, sizeof(MEMORY_BASIC_INFORMATION)) == 0)
|
||||
break;
|
||||
|
||||
std::string name;
|
||||
if (memoryInfo.State & MEM_IMAGE) continue;
|
||||
if (memoryInfo.State & MEM_FREE) continue;
|
||||
if (memoryInfo.State & MEM_COMMIT) name += hex::format("{} ", "hex.windows.provider.process_memory.region.commit"_lang);
|
||||
if (memoryInfo.State & MEM_RESERVE) name += hex::format("{} ", "hex.windows.provider.process_memory.region.reserve"_lang);
|
||||
if (memoryInfo.State & MEM_PRIVATE) name += hex::format("{} ", "hex.windows.provider.process_memory.region.private"_lang);
|
||||
if (memoryInfo.State & MEM_MAPPED) name += hex::format("{} ", "hex.windows.provider.process_memory.region.mapped"_lang);
|
||||
|
||||
this->m_memoryRegions.insert({ { u64(memoryInfo.BaseAddress), u64(memoryInfo.BaseAddress) + memoryInfo.RegionSize }, name });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::variant<std::string, i128> ProcessMemoryProvider::queryInformation(const std::string &category, const std::string &argument) {
|
||||
auto findRegionByName = [this](const std::string &name) {
|
||||
return std::find_if(this->m_memoryRegions.begin(), this->m_memoryRegions.end(),
|
||||
[name](const auto ®ion) {
|
||||
return region.name == name;
|
||||
});
|
||||
};
|
||||
|
||||
if (category == "region_address") {
|
||||
if (auto iter = findRegionByName(argument); iter != this->m_memoryRegions.end())
|
||||
return iter->region.getStartAddress();
|
||||
else
|
||||
return 0;
|
||||
} else if (category == "region_size") {
|
||||
if (auto iter = findRegionByName(argument); iter != this->m_memoryRegions.end())
|
||||
return iter->region.getSize();
|
||||
else
|
||||
return 0;
|
||||
} else if (category == "process_id") {
|
||||
return this->m_selectedProcess->id;
|
||||
} else if (category == "process_name") {
|
||||
return this->m_selectedProcess->name;
|
||||
} else
|
||||
return Provider::queryInformation(category, argument);
|
||||
}
|
||||
|
||||
void ProcessMemoryProvider::loadSettings(const nlohmann::json&) {
|
||||
|
||||
}
|
||||
|
||||
nlohmann::json ProcessMemoryProvider::storeSettings(nlohmann::json) const {
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
Loading…
Reference in New Issue