massive refactor
This commit is contained in:
parent
d03bd93bec
commit
2c37a50f7b
@ -7,7 +7,6 @@ set(RESOURCE_FILES NextHooker.rc NextHooker.ico)
|
|||||||
set(gui_SRCS
|
set(gui_SRCS
|
||||||
main.cpp
|
main.cpp
|
||||||
mainwindow.cpp
|
mainwindow.cpp
|
||||||
hostsignaller.cpp
|
|
||||||
misc.cpp
|
misc.cpp
|
||||||
extensions.cpp
|
extensions.cpp
|
||||||
${RESOURCE_FILES}
|
${RESOURCE_FILES}
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
#include "extensions.h"
|
#include "extensions.h"
|
||||||
|
#include <shared_mutex>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
|
|
||||||
|
std::shared_mutex extenMutex;
|
||||||
std::map<int, ExtensionFunction> extensions;
|
std::map<int, ExtensionFunction> extensions;
|
||||||
int processing;
|
int processing;
|
||||||
|
|
||||||
@ -21,50 +23,29 @@ std::map<int, QString> LoadExtensions()
|
|||||||
file.remove(0, extensionNumber.length() + 1);
|
file.remove(0, extensionNumber.length() + 1);
|
||||||
extensionNames[extensionNumber.toInt()] = file;
|
extensionNames[extensionNumber.toInt()] = file;
|
||||||
}
|
}
|
||||||
while (processing) Sleep(10);
|
extenMutex.lock();
|
||||||
processing = -1;
|
|
||||||
extensions = newExtensions;
|
extensions = newExtensions;
|
||||||
processing = 0;
|
extenMutex.unlock();
|
||||||
return extensionNames;
|
return extensionNames;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::wstring DispatchSentenceToExtensions(std::wstring sentence, std::unordered_map<std::string, int> miscInfo)
|
std::wstring DispatchSentenceToExtensions(std::wstring sentence, std::unordered_map<std::string, int> miscInfo)
|
||||||
{
|
{
|
||||||
while (processing < 0) Sleep(10);
|
wchar_t* sentenceBuffer = (wchar_t*)malloc((sentence.size() + 1) * sizeof(wchar_t));
|
||||||
processing++;
|
wcscpy(sentenceBuffer, sentence.c_str());
|
||||||
wchar_t* sentenceOrigBuffer = (wchar_t*)malloc((sentence.size() + 1) * sizeof(wchar_t));
|
|
||||||
wcscpy(sentenceOrigBuffer, sentence.c_str());
|
|
||||||
const wchar_t* sentenceBuffer = sentenceOrigBuffer;
|
|
||||||
InfoForExtension* miscInfoLinkedList = new InfoForExtension;
|
InfoForExtension* miscInfoLinkedList = new InfoForExtension;
|
||||||
InfoForExtension* miscInfoTraverser = miscInfoLinkedList;
|
InfoForExtension* miscInfoTraverser = miscInfoLinkedList;
|
||||||
for (auto i : miscInfo)
|
for (auto& i : miscInfo) miscInfoTraverser = miscInfoTraverser->nextProperty = new InfoForExtension{ i.first.c_str(), i.second, new InfoForExtension };
|
||||||
{
|
extenMutex.lock_shared();
|
||||||
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 : extensions)
|
for (auto i : extensions)
|
||||||
{
|
{
|
||||||
const wchar_t* prev = sentenceBuffer;
|
wchar_t* prev = sentenceBuffer;
|
||||||
sentenceBuffer = i.second(sentenceBuffer, miscInfoLinkedList);
|
sentenceBuffer = i.second(sentenceBuffer, miscInfoLinkedList);
|
||||||
if (sentenceBuffer == nullptr) sentence = prev;
|
|
||||||
if (sentenceBuffer != prev) free((void*)prev);
|
if (sentenceBuffer != prev) free((void*)prev);
|
||||||
}
|
}
|
||||||
miscInfoTraverser = miscInfoLinkedList;
|
extenMutex.unlock_shared();
|
||||||
while (miscInfoTraverser != nullptr)
|
delete miscInfoLinkedList;
|
||||||
{
|
|
||||||
InfoForExtension* nextNode = miscInfoTraverser->nextProperty;
|
|
||||||
delete[] miscInfoTraverser->propertyName;
|
|
||||||
delete miscInfoTraverser;
|
|
||||||
miscInfoTraverser = nextNode;
|
|
||||||
}
|
|
||||||
std::wstring newSentence = std::wstring(sentenceBuffer);
|
std::wstring newSentence = std::wstring(sentenceBuffer);
|
||||||
free((void*)sentenceBuffer);
|
free((void*)sentenceBuffer);
|
||||||
processing--;
|
|
||||||
return newSentence;
|
return newSentence;
|
||||||
}
|
}
|
||||||
|
@ -12,10 +12,11 @@ std::map<int, QString> LoadExtensions();
|
|||||||
std::wstring DispatchSentenceToExtensions(std::wstring sentence, std::unordered_map<std::string, int> miscInfo);
|
std::wstring DispatchSentenceToExtensions(std::wstring sentence, std::unordered_map<std::string, int> miscInfo);
|
||||||
struct InfoForExtension
|
struct InfoForExtension
|
||||||
{
|
{
|
||||||
char* propertyName;
|
~InfoForExtension() { if (nextProperty) delete nextProperty; };
|
||||||
int propertyValue;
|
const char* propertyName = "";
|
||||||
InfoForExtension* nextProperty;
|
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
|
#endif // EXTENSIONS_H
|
||||||
|
@ -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); });
|
|
||||||
}
|
|
@ -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
|
|
@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
if (!Host::Start()) return 1;
|
|
||||||
QApplication a(argc, argv);
|
QApplication a(argc, argv);
|
||||||
MainWindow w;
|
MainWindow w;
|
||||||
w.show();
|
w.show();
|
||||||
|
@ -21,12 +21,6 @@
|
|||||||
#include "../vnrhook/include/const.h"
|
#include "../vnrhook/include/const.h"
|
||||||
#include "misc.h"
|
#include "misc.h"
|
||||||
|
|
||||||
QMainWindow* mainWindow;
|
|
||||||
QComboBox* processCombo;
|
|
||||||
QComboBox* ttCombo;
|
|
||||||
QComboBox* extenCombo;
|
|
||||||
QPlainTextEdit* textOutput;
|
|
||||||
|
|
||||||
QString ProcessString(DWORD processId)
|
QString ProcessString(DWORD processId)
|
||||||
{
|
{
|
||||||
return QString("%1: %2").arg(QString::number(processId), GetModuleName(processId));
|
return QString("%1: %2").arg(QString::number(processId), GetModuleName(processId));
|
||||||
@ -35,26 +29,28 @@ QString ProcessString(DWORD processId)
|
|||||||
QString TextThreadString(TextThread* thread)
|
QString TextThreadString(TextThread* thread)
|
||||||
{
|
{
|
||||||
ThreadParameter tp = thread->GetThreadParameter();
|
ThreadParameter tp = thread->GetThreadParameter();
|
||||||
return QString("%1:%2:%3:%4:%5: ").arg(
|
return QString("%1:0x%2:0x%3:0x%4: ").arg(
|
||||||
QString::number(thread->Number()),
|
QString::number(tp.pid).toUpper(),
|
||||||
QString::number(tp.pid),
|
QString::number(tp.hook, 16).toUpper(),
|
||||||
QString::number(tp.hook, 16),
|
QString::number(tp.retn, 16).toUpper(),
|
||||||
QString::number(tp.retn, 16),
|
QString::number(tp.spl, 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) :
|
MainWindow::MainWindow(QWidget *parent) :
|
||||||
QMainWindow(parent),
|
QMainWindow(parent),
|
||||||
ui(new Ui::MainWindow),
|
ui(new Ui::MainWindow)
|
||||||
hostSignaller(new HostSignaller)
|
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
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");
|
QFile settings("NHWindow");
|
||||||
settings.open(QIODevice::ReadOnly);
|
settings.open(QIODevice::ReadOnly);
|
||||||
QDataStream reader(&settings);
|
QDataStream reader(&settings);
|
||||||
@ -62,14 +58,24 @@ MainWindow::MainWindow(QWidget *parent) :
|
|||||||
reader >> rect;
|
reader >> rect;
|
||||||
if (rect.bottom()) this->setGeometry(rect);
|
if (rect.bottom()) this->setGeometry(rect);
|
||||||
|
|
||||||
hostSignaller->Initialize();
|
processCombo = findChild<QComboBox*>("processCombo");
|
||||||
connect(hostSignaller, &HostSignaller::AddProcess, this, &MainWindow::AddProcess);
|
ttCombo = findChild<QComboBox*>("ttCombo");
|
||||||
connect(hostSignaller, &HostSignaller::RemoveProcess, this, &MainWindow::RemoveProcess);
|
extenCombo = findChild<QComboBox*>("extenCombo");
|
||||||
connect(hostSignaller, &HostSignaller::AddThread, this, &MainWindow::AddThread);
|
textOutput = findChild<QPlainTextEdit*>("textOutput");
|
||||||
connect(hostSignaller, &HostSignaller::RemoveThread, this, &MainWindow::RemoveThread);
|
|
||||||
connect(this, &MainWindow::ThreadOutputReceived, this, &MainWindow::ThreadOutput);
|
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();
|
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");
|
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)
|
for (int i = allProcesses.length() - 1; i >= 0; --i)
|
||||||
if (allProcesses.at(i).contains(processName))
|
if (allProcesses.at(i).contains(processName))
|
||||||
{
|
{
|
||||||
Sleep(50);
|
|
||||||
QStringList hooks = allProcesses.at(i).split(" , ");
|
QStringList hooks = allProcesses.at(i).split(" , ");
|
||||||
for (int j = 1; j < hooks.length(); ++j)
|
for (int j = 1; j < hooks.length(); ++j)
|
||||||
{
|
|
||||||
Sleep(10);
|
|
||||||
Host::InsertHook(processId, ParseCode(hooks.at(j)));
|
Host::InsertHook(processId, ParseCode(hooks.at(j)));
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -115,27 +117,28 @@ void MainWindow::AddThread(TextThread* thread)
|
|||||||
TextThreadString(thread) +
|
TextThreadString(thread) +
|
||||||
QString::fromStdWString(Host::GetHookName(thread->GetThreadParameter().pid, thread->GetThreadParameter().hook)) +
|
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)
|
thread->RegisterOutputCallBack([&](TextThread* thread, std::wstring output)
|
||||||
{
|
{
|
||||||
output = DispatchSentenceToExtensions(output, GetInfoForExtensions(thread));
|
output = DispatchSentenceToExtensions(output, GetInfoForExtensions(thread));
|
||||||
output += L"\r\n";
|
output += L"\r\n";
|
||||||
emit ThreadOutputReceived(thread, QString::fromStdWString(output));
|
emit SigThreadOutput(thread, QString::fromStdWString(output));
|
||||||
return output;
|
return output;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::RemoveThread(TextThread* thread)
|
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())
|
if (threadIndex == ttCombo->currentIndex())
|
||||||
{
|
{
|
||||||
ttCombo->setCurrentIndex(0);
|
ttCombo->setCurrentIndex(0);
|
||||||
on_ttCombo_activated(0);
|
on_ttCombo_activated(0);
|
||||||
}
|
}
|
||||||
ttCombo->removeItem(threadIndex);
|
ttCombo->removeItem(threadIndex);
|
||||||
|
delete thread;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::ThreadOutput(TextThread* thread, QString output)
|
void MainWindow::ThreadOutput(TextThread* thread, QString output)
|
||||||
@ -159,8 +162,8 @@ std::unordered_map<std::string, int> MainWindow::GetInfoForExtensions(TextThread
|
|||||||
{
|
{
|
||||||
return
|
return
|
||||||
{
|
{
|
||||||
{ "current select", ttCombo->currentText().split(":")[0].toInt() == thread->Number() ? 1 : 0 },
|
{ "current select", (int)ttCombo->currentText().startsWith(TextThreadString(thread)) },
|
||||||
{ "text number", thread->Number() },
|
{ "text number", 0 },
|
||||||
{ "process id", thread->GetThreadParameter().pid },
|
{ "process id", thread->GetThreadParameter().pid },
|
||||||
{ "hook address", (int)thread->GetThreadParameter().hook },
|
{ "hook address", (int)thread->GetThreadParameter().hook },
|
||||||
{ "hook address (upper 32 bits)", (int)(thread->GetThreadParameter().hook >> 32) }
|
{ "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;
|
std::unordered_set<DWORD> addresses;
|
||||||
QVector<HookParam> hooks;
|
QVector<HookParam> hooks;
|
||||||
for (int i = 0; i < ttCombo->count(); ++i)
|
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));
|
ThreadParameter tp = ParseTextThreadString(ttCombo->itemText(i));
|
||||||
hooks.push_back(Host::GetHookParam(ttCombo->itemText(i).split(":")[1].toInt(), ttCombo->itemText(i).split(":")[2].toInt(nullptr, 16)));
|
if (tp.pid == processId && !addresses.count(tp.hook))
|
||||||
|
{
|
||||||
|
addresses.insert(tp.hook);
|
||||||
|
hooks.push_back(Host::GetHookParam(tp));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return hooks;
|
return hooks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DWORD MainWindow::GetSelectedProcessId()
|
||||||
|
{
|
||||||
|
return processCombo->currentText().split(":")[0].toULong();
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::on_attachButton_clicked()
|
void MainWindow::on_attachButton_clicked()
|
||||||
{
|
{
|
||||||
std::unordered_map<std::wstring, DWORD> allProcesses = GetAllProcesses();
|
std::unordered_map<std::wstring, DWORD> allProcesses = GetAllProcesses();
|
||||||
@ -203,7 +213,7 @@ void MainWindow::on_attachButton_clicked()
|
|||||||
|
|
||||||
void MainWindow::on_detachButton_clicked()
|
void MainWindow::on_detachButton_clicked()
|
||||||
{
|
{
|
||||||
Host::DetachProcess(processCombo->currentText().split(":")[0].toInt());
|
Host::DetachProcess(GetSelectedProcessId());
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::on_hookButton_clicked()
|
void MainWindow::on_hookButton_clicked()
|
||||||
@ -217,30 +227,30 @@ void MainWindow::on_hookButton_clicked()
|
|||||||
Host::AddConsoleOutput(L"invalid code");
|
Host::AddConsoleOutput(L"invalid code");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Host::InsertHook(processCombo->currentText().split(":")[0].toInt(), ParseCode(hookCode));
|
Host::InsertHook(GetSelectedProcessId(), ParseCode(hookCode));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::on_unhookButton_clicked()
|
void MainWindow::on_unhookButton_clicked()
|
||||||
{
|
{
|
||||||
QVector<HookParam> hooks = GetAllHooks(processCombo->currentText().split(":")[0].toInt());
|
QVector<HookParam> hooks = GetAllHooks(GetSelectedProcessId());
|
||||||
QStringList hookList;
|
QStringList hookList;
|
||||||
for (auto i : hooks) hookList.push_back(
|
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;
|
bool ok;
|
||||||
QString hook = QInputDialog::getItem(this, "Unhook", "Which hook to remove?", hookList, 0, false, &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()
|
void MainWindow::on_saveButton_clicked()
|
||||||
{
|
{
|
||||||
QVector<HookParam> hooks = GetAllHooks(processCombo->currentText().split(":")[0].toInt());
|
QVector<HookParam> hooks = GetAllHooks(GetSelectedProcessId());
|
||||||
QString hookList = GetFullModuleName(processCombo->currentText().split(":")[0].toInt());;
|
QString hookList = GetFullModuleName(GetSelectedProcessId());
|
||||||
for (auto i : hooks)
|
for (auto i : hooks)
|
||||||
if (!(i.type & HOOK_ENGINE))
|
if (!(i.type & HOOK_ENGINE))
|
||||||
hookList += " , " + GenerateCode(i, processCombo->currentText().split(":")[0].toInt());
|
hookList += " , " + GenerateCode(i, GetSelectedProcessId());
|
||||||
QFile file("SavedHooks.txt");
|
QFile file("SavedHooks.txt");
|
||||||
if (!file.open(QIODevice::Append | QIODevice::Text)) return;
|
if (!file.open(QIODevice::Append | QIODevice::Text)) return;
|
||||||
file.write((hookList + "\r\n").toUtf8());
|
file.write((hookList + "\r\n").toUtf8());
|
||||||
@ -248,7 +258,7 @@ void MainWindow::on_saveButton_clicked()
|
|||||||
|
|
||||||
void MainWindow::on_ttCombo_activated(int index)
|
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);
|
textOutput->moveCursor(QTextCursor::End);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,10 +4,11 @@
|
|||||||
#include <QMainWindow>
|
#include <QMainWindow>
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
|
#include <QPlainTextEdit>
|
||||||
|
#include <QComboBox>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "../host/host.h"
|
#include "../host/host.h"
|
||||||
#include "hostsignaller.h"
|
|
||||||
|
|
||||||
namespace Ui
|
namespace Ui
|
||||||
{
|
{
|
||||||
@ -23,7 +24,11 @@ public:
|
|||||||
~MainWindow();
|
~MainWindow();
|
||||||
|
|
||||||
signals:
|
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:
|
private slots:
|
||||||
void AddProcess(unsigned int processId);
|
void AddProcess(unsigned int processId);
|
||||||
@ -44,9 +49,13 @@ private:
|
|||||||
void ReloadExtensions();
|
void ReloadExtensions();
|
||||||
std::unordered_map<std::string, int> GetInfoForExtensions(TextThread* thread);
|
std::unordered_map<std::string, int> GetInfoForExtensions(TextThread* thread);
|
||||||
QVector<HookParam> GetAllHooks(DWORD processId);
|
QVector<HookParam> GetAllHooks(DWORD processId);
|
||||||
|
DWORD GetSelectedProcessId();
|
||||||
|
|
||||||
Ui::MainWindow *ui;
|
Ui::MainWindow *ui;
|
||||||
HostSignaller* hostSignaller;
|
QComboBox* processCombo;
|
||||||
|
QComboBox* ttCombo;
|
||||||
|
QComboBox* extenCombo;
|
||||||
|
QPlainTextEdit* textOutput;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // MAINWINDOW_H
|
#endif // MAINWINDOW_H
|
||||||
|
@ -210,11 +210,11 @@ QString GenerateHCode(HookParam hp, DWORD processId)
|
|||||||
if (!(processHandle = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, processId))) return badCode;
|
if (!(processHandle = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, processId))) return badCode;
|
||||||
MEMORY_BASIC_INFORMATION info;
|
MEMORY_BASIC_INFORMATION info;
|
||||||
if (!VirtualQueryEx(processHandle, (LPCVOID)hp.address, &info, sizeof(info))) return badCode;
|
if (!VirtualQueryEx(processHandle, (LPCVOID)hp.address, &info, sizeof(info))) return badCode;
|
||||||
wchar_t buffer[MAX_PATH];
|
QString moduleName = GetModuleName(processId, (HMODULE)info.AllocationBase);
|
||||||
if (!GetModuleFileNameExW(processHandle, (HMODULE)info.AllocationBase, buffer, MAX_PATH)) return badCode;
|
if (moduleName.size() == 0) return badCode;
|
||||||
code += QString::number(hp.address - (DWORD)info.AllocationBase, 16) + ":";
|
code += QString::number(hp.address - (DWORD)info.AllocationBase, 16) + ":";
|
||||||
code = code.toUpper();
|
code = code.toUpper();
|
||||||
code += QString::fromWCharArray(wcsrchr(buffer, L'\\') + 1);
|
code += moduleName;
|
||||||
return code;
|
return code;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ extern "C"
|
|||||||
*/
|
*/
|
||||||
__declspec(dllexport) const wchar_t* OnNewSentence(const wchar_t* sentence, const InfoForExtension* miscInfo)
|
__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));
|
HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE, (wcslen(sentence) + 1) * sizeof(wchar_t));
|
||||||
memcpy(GlobalLock(hMem), sentence, (wcslen(sentence) + 1) * sizeof(wchar_t));
|
memcpy(GlobalLock(hMem), sentence, (wcslen(sentence) + 1) * sizeof(wchar_t));
|
||||||
|
@ -13,7 +13,7 @@ extern "C"
|
|||||||
*/
|
*/
|
||||||
__declspec(dllexport) const wchar_t* OnNewSentence(const wchar_t* sentence, const InfoForExtension* miscInfo)
|
__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));
|
wchar_t* newSentence = (wchar_t*)malloc((wcslen(sentence) + 6) * sizeof(wchar_t));
|
||||||
swprintf(newSentence, wcslen(sentence) + 6, L"%s\r\n", sentence);
|
swprintf(newSentence, wcslen(sentence) + 6, L"%s\r\n", sentence);
|
||||||
return newSentence;
|
return newSentence;
|
||||||
|
@ -49,7 +49,7 @@ extern "C"
|
|||||||
wchar_t translation[10000] = {};
|
wchar_t translation[10000] = {};
|
||||||
wchar_t* message = error;
|
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)
|
if (internet)
|
||||||
{
|
{
|
||||||
|
83
host/host.cc
83
host/host.cc
@ -5,51 +5,41 @@
|
|||||||
#include "host.h"
|
#include "host.h"
|
||||||
#include "pipe.h"
|
#include "pipe.h"
|
||||||
#include "winmutex.h"
|
#include "winmutex.h"
|
||||||
|
#include <mutex>
|
||||||
|
#include <thread>
|
||||||
#include <atlbase.h>
|
#include <atlbase.h>
|
||||||
#include "../vnrhook/include/const.h"
|
#include "../vnrhook/include/const.h"
|
||||||
#include "../vnrhook/include/defs.h"
|
#include "../vnrhook/include/defs.h"
|
||||||
#include "../vnrhook/include/types.h"
|
#include "../vnrhook/include/types.h"
|
||||||
#include <unordered_map>
|
#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::recursive_mutex hostMutex;
|
||||||
std::unordered_map<DWORD, ProcessRecord> processRecordsByIds(2);
|
|
||||||
|
|
||||||
CRITICAL_SECTION hostCs;
|
ThreadEventCallback OnCreate, OnRemove;
|
||||||
|
ProcessEventCallback OnAttach, OnDetach;
|
||||||
|
|
||||||
ThreadEventCallback onCreate(nullptr), onRemove(nullptr);
|
DWORD DUMMY[100];
|
||||||
ProcessEventCallback onAttach(nullptr), onDetach(nullptr);
|
|
||||||
|
|
||||||
WORD nextThreadNumber(0);
|
#define HOST_LOCK std::lock_guard<std::recursive_mutex> hostLocker(hostMutex) // Synchronized scope for accessing private data
|
||||||
|
|
||||||
#define HOST_LOCK CriticalSectionLocker hostLocker(&hostCs) // Synchronized scope for accessing private data
|
|
||||||
|
|
||||||
namespace Host
|
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);
|
std::tie(OnAttach, OnDetach, OnCreate, OnRemove) = { onAttach, onDetach, onCreate, onRemove };
|
||||||
if (onCreate) onCreate(console);
|
OnCreate(textThreadsByParams[{ 0, -1UL, -1UL, -1UL }] = new TextThread({ 0, -1UL, -1UL, -1UL }, USING_UNICODE));
|
||||||
CreateNewPipe();
|
CreateNewPipe();
|
||||||
}
|
}
|
||||||
|
|
||||||
DLLEXPORT void Close()
|
DLLEXPORT void Close()
|
||||||
{
|
{
|
||||||
// Artikash 7/25/2018: This is only called when NextHooker is closed, at which point Windows should free everything itself...right?
|
// Artikash 7/25/2018: This is only called when NextHooker is closed, at which point Windows should free everything itself...right?
|
||||||
//EnterCriticalSection(&hostCs);
|
HOST_LOCK;
|
||||||
//DestroyWindow(dummyWindow);
|
for (auto i : processRecordsByIds) UnregisterProcess(i.first);
|
||||||
//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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DLLEXPORT bool InjectProcess(DWORD processId, DWORD timeout)
|
DLLEXPORT bool InjectProcess(DWORD processId, DWORD timeout)
|
||||||
@ -87,8 +77,7 @@ namespace Host
|
|||||||
DLLEXPORT bool DetachProcess(DWORD processId)
|
DLLEXPORT bool DetachProcess(DWORD processId)
|
||||||
{
|
{
|
||||||
DWORD command = HOST_COMMAND_DETACH;
|
DWORD command = HOST_COMMAND_DETACH;
|
||||||
DWORD unused;
|
return WriteFile(processRecordsByIds[processId].hostPipe, &command, sizeof(command), DUMMY, nullptr);
|
||||||
return WriteFile(processRecordsByIds[processId].hostPipe, &command, sizeof(command), &unused, nullptr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DLLEXPORT bool InsertHook(DWORD pid, HookParam hp, std::string name)
|
DLLEXPORT bool InsertHook(DWORD pid, HookParam hp, std::string name)
|
||||||
@ -97,8 +86,7 @@ namespace Host
|
|||||||
*(DWORD*)buffer = HOST_COMMAND_NEW_HOOK;
|
*(DWORD*)buffer = HOST_COMMAND_NEW_HOOK;
|
||||||
*(HookParam*)(buffer + sizeof(DWORD)) = hp;
|
*(HookParam*)(buffer + sizeof(DWORD)) = hp;
|
||||||
if (name.size()) strcpy((char*)buffer + sizeof(DWORD) + sizeof(HookParam), name.c_str());
|
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(), DUMMY, nullptr);
|
||||||
return WriteFile(processRecordsByIds[pid].hostPipe, buffer, sizeof(DWORD) + sizeof(HookParam) + name.size(), &unused, nullptr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DLLEXPORT bool RemoveHook(DWORD pid, DWORD addr)
|
DLLEXPORT bool RemoveHook(DWORD pid, DWORD addr)
|
||||||
@ -106,8 +94,7 @@ namespace Host
|
|||||||
BYTE buffer[sizeof(DWORD) * 2] = {};
|
BYTE buffer[sizeof(DWORD) * 2] = {};
|
||||||
*(DWORD*)buffer = HOST_COMMAND_REMOVE_HOOK;
|
*(DWORD*)buffer = HOST_COMMAND_REMOVE_HOOK;
|
||||||
*(DWORD*)(buffer + sizeof(DWORD)) = addr;
|
*(DWORD*)(buffer + sizeof(DWORD)) = addr;
|
||||||
DWORD unused;
|
return WriteFile(processRecordsByIds[pid].hostPipe, buffer, sizeof(DWORD) * 2, DUMMY, nullptr);
|
||||||
return WriteFile(processRecordsByIds[pid].hostPipe, buffer, sizeof(DWORD) * 2, &unused, nullptr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DLLEXPORT HookParam GetHookParam(DWORD pid, DWORD addr)
|
DLLEXPORT HookParam GetHookParam(DWORD pid, DWORD addr)
|
||||||
@ -124,6 +111,8 @@ namespace Host
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DLLEXPORT HookParam GetHookParam(ThreadParameter tp) { return GetHookParam(tp.pid, tp.hook); }
|
||||||
|
|
||||||
DLLEXPORT std::wstring GetHookName(DWORD pid, DWORD addr)
|
DLLEXPORT std::wstring GetHookName(DWORD pid, DWORD addr)
|
||||||
{
|
{
|
||||||
if (pid == 0) return L"Console";
|
if (pid == 0) return L"Console";
|
||||||
@ -143,13 +132,10 @@ namespace Host
|
|||||||
return std::wstring(A2W(buffer.c_str()));
|
return std::wstring(A2W(buffer.c_str()));
|
||||||
}
|
}
|
||||||
|
|
||||||
DLLEXPORT TextThread* GetThread(DWORD number)
|
DLLEXPORT TextThread* GetThread(ThreadParameter tp)
|
||||||
{
|
{
|
||||||
HOST_LOCK;
|
HOST_LOCK;
|
||||||
for (auto i : textThreadsByParams)
|
return textThreadsByParams[tp];
|
||||||
if (i.second->Number() == number)
|
|
||||||
return i.second;
|
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DLLEXPORT void AddConsoleOutput(std::wstring text)
|
DLLEXPORT void AddConsoleOutput(std::wstring text)
|
||||||
@ -158,24 +144,20 @@ namespace Host
|
|||||||
textThreadsByParams[{ 0, -1UL, -1UL, -1UL }]->AddSentence(std::wstring(text));
|
textThreadsByParams[{ 0, -1UL, -1UL, -1UL }]->AddSentence(std::wstring(text));
|
||||||
}
|
}
|
||||||
|
|
||||||
DLLEXPORT void RegisterThreadCreateCallback(ThreadEventCallback cf) { onCreate = cf; }
|
DLLEXPORT void RegisterThreadCreateCallback(ThreadEventCallback cf) { OnCreate = cf; }
|
||||||
DLLEXPORT void RegisterThreadRemoveCallback(ThreadEventCallback cf) { onRemove = cf; }
|
DLLEXPORT void RegisterThreadRemoveCallback(ThreadEventCallback cf) { OnRemove = cf; }
|
||||||
DLLEXPORT void RegisterProcessAttachCallback(ProcessEventCallback cf) { onAttach = cf; }
|
DLLEXPORT void RegisterProcessAttachCallback(ProcessEventCallback cf) { OnAttach = cf; }
|
||||||
DLLEXPORT void RegisterProcessDetachCallback(ProcessEventCallback cf) { onDetach = 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
|
// 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;
|
HOST_LOCK;
|
||||||
ThreadParameter tp = { pid, hook, retn, split };
|
|
||||||
TextThread *it;
|
TextThread *it;
|
||||||
if ((it = textThreadsByParams[tp]) == nullptr)
|
if ((it = textThreadsByParams[tp]) == nullptr)
|
||||||
{
|
OnCreate(it = textThreadsByParams[tp] = new TextThread(tp, Host::GetHookParam(tp).type));
|
||||||
it = textThreadsByParams[tp] = new TextThread(tp, nextThreadNumber++, Host::GetHookParam(pid, hook).type);
|
|
||||||
if (onCreate) onCreate(it);
|
|
||||||
}
|
|
||||||
it->AddText(text, len);
|
it->AddText(text, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,9 +168,8 @@ void RemoveThreads(bool(*RemoveIf)(ThreadParameter, ThreadParameter), ThreadPara
|
|||||||
for (auto i : textThreadsByParams)
|
for (auto i : textThreadsByParams)
|
||||||
if (RemoveIf(i.first, cmp))
|
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.
|
//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);
|
removedThreads.push_back(i.first);
|
||||||
}
|
}
|
||||||
for (auto i : removedThreads) textThreadsByParams.erase(i);
|
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.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());
|
record.hookman_mutex = OpenMutexW(MUTEX_ALL_ACCESS, FALSE, (ITH_HOOKMAN_MUTEX_ + std::to_wstring(pid)).c_str());
|
||||||
processRecordsByIds[pid] = record;
|
processRecordsByIds[pid] = record;
|
||||||
if (onAttach) onAttach(pid);
|
OnAttach(pid);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UnregisterProcess(DWORD pid)
|
void UnregisterProcess(DWORD pid)
|
||||||
@ -216,9 +197,9 @@ void UnregisterProcess(DWORD pid)
|
|||||||
UnmapViewOfFile(pr.hookman_map);
|
UnmapViewOfFile(pr.hookman_map);
|
||||||
CloseHandle(pr.process_handle);
|
CloseHandle(pr.process_handle);
|
||||||
CloseHandle(pr.hookman_section);
|
CloseHandle(pr.hookman_section);
|
||||||
processRecordsByIds.erase(pid);
|
processRecordsByIds[pid] = {};
|
||||||
RemoveThreads([](auto one, auto two) { return one.pid == two.pid; }, { pid, 0, 0, 0 });
|
RemoveThreads([](auto one, auto two) { return one.pid == two.pid; }, { pid, 0, 0, 0 });
|
||||||
if (onDetach) onDetach(pid);
|
OnDetach(pid);
|
||||||
}
|
}
|
||||||
|
|
||||||
// EOF
|
// EOF
|
||||||
|
@ -34,8 +34,7 @@ struct ThreadParameterHasher
|
|||||||
|
|
||||||
namespace Host
|
namespace Host
|
||||||
{
|
{
|
||||||
DLLEXPORT void Open();
|
DLLEXPORT void Start(ProcessEventCallback onAttach, ProcessEventCallback onDetach, ThreadEventCallback onCreate, ThreadEventCallback onRemove);
|
||||||
DLLEXPORT bool Start();
|
|
||||||
DLLEXPORT void Close();
|
DLLEXPORT void Close();
|
||||||
DLLEXPORT bool InjectProcess(DWORD pid, DWORD timeout = 5000);
|
DLLEXPORT bool InjectProcess(DWORD pid, DWORD timeout = 5000);
|
||||||
DLLEXPORT bool DetachProcess(DWORD pid);
|
DLLEXPORT bool DetachProcess(DWORD pid);
|
||||||
@ -43,9 +42,10 @@ namespace Host
|
|||||||
DLLEXPORT bool InsertHook(DWORD pid, HookParam hp, std::string name = "");
|
DLLEXPORT bool InsertHook(DWORD pid, HookParam hp, std::string name = "");
|
||||||
DLLEXPORT bool RemoveHook(DWORD pid, DWORD addr);
|
DLLEXPORT bool RemoveHook(DWORD pid, DWORD addr);
|
||||||
DLLEXPORT HookParam GetHookParam(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 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 AddConsoleOutput(std::wstring text);
|
||||||
|
|
||||||
DLLEXPORT void RegisterThreadCreateCallback(ThreadEventCallback cf);
|
DLLEXPORT void RegisterThreadCreateCallback(ThreadEventCallback cf);
|
||||||
@ -54,7 +54,7 @@ namespace Host
|
|||||||
DLLEXPORT void RegisterProcessDetachCallback(ProcessEventCallback cf);
|
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 RemoveThreads(bool(*RemoveIf)(ThreadParameter, ThreadParameter), ThreadParameter cmp);
|
||||||
void RegisterProcess(DWORD pid, HANDLE hostPipe);
|
void RegisterProcess(DWORD pid, HANDLE hostPipe);
|
||||||
void UnregisterProcess(DWORD pid);
|
void UnregisterProcess(DWORD pid);
|
||||||
|
17
host/pipe.cc
17
host/pipe.cc
@ -10,14 +10,14 @@
|
|||||||
|
|
||||||
void CreateNewPipe()
|
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 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);
|
ConnectNamedPipe(hookPipe, nullptr);
|
||||||
|
|
||||||
// jichi 9/27/2013: why recursion?
|
// 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();
|
CreateNewPipe();
|
||||||
|
|
||||||
BYTE buffer[PIPE_BUFFER_SIZE + 1] = {};
|
BYTE buffer[PIPE_BUFFER_SIZE + 1] = {};
|
||||||
@ -44,10 +44,13 @@ void CreateNewPipe()
|
|||||||
Host::AddConsoleOutput(A2W((LPCSTR)(buffer + sizeof(DWORD) * 2))); // Text
|
Host::AddConsoleOutput(A2W((LPCSTR)(buffer + sizeof(DWORD) * 2))); // Text
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else DispatchText(processId,
|
else DispatchText(
|
||||||
|
{
|
||||||
|
processId,
|
||||||
*(DWORD*)buffer, // Hook address
|
*(DWORD*)buffer, // Hook address
|
||||||
*(DWORD*)(buffer + sizeof(DWORD)), // Return address
|
*(DWORD*)(buffer + sizeof(DWORD)), // Return address
|
||||||
*(DWORD*)(buffer + sizeof(DWORD) * 2), // Split
|
*(DWORD*)(buffer + sizeof(DWORD) * 2) // Split
|
||||||
|
},
|
||||||
buffer + HEADER_SIZE, // Data
|
buffer + HEADER_SIZE, // Data
|
||||||
bytesRead - HEADER_SIZE // Data size
|
bytesRead - HEADER_SIZE // Data size
|
||||||
);
|
);
|
||||||
@ -58,9 +61,7 @@ void CreateNewPipe()
|
|||||||
UnregisterProcess(processId);
|
UnregisterProcess(processId);
|
||||||
CloseHandle(hookPipe);
|
CloseHandle(hookPipe);
|
||||||
CloseHandle(hostPipe);
|
CloseHandle(hostPipe);
|
||||||
return (DWORD)0;
|
}).detach();
|
||||||
},
|
|
||||||
nullptr, 0, nullptr));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// EOF
|
// EOF
|
||||||
|
@ -6,44 +6,24 @@
|
|||||||
#endif // _MSC_VER
|
#endif // _MSC_VER
|
||||||
|
|
||||||
#include "textthread.h"
|
#include "textthread.h"
|
||||||
|
#include <mutex>
|
||||||
#include "../vnrhook/include/const.h"
|
#include "../vnrhook/include/const.h"
|
||||||
#include "winmutex.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) :
|
TextThread::TextThread(ThreadParameter tp, DWORD status) :
|
||||||
storage(),
|
|
||||||
sentenceBuffer(),
|
|
||||||
status(status),
|
status(status),
|
||||||
timestamp(GetTickCount()),
|
timestamp(GetTickCount()),
|
||||||
threadNumber(threadNumber),
|
Output(nullptr),
|
||||||
output(nullptr),
|
tp(tp),
|
||||||
tp(tp)
|
flushThread([&]() { while (Sleep(25), FlushSentenceBuffer()); })
|
||||||
{
|
{}
|
||||||
InitializeCriticalSection(&ttCs);
|
|
||||||
flushThread = CreateThread(nullptr, 0, [](void* textThread)
|
|
||||||
{
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
Sleep(100);
|
|
||||||
((TextThread*)textThread)->FlushSentenceBuffer();
|
|
||||||
}
|
|
||||||
return (DWORD)0;
|
|
||||||
}, this, 0, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
TextThread::~TextThread()
|
TextThread::~TextThread()
|
||||||
{
|
{
|
||||||
EnterCriticalSection(&ttCs);
|
status = -1UL;
|
||||||
LeaveCriticalSection(&ttCs);
|
flushThread.join();
|
||||||
DeleteCriticalSection(&ttCs);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextThread::Clear()
|
|
||||||
{
|
|
||||||
TT_LOCK;
|
|
||||||
storage.clear();
|
|
||||||
storage.shrink_to_fit();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::wstring TextThread::GetStore()
|
std::wstring TextThread::GetStore()
|
||||||
@ -52,10 +32,11 @@ std::wstring TextThread::GetStore()
|
|||||||
return storage;
|
return storage;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextThread::FlushSentenceBuffer()
|
bool TextThread::FlushSentenceBuffer()
|
||||||
{
|
{
|
||||||
TT_LOCK;
|
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;
|
std::wstring sentence;
|
||||||
if (status & USING_UNICODE)
|
if (status & USING_UNICODE)
|
||||||
{
|
{
|
||||||
@ -75,12 +56,13 @@ void TextThread::FlushSentenceBuffer()
|
|||||||
}
|
}
|
||||||
AddSentence(sentence);
|
AddSentence(sentence);
|
||||||
sentenceBuffer.clear();
|
sentenceBuffer.clear();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextThread::AddSentence(std::wstring sentence)
|
void TextThread::AddSentence(std::wstring sentence)
|
||||||
{
|
{
|
||||||
TT_LOCK;
|
TT_LOCK;
|
||||||
if (output) sentence = output(this, sentence);
|
if (Output) sentence = Output(this, sentence);
|
||||||
storage.append(sentence);
|
storage.append(sentence);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,13 +8,15 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <mutex>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
struct ThreadParameter
|
struct ThreadParameter
|
||||||
{
|
{
|
||||||
DWORD pid; // jichi: 5/11/2014: The process ID
|
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
|
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
|
// Artikash 5/31/2018: required for unordered_map to work with struct key
|
||||||
friend bool operator==(const ThreadParameter& one, const ThreadParameter& two)
|
friend bool operator==(const ThreadParameter& one, const ThreadParameter& two)
|
||||||
@ -31,29 +33,27 @@ typedef std::function<std::wstring(TextThread*, std::wstring)> ThreadOutputCallb
|
|||||||
class TextThread
|
class TextThread
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TextThread(ThreadParameter tp, unsigned int threadNumber, DWORD status);
|
TextThread(ThreadParameter tp, DWORD status);
|
||||||
~TextThread();
|
virtual ~TextThread();
|
||||||
|
|
||||||
virtual std::wstring GetStore();
|
virtual std::wstring GetStore();
|
||||||
WORD Number() const { return threadNumber; }
|
|
||||||
ThreadParameter GetThreadParameter() { return tp; }
|
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 AddText(const BYTE *con, int len);
|
||||||
void FlushSentenceBuffer();
|
|
||||||
void AddSentence(std::wstring sentence);
|
void AddSentence(std::wstring sentence);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CRITICAL_SECTION ttCs;
|
bool FlushSentenceBuffer();
|
||||||
ThreadOutputCallback output;
|
|
||||||
|
ThreadOutputCallback Output;
|
||||||
std::vector<char> sentenceBuffer;
|
std::vector<char> sentenceBuffer;
|
||||||
std::wstring storage;
|
std::wstring storage;
|
||||||
|
|
||||||
ThreadParameter tp;
|
ThreadParameter tp;
|
||||||
HANDLE flushThread;
|
std::recursive_mutex ttMutex;
|
||||||
unsigned int threadNumber;
|
std::thread flushThread;
|
||||||
DWORD timestamp;
|
DWORD timestamp;
|
||||||
DWORD status;
|
DWORD status;
|
||||||
};
|
};
|
||||||
|
@ -8,28 +8,14 @@
|
|||||||
# pragma warning(disable:4800) // C4800: forcing value to bool
|
# pragma warning(disable:4800) // C4800: forcing value to bool
|
||||||
#endif // _MSC_VER
|
#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
|
class MutexLocker
|
||||||
{
|
{
|
||||||
HANDLE mutex;
|
HANDLE mutex;
|
||||||
public:
|
public:
|
||||||
explicit MutexLocker(HANDLE mutex) : mutex(mutex)
|
explicit MutexLocker(HANDLE mutex) : mutex(mutex) { WaitForSingleObject(mutex, 0); }
|
||||||
{
|
|
||||||
WaitForSingleObject(mutex, 0);
|
|
||||||
}
|
|
||||||
~MutexLocker() { if (mutex != INVALID_HANDLE_VALUE && mutex != nullptr) ReleaseMutex(mutex); }
|
~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
|
// EOF
|
||||||
|
Loading…
Reference in New Issue
Block a user