mirror of
https://github.com/NohamR/Reclass.git
synced 2026-05-10 19:59:21 +00:00
Provider refactor: 2-method base class, ProcessProvider, ProcessPicker
Collapse Provider interface from 9 virtual methods to 2 (read + size), move providers to src/providers/, add name()/kind()/getSymbol() virtuals. Replace FileProvider with BufferProvider, add ProcessProvider (Win32) with module-based symbol resolution, wire ProcessPicker dialog, and integrate getSymbol into pointer display and command row. - Fix isReadable overflow for large addresses - Guard deferred showSourcePicker/showTypeAutocomplete against stale edits - 7/7 tests pass including 3 new provider test suites Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
47
src/providers/buffer_provider.h
Normal file
47
src/providers/buffer_provider.h
Normal file
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
#include "provider.h"
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
|
||||
namespace rcx {
|
||||
|
||||
class BufferProvider : public Provider {
|
||||
QByteArray m_data;
|
||||
QString m_name;
|
||||
|
||||
public:
|
||||
explicit BufferProvider(QByteArray data, const QString& name = {})
|
||||
: m_data(std::move(data))
|
||||
, m_name(name) {}
|
||||
|
||||
static BufferProvider fromFile(const QString& path) {
|
||||
QFile f(path);
|
||||
if (f.open(QIODevice::ReadOnly))
|
||||
return BufferProvider(f.readAll(), QFileInfo(path).fileName());
|
||||
return BufferProvider({});
|
||||
}
|
||||
|
||||
int size() const override { return m_data.size(); }
|
||||
|
||||
bool read(uint64_t addr, void* buf, int len) const override {
|
||||
if (!isReadable(addr, len)) return false;
|
||||
std::memcpy(buf, m_data.constData() + addr, len);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isWritable() const override { return true; }
|
||||
|
||||
bool write(uint64_t addr, const void* buf, int len) override {
|
||||
if (!isReadable(addr, len)) return false;
|
||||
std::memcpy(m_data.data() + addr, buf, len);
|
||||
return true;
|
||||
}
|
||||
|
||||
QString name() const override { return m_name; }
|
||||
QString kind() const override { return QStringLiteral("File"); }
|
||||
|
||||
const QByteArray& data() const { return m_data; }
|
||||
QByteArray& data() { return m_data; }
|
||||
};
|
||||
|
||||
} // namespace rcx
|
||||
14
src/providers/null_provider.h
Normal file
14
src/providers/null_provider.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
#include "provider.h"
|
||||
|
||||
namespace rcx {
|
||||
|
||||
class NullProvider : public Provider {
|
||||
public:
|
||||
int size() const override { return 0; }
|
||||
bool read(uint64_t, void*, int) const override { return false; }
|
||||
// name() returns "" via base default -- triggers <Select Source> in command row
|
||||
// kind() returns "File" via base default
|
||||
};
|
||||
|
||||
} // namespace rcx
|
||||
102
src/providers/process_provider.h
Normal file
102
src/providers/process_provider.h
Normal file
@@ -0,0 +1,102 @@
|
||||
#pragma once
|
||||
#include "provider.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <psapi.h>
|
||||
|
||||
namespace rcx {
|
||||
|
||||
class ProcessProvider : public Provider {
|
||||
HANDLE m_handle = nullptr;
|
||||
uint64_t m_base = 0;
|
||||
int m_size = 0;
|
||||
QString m_name;
|
||||
|
||||
struct ModuleInfo {
|
||||
QString name;
|
||||
uint64_t base;
|
||||
uint64_t size;
|
||||
};
|
||||
QVector<ModuleInfo> m_modules;
|
||||
|
||||
public:
|
||||
ProcessProvider(HANDLE proc, uint64_t base, int regionSize, const QString& name)
|
||||
: m_handle(proc), m_base(base), m_size(regionSize), m_name(name)
|
||||
{
|
||||
cacheModules();
|
||||
}
|
||||
|
||||
~ProcessProvider() override {
|
||||
if (m_handle) CloseHandle(m_handle);
|
||||
}
|
||||
|
||||
ProcessProvider(const ProcessProvider&) = delete;
|
||||
ProcessProvider& operator=(const ProcessProvider&) = delete;
|
||||
|
||||
int size() const override { return m_size; }
|
||||
|
||||
bool read(uint64_t addr, void* buf, int len) const override {
|
||||
SIZE_T got = 0;
|
||||
BOOL ok = ReadProcessMemory(m_handle,
|
||||
(LPCVOID)(m_base + addr), buf, len, &got);
|
||||
return ok && (int)got == len;
|
||||
}
|
||||
|
||||
bool isWritable() const override { return true; }
|
||||
|
||||
bool write(uint64_t addr, const void* buf, int len) override {
|
||||
SIZE_T got = 0;
|
||||
BOOL ok = WriteProcessMemory(m_handle,
|
||||
(LPVOID)(m_base + addr), buf, len, &got);
|
||||
return ok && (int)got == len;
|
||||
}
|
||||
|
||||
QString name() const override { return m_name; }
|
||||
QString kind() const override { return QStringLiteral("Process"); }
|
||||
|
||||
// getSymbol takes an absolute virtual address and resolves it to
|
||||
// "module.dll+0xOFFSET" using the cached module list.
|
||||
QString getSymbol(uint64_t absAddr) const override {
|
||||
for (const auto& mod : m_modules) {
|
||||
if (absAddr >= mod.base && absAddr < mod.base + mod.size) {
|
||||
uint64_t offset = absAddr - mod.base;
|
||||
return QStringLiteral("%1+0x%2")
|
||||
.arg(mod.name)
|
||||
.arg(offset, 0, 16, QChar('0'));
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
HANDLE handle() const { return m_handle; }
|
||||
uint64_t baseAddress() const { return m_base; }
|
||||
void refreshModules() { m_modules.clear(); cacheModules(); }
|
||||
|
||||
private:
|
||||
void cacheModules() {
|
||||
HMODULE mods[1024];
|
||||
DWORD needed = 0;
|
||||
if (!EnumProcessModulesEx(m_handle, mods, sizeof(mods),
|
||||
&needed, LIST_MODULES_ALL))
|
||||
return;
|
||||
int count = qMin((int)(needed / sizeof(HMODULE)), 1024);
|
||||
m_modules.reserve(count);
|
||||
for (int i = 0; i < count; ++i) {
|
||||
MODULEINFO mi{};
|
||||
WCHAR modName[MAX_PATH];
|
||||
if (GetModuleInformation(m_handle, mods[i], &mi, sizeof(mi))
|
||||
&& GetModuleBaseNameW(m_handle, mods[i], modName, MAX_PATH))
|
||||
{
|
||||
m_modules.append({
|
||||
QString::fromWCharArray(modName),
|
||||
(uint64_t)mi.lpBaseOfDll,
|
||||
(uint64_t)mi.SizeOfImage
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace rcx
|
||||
#endif // _WIN32
|
||||
78
src/providers/provider.h
Normal file
78
src/providers/provider.h
Normal file
@@ -0,0 +1,78 @@
|
||||
#pragma once
|
||||
#include <QByteArray>
|
||||
#include <QString>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
namespace rcx {
|
||||
|
||||
class Provider {
|
||||
public:
|
||||
virtual ~Provider() = default;
|
||||
|
||||
// --- Subclasses MUST implement these two ---
|
||||
virtual bool read(uint64_t addr, void* buf, int len) const = 0;
|
||||
virtual int size() const = 0;
|
||||
|
||||
// --- Optional overrides ---
|
||||
virtual bool write(uint64_t addr, const void* buf, int len) {
|
||||
Q_UNUSED(addr); Q_UNUSED(buf); Q_UNUSED(len);
|
||||
return false;
|
||||
}
|
||||
virtual bool isWritable() const { return false; }
|
||||
|
||||
// Human-readable label for this source.
|
||||
// Examples: "notepad.exe", "dump.bin", "tcp://10.0.0.1:1337"
|
||||
virtual QString name() const { return {}; }
|
||||
|
||||
// Category tag for the command row Source span.
|
||||
// Examples: "File", "Process", "Socket"
|
||||
virtual QString kind() const { return QStringLiteral("File"); }
|
||||
|
||||
// Resolve an absolute address to a symbol name.
|
||||
// Returns empty string if no symbol is known.
|
||||
// ProcessProvider: "ntdll.dll+0x1A30"
|
||||
// BufferProvider: "" (no symbols in flat files)
|
||||
virtual QString getSymbol(uint64_t addr) const {
|
||||
Q_UNUSED(addr);
|
||||
return {};
|
||||
}
|
||||
|
||||
// --- Derived convenience (non-virtual, never override) ---
|
||||
|
||||
bool isValid() const { return size() > 0; }
|
||||
|
||||
bool isReadable(uint64_t addr, int len) const {
|
||||
if (len <= 0) return (len == 0);
|
||||
uint64_t ulen = (uint64_t)len;
|
||||
return addr <= (uint64_t)size() && ulen <= (uint64_t)size() - addr;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T readAs(uint64_t addr) const {
|
||||
T v{};
|
||||
read(addr, &v, sizeof(T));
|
||||
return v;
|
||||
}
|
||||
|
||||
uint8_t readU8 (uint64_t a) const { return readAs<uint8_t>(a); }
|
||||
uint16_t readU16(uint64_t a) const { return readAs<uint16_t>(a); }
|
||||
uint32_t readU32(uint64_t a) const { return readAs<uint32_t>(a); }
|
||||
uint64_t readU64(uint64_t a) const { return readAs<uint64_t>(a); }
|
||||
float readF32(uint64_t a) const { return readAs<float>(a); }
|
||||
double readF64(uint64_t a) const { return readAs<double>(a); }
|
||||
|
||||
QByteArray readBytes(uint64_t addr, int len) const {
|
||||
if (len <= 0) return {};
|
||||
QByteArray buf(len, Qt::Uninitialized);
|
||||
if (!read(addr, buf.data(), len))
|
||||
buf.fill('\0');
|
||||
return buf;
|
||||
}
|
||||
|
||||
bool writeBytes(uint64_t addr, const QByteArray& d) {
|
||||
return write(addr, d.constData(), d.size());
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace rcx
|
||||
Reference in New Issue
Block a user