sanitize surrogate characters which cause qt to crash if unpaired

This commit is contained in:
Akash Mozumdar 2020-01-18 23:25:57 -07:00
parent 81d5b745aa
commit 0ace753199
7 changed files with 28 additions and 17 deletions

View File

@ -60,7 +60,7 @@ namespace
std::scoped_lock writeLock(extenMutex); std::scoped_lock writeLock(extenMutex);
std::vector<Extension> extensions; std::vector<Extension> extensions;
for (auto extenName : extenNames) 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; ::extensions = extensions;
} }
} }

View File

@ -54,7 +54,7 @@ void TextThread::Push(BYTE* data, int length)
if (filterRepetition) 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 (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); if (hp.type & BLOCK_FLOOD) Host::RemoveHook(tp.processId, tp.addr);

View File

@ -66,7 +66,7 @@ MainWindow::MainWindow(QWidget *parent) :
extenWindow(new ExtenWindow(this)) extenWindow(new ExtenWindow(this))
{ {
ui->setupUi(this); ui->setupUi(this);
for (auto [text, slot] : Array<std::tuple<const char*, void(MainWindow::*)()>>{ for (auto [text, slot] : Array<const char*, void(MainWindow::*)()>{
{ ATTACH, &MainWindow::AttachProcess }, { ATTACH, &MainWindow::AttachProcess },
{ LAUNCH, &MainWindow::LaunchProcess }, { LAUNCH, &MainWindow::LaunchProcess },
{ DETACH, &MainWindow::DetachProcess }, { DETACH, &MainWindow::DetachProcess },
@ -211,8 +211,9 @@ bool MainWindow::SentenceReceived(TextThread& thread, std::wstring& sentence)
{ {
if (!DispatchSentenceToExtensions(sentence, GetSentenceInfo(thread).data())) return false; if (!DispatchSentenceToExtensions(sentence, GetSentenceInfo(thread).data())) return false;
sentence += L'\n'; 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(); auto scrollbar = ui->textOutput->verticalScrollBar();
bool atBottom = scrollbar->value() + 3 > scrollbar->maximum() || (double)scrollbar->value() / scrollbar->maximum() > 0.975; // arbitrary bool atBottom = scrollbar->value() + 3 > scrollbar->maximum() || (double)scrollbar->value() / scrollbar->maximum() > 0.975; // arbitrary
QTextCursor cursor(ui->textOutput->document()); QTextCursor cursor(ui->textOutput->document());
@ -354,7 +355,7 @@ void MainWindow::ForgetProcess()
for (auto file : { GAME_SAVE_FILE, HOOK_SAVE_FILE }) for (auto file : { GAME_SAVE_FILE, HOOK_SAVE_FILE })
{ {
QStringList lines = QString::fromUtf8(QTextFile(file, QIODevice::ReadOnly).readAll()).split("\n", QString::SkipEmptyParts); 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()); 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); QLineEdit patternInput(x64 ? "CC CC 48 89" : "55 8B EC", &dialog);
assert(QByteArray::fromHex(patternInput.text().toUtf8()) == QByteArray((const char*)sp.pattern, sp.length)); assert(QByteArray::fromHex(patternInput.text().toUtf8()) == QByteArray((const char*)sp.pattern, sp.length));
layout.addRow(SEARCH_PATTERN, &patternInput); layout.addRow(SEARCH_PATTERN, &patternInput);
for (auto [value, label] : Array<std::tuple<int&, const char*>>{ for (auto [value, label] : Array<int&, const char*>{
{ sp.searchTime, SEARCH_DURATION }, { sp.searchTime, SEARCH_DURATION },
{ sp.offset, PATTERN_OFFSET }, { sp.offset, PATTERN_OFFSET },
{ sp.maxRecords, MAX_HOOK_SEARCH_RECORDS }, { 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); QLineEdit boundInput(QFileInfo(S(Util::GetModuleFilename(GetSelectedProcessId()).value_or(L""))).fileName(), &dialog);
layout.addRow(SEARCH_MODULE, &boundInput); layout.addRow(SEARCH_MODULE, &boundInput);
for (auto [value, label] : Array<std::tuple<uintptr_t&, const char*>>{ for (auto [value, label] : Array<uintptr_t&, const char*>{
{ sp.minAddress, MIN_ADDRESS }, { sp.minAddress, MIN_ADDRESS },
{ sp.maxAddress, MAX_ADDRESS }, { sp.maxAddress, MAX_ADDRESS },
{ sp.padding, STRING_OFFSET }, { sp.padding, STRING_OFFSET },
@ -528,7 +529,8 @@ void MainWindow::FindHooks()
auto hooks = std::make_shared<QStringList>(); auto hooks = std::make_shared<QStringList>();
try 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; } catch (std::out_of_range) { return; }
std::thread([this, hooks] std::thread([this, hooks]
@ -572,7 +574,7 @@ void MainWindow::Settings()
QSettings settings(CONFIG_FILE, QSettings::IniFormat, &dialog); QSettings settings(CONFIG_FILE, QSettings::IniFormat, &dialog);
QFormLayout layout(&dialog); QFormLayout layout(&dialog);
QPushButton saveButton(SAVE_SETTINGS, &dialog); QPushButton saveButton(SAVE_SETTINGS, &dialog);
for (auto [value, label] : Array<std::tuple<bool&, const char*>>{ for (auto [value, label] : Array<bool&, const char*>{
{ TextThread::filterRepetition, FILTER_REPETITION }, { TextThread::filterRepetition, FILTER_REPETITION },
{ autoAttach, AUTO_ATTACH }, { autoAttach, AUTO_ATTACH },
{ autoAttachSavedOnly, ATTACH_SAVED_ONLY }, { autoAttachSavedOnly, ATTACH_SAVED_ONLY },
@ -584,7 +586,7 @@ void MainWindow::Settings()
layout.addRow(label, checkBox); layout.addRow(label, checkBox);
connect(&saveButton, &QPushButton::clicked, [checkBox, label, &settings, &value] { settings.setValue(label, value = checkBox->isChecked()); }); connect(&saveButton, &QPushButton::clicked, [checkBox, label, &settings, &value] { settings.setValue(label, value = checkBox->isChecked()); });
} }
for (auto [value, label] : Array<std::tuple<int&, const char*>>{ for (auto [value, label] : Array<int&, const char*>{
{ TextThread::maxBufferSize, MAX_BUFFER_SIZE }, { TextThread::maxBufferSize, MAX_BUFFER_SIZE },
{ TextThread::flushDelay, FLUSH_DELAY }, { TextThread::flushDelay, FLUSH_DELAY },
{ TextThread::maxHistorySize, MAX_HISTORY_SIZE }, { TextThread::maxHistorySize, MAX_HISTORY_SIZE },
@ -612,7 +614,7 @@ void MainWindow::Extensions()
void MainWindow::ViewThread(int index) void MainWindow::ViewThread(int index)
{ {
ui->ttCombo->setCurrentIndex(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); ui->textOutput->moveCursor(QTextCursor::End);
} }

View File

@ -157,7 +157,7 @@ public:
ui.display->setTextFormat(Qt::PlainText); ui.display->setTextFormat(Qt::PlainText);
setGeometry(settings.value(WINDOW, geometry()).toRect()); setGeometry(settings.value(WINDOW, geometry()).toRect());
for (auto [name, default, slot] : Array<std::tuple<const char*, bool, void(ExtraWindow::*)(bool)>>{ for (auto [name, default, slot] : Array<const char*, bool, void(ExtraWindow::*)(bool)>{
{ TOPMOST, false, &ExtraWindow::setTopmost }, { TOPMOST, false, &ExtraWindow::setTopmost },
{ SIZE_LOCK, false, &ExtraWindow::setLock }, { SIZE_LOCK, false, &ExtraWindow::setLock },
{ SHOW_ORIGINAL, true, &ExtraWindow::setShowOriginal }, { SHOW_ORIGINAL, true, &ExtraWindow::setShowOriginal },
@ -187,6 +187,7 @@ public:
void AddSentence(QString sentence) void AddSentence(QString sentence)
{ {
if (!showOriginal) sentence = sentence.section('\n', sentence.count('\n') / 2 + 1); if (!showOriginal) sentence = sentence.section('\n', sentence.count('\n') / 2 + 1);
sanitize(sentence);
sentenceHistory.push_back(sentence); sentenceHistory.push_back(sentence);
historyIndex = sentenceHistory.size() - 1; historyIndex = sentenceHistory.size() - 1;
ui.display->setText(sentence); ui.display->setText(sentence);

View File

@ -101,7 +101,7 @@ std::wstring GetTranslationUri(const std::wstring& text)
bool IsHash(const std::wstring& result) 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<bool, std::wstring> Translate(const std::wstring& text) std::pair<bool, std::wstring> Translate(const std::wstring& text)

View File

@ -26,7 +26,12 @@ constexpr bool x64 = true;
constexpr bool x64 = false; constexpr bool x64 = false;
#endif #endif
template <typename T, typename... X> using Array = std::conditional_t<sizeof...(X), std::tuple<T, X...>[], T[]>; template <typename T, typename... Xs>
struct ArrayImpl { using type = std::tuple<T, Xs...>[]; };
template <typename T>
struct ArrayImpl<T> { using type = T[]; };
template <typename... Ts>
using Array = typename ArrayImpl<Ts...>::type;
template <auto F> using Functor = std::integral_constant<std::decay_t<decltype(F)>, F>; template <auto F> using Functor = std::integral_constant<std::decay_t<decltype(F)>, F>;

View File

@ -23,6 +23,9 @@
static thread_local bool ok; static thread_local bool ok;
struct QTextFile : QFile { QTextFile(QString name, QIODevice::OpenMode mode) : QFile(name) { open(mode | QIODevice::Text); } }; 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 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::string& s) { return QString::fromStdString(s); }
inline QString S(const std::wstring& S) { return QString::fromStdWString(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; }