holy shit you can overload operator-> to do WHAT??

This commit is contained in:
Akash Mozumdar 2018-11-27 15:54:04 -05:00
parent 24e31247af
commit efa8d26ada
12 changed files with 224 additions and 240 deletions

View File

@ -1,7 +1,10 @@
#include "extenwindow.h"
#include "ui_extenwindow.h"
#include "text.h"
#include "defs.h"
#include "types.h"
#include "misc.h"
#include <shared_mutex>
#include <QFileDialog>
#include <QMimeData>
#include <QUrl>
@ -81,12 +84,7 @@ ExtenWindow::ExtenWindow(QWidget* parent) :
extenList = findChild<QListWidget*>("extenList");
extenList->installEventFilter(this);
if (extensions.empty())
{
extenSaveFile.open(QIODevice::ReadOnly);
for (auto extenName : QString(extenSaveFile.readAll()).split(">")) Load(extenName);
extenSaveFile.close();
}
for (auto extenName : QString(QAutoFile(EXTEN_SAVE_FILE, QIODevice::ReadOnly)->readAll()).split(">")) Load(extenName);
Sync();
}
@ -98,14 +96,13 @@ ExtenWindow::~ExtenWindow()
void ExtenWindow::Sync()
{
extenList->clear();
extenSaveFile.open(QIODevice::WriteOnly | QIODevice::Truncate);
QAutoFile extenSaveFile(EXTEN_SAVE_FILE, QIODevice::WriteOnly | QIODevice::Truncate);
std::shared_lock sharedLock(extenMutex);
for (auto extenName : extenNames)
{
extenList->addItem(extenName);
extenSaveFile.write((extenName + ">").toUtf8());
extenSaveFile->write((extenName + ">").toUtf8());
}
extenSaveFile.close();
}
void ExtenWindow::Add(QString fileName)

View File

@ -2,8 +2,6 @@
#define EXTENSIONS_H
#include "qtcommon.h"
#include "defs.h"
#include <shared_mutex>
#include <QListWidget>
#include <QDragEnterEvent>
#include <QDropEvent>
@ -35,7 +33,6 @@ private:
void dropEvent(QDropEvent* event);
Ui::ExtenWindow* ui;
QFile extenSaveFile = QFile(EXTEN_SAVE_FILE);
QListWidget* extenList;
};

View File

@ -10,86 +10,58 @@ namespace
class ProcessRecord
{
public:
ProcessRecord(DWORD processId, HANDLE hostPipe) :
hostPipe(hostPipe),
section(OpenFileMappingW(FILE_MAP_READ, FALSE, (ITH_SECTION_ + std::to_wstring(processId)).c_str())),
sectionMap(MapViewOfFile(section, FILE_MAP_READ, 0, 0, HOOK_SECTION_SIZE / 2)), // jichi 1/16/2015: Changed to half to hook section size
sectionMutex(ITH_HOOKMAN_MUTEX_ + std::to_wstring(processId))
{}
inline static Host::ProcessEventCallback OnConnect, OnDisconnect;
ProcessRecord(ProcessRecord&) = delete;
ProcessRecord& operator=(ProcessRecord) = delete;
ProcessRecord(DWORD processId, HANDLE pipe) :
processId(processId),
pipe(pipe),
fileMapping(OpenFileMappingW(FILE_MAP_READ, FALSE, (ITH_SECTION_ + std::to_wstring(processId)).c_str())),
mappedView(MapViewOfFile(fileMapping, FILE_MAP_READ, 0, 0, HOOK_SECTION_SIZE / 2)), // jichi 1/16/2015: Changed to half to hook section size
sectionMutex(ITH_HOOKMAN_MUTEX_ + std::to_wstring(processId))
{
OnConnect(processId);
}
~ProcessRecord()
{
UnmapViewOfFile(sectionMap);
CloseHandle(section);
OnDisconnect(processId);
UnmapViewOfFile(mappedView);
}
TextHook GetHook(uint64_t addr)
{
if (sectionMap == nullptr) return {};
if (mappedView == nullptr) return {};
LOCK(sectionMutex);
auto hooks = (const TextHook*)sectionMap;
auto hooks = (const TextHook*)mappedView;
for (int i = 0; i < MAX_HOOK; ++i)
if (hooks[i].hp.insertion_address == addr) return hooks[i];
return {};
}
HANDLE hostPipe;
template <typename T>
void Send(T data)
{
DWORD DUMMY;
WriteFile(pipe, &data, sizeof(data), &DUMMY, nullptr);
}
private:
HANDLE section;
LPVOID sectionMap;
DWORD processId;
HANDLE pipe;
AutoHandle<> fileMapping;
LPCVOID mappedView;
WinMutex sectionMutex;
};
ThreadEventCallback OnCreate, OnDestroy;
ProcessEventCallback OnAttach, OnDetach;
ThreadSafePtr<std::unordered_map<ThreadParam, std::shared_ptr<TextThread>>> textThreadsByParams;
ThreadSafePtr<std::unordered_map<DWORD, std::unique_ptr<ProcessRecord>>> processRecordsByIds;
std::unordered_map<ThreadParam, std::shared_ptr<TextThread>> textThreadsByParams;
std::unordered_map<DWORD, std::unique_ptr<ProcessRecord>> processRecordsByIds;
std::recursive_mutex hostMutex;
DWORD DUMMY;
ThreadParam CONSOLE{ 0, -1ULL, -1ULL, -1ULL }, CLIPBOARD{ 0, 0, -1ULL, -1ULL };
void DispatchText(ThreadParam tp, const BYTE* text, int len)
{
LOCK(hostMutex);
if (textThreadsByParams[tp] == nullptr)
{
if (textThreadsByParams.size() > MAX_THREAD_COUNT) return Host::AddConsoleOutput(TOO_MANY_THREADS);
OnCreate(textThreadsByParams[tp] = std::make_shared<TextThread>(tp, Host::GetHookParam(tp), Host::GetHookName(tp)));
}
textThreadsByParams[tp]->Push(text, len);
}
void RemoveThreads(std::function<bool(ThreadParam)> removeIf)
{
LOCK(hostMutex);
for (auto it = textThreadsByParams.begin(); it != textThreadsByParams.end();)
if (auto curr = it++; removeIf(curr->first))
{
OnDestroy(curr->second);
textThreadsByParams.erase(curr->first);
}
}
void RegisterProcess(DWORD processId, HANDLE hostPipe)
{
LOCK(hostMutex);
processRecordsByIds.insert({ processId, std::make_unique<ProcessRecord>(processId, hostPipe) });
OnAttach(processId);
}
void UnregisterProcess(DWORD processId)
{
OnDetach(processId);
LOCK(hostMutex);
processRecordsByIds.erase(processId);
RemoveThreads([&](ThreadParam tp) { return tp.processId == processId; });
auto lockedTextThreadsByParams = textThreadsByParams.operator->();
for (auto it = lockedTextThreadsByParams->begin(); it != lockedTextThreadsByParams->end(); removeIf(it->first) ? it = lockedTextThreadsByParams->erase(it) : ++it);
}
void CreatePipe()
@ -100,14 +72,15 @@ namespace
InitializeSecurityDescriptor(&pipeSD, SECURITY_DESCRIPTOR_REVISION);
SetSecurityDescriptorDacl(&pipeSD, TRUE, NULL, FALSE); // Allow non-admin processes to connect to pipe created by admin host
SECURITY_ATTRIBUTES pipeSA = { sizeof(SECURITY_ATTRIBUTES), &pipeSD, FALSE };
HANDLE hookPipe = CreateNamedPipeW(HOOK_PIPE, PIPE_ACCESS_INBOUND, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE, PIPE_UNLIMITED_INSTANCES, 0, PIPE_BUFFER_SIZE, MAXDWORD, &pipeSA);
HANDLE hostPipe = CreateNamedPipeW(HOST_PIPE, PIPE_ACCESS_OUTBOUND, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE, PIPE_UNLIMITED_INSTANCES, PIPE_BUFFER_SIZE, 0, MAXDWORD, &pipeSA);
AutoHandle<Util::NamedPipeHandleCloser>
hookPipe = CreateNamedPipeW(HOOK_PIPE, PIPE_ACCESS_INBOUND, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE, PIPE_UNLIMITED_INSTANCES, 0, PIPE_BUFFER_SIZE, MAXDWORD, &pipeSA),
hostPipe = CreateNamedPipeW(HOST_PIPE, PIPE_ACCESS_OUTBOUND, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE, PIPE_UNLIMITED_INSTANCES, PIPE_BUFFER_SIZE, 0, MAXDWORD, &pipeSA);
ConnectNamedPipe(hookPipe, nullptr);
BYTE buffer[PIPE_BUFFER_SIZE] = {};
DWORD bytesRead, processId;
ReadFile(hookPipe, &processId, sizeof(processId), &bytesRead, nullptr);
RegisterProcess(processId, hostPipe);
processRecordsByIds->insert({ processId, std::make_unique<ProcessRecord>(processId, hostPipe) });
CreatePipe();
@ -129,16 +102,19 @@ namespace
default:
{
auto tp = *(ThreadParam*)buffer;
DispatchText(tp, buffer + sizeof(tp), bytesRead - sizeof(tp));
if (textThreadsByParams->count(tp) == 0)
{
auto textThread = textThreadsByParams->insert({ tp, std::make_shared<TextThread>(tp, Host::GetHookParam(tp), Host::GetHookName(tp)) }).first->second;
if (textThreadsByParams->size() > MAX_THREAD_COUNT) Host::AddConsoleOutput(TOO_MANY_THREADS);
else textThread->Start();
}
textThreadsByParams->at(tp)->Push(buffer + sizeof(tp), bytesRead - sizeof(tp));
}
break;
}
UnregisterProcess(processId);
DisconnectNamedPipe(hookPipe);
DisconnectNamedPipe(hostPipe);
CloseHandle(hookPipe);
CloseHandle(hostPipe);
RemoveThreads([&](ThreadParam tp) { return tp.processId == processId; });
processRecordsByIds->erase(processId);
}).detach();
}
@ -156,12 +132,16 @@ namespace
namespace Host
{
void Start(ProcessEventCallback onAttach, ProcessEventCallback onDetach, ThreadEventCallback onCreate, ThreadEventCallback onDestroy, TextThread::OutputCallback output)
void Start(ProcessEventCallback OnConnect, ProcessEventCallback OnDisconnect, TextThread::EventCallback OnCreate, TextThread::EventCallback OnDestroy, TextThread::OutputCallback Output)
{
OnAttach = onAttach; OnDetach = onDetach; OnCreate = onCreate; OnDestroy = onDestroy; TextThread::Output = output;
RegisterProcess(CONSOLE.processId, INVALID_HANDLE_VALUE);
OnCreate(textThreadsByParams[CONSOLE] = std::make_shared<TextThread>(CONSOLE, HookParam{}, L"Console"));
OnCreate(textThreadsByParams[CLIPBOARD] = std::make_shared<TextThread>(CLIPBOARD, HookParam{}, L"Clipboard"));
ProcessRecord::OnConnect = OnConnect;
ProcessRecord::OnDisconnect = OnDisconnect;
TextThread::OnCreate = OnCreate;
TextThread::OnDestroy = OnDestroy;
TextThread::Output = Output;
processRecordsByIds->insert({ CONSOLE.processId, std::make_unique<ProcessRecord>(CONSOLE.processId, INVALID_HANDLE_VALUE) });
textThreadsByParams->insert({ CONSOLE, std::make_shared<TextThread>(CONSOLE, HookParam{}, L"Console") });
textThreadsByParams->insert({ CLIPBOARD, std::make_shared<TextThread>(CLIPBOARD, HookParam{}, L"Clipboard") });
StartCapturingClipboard();
CreatePipe();
}
@ -170,9 +150,10 @@ namespace Host
{
// Artikash 7/25/2018: This is only called when Textractor is closed, at which point Windows should free everything itself...right?
#ifdef _DEBUG // Check memory leaks
LOCK(hostMutex);
processRecordsByIds.clear();
textThreadsByParams.clear();
ProcessRecord::OnConnect = ProcessRecord::OnDisconnect = [](auto) {};
TextThread::OnCreate = TextThread::OnDestroy = [](auto) {};
processRecordsByIds->clear();
textThreadsByParams->clear();
#endif
}
@ -180,43 +161,37 @@ namespace Host
{
if (processId == GetCurrentProcessId()) return false;
CloseHandle(CreateMutexW(nullptr, FALSE, (ITH_HOOKMAN_MUTEX_ + std::to_wstring(processId)).c_str()));
WinMutex(ITH_HOOKMAN_MUTEX_ + std::to_wstring(processId));
if (GetLastError() == ERROR_ALREADY_EXISTS)
{
AddConsoleOutput(ALREADY_INJECTED);
return false;
}
HMODULE textHooker = LoadLibraryExW(ITH_DLL, nullptr, DONT_RESOLVE_DLL_REFERENCES);
wchar_t textHookerPath[MAX_PATH];
DWORD textHookerPathSize = GetModuleFileNameW(textHooker, textHookerPath, MAX_PATH) * 2 + 2;
FreeLibrary(textHooker);
static HMODULE vnrhook = LoadLibraryExW(ITH_DLL, nullptr, DONT_RESOLVE_DLL_REFERENCES);
static std::wstring location = Util::GetModuleFileName(vnrhook).value();
if (HANDLE processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processId))
if (AutoHandle<> process = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processId))
{
#ifdef _WIN64
BOOL invalidProcess = FALSE;
IsWow64Process(processHandle, &invalidProcess);
IsWow64Process(process, &invalidProcess);
if (invalidProcess)
{
AddConsoleOutput(ARCHITECTURE_MISMATCH);
CloseHandle(processHandle);
return false;
}
#endif
if (LPVOID remoteData = VirtualAllocEx(processHandle, nullptr, textHookerPathSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE))
if (LPVOID remoteData = VirtualAllocEx(process, nullptr, location.size() * 2 + 2, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE))
{
WriteProcessMemory(processHandle, remoteData, textHookerPath, textHookerPathSize, nullptr);
if (HANDLE thread = CreateRemoteThread(processHandle, nullptr, 0, (LPTHREAD_START_ROUTINE)LoadLibraryW, remoteData, 0, nullptr))
WriteProcessMemory(process, remoteData, location.c_str(), location.size() * 2 + 2, nullptr);
if (AutoHandle<> thread = CreateRemoteThread(process, nullptr, 0, (LPTHREAD_START_ROUTINE)LoadLibraryW, remoteData, 0, nullptr))
{
WaitForSingleObject(thread, timeout);
CloseHandle(thread);
VirtualFreeEx(processHandle, remoteData, 0, MEM_RELEASE);
CloseHandle(processHandle);
VirtualFreeEx(process, remoteData, 0, MEM_RELEASE);
return true;
}
VirtualFreeEx(processHandle, remoteData, 0, MEM_RELEASE);
CloseHandle(processHandle);
VirtualFreeEx(process, remoteData, 0, MEM_RELEASE);
}
}
@ -226,41 +201,32 @@ namespace Host
void DetachProcess(DWORD processId)
{
LOCK(hostMutex);
HostCommandType buffer(HOST_COMMAND_DETACH);
WriteFile(processRecordsByIds.at(processId)->hostPipe, &buffer, sizeof(buffer), &DUMMY, nullptr);
processRecordsByIds->at(processId)->Send(HostCommandType(HOST_COMMAND_DETACH));
}
void InsertHook(DWORD processId, HookParam hp, std::string name)
{
LOCK(hostMutex);
InsertHookCmd buffer(hp, name);
WriteFile(processRecordsByIds.at(processId)->hostPipe, &buffer, sizeof(buffer), &DUMMY, nullptr);
processRecordsByIds->at(processId)->Send(InsertHookCmd(hp, name));
}
void RemoveHook(DWORD processId, uint64_t addr)
{
LOCK(hostMutex);
RemoveHookCmd buffer(addr);
WriteFile(processRecordsByIds.at(processId)->hostPipe, &buffer, sizeof(buffer), &DUMMY, nullptr);
processRecordsByIds->at(processId)->Send(RemoveHookCmd(addr));
}
HookParam GetHookParam(DWORD processId, uint64_t addr)
{
LOCK(hostMutex);
return processRecordsByIds.at(processId)->GetHook(addr).hp;
return processRecordsByIds->at(processId)->GetHook(addr).hp;
}
std::wstring GetHookName(DWORD processId, uint64_t addr)
{
LOCK(hostMutex);
return Util::StringToWideString(processRecordsByIds.at(processId)->GetHook(addr).hookName).value();
return Util::StringToWideString(processRecordsByIds->at(processId)->GetHook(addr).hookName).value();
}
std::shared_ptr<TextThread> GetThread(ThreadParam tp)
{
LOCK(hostMutex);
return textThreadsByParams[tp];
return textThreadsByParams->at(tp);
}
void AddConsoleOutput(std::wstring text)

View File

@ -3,12 +3,10 @@
#include "common.h"
#include "textthread.h"
typedef std::function<void(DWORD)> ProcessEventCallback;
typedef std::function<void(std::shared_ptr<TextThread>)> ThreadEventCallback;
namespace Host
{
void Start(ProcessEventCallback onAttach, ProcessEventCallback onDetach, ThreadEventCallback onCreate, ThreadEventCallback onDestroy, TextThread::OutputCallback output);
typedef std::function<void(DWORD)> ProcessEventCallback;
void Start(ProcessEventCallback OnConnect, ProcessEventCallback OnDisconnect, TextThread::EventCallback OnCreate, TextThread::EventCallback OnDestroy, TextThread::OutputCallback Output);
void Close();
bool InjectProcess(DWORD processId, DWORD timeout = 5000);

View File

@ -4,33 +4,37 @@
#include "host.h"
#include "util.h"
TextThread::TextThread(ThreadParam tp, HookParam hp, std::wstring name) : handle(threadCounter++), name(name), tp(tp), hp(hp) {}
TextThread::TextThread(ThreadParam tp, HookParam hp, std::wstring name) : handle(threadCounter++), name(name), tp(tp), hp(hp)
{
OnCreate(this);
}
TextThread::~TextThread()
{
SetEvent(deletionEvent);
flushThread.join();
CloseHandle(deletionEvent);
OnDestroy(this);
}
std::wstring TextThread::GetStorage()
{
LOCK(storageMutex);
return storage;
return storage->c_str();
}
void TextThread::Start()
{
deletionEvent = CreateEventW(nullptr, FALSE, FALSE, NULL);
flushThread = std::thread([&] { while (WaitForSingleObject(deletionEvent, 10) == WAIT_TIMEOUT) Flush(); });
}
void TextThread::AddSentence(std::wstring sentence)
{
if (Output(this, sentence))
{
LOCK(storageMutex);
storage += sentence;
}
if (Output(this, sentence)) storage->append(sentence);
}
void TextThread::Push(const BYTE* data, int len)
{
if (len < 0) return;
if (!flushThread.joinable() || len < 0) return;
LOCK(bufferMutex);
if (hp.type & USING_UNICODE) buffer += std::wstring((wchar_t*)data, len / 2);
else if (auto converted = Util::StringToWideString(std::string((char*)data, len), hp.codepage ? hp.codepage : defaultCodepage)) buffer += converted.value();

View File

@ -6,8 +6,9 @@
class TextThread
{
public:
typedef std::function<void(TextThread*)> EventCallback;
typedef std::function<bool(TextThread*, std::wstring&)> OutputCallback;
inline static EventCallback OnCreate, OnDestroy;
inline static OutputCallback Output;
inline static int flushDelay = 400; // flush every 400ms by default
@ -21,6 +22,7 @@ public:
~TextThread();
std::wstring GetStorage();
void Start();
void AddSentence(std::wstring sentence);
void Push(const BYTE* data, int len);
@ -32,13 +34,11 @@ public:
private:
void Flush();
ThreadSafePtr<std::wstring> storage;
std::wstring buffer;
std::unordered_set<wchar_t> repeatingChars;
std::mutex bufferMutex;
std::wstring storage;
std::mutex storageMutex;
HANDLE deletionEvent = CreateEventW(nullptr, FALSE, FALSE, NULL);
std::thread flushThread = std::thread([&] { while (WaitForSingleObject(deletionEvent, 10) == WAIT_TIMEOUT) Flush(); });
DWORD lastPushTime = GetTickCount();
AutoHandle<> deletionEvent = NULL;
std::thread flushThread;
DWORD lastPushTime;
};

View File

@ -1,16 +1,36 @@
#include "util.h"
#include "types.h"
#include <Psapi.h>
namespace Util
{
std::optional<std::wstring> GetModuleFileName(DWORD processId, HMODULE module)
{
if (AutoHandle<> process = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, processId))
{
std::vector<wchar_t> buffer(MAX_PATH);
if (GetModuleFileNameExW(process, module, buffer.data(), MAX_PATH)) return buffer.data();
else return {};
}
return {};
}
std::optional<std::wstring> GetModuleFileName(HMODULE module)
{
std::vector<wchar_t> buffer(MAX_PATH);
if (::GetModuleFileNameW(module, buffer.data(), MAX_PATH)) return buffer.data();
else return {};
}
std::optional<std::wstring> GetClipboardText()
{
if (!IsClipboardFormatAvailable(CF_UNICODETEXT)) return {};
if (!OpenClipboard(NULL)) return {};
if (HANDLE clipboardHandle = GetClipboardData(CF_UNICODETEXT))
if (HANDLE clipboard = GetClipboardData(CF_UNICODETEXT))
{
std::wstring ret = (wchar_t*)GlobalLock(clipboardHandle);
GlobalUnlock(clipboardHandle);
std::wstring ret = (wchar_t*)GlobalLock(clipboard);
GlobalUnlock(clipboard);
CloseClipboard();
return ret;
}

View File

@ -4,6 +4,9 @@
namespace Util
{
struct NamedPipeHandleCloser { void operator()(void* h) { DisconnectNamedPipe(h); CloseHandle(h); } };
std::optional<std::wstring> GetModuleFileName(DWORD processId, HMODULE module = NULL);
std::optional<std::wstring> GetModuleFileName(HMODULE module = NULL);
std::optional<std::wstring> GetClipboardText();
std::optional<std::wstring> StringToWideString(std::string text, UINT encoding = CP_UTF8);
// return true if repetition found (see https://github.com/Artikash/Textractor/issues/40)

View File

@ -1,5 +1,5 @@
#include "mainwindow.h"
#include "misc.h"
#include "host/util.h"
#include <sstream>
#include <QApplication>
@ -19,17 +19,19 @@ namespace
{
MEMORY_BASIC_INFORMATION info = {};
VirtualQuery(exception->ExceptionRecord->ExceptionAddress, &info, sizeof(info));
wchar_t moduleName[MAX_PATH] = {};
GetModuleFileNameW((HMODULE)info.AllocationBase, moduleName, MAX_PATH);
std::wstringstream errorMsg;
errorMsg << std::uppercase << std::hex <<
L"Error code: " << exception->ExceptionRecord->ExceptionCode << std::endl <<
L"Error address: " << (uint64_t)exception->ExceptionRecord->ExceptionAddress << std::endl <<
L"Error in module: " << moduleName << std::endl;
L"Error address: " << exception->ExceptionRecord->ExceptionAddress << std::endl <<
L"Error in module: " << Util::GetModuleFileName((HMODULE)info.AllocationBase).value_or(L"Could not find") << std::endl <<
L"Additional info: " << info.AllocationBase << std::endl;
if (exception->ExceptionRecord->ExceptionCode == 0xE06D7363)
{
errorMsg << L"Additional info: " << GetCppExceptionInfo(exception) << std::endl;
if (errorMsg.str().find(L"exception")) errorMsg << ((std::exception*)exception->ExceptionRecord->ExceptionInformation[1])->what();
}
for (int i = 0; i < exception->ExceptionRecord->NumberParameters; ++i)
errorMsg << L"Additional info: " << exception->ExceptionRecord->ExceptionInformation[i] << std::endl;
@ -38,7 +40,7 @@ namespace
return EXCEPTION_CONTINUE_SEARCH;
}
void Terminate()
__declspec(noreturn) void Terminate()
{
MessageBoxW(NULL, lastError.c_str(), L"Textractor ERROR", MB_ICONERROR);
std::abort();
@ -50,9 +52,12 @@ namespace
int main(int argc, char *argv[])
{
AddVectoredExceptionHandler(FALSE, ExceptionLogger);
SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)Terminate);
QString exe = GetFullModuleName(GetCurrentProcessId());
SetCurrentDirectoryW(exe.left(exe.lastIndexOf("\\")).toStdWString().c_str());
SetUnhandledExceptionFilter([](auto) -> LONG { Terminate(); });
std::wstring exe = Util::GetModuleFileName().value();
while (exe.back() != L'\\') exe.pop_back();
SetCurrentDirectoryW(exe.c_str());
QApplication a(argc, argv);
MainWindow w;
w.show();

View File

@ -4,6 +4,7 @@
#include "extenwindow.h"
#include "setdialog.h"
#include "misc.h"
#include <QTimer>
#include <QInputDialog>
MainWindow::MainWindow(QWidget *parent) :
@ -22,20 +23,12 @@ MainWindow::MainWindow(QWidget *parent) :
if (settings.contains(MAX_BUFFER_SIZE)) TextThread::maxBufferSize = settings.value(MAX_BUFFER_SIZE).toInt();
if (settings.contains(DEFAULT_CODEPAGE)) TextThread::defaultCodepage = settings.value(DEFAULT_CODEPAGE).toInt();
qRegisterMetaType<std::shared_ptr<TextThread>>();
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); },
[&](std::shared_ptr<TextThread> thread) { emit SigAddThread(thread); },
[&](std::shared_ptr<TextThread> thread) { emit SigRemoveThread(thread); },
[&](TextThread* thread, std::wstring& output) { return ProcessThreadOutput(thread, output); }
[&](DWORD processId) { ProcessConnected(processId); },
[&](DWORD processId) { ProcessDisconnected(processId); },
[&](TextThread* thread) { ThreadAdded(thread); },
[&](TextThread* thread) { ThreadRemoved(thread); },
[&](TextThread* thread, std::wstring& output) { return SentenceReceived(thread, output); }
);
Host::AddConsoleOutput(ABOUT);
}
@ -44,8 +37,8 @@ MainWindow::~MainWindow()
{
settings.setValue(WINDOW, geometry());
settings.sync();
delete ui;
Host::Close();
delete ui;
}
void MainWindow::closeEvent(QCloseEvent*)
@ -53,66 +46,71 @@ void MainWindow::closeEvent(QCloseEvent*)
QCoreApplication::quit(); // Need to do this to kill any windows that might've been made by extensions
}
void MainWindow::AddProcess(unsigned processId)
void MainWindow::InvokeOnMainThread(std::function<void()>&& f)
{
QMetaObject::invokeMethod(this, f);
}
void MainWindow::ProcessConnected(DWORD processId)
{
if (processId == 0) return;
processCombo->addItem(QString::number(processId, 16).toUpper() + ": " + GetModuleName(processId));
QFile file(HOOK_SAVE_FILE);
file.open(QIODevice::ReadOnly);
QString processName = GetFullModuleName(processId);
QStringList allProcesses = QString(file.readAll()).split("\r", QString::SkipEmptyParts);
for (auto hooks = allProcesses.rbegin(); hooks != allProcesses.rend(); ++hooks)
if (hooks->contains(processName))
InvokeOnMainThread([&, processId]
{
processCombo->addItem(QString::number(processId, 16).toUpper() + ": " + GetModuleName(processId));
QString processName = GetFullModuleName(processId);
QStringList allProcesses = QString(QAutoFile(HOOK_SAVE_FILE, QIODevice::ReadOnly)->readAll()).split("\r", QString::SkipEmptyParts);
for (auto hooks = allProcesses.rbegin(); hooks != allProcesses.rend(); ++hooks)
if (hooks->contains(processName))
{
for (auto hook : hooks->split(" , "))
if (auto hp = ParseCode(hook)) Host::InsertHook(processId, hp.value());
return;
}
});
}
void MainWindow::ProcessDisconnected(DWORD processId)
{
InvokeOnMainThread([&, processId] { processCombo->removeItem(processCombo->findText(QString::number(processId, 16).toUpper() + ":", Qt::MatchStartsWith)); });
}
void MainWindow::ThreadAdded(TextThread* thread)
{
QString ttString = TextThreadString(thread) + QString::fromStdWString(thread->name) + " (" + GenerateCode(thread->hp, thread->tp.processId) + ")";
InvokeOnMainThread([&, ttString] { ttCombo->addItem(ttString); });
}
void MainWindow::ThreadRemoved(TextThread* thread)
{
QString ttString = TextThreadString(thread);
InvokeOnMainThread([&, ttString]
{
int threadIndex = ttCombo->findText(ttString, Qt::MatchStartsWith);
if (threadIndex == ttCombo->currentIndex())
{
for (auto hook : hooks->split(" , "))
if (auto hp = ParseCode(hook)) Host::InsertHook(processId, hp.value());
return;
ttCombo->setCurrentIndex(0);
on_ttCombo_activated(0);
}
ttCombo->removeItem(threadIndex);
});
}
void MainWindow::RemoveProcess(unsigned processId)
bool MainWindow::SentenceReceived(TextThread* thread, std::wstring& sentence)
{
processCombo->removeItem(processCombo->findText(QString::number(processId, 16).toUpper() + ":", Qt::MatchStartsWith));
}
void MainWindow::AddThread(std::shared_ptr<TextThread> thread)
{
ttCombo->addItem(
TextThreadString(thread.get()) +
QString::fromStdWString(thread->name) +
" (" +
GenerateCode(thread->hp, thread->tp.processId) +
")"
);
}
void MainWindow::RemoveThread(std::shared_ptr<TextThread> thread)
{
int threadIndex = ttCombo->findText(TextThreadString(thread.get()), Qt::MatchStartsWith);
if (threadIndex == ttCombo->currentIndex())
if (DispatchSentenceToExtensions(sentence, GetMiscInfo(thread)))
{
ttCombo->setCurrentIndex(0);
on_ttCombo_activated(0);
}
ttCombo->removeItem(threadIndex);
}
void MainWindow::ThreadOutput(QString threadString, QString output)
{
if (ttCombo->currentText().startsWith(threadString))
{
textOutput->moveCursor(QTextCursor::End);
textOutput->insertPlainText(output);
textOutput->moveCursor(QTextCursor::End);
}
}
bool MainWindow::ProcessThreadOutput(TextThread* thread, std::wstring& output)
{
if (DispatchSentenceToExtensions(output, GetMiscInfo(thread)))
{
output += L"\r\n";
emit SigThreadOutput(TextThreadString(thread), QString::fromStdWString(output));
sentence += L"\r\n";
QString ttString = TextThreadString(thread);
InvokeOnMainThread([&, ttString, sentence]
{
if (ttCombo->currentText().startsWith(ttString))
{
textOutput->moveCursor(QTextCursor::End);
textOutput->insertPlainText(QString::fromStdWString(sentence));
textOutput->moveCursor(QTextCursor::End);
}
});
return true;
}
return false;
@ -130,9 +128,9 @@ QString MainWindow::TextThreadString(TextThread* thread)
).toUpper();
}
ThreadParam MainWindow::ParseTextThreadString(QString textThreadString)
ThreadParam MainWindow::ParseTextThreadString(QString ttString)
{
QStringList threadParam = textThreadString.split(":");
QStringList threadParam = ttString.split(":");
return { threadParam[1].toUInt(nullptr, 16), threadParam[2].toULongLong(nullptr, 16), threadParam[3].toULongLong(nullptr, 16), threadParam[4].toULongLong(nullptr, 16) };
}
@ -214,14 +212,10 @@ void MainWindow::on_unhookButton_clicked()
void MainWindow::on_saveButton_clicked()
{
auto hooks = GetAllHooks(GetSelectedProcessId());
QString hookList = GetFullModuleName(GetSelectedProcessId());
for (auto hp : hooks)
if (!(hp.type & HOOK_ENGINE))
hookList += " , " + GenerateCode(hp, GetSelectedProcessId());
QFile file(HOOK_SAVE_FILE);
file.open(QIODevice::Append);
file.write((hookList + "\r\n").toUtf8());
for (auto hp : GetAllHooks(GetSelectedProcessId()))
if (!(hp.type & HOOK_ENGINE)) hookList += " , " + GenerateCode(hp, GetSelectedProcessId());
QAutoFile(HOOK_SAVE_FILE, QIODevice::Append)->write((hookList + "\r\n").toUtf8());
}
void MainWindow::on_setButton_clicked()

View File

@ -13,8 +13,6 @@ namespace Ui
class MainWindow;
}
Q_DECLARE_METATYPE(std::shared_ptr<TextThread>);
class MainWindow : public QMainWindow
{
Q_OBJECT
@ -23,19 +21,7 @@ public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
signals:
void SigAddProcess(unsigned processId);
void SigRemoveProcess(unsigned processId);
void SigAddThread(std::shared_ptr<TextThread>);
void SigRemoveThread(std::shared_ptr<TextThread>);
void SigThreadOutput(QString threadString, QString output);
private slots:
void AddProcess(unsigned processId);
void RemoveProcess(unsigned processId);
void AddThread(std::shared_ptr<TextThread> thread);
void RemoveThread(std::shared_ptr<TextThread> thread);
void ThreadOutput(QString threadString, QString output); // this function doesn't take TextThread* because it might be destroyed on pipe thread
void on_attachButton_clicked();
void on_detachButton_clicked();
void on_unhookButton_clicked();
@ -46,9 +32,14 @@ private slots:
void on_ttCombo_activated(int index);
private:
bool ProcessThreadOutput(TextThread* thread, std::wstring& output);
void InvokeOnMainThread(std::function<void()>&& f);
void ProcessConnected(DWORD processId);
void ProcessDisconnected(DWORD processId);
void ThreadAdded(TextThread* thread);
void ThreadRemoved(TextThread* thread);
bool SentenceReceived(TextThread* thread, std::wstring& sentence);
QString TextThreadString(TextThread* thread);
ThreadParam ParseTextThreadString(QString textThreadString);
ThreadParam ParseTextThreadString(QString ttString);
DWORD GetSelectedProcessId();
std::unordered_map<std::string, int64_t> GetMiscInfo(TextThread* thread);
QVector<HookParam> GetAllHooks(DWORD processId);

View File

@ -4,6 +4,15 @@
#include "qtcommon.h"
#include "types.h"
class QAutoFile
{
public:
QAutoFile(QString name, QIODevice::OpenMode mode) : f(name) { f.open(mode); }
QFile* operator->() { return &f; }
private:
QFile f;
};
QString GetFullModuleName(DWORD processId, HMODULE module = NULL);
QString GetModuleName(DWORD processId, HMODULE module = NULL);
QMultiHash<QString, DWORD> GetAllProcesses();