forked from Public-Mirror/Textractor
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
|
||||
main.cpp
|
||||
mainwindow.cpp
|
||||
hostsignaller.cpp
|
||||
misc.cpp
|
||||
extensions.cpp
|
||||
${RESOURCE_FILES}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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[])
|
||||
{
|
||||
if (!Host::Start()) return 1;
|
||||
QApplication a(argc, argv);
|
||||
MainWindow w;
|
||||
w.show();
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
{
|
||||
|
83
host/host.cc
83
host/host.cc
@ -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
|
||||
|
@ -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);
|
||||
|
21
host/pipe.cc
21
host/pipe.cc
@ -10,14 +10,14 @@
|
||||
|
||||
void CreateNewPipe()
|
||||
{
|
||||
CloseHandle(CreateThread(nullptr, 0, [](auto)
|
||||
std::thread([]()
|
||||
{
|
||||
HANDLE hookPipe = CreateNamedPipeW(ITH_TEXT_PIPE, PIPE_ACCESS_INBOUND, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE, PIPE_UNLIMITED_INSTANCES, PIPE_BUFFER_SIZE, PIPE_BUFFER_SIZE, MAXDWORD, NULL);
|
||||
HANDLE hostPipe = CreateNamedPipeW(ITH_COMMAND_PIPE, PIPE_ACCESS_OUTBOUND, 0, PIPE_UNLIMITED_INSTANCES, PIPE_BUFFER_SIZE, PIPE_BUFFER_SIZE, MAXDWORD, NULL);
|
||||
HANDLE hostPipe = CreateNamedPipeW(ITH_COMMAND_PIPE, PIPE_ACCESS_OUTBOUND, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE, PIPE_UNLIMITED_INSTANCES, PIPE_BUFFER_SIZE, PIPE_BUFFER_SIZE, MAXDWORD, NULL);
|
||||
ConnectNamedPipe(hookPipe, nullptr);
|
||||
|
||||
// jichi 9/27/2013: why recursion?
|
||||
// Artikash 5/20/2018: To create a new pipe for another process
|
||||
// Artikash 5/20/2018: Easy way to create a new pipe for another process
|
||||
CreateNewPipe();
|
||||
|
||||
BYTE buffer[PIPE_BUFFER_SIZE + 1] = {};
|
||||
@ -44,10 +44,13 @@ void CreateNewPipe()
|
||||
Host::AddConsoleOutput(A2W((LPCSTR)(buffer + sizeof(DWORD) * 2))); // Text
|
||||
break;
|
||||
}
|
||||
else DispatchText(processId,
|
||||
*(DWORD*)buffer, // Hook address
|
||||
*(DWORD*)(buffer + sizeof(DWORD)), // Return address
|
||||
*(DWORD*)(buffer + sizeof(DWORD) * 2), // Split
|
||||
else DispatchText(
|
||||
{
|
||||
processId,
|
||||
*(DWORD*)buffer, // Hook address
|
||||
*(DWORD*)(buffer + sizeof(DWORD)), // Return address
|
||||
*(DWORD*)(buffer + sizeof(DWORD) * 2) // Split
|
||||
},
|
||||
buffer + HEADER_SIZE, // Data
|
||||
bytesRead - HEADER_SIZE // Data size
|
||||
);
|
||||
@ -58,9 +61,7 @@ void CreateNewPipe()
|
||||
UnregisterProcess(processId);
|
||||
CloseHandle(hookPipe);
|
||||
CloseHandle(hostPipe);
|
||||
return (DWORD)0;
|
||||
},
|
||||
nullptr, 0, nullptr));
|
||||
}).detach();
|
||||
}
|
||||
|
||||
// EOF
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user