diff --git a/GUI/CMakeLists.txt b/GUI/CMakeLists.txt index 2ddc3fd..a8bdd13 100644 --- a/GUI/CMakeLists.txt +++ b/GUI/CMakeLists.txt @@ -7,7 +7,6 @@ set(RESOURCE_FILES NextHooker.rc NextHooker.ico) set(gui_SRCS main.cpp mainwindow.cpp - hostsignaller.cpp misc.cpp extensions.cpp ${RESOURCE_FILES} diff --git a/GUI/extensions.cpp b/GUI/extensions.cpp index a86a650..1ae9016 100644 --- a/GUI/extensions.cpp +++ b/GUI/extensions.cpp @@ -1,7 +1,9 @@ #include "extensions.h" +#include #include #include +std::shared_mutex extenMutex; std::map extensions; int processing; @@ -21,50 +23,29 @@ std::map LoadExtensions() file.remove(0, extensionNumber.length() + 1); extensionNames[extensionNumber.toInt()] = file; } - while (processing) Sleep(10); - processing = -1; + extenMutex.lock(); extensions = newExtensions; - processing = 0; + extenMutex.unlock(); return extensionNames; } std::wstring DispatchSentenceToExtensions(std::wstring sentence, std::unordered_map miscInfo) { - while (processing < 0) Sleep(10); - processing++; - wchar_t* sentenceOrigBuffer = (wchar_t*)malloc((sentence.size() + 1) * sizeof(wchar_t)); - wcscpy(sentenceOrigBuffer, sentence.c_str()); - const wchar_t* sentenceBuffer = sentenceOrigBuffer; + wchar_t* sentenceBuffer = (wchar_t*)malloc((sentence.size() + 1) * sizeof(wchar_t)); + wcscpy(sentenceBuffer, sentence.c_str()); InfoForExtension* miscInfoLinkedList = new InfoForExtension; InfoForExtension* miscInfoTraverser = miscInfoLinkedList; - for (auto i : miscInfo) - { - miscInfoTraverser->propertyName = new char[i.first.size() + 1]; - strcpy(miscInfoTraverser->propertyName, i.first.c_str()); - miscInfoTraverser->propertyValue = i.second; - miscInfoTraverser->nextProperty = new InfoForExtension; - miscInfoTraverser = miscInfoTraverser->nextProperty; - } - miscInfoTraverser->propertyName = new char[sizeof("END")]; - strcpy(miscInfoTraverser->propertyName, "END"); - miscInfoTraverser->nextProperty = nullptr; + for (auto& i : miscInfo) miscInfoTraverser = miscInfoTraverser->nextProperty = new InfoForExtension{ i.first.c_str(), i.second, new InfoForExtension }; + extenMutex.lock_shared(); for (auto i : extensions) { - const wchar_t* prev = sentenceBuffer; + wchar_t* prev = sentenceBuffer; sentenceBuffer = i.second(sentenceBuffer, miscInfoLinkedList); - if (sentenceBuffer == nullptr) sentence = prev; if (sentenceBuffer != prev) free((void*)prev); } - miscInfoTraverser = miscInfoLinkedList; - while (miscInfoTraverser != nullptr) - { - InfoForExtension* nextNode = miscInfoTraverser->nextProperty; - delete[] miscInfoTraverser->propertyName; - delete miscInfoTraverser; - miscInfoTraverser = nextNode; - } + extenMutex.unlock_shared(); + delete miscInfoLinkedList; std::wstring newSentence = std::wstring(sentenceBuffer); free((void*)sentenceBuffer); - processing--; return newSentence; } diff --git a/GUI/extensions.h b/GUI/extensions.h index fb9b27c..bf5283a 100644 --- a/GUI/extensions.h +++ b/GUI/extensions.h @@ -12,10 +12,11 @@ std::map LoadExtensions(); std::wstring DispatchSentenceToExtensions(std::wstring sentence, std::unordered_map miscInfo); struct InfoForExtension { - char* propertyName; - int propertyValue; - InfoForExtension* nextProperty; + ~InfoForExtension() { if (nextProperty) delete nextProperty; }; + const char* propertyName = ""; + int propertyValue = 0; + InfoForExtension* nextProperty = nullptr; }; -typedef const wchar_t*(*ExtensionFunction)(const wchar_t*, const InfoForExtension*); +typedef wchar_t*(*ExtensionFunction)(const wchar_t*, const InfoForExtension*); #endif // EXTENSIONS_H diff --git a/GUI/hostsignaller.cpp b/GUI/hostsignaller.cpp deleted file mode 100644 index bbcbb40..0000000 --- a/GUI/hostsignaller.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include "hostsignaller.h" -#include "extensions.h" - -void HostSignaller::Initialize() -{ - Host::RegisterProcessAttachCallback([&](DWORD pid){ emit AddProcess(pid); }); - Host::RegisterProcessDetachCallback([&](DWORD pid){ emit RemoveProcess(pid); }); - Host::RegisterThreadCreateCallback([&](TextThread* thread) { emit AddThread(thread); }); - Host::RegisterThreadRemoveCallback([&](TextThread* thread){ emit RemoveThread(thread); }); -} diff --git a/GUI/hostsignaller.h b/GUI/hostsignaller.h deleted file mode 100644 index 6a1a627..0000000 --- a/GUI/hostsignaller.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef HOSTSIGNALLER_H -#define HOSTSIGNALLER_H - -#include -#include -#include "../host/host.h" - -// Artikash 7/24/2018: This class is a workaround for the fact that Qt only lets me manipulate the GUI in the main thread. -class HostSignaller : public QObject -{ - Q_OBJECT - -public: - void Initialize(); - -signals: - void AddProcess(unsigned int processId); - void RemoveProcess(unsigned int processId); - void AddThread(TextThread* thread); - void RemoveThread(TextThread* thread); -}; - -#endif // HOSTSIGNALLER_H diff --git a/GUI/main.cpp b/GUI/main.cpp index 09a6de9..2984ab4 100644 --- a/GUI/main.cpp +++ b/GUI/main.cpp @@ -4,7 +4,6 @@ int main(int argc, char *argv[]) { - if (!Host::Start()) return 1; QApplication a(argc, argv); MainWindow w; w.show(); diff --git a/GUI/mainwindow.cpp b/GUI/mainwindow.cpp index e386eea..a4bffa7 100644 --- a/GUI/mainwindow.cpp +++ b/GUI/mainwindow.cpp @@ -21,12 +21,6 @@ #include "../vnrhook/include/const.h" #include "misc.h" -QMainWindow* mainWindow; -QComboBox* processCombo; -QComboBox* ttCombo; -QComboBox* extenCombo; -QPlainTextEdit* textOutput; - QString ProcessString(DWORD processId) { return QString("%1: %2").arg(QString::number(processId), GetModuleName(processId)); @@ -35,26 +29,28 @@ QString ProcessString(DWORD processId) QString TextThreadString(TextThread* thread) { ThreadParameter tp = thread->GetThreadParameter(); - return QString("%1:%2:%3:%4:%5: ").arg( - QString::number(thread->Number()), - QString::number(tp.pid), - QString::number(tp.hook, 16), - QString::number(tp.retn, 16), - QString::number(tp.spl, 16) - ).toUpper(); + return QString("%1:0x%2:0x%3:0x%4: ").arg( + QString::number(tp.pid).toUpper(), + QString::number(tp.hook, 16).toUpper(), + QString::number(tp.retn, 16).toUpper(), + QString::number(tp.spl, 16).toUpper() + ); +} + +ThreadParameter ParseTextThreadString(QString textThreadString) +{ + QStringList threadParam = textThreadString.split(":"); + ThreadParameter tp = {}; + tp.hook = threadParam[1].toULongLong(); + return { threadParam[0].toUInt(), threadParam[1].toULongLong(nullptr, 0), threadParam[2].toULongLong(nullptr, 0), threadParam[3].toULongLong(nullptr, 0) }; } MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), - ui(new Ui::MainWindow), - hostSignaller(new HostSignaller) + ui(new Ui::MainWindow) { ui->setupUi(this); - mainWindow = this; - processCombo = mainWindow->findChild("processCombo"); - ttCombo = mainWindow->findChild("ttCombo"); - extenCombo = mainWindow->findChild("extenCombo"); - textOutput = mainWindow->findChild("textOutput"); + QFile settings("NHWindow"); settings.open(QIODevice::ReadOnly); QDataStream reader(&settings); @@ -62,14 +58,24 @@ MainWindow::MainWindow(QWidget *parent) : reader >> rect; if (rect.bottom()) this->setGeometry(rect); - hostSignaller->Initialize(); - connect(hostSignaller, &HostSignaller::AddProcess, this, &MainWindow::AddProcess); - connect(hostSignaller, &HostSignaller::RemoveProcess, this, &MainWindow::RemoveProcess); - connect(hostSignaller, &HostSignaller::AddThread, this, &MainWindow::AddThread); - connect(hostSignaller, &HostSignaller::RemoveThread, this, &MainWindow::RemoveThread); - connect(this, &MainWindow::ThreadOutputReceived, this, &MainWindow::ThreadOutput); + processCombo = findChild("processCombo"); + ttCombo = findChild("ttCombo"); + extenCombo = findChild("extenCombo"); + textOutput = findChild("textOutput"); + + connect(this, &MainWindow::SigAddProcess, this, &MainWindow::AddProcess); + connect(this, &MainWindow::SigRemoveProcess, this, &MainWindow::RemoveProcess); + connect(this, &MainWindow::SigAddThread, this, &MainWindow::AddThread); + connect(this, &MainWindow::SigRemoveThread, this, &MainWindow::RemoveThread); + connect(this, &MainWindow::SigThreadOutput, this, &MainWindow::ThreadOutput); + Host::Start( + [&](DWORD processId) { emit SigAddProcess(processId); }, + [&](DWORD processId) { emit SigRemoveProcess(processId); }, + [&](TextThread* thread) { emit SigAddThread(thread); }, + [&](TextThread* thread) { emit SigRemoveThread(thread); } + ); + ReloadExtensions(); - Host::Open(); Host::AddConsoleOutput(L"NextHooker beta v2.1.3 by Artikash\r\nSource code and more information available under GPLv3 at https://github.com/Artikash/NextHooker"); } @@ -93,13 +99,9 @@ void MainWindow::AddProcess(unsigned int processId) for (int i = allProcesses.length() - 1; i >= 0; --i) if (allProcesses.at(i).contains(processName)) { - Sleep(50); QStringList hooks = allProcesses.at(i).split(" , "); for (int j = 1; j < hooks.length(); ++j) - { - Sleep(10); Host::InsertHook(processId, ParseCode(hooks.at(j))); - } return; } } @@ -115,27 +117,28 @@ void MainWindow::AddThread(TextThread* thread) TextThreadString(thread) + QString::fromStdWString(Host::GetHookName(thread->GetThreadParameter().pid, thread->GetThreadParameter().hook)) + " (" + - GenerateCode(Host::GetHookParam(thread->GetThreadParameter().pid, thread->GetThreadParameter().hook), thread->GetThreadParameter().pid) + + GenerateCode(Host::GetHookParam(thread->GetThreadParameter()), thread->GetThreadParameter().pid) + ")" ); thread->RegisterOutputCallBack([&](TextThread* thread, std::wstring output) { output = DispatchSentenceToExtensions(output, GetInfoForExtensions(thread)); output += L"\r\n"; - emit ThreadOutputReceived(thread, QString::fromStdWString(output)); + emit SigThreadOutput(thread, QString::fromStdWString(output)); return output; }); } void MainWindow::RemoveThread(TextThread* thread) { - int threadIndex = ttCombo->findText(QString::number(thread->Number()) + ":", Qt::MatchStartsWith); + int threadIndex = ttCombo->findText(TextThreadString(thread), Qt::MatchStartsWith); if (threadIndex == ttCombo->currentIndex()) { ttCombo->setCurrentIndex(0); on_ttCombo_activated(0); } ttCombo->removeItem(threadIndex); + delete thread; } void MainWindow::ThreadOutput(TextThread* thread, QString output) @@ -159,8 +162,8 @@ std::unordered_map MainWindow::GetInfoForExtensions(TextThread { return { - { "current select", ttCombo->currentText().split(":")[0].toInt() == thread->Number() ? 1 : 0 }, - { "text number", thread->Number() }, + { "current select", (int)ttCombo->currentText().startsWith(TextThreadString(thread)) }, + { "text number", 0 }, { "process id", thread->GetThreadParameter().pid }, { "hook address", (int)thread->GetThreadParameter().hook }, { "hook address (upper 32 bits)", (int)(thread->GetThreadParameter().hook >> 32) } @@ -172,15 +175,22 @@ QVector MainWindow::GetAllHooks(DWORD processId) std::unordered_set addresses; QVector hooks; for (int i = 0; i < ttCombo->count(); ++i) - if (ttCombo->itemText(i).split(":")[1].toInt() == processId && - !addresses.count(ttCombo->itemText(i).split(":")[2].toInt(nullptr, 16))) + { + ThreadParameter tp = ParseTextThreadString(ttCombo->itemText(i)); + if (tp.pid == processId && !addresses.count(tp.hook)) { - addresses.insert(ttCombo->itemText(i).split(":")[2].toInt(nullptr, 16)); - hooks.push_back(Host::GetHookParam(ttCombo->itemText(i).split(":")[1].toInt(), ttCombo->itemText(i).split(":")[2].toInt(nullptr, 16))); + addresses.insert(tp.hook); + hooks.push_back(Host::GetHookParam(tp)); } + } return hooks; } +DWORD MainWindow::GetSelectedProcessId() +{ + return processCombo->currentText().split(":")[0].toULong(); +} + void MainWindow::on_attachButton_clicked() { std::unordered_map allProcesses = GetAllProcesses(); @@ -203,7 +213,7 @@ void MainWindow::on_attachButton_clicked() void MainWindow::on_detachButton_clicked() { - Host::DetachProcess(processCombo->currentText().split(":")[0].toInt()); + Host::DetachProcess(GetSelectedProcessId()); } void MainWindow::on_hookButton_clicked() @@ -217,30 +227,30 @@ void MainWindow::on_hookButton_clicked() Host::AddConsoleOutput(L"invalid code"); return; } - Host::InsertHook(processCombo->currentText().split(":")[0].toInt(), ParseCode(hookCode)); + Host::InsertHook(GetSelectedProcessId(), ParseCode(hookCode)); } void MainWindow::on_unhookButton_clicked() { - QVector hooks = GetAllHooks(processCombo->currentText().split(":")[0].toInt()); + QVector hooks = GetAllHooks(GetSelectedProcessId()); QStringList hookList; for (auto i : hooks) hookList.push_back( - QString::fromStdWString(Host::GetHookName(processCombo->currentText().split(":")[0].toInt(), i.address)) + + QString::fromStdWString(Host::GetHookName(GetSelectedProcessId(), i.address)) + ": " + - GenerateCode(i, processCombo->currentText().split(":")[0].toInt()) + GenerateCode(i, GetSelectedProcessId()) ); bool ok; QString hook = QInputDialog::getItem(this, "Unhook", "Which hook to remove?", hookList, 0, false, &ok); - if (ok) Host::RemoveHook(processCombo->currentText().split(":")[0].toInt(), hooks.at(hookList.indexOf(hook)).address); + if (ok) Host::RemoveHook(GetSelectedProcessId(), hooks.at(hookList.indexOf(hook)).address); } void MainWindow::on_saveButton_clicked() { - QVector hooks = GetAllHooks(processCombo->currentText().split(":")[0].toInt()); - QString hookList = GetFullModuleName(processCombo->currentText().split(":")[0].toInt());; + QVector hooks = GetAllHooks(GetSelectedProcessId()); + QString hookList = GetFullModuleName(GetSelectedProcessId()); for (auto i : hooks) if (!(i.type & HOOK_ENGINE)) - hookList += " , " + GenerateCode(i, processCombo->currentText().split(":")[0].toInt()); + hookList += " , " + GenerateCode(i, GetSelectedProcessId()); QFile file("SavedHooks.txt"); if (!file.open(QIODevice::Append | QIODevice::Text)) return; file.write((hookList + "\r\n").toUtf8()); @@ -248,7 +258,7 @@ void MainWindow::on_saveButton_clicked() void MainWindow::on_ttCombo_activated(int index) { - textOutput->setPlainText(QString::fromStdWString(Host::GetThread(ttCombo->itemText(index).split(":")[0].toInt())->GetStore())); + textOutput->setPlainText(QString::fromStdWString(Host::GetThread(ParseTextThreadString(ttCombo->itemText(index)))->GetStore())); textOutput->moveCursor(QTextCursor::End); } diff --git a/GUI/mainwindow.h b/GUI/mainwindow.h index ba01025..8cad8c3 100644 --- a/GUI/mainwindow.h +++ b/GUI/mainwindow.h @@ -4,10 +4,11 @@ #include #include #include +#include +#include #include #include #include "../host/host.h" -#include "hostsignaller.h" namespace Ui { @@ -23,7 +24,11 @@ public: ~MainWindow(); signals: - void ThreadOutputReceived(TextThread* thread, QString output); + void SigAddProcess(unsigned int processId); + void SigRemoveProcess(unsigned int processId); + void SigAddThread(TextThread* thread); + void SigRemoveThread(TextThread* thread); + void SigThreadOutput(TextThread* thread, QString output); private slots: void AddProcess(unsigned int processId); @@ -44,9 +49,13 @@ private: void ReloadExtensions(); std::unordered_map GetInfoForExtensions(TextThread* thread); QVector GetAllHooks(DWORD processId); + DWORD GetSelectedProcessId(); Ui::MainWindow *ui; - HostSignaller* hostSignaller; + QComboBox* processCombo; + QComboBox* ttCombo; + QComboBox* extenCombo; + QPlainTextEdit* textOutput; }; #endif // MAINWINDOW_H diff --git a/GUI/misc.cpp b/GUI/misc.cpp index d94badd..971d494 100644 --- a/GUI/misc.cpp +++ b/GUI/misc.cpp @@ -210,11 +210,11 @@ QString GenerateHCode(HookParam hp, DWORD processId) if (!(processHandle = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, processId))) return badCode; MEMORY_BASIC_INFORMATION info; if (!VirtualQueryEx(processHandle, (LPCVOID)hp.address, &info, sizeof(info))) return badCode; - wchar_t buffer[MAX_PATH]; - if (!GetModuleFileNameExW(processHandle, (HMODULE)info.AllocationBase, buffer, MAX_PATH)) return badCode; + QString moduleName = GetModuleName(processId, (HMODULE)info.AllocationBase); + if (moduleName.size() == 0) return badCode; code += QString::number(hp.address - (DWORD)info.AllocationBase, 16) + ":"; code = code.toUpper(); - code += QString::fromWCharArray(wcsrchr(buffer, L'\\') + 1); + code += moduleName; return code; } diff --git a/extensions/copyclipboard.cpp b/extensions/copyclipboard.cpp index 44bee13..53b639d 100644 --- a/extensions/copyclipboard.cpp +++ b/extensions/copyclipboard.cpp @@ -13,7 +13,7 @@ extern "C" */ __declspec(dllexport) const wchar_t* OnNewSentence(const wchar_t* sentence, const InfoForExtension* miscInfo) { - if (GetProperty("current select", miscInfo) && GetProperty("text number", miscInfo) > 0) + if (GetProperty("current select", miscInfo) && GetProperty("hook address", miscInfo) != -1) { HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE, (wcslen(sentence) + 1) * sizeof(wchar_t)); memcpy(GlobalLock(hMem), sentence, (wcslen(sentence) + 1) * sizeof(wchar_t)); diff --git a/extensions/extranewlines.cpp b/extensions/extranewlines.cpp index 1be85b0..605b244 100644 --- a/extensions/extranewlines.cpp +++ b/extensions/extranewlines.cpp @@ -13,7 +13,7 @@ extern "C" */ __declspec(dllexport) const wchar_t* OnNewSentence(const wchar_t* sentence, const InfoForExtension* miscInfo) { - if (GetProperty("text number", miscInfo) == 0) return sentence; + if (GetProperty("hook address", miscInfo) == -1) return sentence; wchar_t* newSentence = (wchar_t*)malloc((wcslen(sentence) + 6) * sizeof(wchar_t)); swprintf(newSentence, wcslen(sentence) + 6, L"%s\r\n", sentence); return newSentence; diff --git a/extensions/googletranslate.cpp b/extensions/googletranslate.cpp index 49348c9..f7d0290 100644 --- a/extensions/googletranslate.cpp +++ b/extensions/googletranslate.cpp @@ -49,7 +49,7 @@ extern "C" wchar_t translation[10000] = {}; wchar_t* message = error; - if (wcslen(sentence) > 2000 || GetProperty("text number", miscInfo) == 0) return sentence; + if (wcslen(sentence) > 2000 || GetProperty("hook address", miscInfo) == -1) return sentence; if (internet) { diff --git a/host/host.cc b/host/host.cc index f3d1edc..a196d89 100644 --- a/host/host.cc +++ b/host/host.cc @@ -5,51 +5,41 @@ #include "host.h" #include "pipe.h" #include "winmutex.h" +#include +#include #include #include "../vnrhook/include/const.h" #include "../vnrhook/include/defs.h" #include "../vnrhook/include/types.h" #include -HANDLE preventDuplicationMutex; +std::unordered_map textThreadsByParams; +std::unordered_map processRecordsByIds; -std::unordered_map textThreadsByParams(10); -std::unordered_map processRecordsByIds(2); +std::recursive_mutex hostMutex; -CRITICAL_SECTION hostCs; +ThreadEventCallback OnCreate, OnRemove; +ProcessEventCallback OnAttach, OnDetach; -ThreadEventCallback onCreate(nullptr), onRemove(nullptr); -ProcessEventCallback onAttach(nullptr), onDetach(nullptr); +DWORD DUMMY[100]; -WORD nextThreadNumber(0); - -#define HOST_LOCK CriticalSectionLocker hostLocker(&hostCs) // Synchronized scope for accessing private data +#define HOST_LOCK std::lock_guard hostLocker(hostMutex) // Synchronized scope for accessing private data namespace Host { - DLLEXPORT bool Start() - { - InitializeCriticalSection(&hostCs); - return true; - } - DLLEXPORT void Open() + DLLEXPORT void Start(ProcessEventCallback onAttach, ProcessEventCallback onDetach, ThreadEventCallback onCreate, ThreadEventCallback onRemove) { - TextThread* console = textThreadsByParams[{ 0, -1UL, -1UL, -1UL }] = new TextThread({ 0, -1UL, -1UL, -1UL }, nextThreadNumber++, USING_UNICODE); - if (onCreate) onCreate(console); + std::tie(OnAttach, OnDetach, OnCreate, OnRemove) = { onAttach, onDetach, onCreate, onRemove }; + OnCreate(textThreadsByParams[{ 0, -1UL, -1UL, -1UL }] = new TextThread({ 0, -1UL, -1UL, -1UL }, USING_UNICODE)); CreateNewPipe(); } DLLEXPORT void Close() { // Artikash 7/25/2018: This is only called when NextHooker is closed, at which point Windows should free everything itself...right? - //EnterCriticalSection(&hostCs); - //DestroyWindow(dummyWindow); - //RemoveThreads([](auto one, auto two) { return true; }, {}); - ////for (auto i : processRecordsByIds) UnregisterProcess(i.first); // Artikash 7/24/2018 FIXME: This segfaults since UnregisterProcess invalidates the iterator - //LeaveCriticalSection(&hostCs); - //DeleteCriticalSection(&hostCs); - //CloseHandle(preventDuplicationMutex); + HOST_LOCK; + for (auto i : processRecordsByIds) UnregisterProcess(i.first); } DLLEXPORT bool InjectProcess(DWORD processId, DWORD timeout) @@ -87,8 +77,7 @@ namespace Host DLLEXPORT bool DetachProcess(DWORD processId) { DWORD command = HOST_COMMAND_DETACH; - DWORD unused; - return WriteFile(processRecordsByIds[processId].hostPipe, &command, sizeof(command), &unused, nullptr); + return WriteFile(processRecordsByIds[processId].hostPipe, &command, sizeof(command), DUMMY, nullptr); } DLLEXPORT bool InsertHook(DWORD pid, HookParam hp, std::string name) @@ -97,8 +86,7 @@ namespace Host *(DWORD*)buffer = HOST_COMMAND_NEW_HOOK; *(HookParam*)(buffer + sizeof(DWORD)) = hp; if (name.size()) strcpy((char*)buffer + sizeof(DWORD) + sizeof(HookParam), name.c_str()); - DWORD unused; - return WriteFile(processRecordsByIds[pid].hostPipe, buffer, sizeof(DWORD) + sizeof(HookParam) + name.size(), &unused, nullptr); + return WriteFile(processRecordsByIds[pid].hostPipe, buffer, sizeof(DWORD) + sizeof(HookParam) + name.size(), DUMMY, nullptr); } DLLEXPORT bool RemoveHook(DWORD pid, DWORD addr) @@ -106,8 +94,7 @@ namespace Host BYTE buffer[sizeof(DWORD) * 2] = {}; *(DWORD*)buffer = HOST_COMMAND_REMOVE_HOOK; *(DWORD*)(buffer + sizeof(DWORD)) = addr; - DWORD unused; - return WriteFile(processRecordsByIds[pid].hostPipe, buffer, sizeof(DWORD) * 2, &unused, nullptr); + return WriteFile(processRecordsByIds[pid].hostPipe, buffer, sizeof(DWORD) * 2, DUMMY, nullptr); } DLLEXPORT HookParam GetHookParam(DWORD pid, DWORD addr) @@ -124,6 +111,8 @@ namespace Host return ret; } + DLLEXPORT HookParam GetHookParam(ThreadParameter tp) { return GetHookParam(tp.pid, tp.hook); } + DLLEXPORT std::wstring GetHookName(DWORD pid, DWORD addr) { if (pid == 0) return L"Console"; @@ -143,13 +132,10 @@ namespace Host return std::wstring(A2W(buffer.c_str())); } - DLLEXPORT TextThread* GetThread(DWORD number) + DLLEXPORT TextThread* GetThread(ThreadParameter tp) { HOST_LOCK; - for (auto i : textThreadsByParams) - if (i.second->Number() == number) - return i.second; - return nullptr; + return textThreadsByParams[tp]; } DLLEXPORT void AddConsoleOutput(std::wstring text) @@ -158,24 +144,20 @@ namespace Host textThreadsByParams[{ 0, -1UL, -1UL, -1UL }]->AddSentence(std::wstring(text)); } - DLLEXPORT void RegisterThreadCreateCallback(ThreadEventCallback cf) { onCreate = cf; } - DLLEXPORT void RegisterThreadRemoveCallback(ThreadEventCallback cf) { onRemove = cf; } - DLLEXPORT void RegisterProcessAttachCallback(ProcessEventCallback cf) { onAttach = cf; } - DLLEXPORT void RegisterProcessDetachCallback(ProcessEventCallback cf) { onDetach = cf; } + DLLEXPORT void RegisterThreadCreateCallback(ThreadEventCallback cf) { OnCreate = cf; } + DLLEXPORT void RegisterThreadRemoveCallback(ThreadEventCallback cf) { OnRemove = cf; } + DLLEXPORT void RegisterProcessAttachCallback(ProcessEventCallback cf) { OnAttach = cf; } + DLLEXPORT void RegisterProcessDetachCallback(ProcessEventCallback cf) { OnDetach = cf; } } -void DispatchText(DWORD pid, DWORD hook, DWORD retn, DWORD split, const BYTE * text, int len) +void DispatchText(ThreadParameter tp, const BYTE* text, int len) { // jichi 2/27/2013: When PID is zero, the text comes from console, which I don't need - if (!text || !pid || len <= 0) return; + if (!text || len <= 0) return; HOST_LOCK; - ThreadParameter tp = { pid, hook, retn, split }; TextThread *it; if ((it = textThreadsByParams[tp]) == nullptr) - { - it = textThreadsByParams[tp] = new TextThread(tp, nextThreadNumber++, Host::GetHookParam(pid, hook).type); - if (onCreate) onCreate(it); - } + OnCreate(it = textThreadsByParams[tp] = new TextThread(tp, Host::GetHookParam(tp).type)); it->AddText(text, len); } @@ -186,9 +168,8 @@ void RemoveThreads(bool(*RemoveIf)(ThreadParameter, ThreadParameter), ThreadPara for (auto i : textThreadsByParams) if (RemoveIf(i.first, cmp)) { - if (onRemove) onRemove(i.second); + OnRemove(i.second); //delete i.second; // Artikash 7/24/2018: FIXME: Qt GUI updates on another thread, so I can't delete this yet. - i.second->Clear(); // Temp workaround to free some memory. removedThreads.push_back(i.first); } for (auto i : removedThreads) textThreadsByParams.erase(i); @@ -204,7 +185,7 @@ void RegisterProcess(DWORD pid, HANDLE hostPipe) record.process_handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); record.hookman_mutex = OpenMutexW(MUTEX_ALL_ACCESS, FALSE, (ITH_HOOKMAN_MUTEX_ + std::to_wstring(pid)).c_str()); processRecordsByIds[pid] = record; - if (onAttach) onAttach(pid); + OnAttach(pid); } void UnregisterProcess(DWORD pid) @@ -216,9 +197,9 @@ void UnregisterProcess(DWORD pid) UnmapViewOfFile(pr.hookman_map); CloseHandle(pr.process_handle); CloseHandle(pr.hookman_section); - processRecordsByIds.erase(pid); + processRecordsByIds[pid] = {}; RemoveThreads([](auto one, auto two) { return one.pid == two.pid; }, { pid, 0, 0, 0 }); - if (onDetach) onDetach(pid); + OnDetach(pid); } // EOF diff --git a/host/host.h b/host/host.h index 4410244..f10dfa6 100644 --- a/host/host.h +++ b/host/host.h @@ -34,8 +34,7 @@ struct ThreadParameterHasher namespace Host { - DLLEXPORT void Open(); - DLLEXPORT bool Start(); + DLLEXPORT void Start(ProcessEventCallback onAttach, ProcessEventCallback onDetach, ThreadEventCallback onCreate, ThreadEventCallback onRemove); DLLEXPORT void Close(); DLLEXPORT bool InjectProcess(DWORD pid, DWORD timeout = 5000); DLLEXPORT bool DetachProcess(DWORD pid); @@ -43,9 +42,10 @@ namespace Host DLLEXPORT bool InsertHook(DWORD pid, HookParam hp, std::string name = ""); DLLEXPORT bool RemoveHook(DWORD pid, DWORD addr); DLLEXPORT HookParam GetHookParam(DWORD pid, DWORD addr); + DLLEXPORT HookParam GetHookParam(ThreadParameter tp); DLLEXPORT std::wstring GetHookName(DWORD pid, DWORD addr); - DLLEXPORT TextThread* GetThread(DWORD number); + DLLEXPORT TextThread* GetThread(ThreadParameter tp); DLLEXPORT void AddConsoleOutput(std::wstring text); DLLEXPORT void RegisterThreadCreateCallback(ThreadEventCallback cf); @@ -54,7 +54,7 @@ namespace Host DLLEXPORT void RegisterProcessDetachCallback(ProcessEventCallback cf); } -void DispatchText(DWORD pid, DWORD hook, DWORD retn, DWORD split, const BYTE *text, int len); +void DispatchText(ThreadParameter tp, const BYTE *text, int len); void RemoveThreads(bool(*RemoveIf)(ThreadParameter, ThreadParameter), ThreadParameter cmp); void RegisterProcess(DWORD pid, HANDLE hostPipe); void UnregisterProcess(DWORD pid); diff --git a/host/pipe.cc b/host/pipe.cc index bb065f4..c13cea5 100644 --- a/host/pipe.cc +++ b/host/pipe.cc @@ -10,14 +10,14 @@ void CreateNewPipe() { - CloseHandle(CreateThread(nullptr, 0, [](auto) + std::thread([]() { HANDLE hookPipe = CreateNamedPipeW(ITH_TEXT_PIPE, PIPE_ACCESS_INBOUND, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE, PIPE_UNLIMITED_INSTANCES, PIPE_BUFFER_SIZE, PIPE_BUFFER_SIZE, MAXDWORD, NULL); - HANDLE hostPipe = CreateNamedPipeW(ITH_COMMAND_PIPE, PIPE_ACCESS_OUTBOUND, 0, PIPE_UNLIMITED_INSTANCES, PIPE_BUFFER_SIZE, PIPE_BUFFER_SIZE, MAXDWORD, NULL); + HANDLE hostPipe = CreateNamedPipeW(ITH_COMMAND_PIPE, PIPE_ACCESS_OUTBOUND, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE, PIPE_UNLIMITED_INSTANCES, PIPE_BUFFER_SIZE, PIPE_BUFFER_SIZE, MAXDWORD, NULL); ConnectNamedPipe(hookPipe, nullptr); // jichi 9/27/2013: why recursion? - // Artikash 5/20/2018: To create a new pipe for another process + // Artikash 5/20/2018: Easy way to create a new pipe for another process CreateNewPipe(); BYTE buffer[PIPE_BUFFER_SIZE + 1] = {}; @@ -44,10 +44,13 @@ void CreateNewPipe() Host::AddConsoleOutput(A2W((LPCSTR)(buffer + sizeof(DWORD) * 2))); // Text break; } - else DispatchText(processId, - *(DWORD*)buffer, // Hook address - *(DWORD*)(buffer + sizeof(DWORD)), // Return address - *(DWORD*)(buffer + sizeof(DWORD) * 2), // Split + else DispatchText( + { + processId, + *(DWORD*)buffer, // Hook address + *(DWORD*)(buffer + sizeof(DWORD)), // Return address + *(DWORD*)(buffer + sizeof(DWORD) * 2) // Split + }, buffer + HEADER_SIZE, // Data bytesRead - HEADER_SIZE // Data size ); @@ -58,9 +61,7 @@ void CreateNewPipe() UnregisterProcess(processId); CloseHandle(hookPipe); CloseHandle(hostPipe); - return (DWORD)0; - }, - nullptr, 0, nullptr)); + }).detach(); } // EOF diff --git a/host/textthread.cc b/host/textthread.cc index bdd21cd..026889b 100644 --- a/host/textthread.cc +++ b/host/textthread.cc @@ -6,44 +6,24 @@ #endif // _MSC_VER #include "textthread.h" +#include #include "../vnrhook/include/const.h" #include "winmutex.h" -#define TT_LOCK CriticalSectionLocker ttLocker(&ttCs) // Synchronized scope for accessing private data +#define TT_LOCK std::lock_guard ttLocker(ttMutex) // Synchronized scope for accessing private data -TextThread::TextThread(ThreadParameter tp, unsigned int threadNumber, DWORD status) : - storage(), - sentenceBuffer(), +TextThread::TextThread(ThreadParameter tp, DWORD status) : status(status), timestamp(GetTickCount()), - threadNumber(threadNumber), - output(nullptr), - tp(tp) -{ - InitializeCriticalSection(&ttCs); - flushThread = CreateThread(nullptr, 0, [](void* textThread) - { - while (true) - { - Sleep(100); - ((TextThread*)textThread)->FlushSentenceBuffer(); - } - return (DWORD)0; - }, this, 0, nullptr); -} + Output(nullptr), + tp(tp), + flushThread([&]() { while (Sleep(25), FlushSentenceBuffer()); }) +{} TextThread::~TextThread() { - EnterCriticalSection(&ttCs); - LeaveCriticalSection(&ttCs); - DeleteCriticalSection(&ttCs); -} - -void TextThread::Clear() -{ - TT_LOCK; - storage.clear(); - storage.shrink_to_fit(); + status = -1UL; + flushThread.join(); } std::wstring TextThread::GetStore() @@ -52,10 +32,11 @@ std::wstring TextThread::GetStore() return storage; } -void TextThread::FlushSentenceBuffer() +bool TextThread::FlushSentenceBuffer() { TT_LOCK; - if (timestamp - GetTickCount() < 250 || sentenceBuffer.size() == 0) return; // TODO: let user change delay before sentence is flushed + if (status == -1UL) return false; + if (timestamp - GetTickCount() < 250 || sentenceBuffer.size() == 0) return true; // TODO: let user change delay before sentence is flushed std::wstring sentence; if (status & USING_UNICODE) { @@ -75,12 +56,13 @@ void TextThread::FlushSentenceBuffer() } AddSentence(sentence); sentenceBuffer.clear(); + return true; } void TextThread::AddSentence(std::wstring sentence) { TT_LOCK; - if (output) sentence = output(this, sentence); + if (Output) sentence = Output(this, sentence); storage.append(sentence); } diff --git a/host/textthread.h b/host/textthread.h index 2b68b6f..8558dca 100644 --- a/host/textthread.h +++ b/host/textthread.h @@ -8,13 +8,15 @@ #include #include #include +#include +#include struct ThreadParameter { DWORD pid; // jichi: 5/11/2014: The process ID - unsigned __int64 hook; // Artikash 6/6/2018: The start address of the hook + unsigned __int64 hook; // Artikash 6/6/2018: The insertion address of the hook unsigned __int64 retn; // jichi 5/11/2014: The return address of the hook - __int64 spl; // jichi 5/11/2014: the processed split value of the hook paramete + unsigned __int64 spl; // jichi 5/11/2014: the processed split value of the hook paramete // Artikash 5/31/2018: required for unordered_map to work with struct key friend bool operator==(const ThreadParameter& one, const ThreadParameter& two) @@ -31,29 +33,27 @@ typedef std::function ThreadOutputCallb class TextThread { public: - TextThread(ThreadParameter tp, unsigned int threadNumber, DWORD status); - ~TextThread(); + TextThread(ThreadParameter tp, DWORD status); + virtual ~TextThread(); virtual std::wstring GetStore(); - WORD Number() const { return threadNumber; } ThreadParameter GetThreadParameter() { return tp; } - void RegisterOutputCallBack(ThreadOutputCallback cb) { output = cb; } + void RegisterOutputCallBack(ThreadOutputCallback cb) { Output = cb; } - void Clear(); void AddText(const BYTE *con, int len); - void FlushSentenceBuffer(); void AddSentence(std::wstring sentence); private: - CRITICAL_SECTION ttCs; - ThreadOutputCallback output; + bool FlushSentenceBuffer(); + + ThreadOutputCallback Output; std::vector sentenceBuffer; std::wstring storage; ThreadParameter tp; - HANDLE flushThread; - unsigned int threadNumber; + std::recursive_mutex ttMutex; + std::thread flushThread; DWORD timestamp; DWORD status; }; diff --git a/host/winmutex.h b/host/winmutex.h index d94890d..b924a2c 100644 --- a/host/winmutex.h +++ b/host/winmutex.h @@ -8,28 +8,14 @@ # pragma warning(disable:4800) // C4800: forcing value to bool #endif // _MSC_VER -// Artikash 7/20/2018: these are similar to std::lock guard but use Winapi objects +// Artikash 7/20/2018: similar to std::lock guard but use Winapi objects for cross process comms class MutexLocker { HANDLE mutex; public: - explicit MutexLocker(HANDLE mutex) : mutex(mutex) - { - WaitForSingleObject(mutex, 0); - } + explicit MutexLocker(HANDLE mutex) : mutex(mutex) { WaitForSingleObject(mutex, 0); } ~MutexLocker() { if (mutex != INVALID_HANDLE_VALUE && mutex != nullptr) ReleaseMutex(mutex); } }; -class CriticalSectionLocker -{ - CRITICAL_SECTION* cs; -public: - explicit CriticalSectionLocker(CRITICAL_SECTION* cs) : cs(cs) - { - EnterCriticalSection(cs); - } - ~CriticalSectionLocker() { LeaveCriticalSection(cs); } -}; - // EOF