diff --git a/GUI/GUI.pro b/GUI/GUI.pro index fac6fea..24d7523 100644 --- a/GUI/GUI.pro +++ b/GUI/GUI.pro @@ -23,22 +23,24 @@ DEFINES += QT_DEPRECATED_WARNINGS CONFIG += c++11 SOURCES += \ - main.cpp \ - mainwindow.cpp \ - hostsignaller.cpp + main.cpp \ + mainwindow.cpp \ + hostsignaller.cpp \ + misc.cpp HEADERS += \ - mainwindow.h \ - hostsignaller.h + mainwindow.h \ + hostsignaller.h \ + misc.h FORMS += \ - mainwindow.ui + mainwindow.ui win32: LIBS += \ - -L$$PWD/../Builds/Debug/Debug/ -lvnrhost + -L$$PWD/../Builds/Debug/Debug/ -lvnrhost QMAKE_CXXFLAGS_RELEASE += \ - /MT + /MT # Default rules for deployment. qnx: target.path = /tmp/$${TARGET}/bin diff --git a/GUI/hostsignaller.cpp b/GUI/hostsignaller.cpp index fb52831..3bc7413 100644 --- a/GUI/hostsignaller.cpp +++ b/GUI/hostsignaller.cpp @@ -1,5 +1,4 @@ #include "hostsignaller.h" -#include "../texthook/host.h" void HostSignaller::Initialize() { diff --git a/GUI/mainwindow.cpp b/GUI/mainwindow.cpp index f642880..7dc1912 100644 --- a/GUI/mainwindow.cpp +++ b/GUI/mainwindow.cpp @@ -1,6 +1,6 @@ #include "mainwindow.h" #include "ui_mainwindow.h" -#include "QCoreApplication" +#include #include "QTextBrowser" #include "QMessageBox" #include "QComboBox" @@ -8,25 +8,17 @@ #include "QInputDialog" #include #include +#include #include #include #include -#include "../texthook/host.h" +#include "misc.h" QMainWindow* mainWindow; QComboBox* processCombo; QComboBox* ttCombo; QTextBrowser* textOutput; -QString GetModuleName(DWORD processId, HMODULE module = NULL) -{ - HANDLE handle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processId); - wchar_t buffer[MAX_PATH]; - GetModuleFileNameExW(handle, module, buffer, MAX_PATH); - CloseHandle(handle); - return QString::fromWCharArray(wcsrchr(buffer, L'\\') + 1); -} - QString ProcessString(DWORD processId) { return QString("%1: %2").arg(QString::number(processId), GetModuleName(processId)); @@ -66,8 +58,6 @@ MainWindow::MainWindow(QWidget *parent) : MainWindow::~MainWindow() { - Host::Close(); - delete hostSignaller; delete ui; } @@ -85,7 +75,10 @@ void MainWindow::AddThread(TextThread* thread) { ttCombo->addItem( TextThreadString(thread) + - QString::fromWCharArray(Host::GetHookName(thread->GetThreadParameter().pid, thread->GetThreadParameter().hook).c_str()) + QString::fromWCharArray(Host::GetHookName(thread->GetThreadParameter().pid, thread->GetThreadParameter().hook).c_str()) + + " (" + + GenerateHCode(Host::GetHookParam(thread->GetThreadParameter().pid, thread->GetThreadParameter().hook), thread->GetThreadParameter().pid) + + ")" ); } @@ -110,9 +103,25 @@ void MainWindow::ThreadOutput(TextThread* thread, QString output) } } +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))) + { + 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))); + } + return hooks; +} + void MainWindow::on_attachButton_clicked() { - Host::InjectProcess(QInputDialog::getInt(this, "Process ID?", "You can find this under Task Manager -> Details")); + bool ok; + int processId = QInputDialog::getInt(this, "Attach Process", "Process ID?\r\nYou can find this under Task Manager -> Details", 0, 0, 100000, 1, &ok); + if (ok) Host::InjectProcess(processId); } void MainWindow::on_detachButton_clicked() @@ -120,6 +129,30 @@ void MainWindow::on_detachButton_clicked() Host::DetachProcess(processCombo->currentText().split(":")[0].toInt()); } +void MainWindow::on_hookButton_clicked() +{ + bool ok; + QString hookCode = QInputDialog::getText(this, "Add Hook", + "Enter hook code\r\n/H{A|B|W|S|Q}[N][data_offset[*drdo]][:sub_offset[*drso]]@addr[:module]", + QLineEdit::Normal, "/H", &ok + ); + if (ok) Host::InsertHook(processCombo->currentText().split(":")[0].toInt(), ParseHCode(hookCode, processCombo->currentText().split(":")[0].toInt())); +} + +void MainWindow::on_unhookButton_clicked() +{ + QVector hooks = GetAllHooks(processCombo->currentText().split(":")[0].toInt()); + QStringList hookList; + for (auto i : hooks) hookList.push_back( + QString::fromWCharArray(Host::GetHookName(processCombo->currentText().split(":")[0].toInt(), i.address).c_str()) + + ": " + + GenerateHCode(i, processCombo->currentText().split(":")[0].toInt()) + ); + 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); +} + void MainWindow::on_ttCombo_activated(int index) { textOutput->setText(QString::fromWCharArray(Host::GetThread(ttCombo->itemText(index).split(":")[0].toInt())->GetStore().c_str())); diff --git a/GUI/mainwindow.h b/GUI/mainwindow.h index 91212e8..ccecda6 100644 --- a/GUI/mainwindow.h +++ b/GUI/mainwindow.h @@ -3,7 +3,8 @@ #include #include -#include "../texthook/textthread.h" +#include +#include "../texthook/host.h" #include "hostsignaller.h" namespace Ui @@ -19,18 +20,22 @@ public: explicit MainWindow(QWidget *parent = nullptr); ~MainWindow(); - QString ProcessOutput(TextThread *thread, QString output); private slots: void on_attachButton_clicked(); void on_detachButton_clicked(); void on_ttCombo_activated(int index); + void on_unhookButton_clicked(); void AddProcess(unsigned int processId); void RemoveProcess(unsigned int processId); void AddThread(TextThread* thread); void RemoveThread(TextThread* thread); void ThreadOutput(TextThread* thread, QString output); + void on_hookButton_clicked(); + private: + QVector GetAllHooks(DWORD processId); + Ui::MainWindow *ui; HostSignaller* hostSignaller; }; diff --git a/GUI/misc.cpp b/GUI/misc.cpp new file mode 100644 index 0000000..1c6cc0e --- /dev/null +++ b/GUI/misc.cpp @@ -0,0 +1,157 @@ +#include "misc.h" +#include "../vnrhook/include/const.h" +#include +#include + +QString GetModuleName(DWORD processId, HMODULE module) +{ + HANDLE handle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processId); + wchar_t buffer[MAX_PATH]; + GetModuleFileNameExW(handle, module, buffer, MAX_PATH); + CloseHandle(handle); + return QString::fromWCharArray(wcsrchr(buffer, L'\\') + 1); +} + +DWORD Hash(QString module) +{ + DWORD hash = 0; + for (auto i : module) hash = _rotr(hash, 7) + i.unicode(); + return hash; +} + +HookParam ParseHCode(QString HCode, DWORD processId) +{ + HookParam hp = {}; + HCode = HCode.toUpper(); + if (!HCode.startsWith("/H")) return {}; + HCode.remove(0, 2); + switch (HCode.at(0).unicode()) + { + case L'S': + hp.type |= USING_STRING; + break; + case L'A': + hp.type |= BIG_ENDIAN; + hp.length_offset = 1; + break; + case L'B': + hp.length_offset = 1; + break; + case L'Q': + hp.type |= USING_STRING | USING_UNICODE; + break; + case L'W': + hp.type |= USING_UNICODE; + hp.length_offset = 1; + break; + default: + return {}; + } + HCode.remove(0, 1); + if (HCode.at(0).unicode() == L'N') + { + hp.type |= NO_CONTEXT; + HCode.remove(0, 1); + } + QRegExp dataOffset("^\\-?[\\dA-F]+"); + if (dataOffset.indexIn(HCode) == -1) return {}; + hp.offset = dataOffset.cap(0).toInt(nullptr, 16); + HCode.remove(0, dataOffset.cap(0).length()); + QRegExp dataIndirect("^\\*(\\-?[\\dA-F]+)"); + if (dataIndirect.indexIn(HCode) != -1) + { + hp.type |= DATA_INDIRECT; + hp.index = dataIndirect.cap(1).toInt(nullptr, 16); + HCode.remove(0, dataIndirect.cap(0).length()); + } + QRegExp split("^\\:(\\-?[\\dA-F]+)"); + if (split.indexIn(HCode) != -1) + { + hp.type |= USING_SPLIT; + hp.split = split.cap(1).toInt(nullptr, 16); + HCode.remove(0, split.cap(0).length()); + QRegExp splitIndirect("^\\*(\\-?[\\dA-F]+)"); + if (splitIndirect.indexIn(HCode) != -1) + { + hp.type |= SPLIT_INDIRECT; + hp.split_index = splitIndirect.cap(1).toInt(nullptr, 16); + HCode.remove(0, splitIndirect.cap(0).length()); + } + } + if (HCode.at(0).unicode() != L'@') return {}; + HCode.remove(0, 1); + QRegExp address("^([\\dA-F]+):?"); + if (address.indexIn(HCode) == -1) return {}; + hp.address = address.cap(1).toInt(nullptr, 16); + HCode.remove(address.cap(0)); + if (HCode.length()) + { + hp.type |= MODULE_OFFSET; + hp.module = Hash(HCode.toLower()); + } + if (hp.offset & 0x80000000) + hp.offset -= 4; + if (hp.split & 0x80000000) + hp.split -= 4; + return hp; +} + +QString GenerateHCode(HookParam hp, DWORD processId) +{ + QString code = "/H"; + if (hp.type & USING_UNICODE) + { + if (hp.type & USING_STRING) + code += "Q"; + else + code += "W"; + } + else + { + if (hp.type & USING_STRING) + code += "S"; + else if (hp.type & BIG_ENDIAN) + code += "A"; + else + code += "B"; + } + if (hp.type & NO_CONTEXT) + code += "N"; + if (hp.offset >> 31) + code += "-" + QString::number(-(hp.offset + 4), 16); + else + code += QString::number(hp.offset, 16); + if (hp.type & DATA_INDIRECT) + { + if (hp.index >> 31) + code += "*-" + QString::number(-hp.index, 16); + else + code += "*" + QString::number(hp.index, 16); + } + if (hp.type & USING_SPLIT) + { + if (hp.split >> 31) + code += ":-" + QString::number(-(hp.split + 4), 16); + else + code += ":" + QString::number(hp.split, 16); + } + if (hp.type & SPLIT_INDIRECT) + { + if (hp.split_index >> 31) + code += "*-" + QString::number(-hp.split_index, 16); + else + code += "*" + QString::number(hp.split_index, 16); + } + code += "@"; + QString badCode = (code + QString::number(hp.address, 16)).toUpper(); + HANDLE processHandle; + 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; + code += QString::number(hp.address - (DWORD)info.AllocationBase, 16) + ":"; + code = code.toUpper(); + code += QString::fromWCharArray(wcsrchr(buffer, L'\\') + 1); + return code; +} diff --git a/GUI/misc.h b/GUI/misc.h new file mode 100644 index 0000000..96e1938 --- /dev/null +++ b/GUI/misc.h @@ -0,0 +1,12 @@ +#ifndef MISC_H +#define MISC_H + +#include +#include +#include "../texthook/host.h" + +QString GetModuleName(DWORD processId, HMODULE module = NULL); +HookParam ParseHCode(QString HCode, DWORD processId); +QString GenerateHCode(HookParam hp, DWORD processId); + +#endif // MISC_H diff --git a/texthook/textthread.cc b/texthook/textthread.cc index 5a38b1d..6164628 100644 --- a/texthook/textthread.cc +++ b/texthook/textthread.cc @@ -39,6 +39,12 @@ void TextThread::Clear() storage.shrink_to_fit(); } +std::wstring TextThread::GetStore() +{ + TT_LOCK; + return storage; +} + void TextThread::AddSentence() { TT_LOCK; diff --git a/texthook/textthread.h b/texthook/textthread.h index 7f3708d..249869c 100644 --- a/texthook/textthread.h +++ b/texthook/textthread.h @@ -34,13 +34,7 @@ public: TextThread(ThreadParameter tp, unsigned int threadNumber, unsigned int splitDelay = 250); ~TextThread(); - void Clear(); - void AddText(const BYTE *con, int len); - void AddSentence(); - void AddSentence(std::wstring sentence); - - // Artikash 7/25/2018: Not thread-safe with Clear(), but since they should both be accessed from GUI thread, should be fine - std::wstring GetStore() { return storage; } + virtual std::wstring GetStore(); DWORD &Status() { return status; } WORD Number() const { return threadNumber; } ThreadParameter GetThreadParameter() { return tp; } @@ -48,6 +42,11 @@ public: void RegisterOutputCallBack(ThreadOutputCallback cb) { output = cb; } + void Clear(); + void AddText(const BYTE *con, int len); + void AddSentence(); + void AddSentence(std::wstring sentence); + private: CRITICAL_SECTION ttCs; ThreadOutputCallback output; diff --git a/vnrhook/src/main.cc b/vnrhook/src/main.cc index 23abbb7..a97ec20 100644 --- a/vnrhook/src/main.cc +++ b/vnrhook/src/main.cc @@ -142,32 +142,27 @@ BOOL WINAPI DllMain(HINSTANCE hModule, DWORD fdwReason, LPVOID unused) } //extern "C" { -DWORD NewHook(const HookParam &hp, LPCSTR name, DWORD flag) +DWORD NewHook(const HookParam &hp, LPCSTR lpname, DWORD flag) { - CHAR str[128]; + std::string name = lpname; int current = ::current_available - ::hookman; if (current < MAX_HOOK) { //flag &= 0xffff; //if ((flag & HOOK_AUXILIARY) == 0) flag |= HOOK_ADDITIONAL; - if (name == NULL || name[0] == '\0') + if (name[0] == '\0') { - sprintf(str, "UserHook%d", user_hook_count++); - } - else - { - strcpy(str, name); + name = "UserHook" + std::to_string(user_hook_count++); } - ConsoleOutput("vnrcli:NewHook: try inserting hook:"); - ConsoleOutput(str); + ConsoleOutput(("vnrcli:NewHook: try inserting hook: " + name).c_str()); // jichi 7/13/2014: This function would raise when too many hooks added - ::hookman[current].InitHook(hp, str, flag & 0xffff); + ::hookman[current].InitHook(hp, name.c_str(), flag & 0xffff); if (::hookman[current].InsertHook() == 0) { - ConsoleOutput("vnrcli:NewHook: hook inserted"); - NotifyHookInsert(hp, str); + ConsoleOutput(("vnrcli:NewHook: inserted hook: " + name).c_str()); + NotifyHookInsert(hp, name.c_str()); } else ConsoleOutput("vnrcli:NewHook:WARNING: failed to insert hook"); }