From 0ace753199f5898b4bf6c8d36aac2b4d34d95200 Mon Sep 17 00:00:00 2001 From: Akash Mozumdar Date: Sat, 18 Jan 2020 23:25:57 -0700 Subject: [PATCH] sanitize surrogate characters which cause qt to crash if unpaired --- GUI/extenwindow.cpp | 2 +- GUI/host/textthread.cpp | 2 +- GUI/mainwindow.cpp | 20 +++++++++++--------- extensions/extrawindow.cpp | 3 ++- extensions/googletranslate.cpp | 2 +- include/common.h | 7 ++++++- include/qtcommon.h | 9 ++++++--- 7 files changed, 28 insertions(+), 17 deletions(-) diff --git a/GUI/extenwindow.cpp b/GUI/extenwindow.cpp index 8c9e8c8..80d1d4b 100644 --- a/GUI/extenwindow.cpp +++ b/GUI/extenwindow.cpp @@ -60,7 +60,7 @@ namespace std::scoped_lock writeLock(extenMutex); std::vector extensions; for (auto extenName : extenNames) - extensions.push_back(*std::find_if(::extensions.begin(), ::extensions.end(), [&](auto extension) { return extension.name == S(extenName); })); + extensions.push_back(*std::find_if(::extensions.begin(), ::extensions.end(), [&](Extension extension) { return extension.name == S(extenName); })); ::extensions = extensions; } } diff --git a/GUI/host/textthread.cpp b/GUI/host/textthread.cpp index b5bfe1c..b448d1b 100644 --- a/GUI/host/textthread.cpp +++ b/GUI/host/textthread.cpp @@ -54,7 +54,7 @@ void TextThread::Push(BYTE* data, int length) if (filterRepetition) { - if (std::all_of(buffer.begin(), buffer.end(), [&](auto ch) { return repeatingChars.find(ch) != repeatingChars.end(); })) buffer.clear(); + if (std::all_of(buffer.begin(), buffer.end(), [&](wchar_t ch) { return repeatingChars.find(ch) != repeatingChars.end(); })) buffer.clear(); if (RemoveRepetition(buffer)) // sentence repetition detected, which means the entire sentence has already been received { if (hp.type & BLOCK_FLOOD) Host::RemoveHook(tp.processId, tp.addr); diff --git a/GUI/mainwindow.cpp b/GUI/mainwindow.cpp index 2cda42f..5f2c689 100644 --- a/GUI/mainwindow.cpp +++ b/GUI/mainwindow.cpp @@ -66,7 +66,7 @@ MainWindow::MainWindow(QWidget *parent) : extenWindow(new ExtenWindow(this)) { ui->setupUi(this); - for (auto [text, slot] : Array>{ + for (auto [text, slot] : Array{ { ATTACH, &MainWindow::AttachProcess }, { LAUNCH, &MainWindow::LaunchProcess }, { DETACH, &MainWindow::DetachProcess }, @@ -211,8 +211,9 @@ bool MainWindow::SentenceReceived(TextThread& thread, std::wstring& sentence) { if (!DispatchSentenceToExtensions(sentence, GetSentenceInfo(thread).data())) return false; sentence += L'\n'; - if (current == &thread) QMetaObject::invokeMethod(this, [this, sentence = S(sentence)] + if (current == &thread) QMetaObject::invokeMethod(this, [this, sentence = S(sentence)]() mutable { + sanitize(sentence); auto scrollbar = ui->textOutput->verticalScrollBar(); bool atBottom = scrollbar->value() + 3 > scrollbar->maximum() || (double)scrollbar->value() / scrollbar->maximum() > 0.975; // arbitrary QTextCursor cursor(ui->textOutput->document()); @@ -354,7 +355,7 @@ void MainWindow::ForgetProcess() for (auto file : { GAME_SAVE_FILE, HOOK_SAVE_FILE }) { QStringList lines = QString::fromUtf8(QTextFile(file, QIODevice::ReadOnly).readAll()).split("\n", QString::SkipEmptyParts); - lines.erase(std::remove_if(lines.begin(), lines.end(), [&](auto line) { return line.contains(S(processName.value())); }), lines.end()); + lines.erase(std::remove_if(lines.begin(), lines.end(), [&](const QString& line) { return line.contains(S(processName.value())); }), lines.end()); QTextFile(file, QIODevice::WriteOnly | QIODevice::Truncate).write(lines.join("\n").append("\n").toUtf8()); } } @@ -475,7 +476,7 @@ void MainWindow::FindHooks() QLineEdit patternInput(x64 ? "CC CC 48 89" : "55 8B EC", &dialog); assert(QByteArray::fromHex(patternInput.text().toUtf8()) == QByteArray((const char*)sp.pattern, sp.length)); layout.addRow(SEARCH_PATTERN, &patternInput); - for (auto [value, label] : Array>{ + for (auto [value, label] : Array{ { sp.searchTime, SEARCH_DURATION }, { sp.offset, PATTERN_OFFSET }, { sp.maxRecords, MAX_HOOK_SEARCH_RECORDS }, @@ -490,7 +491,7 @@ void MainWindow::FindHooks() } QLineEdit boundInput(QFileInfo(S(Util::GetModuleFilename(GetSelectedProcessId()).value_or(L""))).fileName(), &dialog); layout.addRow(SEARCH_MODULE, &boundInput); - for (auto [value, label] : Array>{ + for (auto [value, label] : Array{ { sp.minAddress, MIN_ADDRESS }, { sp.maxAddress, MAX_ADDRESS }, { sp.padding, STRING_OFFSET }, @@ -528,7 +529,8 @@ void MainWindow::FindHooks() auto hooks = std::make_shared(); try { - Host::FindHooks(processId, sp, [=](HookParam hp, std::wstring text) { if (std::regex_search(text, filter)) *hooks << S(Util::GenerateCode(hp) + L" => " + text); }); + Host::FindHooks(processId, sp, + [=](HookParam hp, std::wstring text) { if (std::regex_search(text, filter)) *hooks << sanitize(S(Util::GenerateCode(hp) + L" => " + text)); }); } catch (std::out_of_range) { return; } std::thread([this, hooks] @@ -572,7 +574,7 @@ void MainWindow::Settings() QSettings settings(CONFIG_FILE, QSettings::IniFormat, &dialog); QFormLayout layout(&dialog); QPushButton saveButton(SAVE_SETTINGS, &dialog); - for (auto [value, label] : Array>{ + for (auto [value, label] : Array{ { TextThread::filterRepetition, FILTER_REPETITION }, { autoAttach, AUTO_ATTACH }, { autoAttachSavedOnly, ATTACH_SAVED_ONLY }, @@ -584,7 +586,7 @@ void MainWindow::Settings() layout.addRow(label, checkBox); connect(&saveButton, &QPushButton::clicked, [checkBox, label, &settings, &value] { settings.setValue(label, value = checkBox->isChecked()); }); } - for (auto [value, label] : Array>{ + for (auto [value, label] : Array{ { TextThread::maxBufferSize, MAX_BUFFER_SIZE }, { TextThread::flushDelay, FLUSH_DELAY }, { TextThread::maxHistorySize, MAX_HISTORY_SIZE }, @@ -612,7 +614,7 @@ void MainWindow::Extensions() void MainWindow::ViewThread(int index) { ui->ttCombo->setCurrentIndex(index); - ui->textOutput->setPlainText(S((current = &Host::GetThread(ParseTextThreadString(ui->ttCombo->itemText(index))))->storage->c_str())); + ui->textOutput->setPlainText(sanitize(S((current = &Host::GetThread(ParseTextThreadString(ui->ttCombo->itemText(index))))->storage->c_str()))); ui->textOutput->moveCursor(QTextCursor::End); } diff --git a/extensions/extrawindow.cpp b/extensions/extrawindow.cpp index 48faaf1..1860351 100644 --- a/extensions/extrawindow.cpp +++ b/extensions/extrawindow.cpp @@ -157,7 +157,7 @@ public: ui.display->setTextFormat(Qt::PlainText); setGeometry(settings.value(WINDOW, geometry()).toRect()); - for (auto [name, default, slot] : Array>{ + for (auto [name, default, slot] : Array{ { TOPMOST, false, &ExtraWindow::setTopmost }, { SIZE_LOCK, false, &ExtraWindow::setLock }, { SHOW_ORIGINAL, true, &ExtraWindow::setShowOriginal }, @@ -187,6 +187,7 @@ public: void AddSentence(QString sentence) { if (!showOriginal) sentence = sentence.section('\n', sentence.count('\n') / 2 + 1); + sanitize(sentence); sentenceHistory.push_back(sentence); historyIndex = sentenceHistory.size() - 1; ui.display->setText(sentence); diff --git a/extensions/googletranslate.cpp b/extensions/googletranslate.cpp index 5ed9f4c..eacd7c1 100644 --- a/extensions/googletranslate.cpp +++ b/extensions/googletranslate.cpp @@ -101,7 +101,7 @@ std::wstring GetTranslationUri(const std::wstring& text) bool IsHash(const std::wstring& result) { - return result.size() == 32 && std::all_of(result.begin(), result.end(), [](auto ch) { return (ch >= L'0' && ch <= L'9') || (ch >= L'a' && ch <= L'z'); }); + return result.size() == 32 && std::all_of(result.begin(), result.end(), [](char ch) { return (ch >= L'0' && ch <= L'9') || (ch >= L'a' && ch <= L'z'); }); } std::pair Translate(const std::wstring& text) diff --git a/include/common.h b/include/common.h index 3a50b90..014a579 100644 --- a/include/common.h +++ b/include/common.h @@ -26,7 +26,12 @@ constexpr bool x64 = true; constexpr bool x64 = false; #endif -template using Array = std::conditional_t[], T[]>; +template +struct ArrayImpl { using type = std::tuple[]; }; +template +struct ArrayImpl { using type = T[]; }; +template +using Array = typename ArrayImpl::type; template using Functor = std::integral_constant, F>; diff --git a/include/qtcommon.h b/include/qtcommon.h index cf4a02c..a483224 100644 --- a/include/qtcommon.h +++ b/include/qtcommon.h @@ -23,6 +23,9 @@ static thread_local bool ok; struct QTextFile : QFile { QTextFile(QString name, QIODevice::OpenMode mode) : QFile(name) { open(mode | QIODevice::Text); } }; -inline std::wstring S(const QString& S) { return { S.toStdWString() }; } -inline QString S(const std::string& S) { return QString::fromStdString(S); } -inline QString S(const std::wstring& S) { return QString::fromStdWString(S); } +inline std::wstring S(const QString& s) { return { s.toStdWString() }; } +inline QString S(const std::string& s) { return QString::fromStdString(s); } +inline QString S(const std::wstring& s) { return QString::fromStdWString(s); } +// TODO: allow paired surrogates +inline void sanitize(QString& s) { s.chop(std::distance(std::remove_if(s.begin(), s.end(), [](QChar ch) { return ch.isSurrogate(); }), s.end())); } +inline QString sanitize(QString&& s) { QString result; for (auto ch : s) if (!ch.isSurrogate()) result.push_back(ch); return result; }