diff --git a/GUI/extenwindow.cpp b/GUI/extenwindow.cpp index 9ab3b1b..3cbfcf3 100644 --- a/GUI/extenwindow.cpp +++ b/GUI/extenwindow.cpp @@ -1,6 +1,6 @@ #include "extenwindow.h" #include "ui_extenwindow.h" -#include "defs.h" +#include "text.h" #include "types.h" #include #include @@ -142,7 +142,7 @@ void ExtenWindow::dropEvent(QDropEvent* event) void ExtenWindow::on_addButton_clicked() { - Add(QFileDialog::getOpenFileName(this, "Select Extension", "C:\\", "Extensions (*.dll)")); + Add(QFileDialog::getOpenFileName(this, SELECT_EXTENSION, "C:\\", EXTENSIONS)); } void ExtenWindow::on_rmvButton_clicked() diff --git a/GUI/extenwindow.h b/GUI/extenwindow.h index 9923674..041dff2 100644 --- a/GUI/extenwindow.h +++ b/GUI/extenwindow.h @@ -2,6 +2,7 @@ #define EXTENSIONS_H #include "qtcommon.h" +#include "defs.h" #include #include #include @@ -34,7 +35,7 @@ private: void dropEvent(QDropEvent* event); Ui::ExtenWindow* ui; - QFile extenSaveFile = QFile("Extensions.txt"); + QFile extenSaveFile = QFile(EXTEN_SAVE_FILE); QListWidget* extenList; }; diff --git a/GUI/host/host.cc b/GUI/host/host.cc index f42597d..8ca9df0 100644 --- a/GUI/host/host.cc +++ b/GUI/host/host.cc @@ -5,6 +5,7 @@ #include "host.h" #include "const.h" #include "defs.h" +#include "text.h" #include "../vnrhook/hijack/texthook.h" namespace @@ -59,7 +60,7 @@ namespace LOCK(hostMutex); if (textThreadsByParams[tp] == nullptr) { - if (textThreadsByParams.size() > MAX_THREAD_COUNT) return Host::AddConsoleOutput(L"too many text threads: can't create more"); + if (textThreadsByParams.size() > MAX_THREAD_COUNT) return Host::AddConsoleOutput(TOO_MANY_THREADS); OnCreate(textThreadsByParams[tp] = std::make_shared(tp, Host::GetHookParam(tp), Host::GetHookName(tp))); } textThreadsByParams[tp]->Push(text, len); @@ -91,7 +92,7 @@ namespace RemoveThreads([&](ThreadParam tp) { return tp.pid == processId; }); } - void StartPipe() + void CreatePipe() { std::thread([] { @@ -108,15 +109,11 @@ namespace ReadFile(hookPipe, &processId, sizeof(processId), &bytesRead, nullptr); RegisterProcess(processId, hostPipe); - // jichi 9/27/2013: why recursion? - // Artikash 5/20/2018: Easy way to create a new pipe for another process - StartPipe(); + CreatePipe(); while (ReadFile(hookPipe, buffer, PIPE_BUFFER_SIZE, &bytesRead, nullptr)) switch (*(int*)buffer) { - //case HOST_NOTIFICATION_NEWHOOK: // Artikash 7/18/2018: Useless for now, but could be used to implement smth later - //break; case HOST_NOTIFICATION_RMVHOOK: { auto info = *(HookRemovedNotif*)buffer; @@ -139,9 +136,9 @@ namespace break; } + UnregisterProcess(processId); DisconnectNamedPipe(hookPipe); DisconnectNamedPipe(hostPipe); - UnregisterProcess(processId); CloseHandle(hookPipe); CloseHandle(hostPipe); }).detach(); @@ -151,7 +148,6 @@ namespace { std::thread([] { - std::wstring last; while (true) { Sleep(50); @@ -161,8 +157,8 @@ namespace { if (wchar_t* clipboardData = (wchar_t*)GlobalLock(clipboardHandle)) { - if (last != clipboardData) - Host::GetThread(CLIPBOARD)->AddSentence(last = clipboardData); + static std::wstring last; + if (last != clipboardData) Host::GetThread(CLIPBOARD)->AddSentence(last = clipboardData); GlobalUnlock(clipboardHandle); } } @@ -182,7 +178,7 @@ namespace Host OnCreate(textThreadsByParams[CONSOLE] = std::make_shared(CONSOLE, HookParam{}, L"Console")); OnCreate(textThreadsByParams[CLIPBOARD] = std::make_shared(CLIPBOARD, HookParam{}, L"Clipboard")); StartCapturingClipboard(); - StartPipe(); + CreatePipe(); } void Close() @@ -202,7 +198,7 @@ namespace Host CloseHandle(CreateMutexW(nullptr, FALSE, (ITH_HOOKMAN_MUTEX_ + std::to_wstring(processId)).c_str())); if (GetLastError() == ERROR_ALREADY_EXISTS) { - AddConsoleOutput(L"already injected"); + AddConsoleOutput(ALREADY_INJECTED); return false; } @@ -218,7 +214,7 @@ namespace Host IsWow64Process(processHandle, &invalidProcess); if (invalidProcess) { - AddConsoleOutput(L"architecture mismatch: try 32 bit Textractor instead"); + AddConsoleOutput(ARCHITECTURE_MISMATCH); CloseHandle(processHandle); return false; } @@ -239,7 +235,7 @@ namespace Host } } - AddConsoleOutput(L"couldn't inject dll"); + AddConsoleOutput(INJECT_FAILED); return false; } diff --git a/GUI/host/host.h b/GUI/host/host.h index 455042a..6702dc0 100644 --- a/GUI/host/host.h +++ b/GUI/host/host.h @@ -30,8 +30,8 @@ namespace Host void AddConsoleOutput(std::wstring text); } -inline UINT DEFAULT_CODEPAGE = SHIFT_JIS; -inline std::wstring StringToWideString(const std::string& text, UINT encoding = DEFAULT_CODEPAGE) +inline UINT CURRENT_CODEPAGE = SHIFT_JIS; +inline std::wstring StringToWideString(const std::string& text, UINT encoding = CURRENT_CODEPAGE) { std::wstring ret(text.size() + 1, 0); ret.resize(MultiByteToWideChar(encoding, 0, text.c_str(), -1, ret.data(), ret.capacity()) - 1); diff --git a/GUI/host/textthread.cc b/GUI/host/textthread.cc index 94258bd..9deb240 100644 --- a/GUI/host/textthread.cc +++ b/GUI/host/textthread.cc @@ -38,7 +38,7 @@ void TextThread::Push(const BYTE* data, int len) LOCK(threadMutex); buffer += hp.type & USING_UNICODE ? std::wstring((wchar_t*)data, len / 2) - : StringToWideString(std::string((char*)data, len), hp.codepage != 0 ? hp.codepage : DEFAULT_CODEPAGE); + : StringToWideString(std::string((char*)data, len), hp.codepage != 0 ? hp.codepage : CURRENT_CODEPAGE); if (std::all_of(buffer.begin(), buffer.end(), [&](wchar_t c) { return repeatingChars.count(c) > 0; })) buffer.clear(); lastPushTime = GetTickCount(); } diff --git a/GUI/mainwindow.cpp b/GUI/mainwindow.cpp index 11605ce..6376e86 100644 --- a/GUI/mainwindow.cpp +++ b/GUI/mainwindow.cpp @@ -1,6 +1,6 @@ #include "mainwindow.h" #include "ui_mainwindow.h" -#include "defs.h" +#include "text.h" #include "extenwindow.h" #include "misc.h" #include @@ -16,11 +16,11 @@ MainWindow::MainWindow(QWidget *parent) : ttCombo = findChild("ttCombo"); textOutput = findChild("textOutput"); - if (settings.contains("Window")) this->setGeometry(settings.value("Window").toRect()); + if (settings.contains(WINDOW)) this->setGeometry(settings.value(WINDOW).toRect()); // TODO: add GUI for changing these - if (settings.contains("Default_Codepage")) DEFAULT_CODEPAGE = settings.value("Default_Codepage").toInt(); - if (settings.contains("Flush_Delay")) TextThread::flushDelay = settings.value("Flush_Delay").toInt(); - if (settings.contains("Max_Buffer_Size")) TextThread::maxBufferSize = settings.value("Max_Buffer_Size").toInt(); + if (settings.contains(DEFAULT_CODEPAGE)) CURRENT_CODEPAGE = settings.value(DEFAULT_CODEPAGE).toInt(); + if (settings.contains(FLUSH_DELAY)) TextThread::flushDelay = settings.value(FLUSH_DELAY).toInt(); + if (settings.contains(MAX_BUFFER_SIZE)) TextThread::maxBufferSize = settings.value(MAX_BUFFER_SIZE).toInt(); qRegisterMetaType>(); @@ -37,15 +37,15 @@ MainWindow::MainWindow(QWidget *parent) : [&](std::shared_ptr thread) { emit SigRemoveThread(thread); }, [&](TextThread* thread, std::wstring& output) { return ProcessThreadOutput(thread, output); } ); - Host::AddConsoleOutput(L"Textractor beta v3.4.0 by Artikash\r\nSource code and more information available under GPLv3 at https://github.com/Artikash/Textractor"); + Host::AddConsoleOutput(ABOUT); } MainWindow::~MainWindow() { - settings.setValue("Window", this->geometry()); - settings.setValue("Default_Codepage", DEFAULT_CODEPAGE); - settings.setValue("Flush_Delay", TextThread::flushDelay); - settings.setValue("Max_Buffer_Size", TextThread::maxBufferSize); + settings.setValue(WINDOW, this->geometry()); + settings.setValue(DEFAULT_CODEPAGE, CURRENT_CODEPAGE); + settings.setValue(FLUSH_DELAY, TextThread::flushDelay); + settings.setValue(MAX_BUFFER_SIZE, TextThread::maxBufferSize); settings.sync(); delete ui; @@ -60,7 +60,7 @@ void MainWindow::closeEvent(QCloseEvent*) void MainWindow::AddProcess(unsigned processId) { processCombo->addItem(QString::number(processId, 16).toUpper() + ": " + GetModuleName(processId)); - QFile file("SavedHooks.txt"); + QFile file(HOOK_SAVE_FILE); file.open(QIODevice::ReadOnly); QString processName = GetFullModuleName(processId); QStringList allProcesses = QString(file.readAll()).split("\r", QString::SkipEmptyParts); @@ -175,18 +175,14 @@ QVector MainWindow::GetAllHooks(DWORD processId) void MainWindow::on_attachButton_clicked() { - QMultiHash allProcesses = GetAllProcesses(); + auto allProcesses = GetAllProcesses(); QStringList processList(allProcesses.uniqueKeys()); processList.sort(Qt::CaseInsensitive); bool ok; - QString process = QInputDialog::getItem(this, "Select Process", - "If you don't see the process you want to inject, try running with admin rights\r\nYou can also type in the process id", - processList, 0, true, &ok); - bool injected = false; + QString process = QInputDialog::getItem(this, SELECT_PROCESS, INJECT_INFO, processList, 0, true, &ok); if (!ok) return; - if (process.toInt(nullptr, 0)) injected |= Host::InjectProcess(process.toInt(nullptr, 0)); - else for (auto processId : allProcesses.values(process)) injected |= Host::InjectProcess(processId); - if (!injected) Host::AddConsoleOutput(L"failed to inject"); + if (process.toInt(nullptr, 0)) Host::InjectProcess(process.toInt(nullptr, 0)); + else for (auto processId : allProcesses.values(process)) Host::InjectProcess(processId); } void MainWindow::on_detachButton_clicked() @@ -197,16 +193,16 @@ void MainWindow::on_detachButton_clicked() void MainWindow::on_hookButton_clicked() { bool ok; - QString hookCode = QInputDialog::getText(this, "Add Hook", CodeInfoDump, QLineEdit::Normal, "", &ok); + QString hookCode = QInputDialog::getText(this, ADD_HOOK, CODE_INFODUMP, QLineEdit::Normal, "", &ok); if (!ok) return; if (auto hp = ParseCode(hookCode)) Host::InsertHook(GetSelectedProcessId(), hp.value()); - else Host::AddConsoleOutput(L"invalid code"); + else Host::AddConsoleOutput(INVALID_CODE); } void MainWindow::on_unhookButton_clicked() { - QVector hooks = GetAllHooks(GetSelectedProcessId()); - if (hooks.empty()) return Host::AddConsoleOutput(L"no hooks detected"); + auto hooks = GetAllHooks(GetSelectedProcessId()); + if (hooks.empty()) return Host::AddConsoleOutput(NO_HOOKS); QStringList hookList; for (auto hook : hooks) hookList.push_back( @@ -215,18 +211,18 @@ void MainWindow::on_unhookButton_clicked() GenerateCode(hook, GetSelectedProcessId()) ); bool ok; - QString hook = QInputDialog::getItem(this, "Unhook", "Which hook to remove?", hookList, 0, false, &ok); + QString hook = QInputDialog::getItem(this, UNHOOK, REMOVE_HOOK, hookList, 0, false, &ok); if (ok) Host::RemoveHook(GetSelectedProcessId(), hooks.at(hookList.indexOf(hook)).insertion_address); } void MainWindow::on_saveButton_clicked() { - QVector hooks = GetAllHooks(GetSelectedProcessId()); + auto hooks = GetAllHooks(GetSelectedProcessId()); QString hookList = GetFullModuleName(GetSelectedProcessId()); for (auto hook : hooks) if (!(hook.type & HOOK_ENGINE)) hookList += " , " + GenerateCode(hook, GetSelectedProcessId()); - QFile file("SavedHooks.txt"); + QFile file(HOOK_SAVE_FILE); file.open(QIODevice::Append); file.write((hookList + "\r\n").toUtf8()); } diff --git a/GUI/mainwindow.h b/GUI/mainwindow.h index a9bd8c4..2b02158 100644 --- a/GUI/mainwindow.h +++ b/GUI/mainwindow.h @@ -3,6 +3,7 @@ #include "qtcommon.h" #include "host/host.h" +#include "defs.h" #include #include #include @@ -53,7 +54,7 @@ private: void closeEvent(QCloseEvent*); Ui::MainWindow* ui; - QSettings settings = QSettings("Textractor.ini", QSettings::IniFormat); + QSettings settings = QSettings(CONFIG_FILE, QSettings::IniFormat); QComboBox* processCombo; QComboBox* ttCombo; QPlainTextEdit* textOutput; diff --git a/GUI/misc.h b/GUI/misc.h index 625597c..00270a9 100644 --- a/GUI/misc.h +++ b/GUI/misc.h @@ -10,17 +10,4 @@ QMultiHash GetAllProcesses(); std::optional ParseCode(QString HCode); QString GenerateCode(HookParam hp, DWORD processId); -static QString CodeInfoDump = -"Enter hook code\r\n\ -/H{A|B|W|S|Q|V}[N][codepage#]data_offset[*deref_offset1][:split_offset[*deref_offset2]]@addr[:module[:func]]\r\n\ -OR\r\n\ -Enter read code\r\n\ -/R{S|Q|V}[codepage#][*deref_offset|0]@addr\r\n\ -All numbers except codepage in hexadecimal\r\n\ -A/B: Shift-JIS char little/big endian\r\n\ -W: UTF-16 char\r\n\ -S/Q/V: Shift-JIS/UTF-16/UTF-8 string\r\n\ -Negatives for data_offset/sub_offset refer to registers\r\n\ --4 for EAX, -8 for ECX, -C for EDX, -10 for EBX, -14 for ESP, -18 for EBP, -1C for ESI, -20 for EDI\r\n\ -* means dereference pointer+deref_offset"; #endif // MISC_H diff --git a/include/defs.h b/include/defs.h index de94067..193d9c2 100644 --- a/include/defs.h +++ b/include/defs.h @@ -3,19 +3,32 @@ // vnrhook/defs.h // 8/23/2013 jichi -#define ITH_DLL L"vnrhook" +constexpr auto ITH_DLL = L"vnrhook"; // Pipes -#define HOOK_PIPE L"\\\\.\\pipe\\TEXTRACTOR_HOOK" -#define HOST_PIPE L"\\\\.\\pipe\\TEXTRACTOR_HOST" +constexpr auto HOOK_PIPE = L"\\\\.\\pipe\\TEXTRACTOR_HOOK"; +constexpr auto HOST_PIPE = L"\\\\.\\pipe\\TEXTRACTOR_HOST"; // Sections -#define ITH_SECTION_ L"VNR_SECTION_" // _%d +constexpr auto ITH_SECTION_ = L"VNR_SECTION_"; // _%d // Mutex -#define ITH_HOOKMAN_MUTEX_ L"VNR_HOOKMAN_" // ITH_HOOKMAN_%d +constexpr auto ITH_HOOKMAN_MUTEX_ = L"VNR_HOOKMAN_"; // ITH_HOOKMAN_%d + +// Files + +constexpr auto CONFIG_FILE = u8"Textractor.ini"; +constexpr auto HOOK_SAVE_FILE = u8"SavedHooks.txt"; +constexpr auto EXTEN_SAVE_FILE = u8"Extensions.txt"; + +// Settings + +constexpr auto WINDOW = u8"Window"; +constexpr auto DEFAULT_CODEPAGE = u8"Default_Codepage"; +constexpr auto FLUSH_DELAY = u8"Flush_Delay"; +constexpr auto MAX_BUFFER_SIZE = u8"Max_Buffer_Size"; // EOF diff --git a/include/text.h b/include/text.h new file mode 100644 index 0000000..414ce90 --- /dev/null +++ b/include/text.h @@ -0,0 +1,30 @@ +#pragma once + +constexpr auto SELECT_PROCESS = u8"Select Process"; +constexpr auto INJECT_INFO = u8"If you don't see the process you want to inject, try running with admin rights\r\n" +"You can also type in the process id"; +constexpr auto ADD_HOOK = u8"Add hook"; +constexpr auto CODE_INFODUMP = u8"Enter hook code\r\n" +"/H{A|B|W|S|Q|V}[N][codepage#]data_offset[*deref_offset1][:split_offset[*deref_offset2]]@addr[:module[:func]]\r\n" +"OR\r\n" +"Enter read code\r\n" +"/R{S|Q|V}[codepage#][*deref_offset|0]@addr\r\n" +"All numbers except codepage in hexadecimal\r\n" +"A/B: Shift-JIS char little/big endian\r\n" +"W: UTF-16 char\r\n" +"S/Q/V: Shift-JIS/UTF-16/UTF-8 string\r\n" +"Negatives for data_offset/sub_offset refer to registers\r\n" +"-4 for EAX, -8 for ECX, -C for EDX, -10 for EBX, -14 for ESP, -18 for EBP, -1C for ESI, -20 for EDI\r\n" +"* means dereference pointer+deref_offset"; +constexpr auto UNHOOK = u8"Unhook"; +constexpr auto REMOVE_HOOK = u8"Which hook to remove?"; +constexpr auto SELECT_EXTENSION = u8"Select Extension"; +constexpr auto EXTENSIONS = u8"Extensions (*.dll)"; +constexpr auto ABOUT = L"Textractor beta v3.4.0 by Artikash\r\n" +"Source code and more information available under GPLv3 at https://github.com/Artikash/Textractor"; +constexpr auto TOO_MANY_THREADS = L"Textractor: ERROR: too many text threads: can't create more"; +constexpr auto ALREADY_INJECTED = L"Textractor: ERROR: already injected"; +constexpr auto ARCHITECTURE_MISMATCH = L"Textractor: ERROR: architecture mismatch: try 32 bit Textractor instead"; +constexpr auto INJECT_FAILED = L"Textractor: ERROR: couldn't inject"; +constexpr auto INVALID_CODE = L"Textractor: invalid code"; +constexpr auto NO_HOOKS = L"Textractor: no hooks detected";