mirror of
https://github.com/NohamR/Reclass.git
synced 2026-05-10 19:59:21 +00:00
Added linux support (tested on Ubuntu)
CMakeList: fixed for building on linux processpicker: linux process enumeration main.cpp: "_Exit()" works on linux & windows "ProcessMemory" plugin: added linux support
This commit is contained in:
@@ -26,8 +26,18 @@ add_library(ProcessMemoryPlugin SHARED ${PLUGIN_SOURCES})
|
||||
# Link Qt
|
||||
target_link_libraries(ProcessMemoryPlugin PRIVATE Qt6::Widgets)
|
||||
|
||||
# Platform-specific linking
|
||||
if(WIN32)
|
||||
target_link_libraries(ProcessMemoryPlugin PRIVATE psapi shell32)
|
||||
endif()
|
||||
|
||||
# On Linux, hide all symbols by default so only RCX_PLUGIN_EXPORT-marked ones are exported
|
||||
if(UNIX AND NOT APPLE)
|
||||
target_compile_options(ProcessMemoryPlugin PRIVATE -fvisibility=hidden)
|
||||
endif()
|
||||
|
||||
# Include directories
|
||||
target_include_directories(ProcessMemoryPlugin PRIVATE
|
||||
target_include_directories(ProcessMemoryPlugin PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../src
|
||||
)
|
||||
|
||||
|
||||
@@ -1,17 +1,40 @@
|
||||
#include "ProcessMemoryPlugin.h"
|
||||
|
||||
#include "../../src/processpicker.h"
|
||||
|
||||
#include <QStyle>
|
||||
#include <QApplication>
|
||||
#include <QRegularExpression>
|
||||
#include <QMessageBox>
|
||||
#include <QPixmap>
|
||||
#include <QImage>
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <tlhelp32.h>
|
||||
#include <psapi.h>
|
||||
#include <shellapi.h>
|
||||
#elif defined(__linux__)
|
||||
#include <climits>
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/uio.h>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <cstring>
|
||||
#endif
|
||||
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
// ProcessMemoryProvider implementation
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
|
||||
ProcessMemoryProvider::ProcessMemoryProvider(DWORD pid, const QString& processName)
|
||||
#ifdef _WIN32
|
||||
|
||||
ProcessMemoryProvider::ProcessMemoryProvider(uint32_t pid, const QString& processName)
|
||||
: m_handle(nullptr)
|
||||
, m_pid(pid)
|
||||
, m_processName(processName)
|
||||
@@ -19,7 +42,7 @@ ProcessMemoryProvider::ProcessMemoryProvider(DWORD pid, const QString& processNa
|
||||
, m_base(0)
|
||||
{
|
||||
// Try to open with write access first
|
||||
m_handle = OpenProcess(PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION | PROCESS_QUERY_INFORMATION,
|
||||
m_handle = OpenProcess(PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION | PROCESS_QUERY_INFORMATION,
|
||||
FALSE, pid);
|
||||
if (m_handle)
|
||||
m_writable = true;
|
||||
@@ -31,21 +54,13 @@ ProcessMemoryProvider::ProcessMemoryProvider(DWORD pid, const QString& processNa
|
||||
}
|
||||
|
||||
if (m_handle)
|
||||
{
|
||||
cacheModules();
|
||||
}
|
||||
}
|
||||
|
||||
ProcessMemoryProvider::~ProcessMemoryProvider()
|
||||
{
|
||||
if (m_handle)
|
||||
CloseHandle(m_handle);
|
||||
}
|
||||
|
||||
bool ProcessMemoryProvider::read(uint64_t addr, void* buf, int len) const
|
||||
{
|
||||
if (!m_handle || len <= 0) return false;
|
||||
|
||||
|
||||
SIZE_T bytesRead = 0;
|
||||
if (ReadProcessMemory(m_handle, (LPCVOID)(m_base + addr), buf, (SIZE_T)len, &bytesRead))
|
||||
return bytesRead == (SIZE_T)len;
|
||||
@@ -55,7 +70,7 @@ bool ProcessMemoryProvider::read(uint64_t addr, void* buf, int len) const
|
||||
bool ProcessMemoryProvider::write(uint64_t addr, const void* buf, int len)
|
||||
{
|
||||
if (!m_handle || !m_writable || len <= 0) return false;
|
||||
|
||||
|
||||
SIZE_T bytesWritten = 0;
|
||||
if (WriteProcessMemory(m_handle, (LPVOID)(m_base + addr), buf, (SIZE_T)len, &bytesWritten))
|
||||
return bytesWritten == (SIZE_T)len;
|
||||
@@ -64,9 +79,16 @@ bool ProcessMemoryProvider::write(uint64_t addr, const void* buf, int len)
|
||||
|
||||
QString ProcessMemoryProvider::getSymbol(uint64_t addr) const
|
||||
{
|
||||
// TODO: Implement module enumeration with EnumProcessModules
|
||||
// For now, just return empty (no symbol resolution)
|
||||
Q_UNUSED(addr);
|
||||
for (const auto& mod : m_modules)
|
||||
{
|
||||
if (addr >= mod.base && addr < mod.base + mod.size)
|
||||
{
|
||||
uint64_t offset = addr - mod.base;
|
||||
return QStringLiteral("%1+0x%2")
|
||||
.arg(mod.name)
|
||||
.arg(offset, 0, 16, QChar('0'));
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -98,6 +120,190 @@ void ProcessMemoryProvider::cacheModules()
|
||||
}
|
||||
}
|
||||
|
||||
#elif defined(__linux__)
|
||||
|
||||
ProcessMemoryProvider::ProcessMemoryProvider(uint32_t pid, const QString& processName)
|
||||
: m_fd(-1)
|
||||
, m_pid(pid)
|
||||
, m_processName(processName)
|
||||
, m_writable(false)
|
||||
, m_base(0)
|
||||
{
|
||||
QString memPath = QStringLiteral("/proc/%1/mem").arg(pid);
|
||||
QByteArray pathUtf8 = memPath.toUtf8();
|
||||
|
||||
// Try read-write first
|
||||
m_fd = ::open(pathUtf8.constData(), O_RDWR);
|
||||
if (m_fd >= 0)
|
||||
m_writable = true;
|
||||
else
|
||||
{
|
||||
// Fall back to read-only
|
||||
m_fd = ::open(pathUtf8.constData(), O_RDONLY);
|
||||
m_writable = false;
|
||||
}
|
||||
|
||||
if (m_fd >= 0)
|
||||
cacheModules();
|
||||
|
||||
}
|
||||
|
||||
bool ProcessMemoryProvider::read(uint64_t addr, void* buf, int len) const
|
||||
{
|
||||
if (m_fd < 0 || len <= 0) return false;
|
||||
|
||||
uint64_t absAddr = m_base + addr;
|
||||
|
||||
// Try process_vm_readv first (faster, no fd seek contention)
|
||||
struct iovec local;
|
||||
local.iov_base = buf;
|
||||
local.iov_len = static_cast<size_t>(len);
|
||||
|
||||
struct iovec remote;
|
||||
remote.iov_base = reinterpret_cast<void*>(absAddr);
|
||||
remote.iov_len = static_cast<size_t>(len);
|
||||
|
||||
ssize_t nread = process_vm_readv(m_pid, &local, 1, &remote, 1, 0);
|
||||
if (nread == static_cast<ssize_t>(len))
|
||||
return true;
|
||||
|
||||
// Fallback: pread on /proc/<pid>/mem
|
||||
nread = ::pread(m_fd, buf, static_cast<size_t>(len), static_cast<off_t>(absAddr));
|
||||
return nread == static_cast<ssize_t>(len);
|
||||
}
|
||||
|
||||
bool ProcessMemoryProvider::write(uint64_t addr, const void* buf, int len)
|
||||
{
|
||||
if (m_fd < 0 || !m_writable || len <= 0) return false;
|
||||
|
||||
uint64_t absAddr = m_base + addr;
|
||||
|
||||
// Try process_vm_writev first
|
||||
struct iovec local;
|
||||
local.iov_base = const_cast<void*>(buf);
|
||||
local.iov_len = static_cast<size_t>(len);
|
||||
|
||||
struct iovec remote;
|
||||
remote.iov_base = reinterpret_cast<void*>(absAddr);
|
||||
remote.iov_len = static_cast<size_t>(len);
|
||||
|
||||
ssize_t nwritten = process_vm_writev(m_pid, &local, 1, &remote, 1, 0);
|
||||
if (nwritten == static_cast<ssize_t>(len))
|
||||
return true;
|
||||
|
||||
// Fallback: pwrite on /proc/<pid>/mem
|
||||
nwritten = ::pwrite(m_fd, buf, static_cast<size_t>(len), static_cast<off_t>(absAddr));
|
||||
return nwritten == static_cast<ssize_t>(len);
|
||||
}
|
||||
|
||||
QString ProcessMemoryProvider::getSymbol(uint64_t addr) const
|
||||
{
|
||||
for (const auto& mod : m_modules)
|
||||
{
|
||||
if (addr >= mod.base && addr < mod.base + mod.size)
|
||||
{
|
||||
uint64_t offset = addr - mod.base;
|
||||
return QStringLiteral("%1+0x%2")
|
||||
.arg(mod.name)
|
||||
.arg(offset, 0, 16, QChar('0'));
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void ProcessMemoryProvider::cacheModules()
|
||||
{
|
||||
// Parse /proc/<pid>/maps to discover loaded modules
|
||||
QString mapsPath = QStringLiteral("/proc/%1/maps").arg(m_pid);
|
||||
std::ifstream mapsFile(mapsPath.toStdString());
|
||||
if (!mapsFile.is_open()) return;
|
||||
|
||||
// Accumulate base/end per path, then convert to ModuleInfo
|
||||
struct Range { uint64_t base; uint64_t end; };
|
||||
QMap<QString, Range> moduleRanges;
|
||||
|
||||
std::string line;
|
||||
bool firstExec = true;
|
||||
while (std::getline(mapsFile, line))
|
||||
{
|
||||
// Format: addr_start-addr_end perms offset dev inode pathname
|
||||
// Example: 00400000-00452000 r-xp 00000000 08:02 173521 /usr/bin/foo
|
||||
std::istringstream iss(line);
|
||||
std::string addrRange, perms, offset, dev, inode, pathname;
|
||||
iss >> addrRange >> perms >> offset >> dev >> inode;
|
||||
std::getline(iss, pathname);
|
||||
|
||||
// Trim leading whitespace from pathname
|
||||
size_t start = pathname.find_first_not_of(" \t");
|
||||
if (start == std::string::npos) continue;
|
||||
pathname = pathname.substr(start);
|
||||
|
||||
// Skip non-file mappings
|
||||
if (pathname.empty() || pathname[0] != '/') continue;
|
||||
// Skip special mappings
|
||||
if (pathname.find("/dev/") == 0 || pathname.find("/memfd:") == 0) continue;
|
||||
|
||||
// Parse address range
|
||||
auto dash = addrRange.find('-');
|
||||
if (dash == std::string::npos) continue;
|
||||
uint64_t addrStart = std::stoull(addrRange.substr(0, dash), nullptr, 16);
|
||||
uint64_t addrEnd = std::stoull(addrRange.substr(dash + 1), nullptr, 16);
|
||||
|
||||
QString qpath = QString::fromStdString(pathname);
|
||||
|
||||
// Track first executable mapping as the base address
|
||||
if (firstExec && perms.size() >= 3 && perms[2] == 'x')
|
||||
{
|
||||
m_base = addrStart;
|
||||
firstExec = false;
|
||||
}
|
||||
|
||||
auto it = moduleRanges.find(qpath);
|
||||
if (it != moduleRanges.end())
|
||||
{
|
||||
if (addrStart < it->base) it->base = addrStart;
|
||||
if (addrEnd > it->end) it->end = addrEnd;
|
||||
}
|
||||
else
|
||||
{
|
||||
moduleRanges.insert(qpath, {addrStart, addrEnd});
|
||||
}
|
||||
}
|
||||
|
||||
m_modules.reserve(moduleRanges.size());
|
||||
for (auto it = moduleRanges.begin(); it != moduleRanges.end(); ++it)
|
||||
{
|
||||
QFileInfo fi(it.key());
|
||||
m_modules.append({
|
||||
fi.fileName(),
|
||||
it->base,
|
||||
it->end - it->base
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#endif // platform
|
||||
|
||||
ProcessMemoryProvider::~ProcessMemoryProvider()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (m_handle)
|
||||
CloseHandle(m_handle);
|
||||
#elif defined(__linux__)
|
||||
if (m_fd >= 0)
|
||||
::close(m_fd);
|
||||
#endif
|
||||
}
|
||||
|
||||
int ProcessMemoryProvider::size() const
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return m_handle ? INT_MAX : 0;
|
||||
#elif defined(__linux__)
|
||||
return m_fd ? INT_MAX : 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
// ProcessMemoryPlugin implementation
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
@@ -119,27 +325,26 @@ std::unique_ptr<rcx::Provider> ProcessMemoryPlugin::createProvider(const QString
|
||||
// Parse target: "pid:name" or just "pid"
|
||||
QStringList parts = target.split(':');
|
||||
bool ok = false;
|
||||
DWORD pid = parts[0].toUInt(&ok);
|
||||
|
||||
if (!ok || pid == 0) {
|
||||
uint32_t pid = parts[0].toUInt(&ok);
|
||||
|
||||
if (!ok || pid == 0)
|
||||
{
|
||||
if (errorMsg) *errorMsg = "Invalid PID: " + target;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
QString name = parts.size() > 1 ? parts[1] : QString("PID %1").arg(pid);
|
||||
|
||||
|
||||
auto provider = std::make_unique<ProcessMemoryProvider>(pid, name);
|
||||
if (!provider->isValid())
|
||||
{
|
||||
if (errorMsg)
|
||||
{
|
||||
*errorMsg = QString("Failed to open process %1 (PID: %2)\n"
|
||||
"Ensure the process is running and you have sufficient permissions.")
|
||||
.arg(name).arg(pid);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
return provider;
|
||||
}
|
||||
|
||||
@@ -151,26 +356,49 @@ uint64_t ProcessMemoryPlugin::getInitialBaseAddress(const QString& target) const
|
||||
bool ok = false;
|
||||
DWORD pid = parts[0].toUInt(&ok);
|
||||
if (!ok || pid == 0) return 0;
|
||||
|
||||
|
||||
// Open process to get main module base
|
||||
HANDLE hProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
|
||||
if (!hProc) return 0;
|
||||
|
||||
|
||||
uint64_t base = 0;
|
||||
HMODULE hMod = nullptr;
|
||||
DWORD needed = 0;
|
||||
|
||||
|
||||
if (EnumProcessModulesEx(hProc, &hMod, sizeof(hMod), &needed, LIST_MODULES_ALL) && hMod)
|
||||
{
|
||||
MODULEINFO mi{};
|
||||
if (GetModuleInformation(hProc, hMod, &mi, sizeof(mi)))
|
||||
{
|
||||
base = (uint64_t)mi.lpBaseOfDll;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CloseHandle(hProc);
|
||||
return base;
|
||||
#elif defined(__linux__)
|
||||
// Parse PID from target
|
||||
QStringList parts = target.split(':');
|
||||
bool ok = false;
|
||||
uint32_t pid = parts[0].toUInt(&ok);
|
||||
if (!ok || pid == 0) return 0;
|
||||
|
||||
// Find first executable mapping from /proc/<pid>/maps
|
||||
QString mapsPath = QStringLiteral("/proc/%1/maps").arg(pid);
|
||||
std::ifstream mapsFile(mapsPath.toStdString());
|
||||
if (!mapsFile.is_open()) return 0;
|
||||
|
||||
std::string line;
|
||||
while (std::getline(mapsFile, line)) {
|
||||
std::istringstream iss(line);
|
||||
std::string addrRange, perms;
|
||||
iss >> addrRange >> perms;
|
||||
if (perms.size() >= 3 && perms[2] == 'x') {
|
||||
auto dash = addrRange.find('-');
|
||||
if (dash != std::string::npos) {
|
||||
return std::stoull(addrRange.substr(0, dash), nullptr, 16);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
#else
|
||||
Q_UNUSED(target);
|
||||
return 0;
|
||||
@@ -181,7 +409,7 @@ bool ProcessMemoryPlugin::selectTarget(QWidget* parent, QString* target)
|
||||
{
|
||||
// Use custom process enumeration from plugin
|
||||
QVector<PluginProcessInfo> pluginProcesses = enumerateProcesses();
|
||||
|
||||
|
||||
// Convert to ProcessInfo for ProcessPicker
|
||||
QList<ProcessInfo> processes;
|
||||
for (const auto& pinfo : pluginProcesses)
|
||||
@@ -193,50 +421,50 @@ bool ProcessMemoryPlugin::selectTarget(QWidget* parent, QString* target)
|
||||
info.icon = pinfo.icon;
|
||||
processes.append(info);
|
||||
}
|
||||
|
||||
|
||||
// Show ProcessPicker with custom process list
|
||||
ProcessPicker picker(processes, parent);
|
||||
if (picker.exec() == QDialog::Accepted) {
|
||||
uint32_t pid = picker.selectedProcessId();
|
||||
QString name = picker.selectedProcessName();
|
||||
|
||||
|
||||
// Format target as "pid:name"
|
||||
*target = QString("%1:%2").arg(pid).arg(name);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QVector<PluginProcessInfo> ProcessMemoryPlugin::enumerateProcesses()
|
||||
{
|
||||
QVector<PluginProcessInfo> processes;
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||
if (snapshot == INVALID_HANDLE_VALUE) {
|
||||
return processes;
|
||||
}
|
||||
|
||||
|
||||
PROCESSENTRY32W entry;
|
||||
entry.dwSize = sizeof(entry);
|
||||
|
||||
|
||||
if (Process32FirstW(snapshot, &entry)) {
|
||||
do {
|
||||
PluginProcessInfo info;
|
||||
info.pid = entry.th32ProcessID;
|
||||
info.name = QString::fromWCharArray(entry.szExeFile);
|
||||
|
||||
|
||||
// Try to get full path and icon
|
||||
HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, entry.th32ProcessID);
|
||||
if (hProcess) {
|
||||
wchar_t path[MAX_PATH * 2];
|
||||
DWORD pathLen = sizeof(path) / sizeof(wchar_t);
|
||||
|
||||
|
||||
// Try QueryFullProcessImageNameW first
|
||||
if (QueryFullProcessImageNameW(hProcess, 0, path, &pathLen)) {
|
||||
info.path = QString::fromWCharArray(path);
|
||||
|
||||
|
||||
// Extract icon
|
||||
SHFILEINFOW sfi = {};
|
||||
if (SHGetFileInfoW(path, 0, &sfi, sizeof(sfi), SHGFI_ICON | SHGFI_SMALLICON)) {
|
||||
@@ -247,18 +475,57 @@ QVector<PluginProcessInfo> ProcessMemoryPlugin::enumerateProcesses()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CloseHandle(hProcess);
|
||||
}
|
||||
|
||||
|
||||
processes.append(info);
|
||||
|
||||
|
||||
} while (Process32NextW(snapshot, &entry));
|
||||
}
|
||||
|
||||
|
||||
CloseHandle(snapshot);
|
||||
#elif defined(__linux__)
|
||||
QDir procDir("/proc");
|
||||
QStringList entries = procDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
|
||||
QIcon defaultIcon = qApp->style()->standardIcon(QStyle::SP_ComputerIcon);
|
||||
|
||||
for (const QString& entry : entries) {
|
||||
bool ok = false;
|
||||
uint32_t pid = entry.toUInt(&ok);
|
||||
if (!ok || pid == 0) continue;
|
||||
|
||||
// Read process name from /proc/<pid>/comm
|
||||
QString commPath = QStringLiteral("/proc/%1/comm").arg(pid);
|
||||
QFile commFile(commPath);
|
||||
QString procName;
|
||||
if (commFile.open(QIODevice::ReadOnly)) {
|
||||
procName = QString::fromUtf8(commFile.readAll()).trimmed();
|
||||
commFile.close();
|
||||
}
|
||||
if (procName.isEmpty()) continue; // Skip kernel threads with no name
|
||||
|
||||
// Read exe path from /proc/<pid>/exe symlink
|
||||
QString exePath = QStringLiteral("/proc/%1/exe").arg(pid);
|
||||
QFileInfo exeInfo(exePath);
|
||||
QString resolvedPath;
|
||||
if (exeInfo.exists())
|
||||
resolvedPath = exeInfo.symLinkTarget();
|
||||
|
||||
// Skip if we can't read the process memory (no access)
|
||||
QString memPath = QStringLiteral("/proc/%1/mem").arg(pid);
|
||||
if (::access(memPath.toUtf8().constData(), R_OK) != 0)
|
||||
continue;
|
||||
|
||||
PluginProcessInfo info;
|
||||
info.pid = pid;
|
||||
info.name = procName;
|
||||
info.path = resolvedPath;
|
||||
info.icon = defaultIcon;
|
||||
processes.append(info);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
return processes;
|
||||
}
|
||||
|
||||
@@ -266,7 +533,7 @@ QVector<PluginProcessInfo> ProcessMemoryPlugin::enumerateProcesses()
|
||||
// Plugin factory
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
|
||||
extern "C" __declspec(dllexport) IPlugin* CreatePlugin()
|
||||
extern "C" RCX_PLUGIN_EXPORT IPlugin* CreatePlugin()
|
||||
{
|
||||
return new ProcessMemoryPlugin();
|
||||
}
|
||||
|
||||
@@ -1,42 +1,45 @@
|
||||
#pragma once
|
||||
#include "../../src/iplugin.h"
|
||||
#include "../../src/core.h"
|
||||
#include <windows.h>
|
||||
#include <tlhelp32.h>
|
||||
#include <psapi.h>
|
||||
#include <shellapi.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
/**
|
||||
* Windows process memory provider
|
||||
* Reads/writes memory from a live process using Win32 API
|
||||
* Process memory provider
|
||||
* Reads/writes memory from a live process using platform APIs
|
||||
*/
|
||||
class ProcessMemoryProvider : public rcx::Provider {
|
||||
class ProcessMemoryProvider : public rcx::Provider
|
||||
{
|
||||
public:
|
||||
ProcessMemoryProvider(DWORD pid, const QString& processName);
|
||||
ProcessMemoryProvider(uint32_t pid, const QString& processName);
|
||||
~ProcessMemoryProvider() override;
|
||||
|
||||
|
||||
// Required overrides
|
||||
bool read(uint64_t addr, void* buf, int len) const override;
|
||||
int size() const override { return m_handle ? INT_MAX : NULL; } // Process memory has no fixed size
|
||||
|
||||
int size() const override;
|
||||
|
||||
// Optional overrides
|
||||
bool write(uint64_t addr, const void* buf, int len) override;
|
||||
bool isWritable() const override { return m_writable; }
|
||||
QString name() const override { return m_processName; }
|
||||
QString kind() const override { return QStringLiteral("LocalProcess"); }
|
||||
QString getSymbol(uint64_t addr) const override;
|
||||
|
||||
|
||||
// Process-specific helpers
|
||||
DWORD pid() const { return m_pid; }
|
||||
uint32_t pid() const { return m_pid; }
|
||||
uint64_t baseAddress() const { return m_base; }
|
||||
void refreshModules() { m_modules.clear(); cacheModules(); }
|
||||
|
||||
private:
|
||||
void cacheModules();
|
||||
|
||||
|
||||
private:
|
||||
HANDLE m_handle;
|
||||
DWORD m_pid;
|
||||
#ifdef _WIN32
|
||||
void* m_handle;
|
||||
#elif defined(__linux__)
|
||||
int m_fd;
|
||||
#endif
|
||||
uint32_t m_pid;
|
||||
QString m_processName;
|
||||
bool m_writable;
|
||||
uint64_t m_base;
|
||||
@@ -52,24 +55,25 @@ private:
|
||||
/**
|
||||
* Plugin that provides ProcessMemoryProvider
|
||||
*/
|
||||
class ProcessMemoryPlugin : public IProviderPlugin {
|
||||
class ProcessMemoryPlugin : public IProviderPlugin
|
||||
{
|
||||
public:
|
||||
std::string Name() const override { return "Process Memory"; }
|
||||
std::string Version() const override { return "1.0.0"; }
|
||||
std::string Author() const override { return "ReclassX"; }
|
||||
std::string Description() const override { return "Read and write memory from local running Windows processes"; }
|
||||
std::string Description() const override { return "Read and write memory from local running processes"; }
|
||||
k_ELoadType LoadType() const override { return k_ELoadTypeAuto; }
|
||||
QIcon Icon() const override;
|
||||
|
||||
|
||||
bool canHandle(const QString& target) const override;
|
||||
std::unique_ptr<rcx::Provider> createProvider(const QString& target, QString* errorMsg) override;
|
||||
uint64_t getInitialBaseAddress(const QString& target) const override;
|
||||
bool selectTarget(QWidget* parent, QString* target) override;
|
||||
|
||||
|
||||
// Optional: provide custom process list
|
||||
bool providesProcessList() const override { return true; }
|
||||
QVector<PluginProcessInfo> enumerateProcesses() override;
|
||||
};
|
||||
|
||||
// Plugin export
|
||||
extern "C" __declspec(dllexport) IPlugin* CreatePlugin();
|
||||
extern "C" RCX_PLUGIN_EXPORT IPlugin* CreatePlugin();
|
||||
|
||||
Reference in New Issue
Block a user