From 8f53eda70e7ae0ecb4ed55e5a3ac49d57b63d0f0 Mon Sep 17 00:00:00 2001 From: Shirona1337 Date: Sat, 20 Jun 2026 01:18:08 +0800 Subject: [PATCH] feat(loader): randomize injector window title and jitter its size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Anti-fingerprint hardening for the loader window: - The OS-level window title (read by GetWindowTextW / window scanners) is now a fresh random alphanumeric string on every launch instead of the fixed "OpenZen Loader" text. The visible custom title bar keeps showing the branded name, so the UI is unchanged. - The window size gets a per-launch random jitter of +/-10px on each axis (base 760x500), with the minimum size lowered by the jitter so a negative jitter isn't clamped. Note: the Win32 *class* name is intentionally not randomized here — a Qt6 top-level window's class atom is fixed at CreateWindow time and Qt 6.10 exposes no hook to customize it (SetClassLongPtr cannot rename a class), so randomizing it would require replacing the Qt window with a hand-rolled native window. Title + size are the practical fingerprint vectors. Co-Authored-By: Claude Opus 4.8 (1M context) --- native/loader/src/MainWindow.cpp | 44 +++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/native/loader/src/MainWindow.cpp b/native/loader/src/MainWindow.cpp index e0f0bf1..b98581b 100644 --- a/native/loader/src/MainWindow.cpp +++ b/native/loader/src/MainWindow.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -32,6 +33,28 @@ namespace loader { namespace { constexpr int kCornerRadius = 12; +constexpr int kBaseWidth = 760; +constexpr int kBaseHeight = 500; +constexpr int kSizeJitter = 10; // ± px of random size jitter applied per launch + +// Random alphanumeric identifier (first char always a letter). Used to give the +// loader window a non-constant Win32 title each launch so a scanner can't match +// it against a fixed window-text string. +QString randomIdent(int minLen, int maxLen) { + static const char kAlphabet[] = + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + constexpr int kLetters = 52; // leading a-zA-Z only + constexpr int kAll = int(sizeof(kAlphabet)) - 1; + QRandomGenerator* rng = QRandomGenerator::global(); + const int len = minLen + int(rng->bounded(quint32(maxLen - minLen + 1))); + QString s; + s.reserve(len); + s.append(QLatin1Char(kAlphabet[rng->bounded(kLetters)])); + for (int i = 1; i < len; ++i) { + s.append(QLatin1Char(kAlphabet[rng->bounded(kAll)])); + } + return s; +} QString fromW(const std::wstring& w) { return QString::fromWCharArray(w.c_str(), static_cast(w.size())); @@ -60,8 +83,14 @@ MainWindow::MainWindow(QWidget* parent) setWindowFlags(Qt::FramelessWindowHint | Qt::Window); setAttribute(Qt::WA_TranslucentBackground); setAttribute(Qt::WA_NoSystemBackground); - setMinimumSize(760, 500); - resize(760, 500); + // Base size with a small random jitter (±kSizeJitter px) on each axis so the + // window dimensions aren't a constant fingerprint. Minimum is lowered by the + // jitter so a negative jitter isn't clamped back to the base size. + setMinimumSize(kBaseWidth - kSizeJitter, kBaseHeight - kSizeJitter); + QRandomGenerator* rng = QRandomGenerator::global(); + const int jitterW = int(rng->bounded(quint32(2 * kSizeJitter + 1))) - kSizeJitter; + const int jitterH = int(rng->bounded(quint32(2 * kSizeJitter + 1))) - kSizeJitter; + resize(kBaseWidth + jitterW, kBaseHeight + jitterH); styleApp(); buildUi(); @@ -75,12 +104,15 @@ MainWindow::MainWindow(QWidget* parent) void MainWindow::buildUi() { #ifdef OPENZEN_BUILD_REVISION - const QString winTitle = QStringLiteral("OpenZen Loader · build %1") + const QString displayTitle = QStringLiteral("OpenZen Loader · build %1") .arg(QString::fromLatin1(OPENZEN_BUILD_REVISION).left(7)); #else - const QString winTitle = QStringLiteral("OpenZen Loader"); + const QString displayTitle = QStringLiteral("OpenZen Loader"); #endif - setWindowTitle(winTitle); + // The OS-level window title (what GetWindowTextW and window scanners read) is + // randomised on every launch so it can't be matched against a fixed string. + // The visible custom title bar below still shows the branded name. + setWindowTitle(randomIdent(8, 16)); auto* central = new QWidget(this); central->setAttribute(Qt::WA_TranslucentBackground); @@ -90,7 +122,7 @@ void MainWindow::buildUi() { root->setSpacing(0); titleBar_ = new TitleBar(central); - titleBar_->setTitleText(winTitle); + titleBar_->setTitleText(displayTitle); root->addWidget(titleBar_); auto* body = new QWidget(central);