massive refactor

This commit is contained in:
Akash Mozumdar 2018-08-21 22:43:30 -04:00
parent d03bd93bec
commit 2c37a50f7b
18 changed files with 169 additions and 253 deletions

View File

@ -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}

View File

@ -1,7 +1,9 @@
#include "extensions.h"
#include <shared_mutex>
#include <map>
#include <QDir>
std::shared_mutex extenMutex;
std::map<int, ExtensionFunction> extensions;
int processing;
@ -21,50 +23,29 @@ std::map<int, QString> 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<std::string, int> 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;
}

View File

@ -12,10 +12,11 @@ std::map<int, QString> LoadExtensions();
std::wstring DispatchSentenceToExtensions(std::wstring sentence, std::unordered_map<std::string, int> 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

View File

@ -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); });
}

View File

@ -1,23 +0,0 @@
#ifndef HOSTSIGNALLER_H
#define HOSTSIGNALLER_H
#include <QObject>
#include <Windows.h>
#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

View File

@ -4,7 +4,6 @@
int main(int argc, char *argv[])
{
if (!Host::Start()) return 1;
QApplication a(argc, argv);
MainWindow w;
w.show();

View File

@ -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<QComboBox*>("processCombo");
ttCombo = mainWindow->findChild<QComboBox*>("ttCombo");
extenCombo = mainWindow->findChild<QComboBox*>("extenCombo");
textOutput = mainWindow->findChild<QPlainTextEdit*>("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<QComboBox*>("processCombo");
ttCombo = findChild<QComboBox*>("ttCombo");
extenCombo = findChild<QComboBox*>("extenCombo");
textOutput = findChild<QPlainTextEdit*>("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<std::string, int> 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<HookParam> MainWindow::GetAllHooks(DWORD processId)
std::unordered_set<DWORD> addresses;
QVector<HookParam> 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<std::wstring, DWORD> 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<HookParam> hooks = GetAllHooks(processCombo->currentText().split(":")[0].toInt());
QVector<HookParam> 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<HookParam> hooks = GetAllHooks(processCombo->currentText().split(":")[0].toInt());
QString hookList = GetFullModuleName(processCombo->currentText().split(":")[0].toInt());;
QVector<HookParam> 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);
}

View File

@ -4,10 +4,11 @@
#include <QMainWindow>
#include <Windows.h>
#include <QVector>
#include <QPlainTextEdit>
#include <QComboBox>
#include <unordered_map>
#include <string>
#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<std::string, int> GetInfoForExtensions(TextThread* thread);
QVector<HookParam> GetAllHooks(DWORD processId);
DWORD GetSelectedProcessId();
Ui::MainWindow *ui;
HostSignaller* hostSignaller;
QComboBox* processCombo;
QComboBox* ttCombo;
QComboBox* extenCombo;
QPlainTextEdit* textOutput;
};
#endif // MAINWINDOW_H

View File

@ -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;
}

View File

@ -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));

View File

@ -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;

View File

@ -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)
{

View File

@ -5,51 +5,41 @@
#include "host.h"
#include "pipe.h"
#include "winmutex.h"
#include <mutex>
#include <thread>
#include <atlbase.h>
#include "../vnrhook/include/const.h"
#include "../vnrhook/include/defs.h"
#include "../vnrhook/include/types.h"
#include <unordered_map>
HANDLE preventDuplicationMutex;
std::unordered_map<ThreadParameter, TextThread*, ThreadParameterHasher> textThreadsByParams;
std::unordered_map<DWORD, ProcessRecord> processRecordsByIds;
std::unordered_map<ThreadParameter, TextThread*, ThreadParameterHasher> textThreadsByParams(10);
std::unordered_map<DWORD, ProcessRecord> 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<std::recursive_mutex> 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

View File

@ -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);

View File

@ -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

View File

@ -6,44 +6,24 @@
#endif // _MSC_VER
#include "textthread.h"
#include <mutex>
#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<std::recursive_mutex> 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);
}

View File

@ -8,13 +8,15 @@
#include <string>
#include <vector>
#include <functional>
#include <mutex>
#include <thread>
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<std::wstring(TextThread*, std::wstring)> 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<char> sentenceBuffer;
std::wstring storage;
ThreadParameter tp;
HANDLE flushThread;
unsigned int threadNumber;
std::recursive_mutex ttMutex;
std::thread flushThread;
DWORD timestamp;
DWORD status;
};

View File

@ -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