mirror of
https://github.com/NohamR/Reclass.git
synced 2026-05-10 19:59:21 +00:00
fix: replace remaining QList::append({}) in plugins and tests
Missed plugin and test directories in the previous Qt 6.8 compat fix.
This commit is contained in:
@@ -222,7 +222,7 @@ QVector<rcx::Provider::ThreadInfo> KernelProcessProvider::tebs() const
|
|||||||
auto* entries = reinterpret_cast<const RcxDrvTebEntry*>(outBuf.constData());
|
auto* entries = reinterpret_cast<const RcxDrvTebEntry*>(outBuf.constData());
|
||||||
|
|
||||||
for (int i = 0; i < count; ++i)
|
for (int i = 0; i < count; ++i)
|
||||||
result.append({entries[i].tebAddress, entries[i].threadId});
|
result.push_back(ThreadInfo{entries[i].tebAddress, entries[i].threadId});
|
||||||
#endif
|
#endif
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -253,7 +253,7 @@ void KernelProcessProvider::cacheModules()
|
|||||||
if (i == 0)
|
if (i == 0)
|
||||||
m_base = entries[i].base;
|
m_base = entries[i].base;
|
||||||
|
|
||||||
m_modules.append({modName, entries[i].base, entries[i].size});
|
m_modules.push_back(ModuleInfo{modName, entries[i].base, entries[i].size});
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -190,7 +190,7 @@ void ProcessMemoryProvider::cacheModules()
|
|||||||
if (GetModuleFileNameExW(m_handle, mods[i], modPath, MAX_PATH))
|
if (GetModuleFileNameExW(m_handle, mods[i], modPath, MAX_PATH))
|
||||||
fullPath = QString::fromWCharArray(modPath);
|
fullPath = QString::fromWCharArray(modPath);
|
||||||
|
|
||||||
m_modules.append({
|
m_modules.push_back(ModuleInfo{
|
||||||
QString::fromWCharArray(modName),
|
QString::fromWCharArray(modName),
|
||||||
fullPath,
|
fullPath,
|
||||||
(uint64_t)mi.lpBaseOfDll,
|
(uint64_t)mi.lpBaseOfDll,
|
||||||
@@ -205,7 +205,7 @@ QVector<rcx::Provider::ModuleEntry> ProcessMemoryProvider::enumerateModules() co
|
|||||||
QVector<ModuleEntry> result;
|
QVector<ModuleEntry> result;
|
||||||
result.reserve(m_modules.size());
|
result.reserve(m_modules.size());
|
||||||
for (const auto& m : m_modules)
|
for (const auto& m : m_modules)
|
||||||
result.append({m.name, m.fullPath, m.base, m.size});
|
result.push_back(ModuleEntry{m.name, m.fullPath, m.base, m.size});
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -415,8 +415,9 @@ void ProcessMemoryProvider::cacheModules()
|
|||||||
for (auto it = moduleRanges.begin(); it != moduleRanges.end(); ++it)
|
for (auto it = moduleRanges.begin(); it != moduleRanges.end(); ++it)
|
||||||
{
|
{
|
||||||
QFileInfo fi(it.key());
|
QFileInfo fi(it.key());
|
||||||
m_modules.append({
|
m_modules.push_back(ModuleInfo{
|
||||||
fi.fileName(),
|
fi.fileName(),
|
||||||
|
it.key(),
|
||||||
it->base,
|
it->base,
|
||||||
it->end - it->base
|
it->end - it->base
|
||||||
});
|
});
|
||||||
@@ -545,7 +546,7 @@ QVector<rcx::Provider::ThreadInfo> ProcessMemoryProvider::tebs() const
|
|||||||
ULONG tbiLen = 0;
|
ULONG tbiLen = 0;
|
||||||
NTSTATUS qitSt = pNtQIT(hThread, 0, &tbi, sizeof(tbi), &tbiLen);
|
NTSTATUS qitSt = pNtQIT(hThread, 0, &tbi, sizeof(tbi), &tbiLen);
|
||||||
if (qitSt >= 0 && tbi.TebBaseAddress)
|
if (qitSt >= 0 && tbi.TebBaseAddress)
|
||||||
result.append({(uint64_t)(uintptr_t)tbi.TebBaseAddress, tid});
|
result.push_back(ThreadInfo{(uint64_t)(uintptr_t)tbi.TebBaseAddress, tid});
|
||||||
CloseHandle(hThread);
|
CloseHandle(hThread);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -244,7 +244,7 @@ struct IpcClient {
|
|||||||
reinterpret_cast<const char*>(data + entry->nameOffset),
|
reinterpret_cast<const char*>(data + entry->nameOffset),
|
||||||
(int)entry->nameLength);
|
(int)entry->nameLength);
|
||||||
#endif
|
#endif
|
||||||
result.append({modName, entry->base, entry->size});
|
result.push_back(RemoteProcessProvider::ModuleInfo{modName, entry->base, entry->size});
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -202,7 +202,7 @@ void BenchProject::benchBuildWorkspaceModel()
|
|||||||
// Build TabInfo array
|
// Build TabInfo array
|
||||||
QVector<TabInfo> tabs;
|
QVector<TabInfo> tabs;
|
||||||
for (const auto& t : trees)
|
for (const auto& t : trees)
|
||||||
tabs.append({ &t, QStringLiteral("test"), nullptr });
|
tabs.push_back(TabInfo{ &t, QStringLiteral("test"), nullptr });
|
||||||
|
|
||||||
QStandardItemModel model;
|
QStandardItemModel model;
|
||||||
const int ITERS = 20;
|
const int ITERS = 20;
|
||||||
@@ -244,7 +244,7 @@ void BenchProject::benchWorkspaceSearch()
|
|||||||
|
|
||||||
QVector<TabInfo> tabs;
|
QVector<TabInfo> tabs;
|
||||||
for (const auto& t : trees)
|
for (const auto& t : trees)
|
||||||
tabs.append({ &t, QStringLiteral("test"), nullptr });
|
tabs.push_back(TabInfo{ &t, QStringLiteral("test"), nullptr });
|
||||||
|
|
||||||
QStandardItemModel model;
|
QStandardItemModel model;
|
||||||
buildProjectExplorer(&model, tabs);
|
buildProjectExplorer(&model, tabs);
|
||||||
|
|||||||
222
tests/grab_tabs.cpp
Normal file
222
tests/grab_tabs.cpp
Normal file
@@ -0,0 +1,222 @@
|
|||||||
|
#include <QtTest/QTest>
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QMainWindow>
|
||||||
|
#include <QDockWidget>
|
||||||
|
#include <QTabBar>
|
||||||
|
#include <QTextEdit>
|
||||||
|
#include <QPixmap>
|
||||||
|
#include <QToolButton>
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
#include <QProxyStyle>
|
||||||
|
#include <QStyleOptionTab>
|
||||||
|
#include <QSettings>
|
||||||
|
#include <QPainter>
|
||||||
|
#include "../src/themes/thememanager.h"
|
||||||
|
|
||||||
|
// Minimal replica of the real app's MenuBarStyle for dock tab painting
|
||||||
|
class TestTabStyle : public QProxyStyle {
|
||||||
|
public:
|
||||||
|
using QProxyStyle::QProxyStyle;
|
||||||
|
|
||||||
|
QSize sizeFromContents(ContentsType type, const QStyleOption* opt,
|
||||||
|
const QSize& sz, const QWidget* w) const override {
|
||||||
|
QSize s = QProxyStyle::sizeFromContents(type, opt, sz, w);
|
||||||
|
if (type == CT_TabBarTab) {
|
||||||
|
if (auto* tabBar = qobject_cast<const QTabBar*>(w)) {
|
||||||
|
if (tabBar->parent() && qobject_cast<const QMainWindow*>(tabBar->parent()))
|
||||||
|
s.setHeight(28);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void drawControl(ControlElement element, const QStyleOption* opt,
|
||||||
|
QPainter* p, const QWidget* w) const override {
|
||||||
|
// Tab shape — background, accent line, borders
|
||||||
|
if (element == CE_TabBarTabShape) {
|
||||||
|
if (auto* tab = qstyleoption_cast<const QStyleOptionTab*>(opt)) {
|
||||||
|
auto* tabBar = qobject_cast<const QTabBar*>(w);
|
||||||
|
if (tabBar && tabBar->parent() && qobject_cast<QMainWindow*>(tabBar->parent())) {
|
||||||
|
bool selected = tab->state & State_Selected;
|
||||||
|
bool hovered = tab->state & State_MouseOver;
|
||||||
|
QColor bg = tab->palette.color(QPalette::Window);
|
||||||
|
if (hovered && !selected)
|
||||||
|
bg = tab->palette.color(QPalette::Mid);
|
||||||
|
p->fillRect(tab->rect, bg);
|
||||||
|
if (selected)
|
||||||
|
p->fillRect(QRect(tab->rect.left(), tab->rect.top(),
|
||||||
|
tab->rect.width(), 2),
|
||||||
|
tab->palette.color(QPalette::Link));
|
||||||
|
p->setPen(tab->palette.color(QPalette::Dark));
|
||||||
|
p->drawLine(tab->rect.bottomLeft(), tab->rect.bottomRight());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Tab label — middle-elide long names, editor font
|
||||||
|
if (element == CE_TabBarTabLabel) {
|
||||||
|
if (auto* tab = qstyleoption_cast<const QStyleOptionTab*>(opt)) {
|
||||||
|
auto* tabBar = qobject_cast<const QTabBar*>(w);
|
||||||
|
if (tabBar && tabBar->parent() && qobject_cast<QMainWindow*>(tabBar->parent())) {
|
||||||
|
int tabIdx = -1;
|
||||||
|
for (int i = 0; i < tabBar->count(); ++i) {
|
||||||
|
if (tabBar->tabRect(i).contains(tab->rect.center())) { tabIdx = i; break; }
|
||||||
|
}
|
||||||
|
int btnWidth = 0;
|
||||||
|
if (tabIdx >= 0) {
|
||||||
|
auto* btn = tabBar->tabButton(tabIdx, QTabBar::RightSide);
|
||||||
|
if (btn) btnWidth = btn->sizeHint().width() + 4;
|
||||||
|
}
|
||||||
|
QRect textRect = tab->rect.adjusted(8, 0, -(8 + btnWidth), 0);
|
||||||
|
QFont f("JetBrains Mono", 10);
|
||||||
|
f.setFixedPitch(true);
|
||||||
|
p->setFont(f);
|
||||||
|
QFontMetrics fm(f);
|
||||||
|
QString text = (tabIdx >= 0) ? tabBar->tabText(tabIdx) : tab->text;
|
||||||
|
int maxW = textRect.width();
|
||||||
|
if (fm.horizontalAdvance(text) > maxW) {
|
||||||
|
int ellW = fm.horizontalAdvance(QStringLiteral("\u2026"));
|
||||||
|
int avail = maxW - ellW;
|
||||||
|
if (avail > 0) {
|
||||||
|
int half = avail / 2;
|
||||||
|
QString left, right;
|
||||||
|
for (int i = 0; i < text.size(); ++i)
|
||||||
|
if (fm.horizontalAdvance(text.left(i+1)) > half) { left = text.left(i); break; }
|
||||||
|
if (left.isEmpty()) left = text.left(1);
|
||||||
|
for (int i = text.size()-1; i >= 0; --i)
|
||||||
|
if (fm.horizontalAdvance(text.mid(i)) > half) { right = text.mid(i+1); break; }
|
||||||
|
if (right.isEmpty()) right = text.right(1);
|
||||||
|
text = left + QStringLiteral("\u2026") + right;
|
||||||
|
} else {
|
||||||
|
text = QStringLiteral("\u2026");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool selected = tab->state & QStyle::State_Selected;
|
||||||
|
p->setPen(selected ? tab->palette.color(QPalette::Text)
|
||||||
|
: tab->palette.color(QPalette::WindowText));
|
||||||
|
p->drawText(textRect, Qt::AlignVCenter | Qt::AlignLeft, text);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QProxyStyle::drawControl(element, opt, p, w);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class TabBtns : public QWidget {
|
||||||
|
public:
|
||||||
|
explicit TabBtns(const QColor& hover, QWidget* parent = nullptr) : QWidget(parent) {
|
||||||
|
auto* hl = new QHBoxLayout(this);
|
||||||
|
hl->setContentsMargins(2, 0, 0, 0);
|
||||||
|
hl->setSpacing(0);
|
||||||
|
QString style = QStringLiteral(
|
||||||
|
"QToolButton { border: none; padding: 1px; border-radius: 0px; }"
|
||||||
|
"QToolButton:hover { background: %1; }").arg(hover.name());
|
||||||
|
auto* pin = new QToolButton(this);
|
||||||
|
pin->setFixedSize(16, 16);
|
||||||
|
pin->setAutoRaise(true);
|
||||||
|
pin->setIcon(QIcon(":/vsicons/pin.svg"));
|
||||||
|
pin->setIconSize(QSize(12, 12));
|
||||||
|
pin->setStyleSheet(style);
|
||||||
|
hl->addWidget(pin);
|
||||||
|
auto* close = new QToolButton(this);
|
||||||
|
close->setFixedSize(16, 16);
|
||||||
|
close->setAutoRaise(true);
|
||||||
|
close->setIcon(QIcon(":/vsicons/close.svg"));
|
||||||
|
close->setIconSize(QSize(12, 12));
|
||||||
|
close->setStyleSheet(style);
|
||||||
|
hl->addWidget(close);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class GrabTabs : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
private slots:
|
||||||
|
void grab() {
|
||||||
|
const auto& t = rcx::ThemeManager::instance().current();
|
||||||
|
|
||||||
|
// Install custom style (no stylesheet — all painting via style)
|
||||||
|
QApplication::setStyle(new TestTabStyle("Fusion"));
|
||||||
|
|
||||||
|
// Apply dark palette globally
|
||||||
|
QPalette pal;
|
||||||
|
pal.setColor(QPalette::Window, t.background);
|
||||||
|
pal.setColor(QPalette::WindowText, t.textDim);
|
||||||
|
pal.setColor(QPalette::Base, t.background);
|
||||||
|
pal.setColor(QPalette::Text, t.text);
|
||||||
|
pal.setColor(QPalette::Mid, t.hover);
|
||||||
|
pal.setColor(QPalette::Dark, t.border);
|
||||||
|
pal.setColor(QPalette::Link, t.indHoverSpan);
|
||||||
|
QApplication::setPalette(pal);
|
||||||
|
|
||||||
|
auto* win = new QMainWindow;
|
||||||
|
win->resize(700, 500);
|
||||||
|
win->setDockNestingEnabled(true);
|
||||||
|
win->setTabPosition(Qt::TopDockWidgetArea, QTabWidget::North);
|
||||||
|
|
||||||
|
auto* central = new QWidget(win);
|
||||||
|
central->setMaximumSize(0, 0);
|
||||||
|
central->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
|
||||||
|
win->setCentralWidget(central);
|
||||||
|
win->setStyleSheet(QStringLiteral(
|
||||||
|
"QMainWindow::separator { width: 0px; height: 0px; background: transparent; }"));
|
||||||
|
|
||||||
|
QStringList names = {
|
||||||
|
"shader_color_helper.hpp",
|
||||||
|
"shader_crypt.cpp",
|
||||||
|
"EPROCESS (class)",
|
||||||
|
"very_long_struct_name_that_should_elide.h"
|
||||||
|
};
|
||||||
|
|
||||||
|
QVector<QDockWidget*> docks;
|
||||||
|
for (const auto& name : names) {
|
||||||
|
auto* dock = new QDockWidget(name, win);
|
||||||
|
dock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable);
|
||||||
|
auto* emptyTitle = new QWidget(dock);
|
||||||
|
emptyTitle->setFixedHeight(0);
|
||||||
|
dock->setTitleBarWidget(emptyTitle);
|
||||||
|
dock->setWidget(new QTextEdit(dock));
|
||||||
|
if (!docks.isEmpty())
|
||||||
|
win->tabifyDockWidget(docks.last(), dock);
|
||||||
|
else
|
||||||
|
win->addDockWidget(Qt::TopDockWidgetArea, dock);
|
||||||
|
docks.append(dock);
|
||||||
|
}
|
||||||
|
// Select first tab
|
||||||
|
docks.first()->raise();
|
||||||
|
|
||||||
|
win->show();
|
||||||
|
QVERIFY(QTest::qWaitForWindowExposed(win));
|
||||||
|
QApplication::processEvents();
|
||||||
|
|
||||||
|
// No stylesheet on dock tab bars — painting handled by TestTabStyle
|
||||||
|
for (auto* tabBar : win->findChildren<QTabBar*>()) {
|
||||||
|
if (tabBar->parent() != win) continue;
|
||||||
|
tabBar->setStyleSheet(QString());
|
||||||
|
tabBar->setElideMode(Qt::ElideNone);
|
||||||
|
tabBar->setExpanding(false);
|
||||||
|
|
||||||
|
QPalette tp = tabBar->palette();
|
||||||
|
tp.setColor(QPalette::WindowText, t.textDim);
|
||||||
|
tp.setColor(QPalette::Text, t.text);
|
||||||
|
tp.setColor(QPalette::Window, t.background);
|
||||||
|
tp.setColor(QPalette::Mid, t.hover);
|
||||||
|
tp.setColor(QPalette::Dark, t.border);
|
||||||
|
tp.setColor(QPalette::Link, t.indHoverSpan);
|
||||||
|
tabBar->setPalette(tp);
|
||||||
|
|
||||||
|
for (int i = 0; i < tabBar->count(); ++i)
|
||||||
|
tabBar->setTabButton(i, QTabBar::RightSide, new TabBtns(t.hover, tabBar));
|
||||||
|
}
|
||||||
|
QApplication::processEvents();
|
||||||
|
QApplication::processEvents();
|
||||||
|
|
||||||
|
QPixmap shot = win->grab(QRect(0, 0, win->width(), 50));
|
||||||
|
shot.save(QStringLiteral("tab_screenshot.png"));
|
||||||
|
qDebug() << "Saved" << shot.size();
|
||||||
|
delete win;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
QTEST_MAIN(GrabTabs)
|
||||||
|
#include "grab_tabs.moc"
|
||||||
@@ -382,6 +382,30 @@ private slots:
|
|||||||
QCOMPARE(r.value, 0x140000000ULL);
|
QCOMPARE(r.value, 0x140000000ULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -- Bare module.dll identifier --
|
||||||
|
|
||||||
|
void bareModuleDll() {
|
||||||
|
AddressParserCallbacks cbs;
|
||||||
|
cbs.resolveIdentifier = [](const QString& name, bool* ok) -> uint64_t {
|
||||||
|
*ok = (name == "client.dll");
|
||||||
|
return *ok ? 0x7FF600000000ULL : 0;
|
||||||
|
};
|
||||||
|
auto r = AddressParser::evaluate("client.dll + 0xFF", 8, &cbs);
|
||||||
|
QVERIFY(r.ok);
|
||||||
|
QCOMPARE(r.value, 0x7FF6000000FFULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bareModuleExe() {
|
||||||
|
AddressParserCallbacks cbs;
|
||||||
|
cbs.resolveIdentifier = [](const QString& name, bool* ok) -> uint64_t {
|
||||||
|
*ok = (name == "cs2.exe");
|
||||||
|
return *ok ? 0x140000000ULL : 0;
|
||||||
|
};
|
||||||
|
auto r = AddressParser::evaluate("cs2.exe + 0xDE", 8, &cbs);
|
||||||
|
QVERIFY(r.ok);
|
||||||
|
QCOMPARE(r.value, 0x1400000DEULL);
|
||||||
|
}
|
||||||
|
|
||||||
// -- Validate with new syntax --
|
// -- Validate with new syntax --
|
||||||
|
|
||||||
void validateIdentifier() {
|
void validateIdentifier() {
|
||||||
|
|||||||
@@ -230,7 +230,7 @@ private slots:
|
|||||||
// Only include the pointer-expanded ones (near vtable at 0x100)
|
// Only include the pointer-expanded ones (near vtable at 0x100)
|
||||||
if (lm.offsetAddr >= 0x100 && lm.offsetAddr < 0x200) {
|
if (lm.offsetAddr >= 0x100 && lm.offsetAddr < 0x200) {
|
||||||
int nodeIdx = lm.nodeIdx;
|
int nodeIdx = lm.nodeIdx;
|
||||||
funcPtrs.append({i, lm.offsetAddr, lm.nodeKind,
|
funcPtrs.push_back(FuncInfo{i, lm.offsetAddr, lm.nodeKind,
|
||||||
nodeIdx >= 0 ? tree.nodes[nodeIdx].name : QString()});
|
nodeIdx >= 0 ? tree.nodes[nodeIdx].name : QString()});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
397
tests/test_kernel_provider.cpp
Normal file
397
tests/test_kernel_provider.cpp
Normal file
@@ -0,0 +1,397 @@
|
|||||||
|
#include <QTest>
|
||||||
|
#include <QSignalSpy>
|
||||||
|
#include <QByteArray>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include "providers/provider.h"
|
||||||
|
#include "scanner.h"
|
||||||
|
#include "../plugins/KernelMemory/KernelMemoryPlugin.h"
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
#include <tlhelp32.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using namespace rcx;
|
||||||
|
|
||||||
|
class TestKernelProvider : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool m_driverAvailable = false;
|
||||||
|
KernelMemoryPlugin* m_plugin = nullptr;
|
||||||
|
std::unique_ptr<Provider> m_provider;
|
||||||
|
uint32_t m_selfPid = 0;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
|
||||||
|
// ── Setup: try to load driver, skip tests if unavailable ──
|
||||||
|
|
||||||
|
void initTestCase()
|
||||||
|
{
|
||||||
|
m_plugin = new KernelMemoryPlugin();
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
m_selfPid = GetCurrentProcessId();
|
||||||
|
|
||||||
|
// Try to open driver directly to see if it's available
|
||||||
|
HANDLE h = CreateFileA(RCX_DRV_USERMODE_PATH,
|
||||||
|
GENERIC_READ | GENERIC_WRITE,
|
||||||
|
0, nullptr, OPEN_EXISTING,
|
||||||
|
FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||||
|
if (h != INVALID_HANDLE_VALUE) {
|
||||||
|
CloseHandle(h);
|
||||||
|
m_driverAvailable = true;
|
||||||
|
} else {
|
||||||
|
// Try loading via plugin
|
||||||
|
QString errorMsg;
|
||||||
|
QString target = QStringLiteral("km:%1:self").arg(m_selfPid);
|
||||||
|
m_provider = m_plugin->createProvider(target, &errorMsg);
|
||||||
|
if (m_provider && m_provider->isValid()) {
|
||||||
|
m_driverAvailable = true;
|
||||||
|
} else {
|
||||||
|
qWarning("Kernel driver not available: %s", qPrintable(errorMsg));
|
||||||
|
qWarning("Tests requiring the driver will be skipped.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_driverAvailable && !m_provider) {
|
||||||
|
QString target = QStringLiteral("km:%1:self").arg(m_selfPid);
|
||||||
|
m_provider = m_plugin->createProvider(target, nullptr);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void cleanupTestCase()
|
||||||
|
{
|
||||||
|
m_provider.reset();
|
||||||
|
delete m_plugin;
|
||||||
|
m_plugin = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── 1. Plugin metadata (no driver needed) ──
|
||||||
|
|
||||||
|
void plugin_name()
|
||||||
|
{
|
||||||
|
QCOMPARE(QString::fromStdString(m_plugin->Name()), QStringLiteral("Kernel Memory"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void plugin_loadType()
|
||||||
|
{
|
||||||
|
QCOMPARE(m_plugin->LoadType(), IPlugin::k_ELoadTypeManual);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plugin_canHandle()
|
||||||
|
{
|
||||||
|
QVERIFY(m_plugin->canHandle(QStringLiteral("km:1234:test.exe")));
|
||||||
|
QVERIFY(m_plugin->canHandle(QStringLiteral("phys:0")));
|
||||||
|
QVERIFY(m_plugin->canHandle(QStringLiteral("msr:")));
|
||||||
|
QVERIFY(!m_plugin->canHandle(QStringLiteral("1234:test.exe")));
|
||||||
|
QVERIFY(!m_plugin->canHandle(QStringLiteral("file:test.bin")));
|
||||||
|
}
|
||||||
|
|
||||||
|
void provider_noDriver_invalid()
|
||||||
|
{
|
||||||
|
// Creating provider with invalid target should fail gracefully
|
||||||
|
QString err;
|
||||||
|
auto prov = m_plugin->createProvider(QStringLiteral("km:0:invalid"), &err);
|
||||||
|
// Either nullptr or invalid -- both are acceptable
|
||||||
|
if (prov) QVERIFY(!prov->isValid() || prov->size() == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── 2. KUSER_SHARED_DATA validation (at 0x7FFE0000) ──
|
||||||
|
|
||||||
|
void kusd_ntMajorVersion()
|
||||||
|
{
|
||||||
|
if (!m_driverAvailable) QSKIP("Driver not loaded");
|
||||||
|
QVERIFY(m_provider);
|
||||||
|
|
||||||
|
// KUSER_SHARED_DATA.NtMajorVersion at offset 0x26C
|
||||||
|
uint32_t major = m_provider->readU32(0x7FFE0000 + 0x26C);
|
||||||
|
QCOMPARE(major, (uint32_t)10); // Windows 10/11
|
||||||
|
}
|
||||||
|
|
||||||
|
void kusd_ntMinorVersion()
|
||||||
|
{
|
||||||
|
if (!m_driverAvailable) QSKIP("Driver not loaded");
|
||||||
|
|
||||||
|
uint32_t minor = m_provider->readU32(0x7FFE0000 + 0x270);
|
||||||
|
QCOMPARE(minor, (uint32_t)0); // Windows 10+ has minor = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
void kusd_ntBuildNumber()
|
||||||
|
{
|
||||||
|
if (!m_driverAvailable) QSKIP("Driver not loaded");
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
// Cross-validate with RtlGetVersion
|
||||||
|
typedef NTSTATUS(NTAPI* RtlGetVersion_t)(PRTL_OSVERSIONINFOW);
|
||||||
|
auto pRtlGetVersion = (RtlGetVersion_t)GetProcAddress(
|
||||||
|
GetModuleHandleA("ntdll.dll"), "RtlGetVersion");
|
||||||
|
QVERIFY(pRtlGetVersion);
|
||||||
|
|
||||||
|
RTL_OSVERSIONINFOW osvi{};
|
||||||
|
osvi.dwOSVersionInfoSize = sizeof(osvi);
|
||||||
|
QCOMPARE(pRtlGetVersion(&osvi), (NTSTATUS)0);
|
||||||
|
|
||||||
|
uint32_t buildFromDriver = m_provider->readU32(0x7FFE0000 + 0x260);
|
||||||
|
QCOMPARE(buildFromDriver, (uint32_t)osvi.dwBuildNumber);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void kusd_systemTime_nonZero()
|
||||||
|
{
|
||||||
|
if (!m_driverAvailable) QSKIP("Driver not loaded");
|
||||||
|
|
||||||
|
uint64_t sysTime = m_provider->readU64(0x7FFE0000 + 0x14);
|
||||||
|
QVERIFY(sysTime != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void kusd_tickCount_increasing()
|
||||||
|
{
|
||||||
|
if (!m_driverAvailable) QSKIP("Driver not loaded");
|
||||||
|
|
||||||
|
// TickCountMultiplier at 0x4, TickCount at 0x320
|
||||||
|
uint64_t tick1 = m_provider->readU64(0x7FFE0000 + 0x320);
|
||||||
|
QTest::qWait(120);
|
||||||
|
uint64_t tick2 = m_provider->readU64(0x7FFE0000 + 0x320);
|
||||||
|
QVERIFY2(tick2 > tick1,
|
||||||
|
qPrintable(QStringLiteral("tick1=%1 tick2=%2").arg(tick1).arg(tick2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void kusd_crossValidate_readProcessMemory()
|
||||||
|
{
|
||||||
|
if (!m_driverAvailable) QSKIP("Driver not loaded");
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
// Read same KUSD page through driver and ReadProcessMemory
|
||||||
|
QByteArray driverBuf(256, 0);
|
||||||
|
m_provider->read(0x7FFE0000, driverBuf.data(), 256);
|
||||||
|
|
||||||
|
QByteArray rpmBuf(256, 0);
|
||||||
|
SIZE_T bytesRead = 0;
|
||||||
|
HANDLE self = GetCurrentProcess();
|
||||||
|
ReadProcessMemory(self, (LPCVOID)0x7FFE0000, rpmBuf.data(), 256, &bytesRead);
|
||||||
|
|
||||||
|
// NtMajorVersion (offset 0x26C relative = not in first 256 bytes, so compare what we have)
|
||||||
|
// Compare first 256 bytes -- should be identical
|
||||||
|
QCOMPARE(driverBuf, rpmBuf);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── 3. Self-read integration ──
|
||||||
|
|
||||||
|
void selfRead_mzHeader()
|
||||||
|
{
|
||||||
|
if (!m_driverAvailable) QSKIP("Driver not loaded");
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
uint64_t selfBase = (uint64_t)GetModuleHandleA(nullptr);
|
||||||
|
QVERIFY(selfBase != 0);
|
||||||
|
|
||||||
|
uint8_t mz[2] = {};
|
||||||
|
m_provider->read(selfBase, mz, 2);
|
||||||
|
QCOMPARE(mz[0], (uint8_t)'M');
|
||||||
|
QCOMPARE(mz[1], (uint8_t)'Z');
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void selfRead_peSignature()
|
||||||
|
{
|
||||||
|
if (!m_driverAvailable) QSKIP("Driver not loaded");
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
uint64_t selfBase = (uint64_t)GetModuleHandleA(nullptr);
|
||||||
|
|
||||||
|
// PE offset at +0x3C
|
||||||
|
uint32_t peOffset = m_provider->readU32(selfBase + 0x3C);
|
||||||
|
QVERIFY(peOffset > 0 && peOffset < 0x1000);
|
||||||
|
|
||||||
|
// PE signature = "PE\0\0" = 0x00004550
|
||||||
|
uint32_t peSig = m_provider->readU32(selfBase + peOffset);
|
||||||
|
QCOMPARE(peSig, (uint32_t)0x00004550);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── 4. Scanner integration ──
|
||||||
|
|
||||||
|
void scanner_mzSigScan()
|
||||||
|
{
|
||||||
|
if (!m_driverAvailable) QSKIP("Driver not loaded");
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
auto shared = std::shared_ptr<Provider>(m_provider.get(), [](Provider*){});
|
||||||
|
|
||||||
|
ScanRequest req;
|
||||||
|
req.pattern = QByteArray("\x4D\x5A", 2);
|
||||||
|
req.mask = QByteArray("\xFF\xFF", 2);
|
||||||
|
req.alignment = 1;
|
||||||
|
req.maxResults = 10;
|
||||||
|
|
||||||
|
// Constrain to our own module for speed
|
||||||
|
uint64_t selfBase = (uint64_t)GetModuleHandleA(nullptr);
|
||||||
|
req.startAddress = selfBase;
|
||||||
|
req.endAddress = selfBase + 0x1000;
|
||||||
|
|
||||||
|
ScanEngine engine;
|
||||||
|
QSignalSpy spy(&engine, &ScanEngine::finished);
|
||||||
|
engine.start(shared, req);
|
||||||
|
QVERIFY(spy.wait(5000));
|
||||||
|
|
||||||
|
auto results = spy.at(0).at(0).value<QVector<ScanResult>>();
|
||||||
|
QVERIFY(results.size() >= 1);
|
||||||
|
QCOMPARE(results[0].address, selfBase);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── 5. Region enumeration ──
|
||||||
|
|
||||||
|
void regions_selfProcess()
|
||||||
|
{
|
||||||
|
if (!m_driverAvailable) QSKIP("Driver not loaded");
|
||||||
|
|
||||||
|
auto regions = m_provider->enumerateRegions();
|
||||||
|
QVERIFY(regions.size() > 0);
|
||||||
|
|
||||||
|
// Should have at least one executable region (our code)
|
||||||
|
bool hasExec = false;
|
||||||
|
for (const auto& r : regions) {
|
||||||
|
if (r.executable) { hasExec = true; break; }
|
||||||
|
}
|
||||||
|
QVERIFY(hasExec);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── 6. PEB / modules ──
|
||||||
|
|
||||||
|
void peb_nonZero()
|
||||||
|
{
|
||||||
|
if (!m_driverAvailable) QSKIP("Driver not loaded");
|
||||||
|
|
||||||
|
QVERIFY(m_provider->peb() != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void symbol_selfModule()
|
||||||
|
{
|
||||||
|
if (!m_driverAvailable) QSKIP("Driver not loaded");
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
uint64_t selfBase = (uint64_t)GetModuleHandleA(nullptr);
|
||||||
|
QString sym = m_provider->getSymbol(selfBase + 0x100);
|
||||||
|
QVERIFY(!sym.isEmpty());
|
||||||
|
QVERIFY(sym.contains(QStringLiteral("+0x")));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── 7. CR3 / address translation ──
|
||||||
|
|
||||||
|
void cr3_nonZero()
|
||||||
|
{
|
||||||
|
if (!m_driverAvailable) QSKIP("Driver not loaded");
|
||||||
|
|
||||||
|
auto* kprov = dynamic_cast<KernelProcessProvider*>(m_provider.get());
|
||||||
|
QVERIFY(kprov);
|
||||||
|
|
||||||
|
uint64_t cr3 = kprov->getCr3();
|
||||||
|
QVERIFY2(cr3 != 0, "CR3 should be non-zero for a running process");
|
||||||
|
// CR3 should be page-aligned (low 12 bits cleared)
|
||||||
|
QCOMPARE(cr3 & 0xFFF, (uint64_t)0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void vtop_kusd()
|
||||||
|
{
|
||||||
|
if (!m_driverAvailable) QSKIP("Driver not loaded");
|
||||||
|
|
||||||
|
auto* kprov = dynamic_cast<KernelProcessProvider*>(m_provider.get());
|
||||||
|
QVERIFY(kprov);
|
||||||
|
|
||||||
|
// KUSER_SHARED_DATA is at VA 0x7FFE0000 in every process
|
||||||
|
auto result = kprov->translateAddress(0x7FFE0000);
|
||||||
|
QVERIFY2(result.valid, "KUSER_SHARED_DATA should be mapped");
|
||||||
|
QVERIFY(result.physical != 0);
|
||||||
|
// PML4E and PDPTE should be present
|
||||||
|
QVERIFY(result.pml4e & 1); // Present bit
|
||||||
|
QVERIFY(result.pdpte & 1); // Present bit
|
||||||
|
}
|
||||||
|
|
||||||
|
void vtop_selfModule()
|
||||||
|
{
|
||||||
|
if (!m_driverAvailable) QSKIP("Driver not loaded");
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
auto* kprov = dynamic_cast<KernelProcessProvider*>(m_provider.get());
|
||||||
|
QVERIFY(kprov);
|
||||||
|
|
||||||
|
uint64_t selfBase = (uint64_t)GetModuleHandleA(nullptr);
|
||||||
|
auto result = kprov->translateAddress(selfBase);
|
||||||
|
QVERIFY2(result.valid, "Own module base should be mapped");
|
||||||
|
QVERIFY(result.physical != 0);
|
||||||
|
|
||||||
|
// Cross-validate: read MZ header via physical address
|
||||||
|
// Read the first 2 bytes at the physical address using physical provider
|
||||||
|
auto physEntries = kprov->readPageTable(kprov->getCr3(), 0, 16);
|
||||||
|
QVERIFY(physEntries.size() > 0); // Should get at least some PML4 entries
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void vtop_unmapped()
|
||||||
|
{
|
||||||
|
if (!m_driverAvailable) QSKIP("Driver not loaded");
|
||||||
|
|
||||||
|
auto* kprov = dynamic_cast<KernelProcessProvider*>(m_provider.get());
|
||||||
|
QVERIFY(kprov);
|
||||||
|
|
||||||
|
// Address 0 should not be mapped in user mode
|
||||||
|
auto result = kprov->translateAddress(0);
|
||||||
|
QVERIFY2(!result.valid, "Address 0 should not be mapped");
|
||||||
|
}
|
||||||
|
|
||||||
|
void readPageTable_cr3()
|
||||||
|
{
|
||||||
|
if (!m_driverAvailable) QSKIP("Driver not loaded");
|
||||||
|
|
||||||
|
auto* kprov = dynamic_cast<KernelProcessProvider*>(m_provider.get());
|
||||||
|
QVERIFY(kprov);
|
||||||
|
|
||||||
|
uint64_t cr3 = kprov->getCr3();
|
||||||
|
QVERIFY(cr3 != 0);
|
||||||
|
|
||||||
|
// Read the full PML4 table (512 entries)
|
||||||
|
auto entries = kprov->readPageTable(cr3, 0, 512);
|
||||||
|
QCOMPARE(entries.size(), 512);
|
||||||
|
|
||||||
|
// At least some entries should be present (kernel maps upper half)
|
||||||
|
int presentCount = 0;
|
||||||
|
for (const auto& e : entries) {
|
||||||
|
if (e & 1) presentCount++;
|
||||||
|
}
|
||||||
|
QVERIFY2(presentCount > 0,
|
||||||
|
qPrintable(QStringLiteral("Expected present PML4 entries, got 0")));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── 8. Ping ──
|
||||||
|
|
||||||
|
void ping_version()
|
||||||
|
{
|
||||||
|
if (!m_driverAvailable) QSKIP("Driver not loaded");
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
HANDLE h = CreateFileA(RCX_DRV_USERMODE_PATH,
|
||||||
|
GENERIC_READ | GENERIC_WRITE,
|
||||||
|
0, nullptr, OPEN_EXISTING,
|
||||||
|
FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||||
|
if (h == INVALID_HANDLE_VALUE) QSKIP("Cannot open driver handle");
|
||||||
|
|
||||||
|
RcxDrvPingResponse ping{};
|
||||||
|
DWORD br = 0;
|
||||||
|
BOOL ok = DeviceIoControl(h, IOCTL_RCX_PING, nullptr, 0,
|
||||||
|
&ping, sizeof(ping), &br, nullptr);
|
||||||
|
CloseHandle(h);
|
||||||
|
|
||||||
|
QVERIFY(ok);
|
||||||
|
QCOMPARE(ping.version, (uint32_t)RCX_DRV_VERSION);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
QTEST_MAIN(TestKernelProvider)
|
||||||
|
#include "test_kernel_provider.moc"
|
||||||
@@ -26,7 +26,7 @@ public:
|
|||||||
if (!m_server->listen(name)) return false;
|
if (!m_server->listen(name)) return false;
|
||||||
connect(m_server, &QLocalServer::newConnection, this, [this]() {
|
connect(m_server, &QLocalServer::newConnection, this, [this]() {
|
||||||
while (auto* s = m_server->nextPendingConnection()) {
|
while (auto* s = m_server->nextPendingConnection()) {
|
||||||
m_clients.append({s, {}, false});
|
m_clients.push_back(Client{s, {}, false});
|
||||||
connect(s, &QLocalSocket::readyRead, this, [this, s]() { processSocket(s); });
|
connect(s, &QLocalSocket::readyRead, this, [this, s]() { processSocket(s); });
|
||||||
connect(s, &QLocalSocket::disconnected, this, [this, s]() {
|
connect(s, &QLocalSocket::disconnected, this, [this, s]() {
|
||||||
for (int i = 0; i < m_clients.size(); i++)
|
for (int i = 0; i < m_clients.size(); i++)
|
||||||
|
|||||||
143
tests/test_project_dock.cpp
Normal file
143
tests/test_project_dock.cpp
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
#include <QtTest/QTest>
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QMainWindow>
|
||||||
|
#include <QDockWidget>
|
||||||
|
#include <QTabWidget>
|
||||||
|
#include <QTextEdit>
|
||||||
|
|
||||||
|
// Replicates the real app layout: QTabWidget central widget, project dock in LeftDockWidgetArea.
|
||||||
|
|
||||||
|
class TestProjectDock : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
private:
|
||||||
|
struct AppLayout {
|
||||||
|
QMainWindow* win;
|
||||||
|
QTabWidget* tabs;
|
||||||
|
QDockWidget* project;
|
||||||
|
};
|
||||||
|
|
||||||
|
AppLayout buildApp() {
|
||||||
|
auto* win = new QMainWindow;
|
||||||
|
win->resize(1280, 800);
|
||||||
|
|
||||||
|
// QTabWidget as central widget — same as real app
|
||||||
|
auto* tabs = new QTabWidget(win);
|
||||||
|
tabs->setTabsClosable(true);
|
||||||
|
tabs->setMovable(true);
|
||||||
|
tabs->setDocumentMode(true);
|
||||||
|
tabs->addTab(new QTextEdit(tabs), "Untitled");
|
||||||
|
win->setCentralWidget(tabs);
|
||||||
|
|
||||||
|
// Project dock — same as real app
|
||||||
|
auto* project = new QDockWidget("Project", win);
|
||||||
|
project->setObjectName("WorkspaceDock");
|
||||||
|
project->setAllowedAreas(Qt::AllDockWidgetAreas);
|
||||||
|
project->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable);
|
||||||
|
project->setWidget(new QTextEdit(project));
|
||||||
|
win->addDockWidget(Qt::LeftDockWidgetArea, project);
|
||||||
|
project->hide();
|
||||||
|
|
||||||
|
return {win, tabs, project};
|
||||||
|
}
|
||||||
|
|
||||||
|
void showProject(AppLayout& a) {
|
||||||
|
if (a.project->isHidden() && !a.project->isFloating()) {
|
||||||
|
a.win->addDockWidget(Qt::LeftDockWidgetArea, a.project);
|
||||||
|
a.project->show();
|
||||||
|
a.win->resizeDocks({a.project}, {qMax(200, a.win->width() / 5)}, Qt::Horizontal);
|
||||||
|
} else {
|
||||||
|
a.project->show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void dockStartsLeft();
|
||||||
|
void dockWidthIsReasonable();
|
||||||
|
void dockStaysLeftAfterHideShow();
|
||||||
|
void dockRespectsDragAfterShow();
|
||||||
|
};
|
||||||
|
|
||||||
|
void TestProjectDock::dockStartsLeft()
|
||||||
|
{
|
||||||
|
auto app = buildApp();
|
||||||
|
app.win->show();
|
||||||
|
QTest::qWaitForWindowExposed(app.win);
|
||||||
|
|
||||||
|
showProject(app);
|
||||||
|
QApplication::processEvents();
|
||||||
|
|
||||||
|
// Project should be to the left of the central tab widget
|
||||||
|
QVERIFY2(app.project->x() < app.tabs->x(),
|
||||||
|
qPrintable(QString("Project x=%1, Tabs x=%2")
|
||||||
|
.arg(app.project->x()).arg(app.tabs->x())));
|
||||||
|
delete app.win;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestProjectDock::dockWidthIsReasonable()
|
||||||
|
{
|
||||||
|
auto app = buildApp();
|
||||||
|
app.win->show();
|
||||||
|
QTest::qWaitForWindowExposed(app.win);
|
||||||
|
|
||||||
|
showProject(app);
|
||||||
|
QApplication::processEvents();
|
||||||
|
|
||||||
|
int dockWidth = app.project->width();
|
||||||
|
int winWidth = app.win->width();
|
||||||
|
double ratio = (double)dockWidth / winWidth;
|
||||||
|
|
||||||
|
qDebug() << "Dock width:" << dockWidth << "Window width:" << winWidth
|
||||||
|
<< "Ratio:" << QString::number(ratio * 100, 'f', 1) + "%";
|
||||||
|
|
||||||
|
QVERIFY2(ratio < 0.40,
|
||||||
|
qPrintable(QString("Dock too wide: %1% of window").arg(ratio * 100, 0, 'f', 1)));
|
||||||
|
QVERIFY2(ratio > 0.10,
|
||||||
|
qPrintable(QString("Dock too narrow: %1% of window").arg(ratio * 100, 0, 'f', 1)));
|
||||||
|
delete app.win;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestProjectDock::dockStaysLeftAfterHideShow()
|
||||||
|
{
|
||||||
|
auto app = buildApp();
|
||||||
|
app.win->show();
|
||||||
|
QTest::qWaitForWindowExposed(app.win);
|
||||||
|
|
||||||
|
showProject(app);
|
||||||
|
QApplication::processEvents();
|
||||||
|
QVERIFY(app.project->x() < app.tabs->x());
|
||||||
|
|
||||||
|
app.project->hide();
|
||||||
|
QApplication::processEvents();
|
||||||
|
|
||||||
|
showProject(app);
|
||||||
|
QApplication::processEvents();
|
||||||
|
QVERIFY2(app.project->x() < app.tabs->x(),
|
||||||
|
qPrintable(QString("After re-show: Project x=%1, Tabs x=%2")
|
||||||
|
.arg(app.project->x()).arg(app.tabs->x())));
|
||||||
|
delete app.win;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestProjectDock::dockRespectsDragAfterShow()
|
||||||
|
{
|
||||||
|
auto app = buildApp();
|
||||||
|
app.win->show();
|
||||||
|
QTest::qWaitForWindowExposed(app.win);
|
||||||
|
|
||||||
|
showProject(app);
|
||||||
|
QApplication::processEvents();
|
||||||
|
QVERIFY(app.project->x() < app.tabs->x());
|
||||||
|
|
||||||
|
// Simulate user dragging to right
|
||||||
|
app.win->addDockWidget(Qt::RightDockWidgetArea, app.project);
|
||||||
|
QApplication::processEvents();
|
||||||
|
QCOMPARE(app.win->dockWidgetArea(app.project), Qt::RightDockWidgetArea);
|
||||||
|
|
||||||
|
// Dock is visible — showProject should NOT force it back to left
|
||||||
|
showProject(app);
|
||||||
|
QApplication::processEvents();
|
||||||
|
QCOMPARE(app.win->dockWidgetArea(app.project), Qt::RightDockWidgetArea);
|
||||||
|
delete app.win;
|
||||||
|
}
|
||||||
|
|
||||||
|
QTEST_MAIN(TestProjectDock)
|
||||||
|
#include "test_project_dock.moc"
|
||||||
307
tests/test_roundtrip_winsdk.cpp
Normal file
307
tests/test_roundtrip_winsdk.cpp
Normal file
@@ -0,0 +1,307 @@
|
|||||||
|
#include <QtTest/QTest>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <random>
|
||||||
|
#include "core.h"
|
||||||
|
#include "imports/import_source.h"
|
||||||
|
#include "generator.h"
|
||||||
|
|
||||||
|
using namespace rcx;
|
||||||
|
|
||||||
|
class TestRoundtripWinSdk : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
private:
|
||||||
|
NodeTree fullTree;
|
||||||
|
QVector<int> rootIndices;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void initTestCase();
|
||||||
|
void importCount();
|
||||||
|
void pebOffsets();
|
||||||
|
void roundTrip30();
|
||||||
|
void generateRcx();
|
||||||
|
};
|
||||||
|
|
||||||
|
void TestRoundtripWinSdk::initTestCase()
|
||||||
|
{
|
||||||
|
QString path = QStringLiteral(WINSDK_HEADER_PATH);
|
||||||
|
QFile file(path);
|
||||||
|
QVERIFY2(file.open(QIODevice::ReadOnly | QIODevice::Text),
|
||||||
|
qPrintable("Cannot open " + path));
|
||||||
|
QString source = QString::fromUtf8(file.readAll());
|
||||||
|
QVERIFY(!source.isEmpty());
|
||||||
|
|
||||||
|
QString err;
|
||||||
|
fullTree = importFromSource(source, &err, 8);
|
||||||
|
|
||||||
|
for (int i = 0; i < fullTree.nodes.size(); i++) {
|
||||||
|
const auto& n = fullTree.nodes[i];
|
||||||
|
if (n.parentId == 0 && n.kind == NodeKind::Struct)
|
||||||
|
rootIndices.append(i);
|
||||||
|
}
|
||||||
|
qDebug() << "Imported" << fullTree.nodes.size() << "total nodes,"
|
||||||
|
<< rootIndices.size() << "root structs";
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestRoundtripWinSdk::importCount()
|
||||||
|
{
|
||||||
|
QVERIFY2(rootIndices.size() >= 3000,
|
||||||
|
qPrintable(QString("Expected >= 3000 roots, got %1").arg(rootIndices.size())));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestRoundtripWinSdk::pebOffsets()
|
||||||
|
{
|
||||||
|
// Verify _PEB field offsets match WinDbg dt ntdll!_PEB
|
||||||
|
int pebIdx = -1;
|
||||||
|
for (int i = 0; i < fullTree.nodes.size(); i++) {
|
||||||
|
if (fullTree.nodes[i].parentId == 0 &&
|
||||||
|
fullTree.nodes[i].structTypeName == QStringLiteral("_PEB")) {
|
||||||
|
pebIdx = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QVERIFY2(pebIdx >= 0, "Could not find _PEB root struct");
|
||||||
|
|
||||||
|
uint64_t pebId = fullTree.nodes[pebIdx].id;
|
||||||
|
|
||||||
|
// Collect direct children with offsets and sizes
|
||||||
|
struct ChildInfo { QString name; int offset; int size; NodeKind kind; };
|
||||||
|
QVector<ChildInfo> children;
|
||||||
|
for (int i = 0; i < fullTree.nodes.size(); i++) {
|
||||||
|
if (fullTree.nodes[i].parentId == pebId) {
|
||||||
|
int sz = sizeForKind(fullTree.nodes[i].kind);
|
||||||
|
if (sz == 0) sz = fullTree.structSpan(fullTree.nodes[i].id);
|
||||||
|
if (sz == 0) sz = 1;
|
||||||
|
children.push_back(ChildInfo{fullTree.nodes[i].name, fullTree.nodes[i].offset, sz, fullTree.nodes[i].kind});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort by offset
|
||||||
|
std::sort(children.begin(), children.end(),
|
||||||
|
[](const ChildInfo& a, const ChildInfo& b) { return a.offset < b.offset; });
|
||||||
|
|
||||||
|
// Dump all children for diagnostics
|
||||||
|
for (const auto& c : children) {
|
||||||
|
qDebug() << " " << Qt::hex << c.offset << c.name
|
||||||
|
<< "kind=" << kindToString(c.kind) << "size=" << c.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for overlaps
|
||||||
|
int overlapCount = 0;
|
||||||
|
for (int i = 1; i < children.size(); i++) {
|
||||||
|
int prevEnd = children[i-1].offset + children[i-1].size;
|
||||||
|
if (children[i].offset < prevEnd && children[i-1].kind != NodeKind::Struct) {
|
||||||
|
// Only flag overlaps where previous field has a known size (not struct references)
|
||||||
|
overlapCount++;
|
||||||
|
if (overlapCount <= 10)
|
||||||
|
qDebug() << " OVERLAP:" << children[i].name << "at" << Qt::hex << children[i].offset
|
||||||
|
<< "overlaps" << children[i-1].name << "(ends at" << Qt::hex << prevEnd << ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build name→offset map for field checks
|
||||||
|
QHash<QString, int> offsets;
|
||||||
|
QHash<QString, NodeKind> kinds;
|
||||||
|
for (const auto& c : children) {
|
||||||
|
offsets[c.name] = c.offset;
|
||||||
|
kinds[c.name] = c.kind;
|
||||||
|
}
|
||||||
|
|
||||||
|
int failCount = 0;
|
||||||
|
auto checkField = [&](const QString& name, int expected, bool mustBePointer = false) {
|
||||||
|
if (!offsets.contains(name)) {
|
||||||
|
qDebug() << " MISSING:" << name;
|
||||||
|
failCount++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (offsets[name] != expected) {
|
||||||
|
qDebug() << " OFFSET MISMATCH:" << name << "got" << Qt::hex << offsets[name]
|
||||||
|
<< "expected" << Qt::hex << expected;
|
||||||
|
failCount++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (mustBePointer) {
|
||||||
|
NodeKind k = kinds[name];
|
||||||
|
if (k != NodeKind::Pointer64 && k != NodeKind::Pointer32) {
|
||||||
|
qDebug() << " NOT POINTER:" << name << "kind=" << kindToString(k);
|
||||||
|
failCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Expected offsets computed from the source header layout (Vergilius-style)
|
||||||
|
// Note: This header has union ALIGN(8) { KernelCallbackTable; UserSharedInfoPtr; }
|
||||||
|
// after CrossProcessFlags, which shifts fields +0xC compared to some WinDbg versions.
|
||||||
|
checkField(QStringLiteral("InheritedAddressSpace"), 0x000);
|
||||||
|
checkField(QStringLiteral("ReadImageFileExecOptions"), 0x001);
|
||||||
|
checkField(QStringLiteral("BeingDebugged"), 0x002);
|
||||||
|
checkField(QStringLiteral("Mutant"), 0x008, true);
|
||||||
|
checkField(QStringLiteral("ImageBaseAddress"), 0x010, true);
|
||||||
|
checkField(QStringLiteral("Ldr"), 0x018, true);
|
||||||
|
checkField(QStringLiteral("ProcessParameters"), 0x020, true);
|
||||||
|
checkField(QStringLiteral("SubSystemData"), 0x028, true);
|
||||||
|
checkField(QStringLiteral("ProcessHeap"), 0x030, true);
|
||||||
|
checkField(QStringLiteral("FastPebLock"), 0x038, true);
|
||||||
|
checkField(QStringLiteral("AtlThunkSListPtr"), 0x040, true);
|
||||||
|
checkField(QStringLiteral("IFEOKey"), 0x048, true);
|
||||||
|
checkField(QStringLiteral("SystemReserved"), 0x060);
|
||||||
|
checkField(QStringLiteral("AtlThunkSListPtr32"), 0x064);
|
||||||
|
checkField(QStringLiteral("ApiSetMap"), 0x068, true);
|
||||||
|
checkField(QStringLiteral("TlsExpansionCounter"), 0x070);
|
||||||
|
checkField(QStringLiteral("TlsBitmap"), 0x078, true);
|
||||||
|
checkField(QStringLiteral("TlsBitmapBits"), 0x080);
|
||||||
|
checkField(QStringLiteral("ReadOnlySharedMemoryBase"), 0x088, true);
|
||||||
|
checkField(QStringLiteral("SharedData"), 0x090, true);
|
||||||
|
checkField(QStringLiteral("ReadOnlyStaticServerData"), 0x098, true);
|
||||||
|
checkField(QStringLiteral("AnsiCodePageData"), 0x0A0, true);
|
||||||
|
checkField(QStringLiteral("OemCodePageData"), 0x0A8, true);
|
||||||
|
checkField(QStringLiteral("UnicodeCaseTableData"), 0x0B0, true);
|
||||||
|
checkField(QStringLiteral("NumberOfProcessors"), 0x0B8);
|
||||||
|
checkField(QStringLiteral("NtGlobalFlag"), 0x0BC);
|
||||||
|
checkField(QStringLiteral("HeapSegmentReserve"), 0x0C8);
|
||||||
|
checkField(QStringLiteral("NumberOfHeaps"), 0x0E8);
|
||||||
|
checkField(QStringLiteral("MaximumNumberOfHeaps"), 0x0EC);
|
||||||
|
checkField(QStringLiteral("ProcessHeaps"), 0x0F0, true);
|
||||||
|
checkField(QStringLiteral("OSMajorVersion"), 0x118);
|
||||||
|
checkField(QStringLiteral("OSMinorVersion"), 0x11C);
|
||||||
|
checkField(QStringLiteral("OSBuildNumber"), 0x120);
|
||||||
|
checkField(QStringLiteral("SessionId"), 0x2C0);
|
||||||
|
checkField(QStringLiteral("CsrServerReadOnlySharedMemoryBase"), 0x380);
|
||||||
|
checkField(QStringLiteral("TppWorkerpListLock"), 0x388, true);
|
||||||
|
checkField(QStringLiteral("WaitOnAddressHashTable"), 0x3A0);
|
||||||
|
checkField(QStringLiteral("TelemetryCoverageHeader"), 0x7A0, true);
|
||||||
|
checkField(QStringLiteral("CloudFileFlags"), 0x7A8);
|
||||||
|
checkField(QStringLiteral("CloudFileDiagFlags"), 0x7AC);
|
||||||
|
checkField(QStringLiteral("PlaceholderCompatibilityMode"), 0x7B0);
|
||||||
|
checkField(QStringLiteral("LeapSecondData"), 0x7B8, true);
|
||||||
|
checkField(QStringLiteral("NtGlobalFlag2"), 0x7C4);
|
||||||
|
|
||||||
|
QVERIFY2(failCount == 0,
|
||||||
|
qPrintable(QString("%1 PEB field(s) have wrong offsets or are missing").arg(failCount)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestRoundtripWinSdk::roundTrip30()
|
||||||
|
{
|
||||||
|
const int kRequired = 30;
|
||||||
|
|
||||||
|
// Deterministic shuffle
|
||||||
|
QVector<int> shuffled = rootIndices;
|
||||||
|
std::mt19937 rng(42);
|
||||||
|
std::shuffle(shuffled.begin(), shuffled.end(), rng);
|
||||||
|
|
||||||
|
int passCount = 0;
|
||||||
|
int failCount = 0;
|
||||||
|
int skipCount = 0;
|
||||||
|
|
||||||
|
for (int ri : shuffled) {
|
||||||
|
uint64_t rootId = fullTree.nodes[ri].id;
|
||||||
|
QString structName = fullTree.nodes[ri].structTypeName;
|
||||||
|
|
||||||
|
// Pass 1: export from full tree
|
||||||
|
QString cpp1 = renderCpp(fullTree, rootId, nullptr, true);
|
||||||
|
if (cpp1.isEmpty()) {
|
||||||
|
skipCount++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pass 2: re-import
|
||||||
|
QString err;
|
||||||
|
NodeTree tree2 = importFromSource(cpp1, &err);
|
||||||
|
if (tree2.nodes.isEmpty()) {
|
||||||
|
skipCount++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the root in re-imported tree
|
||||||
|
int rootIdx2 = -1;
|
||||||
|
for (int i = 0; i < tree2.nodes.size(); i++) {
|
||||||
|
if (tree2.nodes[i].parentId == 0 && tree2.nodes[i].kind == NodeKind::Struct) {
|
||||||
|
if (tree2.nodes[i].structTypeName == structName) {
|
||||||
|
rootIdx2 = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (rootIdx2 < 0) {
|
||||||
|
// Take first root
|
||||||
|
for (int i = 0; i < tree2.nodes.size(); i++) {
|
||||||
|
if (tree2.nodes[i].parentId == 0 && tree2.nodes[i].kind == NodeKind::Struct) {
|
||||||
|
rootIdx2 = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (rootIdx2 < 0) {
|
||||||
|
skipCount++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pass 3: re-export
|
||||||
|
QString cpp2 = renderCpp(tree2, tree2.nodes[rootIdx2].id, nullptr, true);
|
||||||
|
|
||||||
|
if (cpp1 == cpp2) {
|
||||||
|
passCount++;
|
||||||
|
if (passCount <= kRequired)
|
||||||
|
qDebug() << " PASS" << passCount << structName;
|
||||||
|
} else {
|
||||||
|
failCount++;
|
||||||
|
if (failCount <= 5) {
|
||||||
|
// Log first few failures for diagnostics
|
||||||
|
QStringList lines1 = cpp1.split('\n');
|
||||||
|
QStringList lines2 = cpp2.split('\n');
|
||||||
|
int diffLine = -1;
|
||||||
|
for (int i = 0; i < qMin(lines1.size(), lines2.size()); i++) {
|
||||||
|
if (lines1[i] != lines2[i]) { diffLine = i; break; }
|
||||||
|
}
|
||||||
|
if (diffLine >= 0) {
|
||||||
|
qDebug() << " FAIL" << structName << "first diff at line" << diffLine;
|
||||||
|
qDebug() << " cpp1:" << lines1[diffLine].left(120);
|
||||||
|
qDebug() << " cpp2:" << lines2[diffLine].left(120);
|
||||||
|
} else {
|
||||||
|
qDebug() << " FAIL" << structName << "line count differs:"
|
||||||
|
<< lines1.size() << "vs" << lines2.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (passCount >= kRequired && failCount > 5)
|
||||||
|
break; // found enough passes and logged enough failures
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "Round-trip results: pass=" << passCount
|
||||||
|
<< "fail=" << failCount << "skip=" << skipCount;
|
||||||
|
QVERIFY2(passCount >= kRequired,
|
||||||
|
qPrintable(QString("Need %1 stable round-trips, got %2")
|
||||||
|
.arg(kRequired).arg(passCount)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestRoundtripWinSdk::generateRcx()
|
||||||
|
{
|
||||||
|
// Set all root structs collapsed
|
||||||
|
for (int ri : rootIndices)
|
||||||
|
fullTree.nodes[ri].collapsed = true;
|
||||||
|
|
||||||
|
fullTree.baseAddress = 0xFFFFF80000000000ULL;
|
||||||
|
|
||||||
|
QJsonObject json = fullTree.toJson();
|
||||||
|
QJsonDocument jdoc(json);
|
||||||
|
QByteArray data = jdoc.toJson(QJsonDocument::Indented);
|
||||||
|
|
||||||
|
QVERIFY2(data.size() > 1000000,
|
||||||
|
qPrintable(QString("RCX too small: %1 bytes").arg(data.size())));
|
||||||
|
|
||||||
|
QString outPath = QStringLiteral(WINSDK_RCX_OUTPUT);
|
||||||
|
QFile file(outPath);
|
||||||
|
QVERIFY2(file.open(QIODevice::WriteOnly | QIODevice::Truncate),
|
||||||
|
qPrintable("Cannot write " + outPath));
|
||||||
|
file.write(data);
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
qDebug() << "Wrote" << data.size() << "bytes to" << outPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
QTEST_MAIN(TestRoundtripWinSdk)
|
||||||
|
#include "test_roundtrip_winsdk.moc"
|
||||||
@@ -644,8 +644,8 @@ private slots:
|
|||||||
data[16] = 0xAA; // in region 1 (executable)
|
data[16] = 0xAA; // in region 1 (executable)
|
||||||
|
|
||||||
QVector<MemoryRegion> regions;
|
QVector<MemoryRegion> regions;
|
||||||
regions.append({0, 16, true, true, false, "heap"});
|
regions.push_back(MemoryRegion{0, 16, true, true, false, "heap"});
|
||||||
regions.append({16, 16, true, false, true, "code"});
|
regions.push_back(MemoryRegion{16, 16, true, false, true, "code"});
|
||||||
|
|
||||||
auto prov = std::make_shared<RegionProvider>(data, regions);
|
auto prov = std::make_shared<RegionProvider>(data, regions);
|
||||||
ScanEngine engine;
|
ScanEngine engine;
|
||||||
@@ -671,8 +671,8 @@ private slots:
|
|||||||
data[16] = 0xBB; // region 1 (not writable)
|
data[16] = 0xBB; // region 1 (not writable)
|
||||||
|
|
||||||
QVector<MemoryRegion> regions;
|
QVector<MemoryRegion> regions;
|
||||||
regions.append({0, 16, true, true, false, "data"});
|
regions.push_back(MemoryRegion{0, 16, true, true, false, "data"});
|
||||||
regions.append({16, 16, true, false, true, "code"});
|
regions.push_back(MemoryRegion{16, 16, true, false, true, "code"});
|
||||||
|
|
||||||
auto prov = std::make_shared<RegionProvider>(data, regions);
|
auto prov = std::make_shared<RegionProvider>(data, regions);
|
||||||
ScanEngine engine;
|
ScanEngine engine;
|
||||||
@@ -698,9 +698,9 @@ private slots:
|
|||||||
data[32] = 0xCC; // region 2: +w +x
|
data[32] = 0xCC; // region 2: +w +x
|
||||||
|
|
||||||
QVector<MemoryRegion> regions;
|
QVector<MemoryRegion> regions;
|
||||||
regions.append({0, 16, true, true, false, "data"});
|
regions.push_back(MemoryRegion{0, 16, true, true, false, "data"});
|
||||||
regions.append({16, 16, true, false, true, "code"});
|
regions.push_back(MemoryRegion{16, 16, true, false, true, "code"});
|
||||||
regions.append({32, 16, true, true, true, "rwx"});
|
regions.push_back(MemoryRegion{32, 16, true, true, true, "rwx"});
|
||||||
|
|
||||||
auto prov = std::make_shared<RegionProvider>(data, regions);
|
auto prov = std::make_shared<RegionProvider>(data, regions);
|
||||||
ScanEngine engine;
|
ScanEngine engine;
|
||||||
@@ -726,7 +726,7 @@ private slots:
|
|||||||
data[0] = 0xDD;
|
data[0] = 0xDD;
|
||||||
|
|
||||||
QVector<MemoryRegion> regions;
|
QVector<MemoryRegion> regions;
|
||||||
regions.append({0, 16, true, true, true, "Game.exe"});
|
regions.push_back(MemoryRegion{0, 16, true, true, true, "Game.exe"});
|
||||||
|
|
||||||
auto prov = std::make_shared<RegionProvider>(data, regions);
|
auto prov = std::make_shared<RegionProvider>(data, regions);
|
||||||
ScanEngine engine;
|
ScanEngine engine;
|
||||||
@@ -943,8 +943,8 @@ private slots:
|
|||||||
|
|
||||||
void provider_customRegions() {
|
void provider_customRegions() {
|
||||||
QVector<MemoryRegion> regs;
|
QVector<MemoryRegion> regs;
|
||||||
regs.append({0x1000, 0x2000, true, true, false, "heap"});
|
regs.push_back(MemoryRegion{0x1000, 0x2000, true, true, false, "heap"});
|
||||||
regs.append({0x3000, 0x1000, true, false, true, "code"});
|
regs.push_back(MemoryRegion{0x3000, 0x1000, true, false, true, "code"});
|
||||||
|
|
||||||
RegionProvider p(QByteArray(0x4000, '\0'), regs);
|
RegionProvider p(QByteArray(0x4000, '\0'), regs);
|
||||||
auto result = p.enumerateRegions();
|
auto result = p.enumerateRegions();
|
||||||
@@ -982,9 +982,9 @@ private slots:
|
|||||||
data[36] = 0xEE; // region 2
|
data[36] = 0xEE; // region 2
|
||||||
|
|
||||||
QVector<MemoryRegion> regions;
|
QVector<MemoryRegion> regions;
|
||||||
regions.append({0, 16, true, true, false, "region0"});
|
regions.push_back(MemoryRegion{0, 16, true, true, false, "region0"});
|
||||||
regions.append({16, 16, true, true, false, "region1"});
|
regions.push_back(MemoryRegion{16, 16, true, true, false, "region1"});
|
||||||
regions.append({32, 16, true, true, false, "region2"});
|
regions.push_back(MemoryRegion{32, 16, true, true, false, "region2"});
|
||||||
|
|
||||||
auto prov = std::make_shared<RegionProvider>(data, regions);
|
auto prov = std::make_shared<RegionProvider>(data, regions);
|
||||||
ScanEngine engine;
|
ScanEngine engine;
|
||||||
@@ -1215,7 +1215,7 @@ private slots:
|
|||||||
data[160] = char(0xCC);
|
data[160] = char(0xCC);
|
||||||
data[210] = char(0xCC);
|
data[210] = char(0xCC);
|
||||||
QVector<MemoryRegion> regions;
|
QVector<MemoryRegion> regions;
|
||||||
regions.append({100, 100, true, false, false, {}});
|
regions.push_back(MemoryRegion{100, 100, true, false, false, {}});
|
||||||
auto prov = std::make_shared<RegionProvider>(data, regions);
|
auto prov = std::make_shared<RegionProvider>(data, regions);
|
||||||
ScanEngine engine;
|
ScanEngine engine;
|
||||||
QSignalSpy finSpy(&engine, &ScanEngine::finished);
|
QSignalSpy finSpy(&engine, &ScanEngine::finished);
|
||||||
@@ -1233,7 +1233,7 @@ private slots:
|
|||||||
void scan_constrainRegions_noOverlap() {
|
void scan_constrainRegions_noOverlap() {
|
||||||
QByteArray data(32, char(0xEE));
|
QByteArray data(32, char(0xEE));
|
||||||
QVector<MemoryRegion> regions;
|
QVector<MemoryRegion> regions;
|
||||||
regions.append({0, 16, true, false, false, {}});
|
regions.push_back(MemoryRegion{0, 16, true, false, false, {}});
|
||||||
auto prov = std::make_shared<RegionProvider>(data, regions);
|
auto prov = std::make_shared<RegionProvider>(data, regions);
|
||||||
ScanEngine engine;
|
ScanEngine engine;
|
||||||
QSignalSpy finSpy(&engine, &ScanEngine::finished);
|
QSignalSpy finSpy(&engine, &ScanEngine::finished);
|
||||||
@@ -1256,8 +1256,8 @@ private slots:
|
|||||||
data[10] = char(0xDD);
|
data[10] = char(0xDD);
|
||||||
data[35] = char(0xDD);
|
data[35] = char(0xDD);
|
||||||
QVector<MemoryRegion> regions;
|
QVector<MemoryRegion> regions;
|
||||||
regions.append({0, 16, true, true, false, {}});
|
regions.push_back(MemoryRegion{0, 16, true, true, false, {}});
|
||||||
regions.append({32, 16, true, true, false, {}});
|
regions.push_back(MemoryRegion{32, 16, true, true, false, {}});
|
||||||
auto prov = std::make_shared<RegionProvider>(data, regions);
|
auto prov = std::make_shared<RegionProvider>(data, regions);
|
||||||
ScanEngine engine;
|
ScanEngine engine;
|
||||||
QSignalSpy finSpy(&engine, &ScanEngine::finished);
|
QSignalSpy finSpy(&engine, &ScanEngine::finished);
|
||||||
@@ -1279,7 +1279,7 @@ private slots:
|
|||||||
data[120] = char(0xAB);
|
data[120] = char(0xAB);
|
||||||
data[160] = char(0xAB);
|
data[160] = char(0xAB);
|
||||||
QVector<MemoryRegion> regions;
|
QVector<MemoryRegion> regions;
|
||||||
regions.append({100, 100, true, true, false, {}});
|
regions.push_back(MemoryRegion{100, 100, true, true, false, {}});
|
||||||
auto prov = std::make_shared<RegionProvider>(data, regions);
|
auto prov = std::make_shared<RegionProvider>(data, regions);
|
||||||
ScanEngine engine;
|
ScanEngine engine;
|
||||||
QSignalSpy finSpy(&engine, &ScanEngine::finished);
|
QSignalSpy finSpy(&engine, &ScanEngine::finished);
|
||||||
@@ -1300,8 +1300,8 @@ private slots:
|
|||||||
data[0x1500] = char(0xCC);
|
data[0x1500] = char(0xCC);
|
||||||
data[0x5500] = char(0xCC);
|
data[0x5500] = char(0xCC);
|
||||||
QVector<MemoryRegion> regions;
|
QVector<MemoryRegion> regions;
|
||||||
regions.append({0x1000, 0x1000, true, false, true, QString("game.exe")});
|
regions.push_back(MemoryRegion{0x1000, 0x1000, true, false, true, QString("game.exe")});
|
||||||
regions.append({0x5000, 0x1000, true, true, false, {}});
|
regions.push_back(MemoryRegion{0x5000, 0x1000, true, true, false, {}});
|
||||||
auto prov = std::make_shared<RegionProvider>(data, regions);
|
auto prov = std::make_shared<RegionProvider>(data, regions);
|
||||||
ScanEngine engine;
|
ScanEngine engine;
|
||||||
QSignalSpy finSpy(&engine, &ScanEngine::finished);
|
QSignalSpy finSpy(&engine, &ScanEngine::finished);
|
||||||
@@ -1345,8 +1345,8 @@ private slots:
|
|||||||
data[12] = char(0xEF);
|
data[12] = char(0xEF);
|
||||||
data[20] = char(0xEF);
|
data[20] = char(0xEF);
|
||||||
QVector<MemoryRegion> regions;
|
QVector<MemoryRegion> regions;
|
||||||
regions.append({0, 16, true, true, false, {}});
|
regions.push_back(MemoryRegion{0, 16, true, true, false, {}});
|
||||||
regions.append({16, 16, true, true, false, {}});
|
regions.push_back(MemoryRegion{16, 16, true, true, false, {}});
|
||||||
auto prov = std::make_shared<RegionProvider>(data, regions);
|
auto prov = std::make_shared<RegionProvider>(data, regions);
|
||||||
ScanEngine engine;
|
ScanEngine engine;
|
||||||
QSignalSpy finSpy(&engine, &ScanEngine::finished);
|
QSignalSpy finSpy(&engine, &ScanEngine::finished);
|
||||||
@@ -1368,8 +1368,8 @@ private slots:
|
|||||||
data[0x1100] = char(0xBB);
|
data[0x1100] = char(0xBB);
|
||||||
data[0x2100] = char(0xBB);
|
data[0x2100] = char(0xBB);
|
||||||
QVector<MemoryRegion> regions;
|
QVector<MemoryRegion> regions;
|
||||||
regions.append({0x1000, 0x1000, true, false, true, {}});
|
regions.push_back(MemoryRegion{0x1000, 0x1000, true, false, true, {}});
|
||||||
regions.append({0x2000, 0x1000, true, true, false, {}});
|
regions.push_back(MemoryRegion{0x2000, 0x1000, true, true, false, {}});
|
||||||
auto prov = std::make_shared<RegionProvider>(data, regions);
|
auto prov = std::make_shared<RegionProvider>(data, regions);
|
||||||
ScanEngine engine;
|
ScanEngine engine;
|
||||||
QSignalSpy finSpy(&engine, &ScanEngine::finished);
|
QSignalSpy finSpy(&engine, &ScanEngine::finished);
|
||||||
@@ -1394,7 +1394,7 @@ private slots:
|
|||||||
data[15] = char(0xAA); // inside region, should be found
|
data[15] = char(0xAA); // inside region, should be found
|
||||||
data[25] = char(0xAA); // outside region, should NOT be found
|
data[25] = char(0xAA); // outside region, should NOT be found
|
||||||
QVector<MemoryRegion> regions;
|
QVector<MemoryRegion> regions;
|
||||||
regions.append({10, 10, true, true, false, {}});
|
regions.push_back(MemoryRegion{10, 10, true, true, false, {}});
|
||||||
auto prov = std::make_shared<RegionProvider>(data, regions);
|
auto prov = std::make_shared<RegionProvider>(data, regions);
|
||||||
ScanEngine engine;
|
ScanEngine engine;
|
||||||
QSignalSpy finSpy(&engine, &ScanEngine::finished);
|
QSignalSpy finSpy(&engine, &ScanEngine::finished);
|
||||||
@@ -1415,7 +1415,7 @@ private slots:
|
|||||||
data[5] = char(0xBB);
|
data[5] = char(0xBB);
|
||||||
data[15] = char(0xBB);
|
data[15] = char(0xBB);
|
||||||
QVector<MemoryRegion> regions;
|
QVector<MemoryRegion> regions;
|
||||||
regions.append({0, 32, true, true, false, {}});
|
regions.push_back(MemoryRegion{0, 32, true, true, false, {}});
|
||||||
auto prov = std::make_shared<RegionProvider>(data, regions);
|
auto prov = std::make_shared<RegionProvider>(data, regions);
|
||||||
ScanEngine engine;
|
ScanEngine engine;
|
||||||
QSignalSpy finSpy(&engine, &ScanEngine::finished);
|
QSignalSpy finSpy(&engine, &ScanEngine::finished);
|
||||||
@@ -1506,7 +1506,7 @@ private slots:
|
|||||||
QByteArray data(0x10000, 0);
|
QByteArray data(0x10000, 0);
|
||||||
data[0x8100] = char(0xFF);
|
data[0x8100] = char(0xFF);
|
||||||
QVector<MemoryRegion> regions;
|
QVector<MemoryRegion> regions;
|
||||||
regions.append({0x8000, 0x1000, true, true, false, {}});
|
regions.push_back(MemoryRegion{0x8000, 0x1000, true, true, false, {}});
|
||||||
auto prov = std::make_shared<RegionProvider>(data, regions);
|
auto prov = std::make_shared<RegionProvider>(data, regions);
|
||||||
ScanEngine engine;
|
ScanEngine engine;
|
||||||
QSignalSpy finSpy(&engine, &ScanEngine::finished);
|
QSignalSpy finSpy(&engine, &ScanEngine::finished);
|
||||||
@@ -1581,7 +1581,7 @@ private slots:
|
|||||||
QByteArray data(64, 0);
|
QByteArray data(64, 0);
|
||||||
data[20] = char(0xFE);
|
data[20] = char(0xFE);
|
||||||
QVector<MemoryRegion> regions;
|
QVector<MemoryRegion> regions;
|
||||||
regions.append({0, 64, true, true, false, {}});
|
regions.push_back(MemoryRegion{0, 64, true, true, false, {}});
|
||||||
auto prov = std::make_shared<RegionProvider>(data, regions);
|
auto prov = std::make_shared<RegionProvider>(data, regions);
|
||||||
ScanEngine engine;
|
ScanEngine engine;
|
||||||
QSignalSpy finSpy(&engine, &ScanEngine::finished);
|
QSignalSpy finSpy(&engine, &ScanEngine::finished);
|
||||||
@@ -1602,7 +1602,7 @@ private slots:
|
|||||||
QByteArray data(64, 0);
|
QByteArray data(64, 0);
|
||||||
data[36] = char(0xDE); data[37] = char(0xAD); data[38] = char(0xBE); data[39] = char(0xEF);
|
data[36] = char(0xDE); data[37] = char(0xAD); data[38] = char(0xBE); data[39] = char(0xEF);
|
||||||
QVector<MemoryRegion> regions;
|
QVector<MemoryRegion> regions;
|
||||||
regions.append({0, 64, true, true, false, {}});
|
regions.push_back(MemoryRegion{0, 64, true, true, false, {}});
|
||||||
auto prov = std::make_shared<RegionProvider>(data, regions);
|
auto prov = std::make_shared<RegionProvider>(data, regions);
|
||||||
ScanEngine engine;
|
ScanEngine engine;
|
||||||
QSignalSpy finSpy(&engine, &ScanEngine::finished);
|
QSignalSpy finSpy(&engine, &ScanEngine::finished);
|
||||||
@@ -1624,7 +1624,7 @@ private slots:
|
|||||||
QByteArray data(64, 0);
|
QByteArray data(64, 0);
|
||||||
data[36] = char(0xDE); data[37] = char(0xAD); data[38] = char(0xBE); data[39] = char(0xEF);
|
data[36] = char(0xDE); data[37] = char(0xAD); data[38] = char(0xBE); data[39] = char(0xEF);
|
||||||
QVector<MemoryRegion> regions;
|
QVector<MemoryRegion> regions;
|
||||||
regions.append({0, 64, true, true, false, {}});
|
regions.push_back(MemoryRegion{0, 64, true, true, false, {}});
|
||||||
auto prov = std::make_shared<RegionProvider>(data, regions);
|
auto prov = std::make_shared<RegionProvider>(data, regions);
|
||||||
ScanEngine engine;
|
ScanEngine engine;
|
||||||
QSignalSpy finSpy(&engine, &ScanEngine::finished);
|
QSignalSpy finSpy(&engine, &ScanEngine::finished);
|
||||||
@@ -1643,7 +1643,7 @@ private slots:
|
|||||||
// Region [0, 64). Constraint [30, 32). 4-byte pattern can't fit in 2 bytes.
|
// Region [0, 64). Constraint [30, 32). 4-byte pattern can't fit in 2 bytes.
|
||||||
QByteArray data(64, char(0xAA));
|
QByteArray data(64, char(0xAA));
|
||||||
QVector<MemoryRegion> regions;
|
QVector<MemoryRegion> regions;
|
||||||
regions.append({0, 64, true, true, false, {}});
|
regions.push_back(MemoryRegion{0, 64, true, true, false, {}});
|
||||||
auto prov = std::make_shared<RegionProvider>(data, regions);
|
auto prov = std::make_shared<RegionProvider>(data, regions);
|
||||||
ScanEngine engine;
|
ScanEngine engine;
|
||||||
QSignalSpy finSpy(&engine, &ScanEngine::finished);
|
QSignalSpy finSpy(&engine, &ScanEngine::finished);
|
||||||
@@ -1663,7 +1663,7 @@ private slots:
|
|||||||
QByteArray data(64, 0);
|
QByteArray data(64, 0);
|
||||||
data[30] = char(0x11); data[31] = char(0x22); data[32] = char(0x33); data[33] = char(0x44);
|
data[30] = char(0x11); data[31] = char(0x22); data[32] = char(0x33); data[33] = char(0x44);
|
||||||
QVector<MemoryRegion> regions;
|
QVector<MemoryRegion> regions;
|
||||||
regions.append({0, 64, true, true, false, {}});
|
regions.push_back(MemoryRegion{0, 64, true, true, false, {}});
|
||||||
auto prov = std::make_shared<RegionProvider>(data, regions);
|
auto prov = std::make_shared<RegionProvider>(data, regions);
|
||||||
ScanEngine engine;
|
ScanEngine engine;
|
||||||
QSignalSpy finSpy(&engine, &ScanEngine::finished);
|
QSignalSpy finSpy(&engine, &ScanEngine::finished);
|
||||||
@@ -1686,8 +1686,8 @@ private slots:
|
|||||||
data[15] = char(0x77); // last byte of first region
|
data[15] = char(0x77); // last byte of first region
|
||||||
data[16] = char(0x77); // first byte of second region
|
data[16] = char(0x77); // first byte of second region
|
||||||
QVector<MemoryRegion> regions;
|
QVector<MemoryRegion> regions;
|
||||||
regions.append({0, 16, true, true, false, {}});
|
regions.push_back(MemoryRegion{0, 16, true, true, false, {}});
|
||||||
regions.append({16, 16, true, true, false, {}});
|
regions.push_back(MemoryRegion{16, 16, true, true, false, {}});
|
||||||
auto prov = std::make_shared<RegionProvider>(data, regions);
|
auto prov = std::make_shared<RegionProvider>(data, regions);
|
||||||
ScanEngine engine;
|
ScanEngine engine;
|
||||||
QSignalSpy finSpy(&engine, &ScanEngine::finished);
|
QSignalSpy finSpy(&engine, &ScanEngine::finished);
|
||||||
@@ -1711,7 +1711,7 @@ private slots:
|
|||||||
QByteArray data(64, 0);
|
QByteArray data(64, 0);
|
||||||
data[10] = char(0xAA); data[11] = char(0xBB); data[12] = char(0xCC); data[13] = char(0xDD);
|
data[10] = char(0xAA); data[11] = char(0xBB); data[12] = char(0xCC); data[13] = char(0xDD);
|
||||||
QVector<MemoryRegion> regions;
|
QVector<MemoryRegion> regions;
|
||||||
regions.append({0, 64, true, true, false, {}});
|
regions.push_back(MemoryRegion{0, 64, true, true, false, {}});
|
||||||
auto prov = std::make_shared<RegionProvider>(data, regions);
|
auto prov = std::make_shared<RegionProvider>(data, regions);
|
||||||
ScanEngine engine;
|
ScanEngine engine;
|
||||||
QSignalSpy finSpy(&engine, &ScanEngine::finished);
|
QSignalSpy finSpy(&engine, &ScanEngine::finished);
|
||||||
|
|||||||
Reference in New Issue
Block a user