mirror of
https://github.com/NohamR/Reclass.git
synced 2026-05-10 19:59:21 +00:00
fix: commit remaining uncommitted source changes for CI
Add extractPdbSymbols declaration to import_pdb.h, enumerateModules to provider.h, and other pending changes that were only local.
This commit is contained in:
@@ -185,8 +185,14 @@ void ProcessMemoryProvider::cacheModules()
|
||||
if ( i == 0 )
|
||||
m_base = (uint64_t)mi.lpBaseOfDll;
|
||||
|
||||
WCHAR modPath[MAX_PATH];
|
||||
QString fullPath;
|
||||
if (GetModuleFileNameExW(m_handle, mods[i], modPath, MAX_PATH))
|
||||
fullPath = QString::fromWCharArray(modPath);
|
||||
|
||||
m_modules.append({
|
||||
QString::fromWCharArray(modName),
|
||||
fullPath,
|
||||
(uint64_t)mi.lpBaseOfDll,
|
||||
(uint64_t)mi.SizeOfImage
|
||||
});
|
||||
@@ -194,6 +200,15 @@ void ProcessMemoryProvider::cacheModules()
|
||||
}
|
||||
}
|
||||
|
||||
QVector<rcx::Provider::ModuleEntry> ProcessMemoryProvider::enumerateModules() const
|
||||
{
|
||||
QVector<ModuleEntry> result;
|
||||
result.reserve(m_modules.size());
|
||||
for (const auto& m : m_modules)
|
||||
result.append({m.name, m.fullPath, m.base, m.size});
|
||||
return result;
|
||||
}
|
||||
|
||||
QVector<rcx::MemoryRegion> ProcessMemoryProvider::enumerateRegions() const
|
||||
{
|
||||
QVector<rcx::MemoryRegion> regions;
|
||||
|
||||
@@ -43,6 +43,7 @@ public:
|
||||
void refreshModules() { m_modules.clear(); cacheModules(); }
|
||||
uint64_t peb() const override { return m_peb; }
|
||||
QVector<ThreadInfo> tebs() const override;
|
||||
QVector<ModuleEntry> enumerateModules() const override;
|
||||
|
||||
private:
|
||||
void cacheModules();
|
||||
@@ -62,6 +63,7 @@ private:
|
||||
|
||||
struct ModuleInfo {
|
||||
QString name;
|
||||
QString fullPath;
|
||||
uint64_t base;
|
||||
uint64_t size;
|
||||
};
|
||||
|
||||
@@ -273,17 +273,32 @@ private:
|
||||
// Identifier or hex literal disambiguation.
|
||||
// Scan [a-zA-Z_][a-zA-Z0-9_]*. If it contains any non-hex char → identifier.
|
||||
// Otherwise → backtrack and parse as hex number.
|
||||
// WinDbg-style "module!symbol" is scanned as a single identifier token.
|
||||
// If the identifier is followed by '(', try to parse as a built-in function call.
|
||||
bool parseIdentifierOrHex(uint64_t& result) {
|
||||
int start = m_pos;
|
||||
bool hasNonHex = false;
|
||||
|
||||
// Scan full token
|
||||
// Scan full token, including "module!symbol" as one token
|
||||
while (!atEnd() && isIdentChar(peek())) {
|
||||
if (!isHexDigit(peek()))
|
||||
hasNonHex = true;
|
||||
advance();
|
||||
}
|
||||
// If we hit '!' and the next char is an identifier start, extend the token
|
||||
// to include the second part (WinDbg module!symbol syntax)
|
||||
if (!atEnd() && peek() == '!' && m_pos > start) {
|
||||
int bangPos = m_pos;
|
||||
advance(); // skip '!'
|
||||
if (!atEnd() && isIdentStart(peek())) {
|
||||
hasNonHex = true;
|
||||
while (!atEnd() && isIdentChar(peek())) {
|
||||
advance();
|
||||
}
|
||||
} else {
|
||||
m_pos = bangPos; // backtrack — '!' at end isn't module!symbol
|
||||
}
|
||||
}
|
||||
|
||||
QString token = m_input.mid(start, m_pos - start);
|
||||
|
||||
|
||||
@@ -71,6 +71,7 @@ struct ComposeState {
|
||||
bool treeLines = false; // draw Unicode tree connectors in indentation
|
||||
bool braceWrap = false; // opening brace on its own line
|
||||
bool typeHints = false; // show type inference hints on hex nodes
|
||||
SymbolLookupFn symbolLookup; // optional PDB symbol lookup callback
|
||||
QVector<bool> siblingStack; // per-depth: true = more siblings follow at this level
|
||||
uint64_t currentPtrBase = 0; // absolute addr of current pointer expansion target
|
||||
|
||||
@@ -262,7 +263,7 @@ void composeLeaf(ComposeState& state, const NodeTree& tree,
|
||||
auto suggestions = inferTypes(
|
||||
reinterpret_cast<const uint8_t*>(b.constData()), sz);
|
||||
if (!suggestions.isEmpty() && suggestions[0].strength >= 3) {
|
||||
lm.typeHintStart = lineText.size() + 2; // after " " gap
|
||||
lm.typeHintStart = kFoldCol + lineText.size() + 2; // after fold prefix + " " gap
|
||||
lm.typeHintKinds = suggestions[0].kinds;
|
||||
QString typeName = formatHint(suggestions[0]);
|
||||
QString preview = formatPreview(
|
||||
@@ -276,6 +277,13 @@ void composeLeaf(ComposeState& state, const NodeTree& tree,
|
||||
}
|
||||
}
|
||||
|
||||
// PDB symbol annotation: show symbol name if this address matches a loaded symbol
|
||||
if (sub == 0 && state.symbolLookup) {
|
||||
QString sym = state.symbolLookup(absAddr);
|
||||
if (!sym.isEmpty())
|
||||
lineText += QStringLiteral(" // ") + sym;
|
||||
}
|
||||
|
||||
state.emitLine(lineText, std::move(lm));
|
||||
}
|
||||
}
|
||||
@@ -1087,12 +1095,13 @@ void composeNode(ComposeState& state, const NodeTree& tree,
|
||||
|
||||
ComposeResult compose(const NodeTree& tree, const Provider& prov, uint64_t viewRootId,
|
||||
bool compactColumns, bool treeLines, bool braceWrap,
|
||||
bool typeHints) {
|
||||
bool typeHints, SymbolLookupFn symbolLookup) {
|
||||
ComposeState state;
|
||||
state.compactColumns = compactColumns;
|
||||
state.treeLines = treeLines;
|
||||
state.braceWrap = braceWrap;
|
||||
state.typeHints = typeHints;
|
||||
state.symbolLookup = std::move(symbolLookup);
|
||||
|
||||
// Precompute parent→children map
|
||||
for (int i = 0; i < tree.nodes.size(); i++)
|
||||
|
||||
@@ -42,7 +42,8 @@ public:
|
||||
|
||||
ComposeResult compose(uint64_t viewRootId = 0, bool compactColumns = false,
|
||||
bool treeLines = false, bool braceWrap = false,
|
||||
bool typeHints = false) const;
|
||||
bool typeHints = false,
|
||||
SymbolLookupFn symbolLookup = {}) const;
|
||||
bool save(const QString& path);
|
||||
bool load(const QString& path);
|
||||
void loadData(const QString& binaryPath);
|
||||
|
||||
@@ -5,6 +5,25 @@
|
||||
|
||||
namespace rcx {
|
||||
|
||||
// ── PDB Symbol Extraction ──
|
||||
|
||||
struct PdbSymbol {
|
||||
QString name;
|
||||
uint32_t rva;
|
||||
};
|
||||
|
||||
struct PdbSymbolResult {
|
||||
QString moduleName; // derived from PDB filename (e.g. "ntoskrnl")
|
||||
QVector<PdbSymbol> symbols;
|
||||
};
|
||||
|
||||
// Extract public/global symbols (name → RVA) from a PDB file.
|
||||
// This reads the DBI stream's public and global symbol sub-streams.
|
||||
PdbSymbolResult extractPdbSymbols(const QString& pdbPath,
|
||||
QString* errorMsg = nullptr);
|
||||
|
||||
// ── PDB Type Import ──
|
||||
|
||||
struct PdbTypeInfo {
|
||||
uint32_t typeIndex; // TPI type index
|
||||
QString name; // struct/class/union/enum name
|
||||
|
||||
57
src/main.cpp
57
src/main.cpp
@@ -5165,12 +5165,40 @@ void MainWindow::createSymbolsDock() {
|
||||
m_symbolsTree->setExpandsOnDoubleClick(false);
|
||||
styleTree(m_symbolsTree);
|
||||
|
||||
// Debounced search
|
||||
// Populate a module item's children (replaces sentinel with real symbols)
|
||||
auto populateModuleItem = [this](QStandardItem* item) {
|
||||
if (!item || item->parent()) return;
|
||||
if (item->rowCount() == 1 && item->child(0)->text().isEmpty()) {
|
||||
item->removeRows(0, 1);
|
||||
QString moduleName = item->data(Qt::UserRole).toString();
|
||||
const auto* set = rcx::SymbolStore::instance().moduleData(moduleName);
|
||||
if (set) {
|
||||
static const QIcon symIcon(":/vsicons/symbol-method.svg");
|
||||
for (const auto& sym : set->rvaToName) {
|
||||
auto* child = new QStandardItem(symIcon,
|
||||
QStringLiteral("%1 [0x%2]")
|
||||
.arg(sym.second)
|
||||
.arg(sym.first, 8, 16, QLatin1Char('0')));
|
||||
child->setData(moduleName, Qt::UserRole);
|
||||
child->setData(sym.first, Qt::UserRole + 1);
|
||||
child->setData(sym.second, Qt::UserRole + 2);
|
||||
item->appendRow(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Debounced search — force-populate all modules so filter can match children
|
||||
auto* searchTimer = new QTimer(this);
|
||||
searchTimer->setSingleShot(true);
|
||||
searchTimer->setInterval(150);
|
||||
connect(searchTimer, &QTimer::timeout, this, [this]() {
|
||||
connect(searchTimer, &QTimer::timeout, this, [this, populateModuleItem]() {
|
||||
QString text = m_symbolsSearch->text();
|
||||
if (!text.isEmpty()) {
|
||||
// Force-populate all modules that still have sentinels
|
||||
for (int i = 0; i < m_symbolsModel->rowCount(); i++)
|
||||
populateModuleItem(m_symbolsModel->item(i));
|
||||
}
|
||||
m_symbolsProxy->setFilterFixedString(text);
|
||||
if (!text.isEmpty())
|
||||
m_symbolsTree->expandAll();
|
||||
@@ -5184,30 +5212,9 @@ void MainWindow::createSymbolsDock() {
|
||||
symLayout->addWidget(m_symbolsTree);
|
||||
|
||||
// Lazy-load children when a module node is expanded
|
||||
connect(m_symbolsTree, &QTreeView::expanded, this, [this](const QModelIndex& proxyIdx) {
|
||||
connect(m_symbolsTree, &QTreeView::expanded, this, [this, populateModuleItem](const QModelIndex& proxyIdx) {
|
||||
QModelIndex srcIdx = m_symbolsProxy->mapToSource(proxyIdx);
|
||||
auto* item = m_symbolsModel->itemFromIndex(srcIdx);
|
||||
if (!item || item->parent()) return;
|
||||
|
||||
if (item->rowCount() == 1 && item->child(0)->text().isEmpty()) {
|
||||
item->removeRows(0, 1);
|
||||
QString moduleName = item->data(Qt::UserRole).toString();
|
||||
const auto* set = rcx::SymbolStore::instance().moduleData(moduleName);
|
||||
if (set) {
|
||||
for (const auto& sym : set->rvaToName) {
|
||||
auto* child = new QStandardItem(
|
||||
QStringLiteral("%1 [0x%2]")
|
||||
.arg(sym.second)
|
||||
.arg(sym.first, 8, 16, QLatin1Char('0')));
|
||||
child->setData(moduleName, Qt::UserRole);
|
||||
child->setData(sym.first, Qt::UserRole + 1);
|
||||
child->setData(sym.second, Qt::UserRole + 2);
|
||||
static const QIcon symIcon(":/vsicons/symbol-method.svg");
|
||||
child->setIcon(symIcon);
|
||||
item->appendRow(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
populateModuleItem(m_symbolsModel->itemFromIndex(srcIdx));
|
||||
});
|
||||
|
||||
// Double-click symbol → navigate to moduleBase + RVA
|
||||
|
||||
@@ -87,6 +87,9 @@ public:
|
||||
struct ThreadInfo { uint64_t tebAddress; uint32_t threadId; };
|
||||
virtual QVector<ThreadInfo> tebs() const { return {}; }
|
||||
|
||||
struct ModuleEntry { QString name; QString fullPath; uint64_t base; uint64_t size; };
|
||||
virtual QVector<ModuleEntry> enumerateModules() const { return {}; }
|
||||
|
||||
// --- Kernel paging capabilities (override in kernel providers) ---
|
||||
virtual bool hasKernelPaging() const { return false; }
|
||||
virtual uint64_t getCr3() const { return 0; }
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
<file alias="file-binary.svg">vsicons/file-binary.svg</file>
|
||||
<file alias="debug.svg">vsicons/debug.svg</file>
|
||||
<file alias="close.svg">vsicons/close.svg</file>
|
||||
<file alias="cloud-download.svg">vsicons/cloud-download.svg</file>
|
||||
<file alias="arrow-left.svg">vsicons/arrow-left.svg</file>
|
||||
<file alias="arrow-right.svg">vsicons/arrow-right.svg</file>
|
||||
<file alias="split-horizontal.svg">vsicons/split-horizontal.svg</file>
|
||||
@@ -55,6 +56,7 @@
|
||||
<file alias="symbol-enum.svg">vsicons/symbol-enum.svg</file>
|
||||
<file alias="symbol-class.svg">vsicons/symbol-class.svg</file>
|
||||
<file alias="symbol-variable.svg">vsicons/symbol-variable.svg</file>
|
||||
<file alias="symbol-method.svg">vsicons/symbol-method.svg</file>
|
||||
<file alias="server-process.svg">vsicons/server-process.svg</file>
|
||||
<file alias="remote.svg">vsicons/remote.svg</file>
|
||||
<file alias="plug.svg">vsicons/plug.svg</file>
|
||||
|
||||
Reference in New Issue
Block a user