using raii for processrecords and qfileinfo for files and other refactors

This commit is contained in:
Akash Mozumdar 2018-12-01 15:55:32 -05:00
parent 0f903940bc
commit 5ef1ff6352
12 changed files with 89 additions and 103 deletions

View File

@ -105,12 +105,11 @@ void ExtenWindow::Sync()
} }
} }
void ExtenWindow::Add(QString fileName) void ExtenWindow::Add(QFileInfo extenFile)
{ {
if (!fileName.endsWith(".dll")) return; if (extenFile.suffix() != "dll") return;
QString extenName = fileName.mid(fileName.lastIndexOf("/") + 1); QFile::copy(extenFile.fileName(), extenFile.absoluteFilePath());
QFile::copy(fileName, extenName); Load(extenFile.completeBaseName());
Load(extenName.split(".dll")[0]);
Sync(); Sync();
} }

View File

@ -26,7 +26,7 @@ private slots:
void on_rmvButton_clicked(); void on_rmvButton_clicked();
private: private:
void Add(QString fileName); void Add(QFileInfo extenFile);
void Sync(); void Sync();
bool eventFilter(QObject* target, QEvent* event); bool eventFilter(QObject* target, QEvent* event);
void dragEnterEvent(QDragEnterEvent* event); void dragEnterEvent(QDragEnterEvent* event);

View File

@ -15,9 +15,9 @@ namespace
ProcessRecord(DWORD processId, HANDLE pipe) : ProcessRecord(DWORD processId, HANDLE pipe) :
processId(processId), processId(processId),
pipe(pipe), pipe(pipe),
fileMapping(OpenFileMappingW(FILE_MAP_READ, FALSE, (ITH_SECTION_ + std::to_wstring(processId)).c_str())), mappedFile(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 view(MapViewOfFile(mappedFile, 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)) viewMutex(ITH_HOOKMAN_MUTEX_ + std::to_wstring(processId))
{ {
OnConnect(processId); OnConnect(processId);
} }
@ -25,14 +25,14 @@ namespace
~ProcessRecord() ~ProcessRecord()
{ {
OnDisconnect(processId); OnDisconnect(processId);
UnmapViewOfFile(mappedView); UnmapViewOfFile(view);
} }
TextHook GetHook(uint64_t addr) TextHook GetHook(uint64_t addr)
{ {
if (mappedView == nullptr) return {}; if (view == nullptr) return {};
LOCK(sectionMutex); LOCK(viewMutex);
auto hooks = (const TextHook*)mappedView; auto hooks = (const TextHook*)view;
for (int i = 0; i < MAX_HOOK; ++i) for (int i = 0; i < MAX_HOOK; ++i)
if (hooks[i].hp.insertion_address == addr) return hooks[i]; if (hooks[i].hp.insertion_address == addr) return hooks[i];
return {}; return {};
@ -41,16 +41,16 @@ namespace
template <typename T> template <typename T>
void Send(T data) void Send(T data)
{ {
DWORD DUMMY; std::enable_if_t<sizeof(data) < PIPE_BUFFER_SIZE, DWORD> DUMMY;
WriteFile(pipe, &data, sizeof(data), &DUMMY, nullptr); WriteFile(pipe, &data, sizeof(data), &DUMMY, nullptr);
} }
private: private:
DWORD processId; DWORD processId;
HANDLE pipe; HANDLE pipe;
AutoHandle<> fileMapping; AutoHandle<> mappedFile;
LPCVOID mappedView; LPCVOID view;
WinMutex sectionMutex; WinMutex viewMutex;
}; };
ThreadSafePtr<std::unordered_map<ThreadParam, std::shared_ptr<TextThread>>> textThreadsByParams; ThreadSafePtr<std::unordered_map<ThreadParam, std::shared_ptr<TextThread>>> textThreadsByParams;
@ -60,8 +60,8 @@ namespace
void RemoveThreads(std::function<bool(ThreadParam)> removeIf) void RemoveThreads(std::function<bool(ThreadParam)> removeIf)
{ {
auto lockedTextThreadsByParams = textThreadsByParams.operator->(); auto[lock, textThreadsByParams] = ::textThreadsByParams.operator->();
for (auto it = lockedTextThreadsByParams->begin(); it != lockedTextThreadsByParams->end(); removeIf(it->first) ? it = lockedTextThreadsByParams->erase(it) : ++it); for (auto it = textThreadsByParams->begin(); it != textThreadsByParams->end(); removeIf(it->first) ? it = textThreadsByParams->erase(it) : ++it);
} }
void CreatePipe() void CreatePipe()
@ -104,9 +104,9 @@ namespace
auto tp = *(ThreadParam*)buffer; auto tp = *(ThreadParam*)buffer;
if (textThreadsByParams->count(tp) == 0) if (textThreadsByParams->count(tp) == 0)
{ {
auto textThread = textThreadsByParams->insert({ tp, std::make_shared<TextThread>(tp, Host::GetHookParam(tp), Host::GetHookName(tp)) }).first->second; auto textThread = textThreadsByParams->insert({ tp, std::make_shared<TextThread>(tp, Host::GetHookParam(tp)) }).first->second;
if (textThreadsByParams->size() > MAX_THREAD_COUNT) Host::AddConsoleOutput(TOO_MANY_THREADS); if (textThreadsByParams->size() < MAX_THREAD_COUNT) textThread->Start();
else textThread->Start(); else Host::AddConsoleOutput(TOO_MANY_THREADS);
} }
textThreadsByParams->at(tp)->Push(buffer + sizeof(tp), bytesRead - sizeof(tp)); textThreadsByParams->at(tp)->Push(buffer + sizeof(tp), bytesRead - sizeof(tp));
} }
@ -146,6 +146,16 @@ namespace Host
CreatePipe(); CreatePipe();
} }
void Shutdown()
{
auto NOP = [](auto... args) { return NULL; };
ProcessRecord::OnConnect = NOP;
ProcessRecord::OnDisconnect = NOP;
TextThread::OnCreate = NOP;
TextThread::OnDestroy = NOP;
TextThread::Output = NOP;
}
bool InjectProcess(DWORD processId, DWORD timeout) bool InjectProcess(DWORD processId, DWORD timeout)
{ {
if (processId == GetCurrentProcessId()) return false; if (processId == GetCurrentProcessId()) return false;
@ -193,19 +203,14 @@ namespace Host
processRecordsByIds->at(processId)->Send(HostCommandType(HOST_COMMAND_DETACH)); processRecordsByIds->at(processId)->Send(HostCommandType(HOST_COMMAND_DETACH));
} }
void InsertHook(DWORD processId, HookParam hp, std::string name) void InsertHook(DWORD processId, HookParam hp)
{ {
processRecordsByIds->at(processId)->Send(InsertHookCmd(hp, name)); processRecordsByIds->at(processId)->Send(InsertHookCmd(hp));
} }
HookParam GetHookParam(DWORD processId, uint64_t addr) HookParam GetHookParam(ThreadParam tp)
{ {
return processRecordsByIds->at(processId)->GetHook(addr).hp; return processRecordsByIds->at(tp.processId)->GetHook(tp.addr).hp;
}
std::wstring GetHookName(DWORD processId, uint64_t addr)
{
return Util::StringToWideString(processRecordsByIds->at(processId)->GetHook(addr).hookName).value();
} }
std::shared_ptr<TextThread> GetThread(ThreadParam tp) std::shared_ptr<TextThread> GetThread(ThreadParam tp)

View File

@ -5,17 +5,15 @@
namespace Host namespace Host
{ {
typedef std::function<void(DWORD)> ProcessEventCallback; using ProcessEventCallback = std::function<void(DWORD)>;
void Start(ProcessEventCallback OnConnect, ProcessEventCallback OnDisconnect, TextThread::EventCallback OnCreate, TextThread::EventCallback OnDestroy, TextThread::OutputCallback Output); void Start(ProcessEventCallback OnConnect, ProcessEventCallback OnDisconnect, TextThread::EventCallback OnCreate, TextThread::EventCallback OnDestroy, TextThread::OutputCallback Output);
void Shutdown();
bool InjectProcess(DWORD processId, DWORD timeout = 5000); bool InjectProcess(DWORD processId, DWORD timeout = 5000);
void DetachProcess(DWORD processId); void DetachProcess(DWORD processId);
void InsertHook(DWORD processId, HookParam hp, std::string name = ""); void InsertHook(DWORD processId, HookParam hp);
HookParam GetHookParam(DWORD processId, uint64_t addr); HookParam GetHookParam(ThreadParam tp);
inline HookParam GetHookParam(ThreadParam tp) { return GetHookParam(tp.processId, tp.addr); }
std::wstring GetHookName(DWORD processId, uint64_t addr);
inline std::wstring GetHookName(ThreadParam tp) { return GetHookName(tp.processId, tp.addr); }
std::shared_ptr<TextThread> GetThread(ThreadParam tp); std::shared_ptr<TextThread> GetThread(ThreadParam tp);
void AddConsoleOutput(std::wstring text); void AddConsoleOutput(std::wstring text);

View File

@ -4,16 +4,23 @@
#include "host.h" #include "host.h"
#include "util.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::optional<std::wstring> name) :
handle(threadCounter++),
name(name.value_or(Util::StringToWideString(hp.name).value())),
tp(tp),
hp(hp)
{ {
OnCreate(this); OnCreate(this);
} }
TextThread::~TextThread() TextThread::~TextThread()
{ {
SetEvent(deletionEvent); if (flushThread.joinable())
flushThread.join(); {
OnDestroy(this); SetEvent(deletionEvent);
flushThread.join();
OnDestroy(this);
}
} }
std::wstring TextThread::GetStorage() std::wstring TextThread::GetStorage()

View File

@ -6,8 +6,8 @@
class TextThread class TextThread
{ {
public: public:
typedef std::function<void(TextThread*)> EventCallback; using EventCallback = std::function<void(TextThread*)>;
typedef std::function<bool(TextThread*, std::wstring&)> OutputCallback; using OutputCallback = std::function<bool(TextThread*, std::wstring&)>;
inline static EventCallback OnCreate, OnDestroy; inline static EventCallback OnCreate, OnDestroy;
inline static OutputCallback Output; inline static OutputCallback Output;
@ -16,9 +16,7 @@ public:
inline static int defaultCodepage = SHIFT_JIS; inline static int defaultCodepage = SHIFT_JIS;
inline static int threadCounter = 0; inline static int threadCounter = 0;
TextThread(ThreadParam tp, HookParam hp, std::wstring name); TextThread(ThreadParam tp, HookParam hp, std::optional<std::wstring> name = {});
TextThread(TextThread&) = delete;
TextThread& operator=(TextThread) = delete;
~TextThread(); ~TextThread();
std::wstring GetStorage(); std::wstring GetStorage();

View File

@ -2,6 +2,7 @@
#include "host/util.h" #include "host/util.h"
#include <sstream> #include <sstream>
#include <QApplication> #include <QApplication>
#include <QDir>
namespace namespace
{ {
@ -15,8 +16,16 @@ namespace
thread_local std::wstring lastError = L"Unknown error"; thread_local std::wstring lastError = L"Unknown error";
__declspec(noreturn) void Terminate()
{
MessageBoxW(NULL, lastError.c_str(), L"Textractor ERROR", MB_ICONERROR);
std::abort();
}
LONG WINAPI ExceptionLogger(EXCEPTION_POINTERS* exception) LONG WINAPI ExceptionLogger(EXCEPTION_POINTERS* exception)
{ {
thread_local static auto terminateSetter = std::invoke(std::set_terminate, Terminate);
MEMORY_BASIC_INFORMATION info = {}; MEMORY_BASIC_INFORMATION info = {};
VirtualQuery(exception->ExceptionRecord->ExceptionAddress, &info, sizeof(info)); VirtualQuery(exception->ExceptionRecord->ExceptionAddress, &info, sizeof(info));
@ -36,14 +45,6 @@ namespace
lastError = errorMsg.str(); lastError = errorMsg.str();
return EXCEPTION_CONTINUE_SEARCH; return EXCEPTION_CONTINUE_SEARCH;
} }
__declspec(noreturn) void Terminate()
{
MessageBoxW(NULL, lastError.c_str(), L"Textractor ERROR", MB_ICONERROR);
std::abort();
}
thread_local auto _ = [] { return std::set_terminate(Terminate); }();
} }
int main(int argc, char *argv[]) int main(int argc, char *argv[])
@ -51,9 +52,7 @@ int main(int argc, char *argv[])
AddVectoredExceptionHandler(FALSE, ExceptionLogger); AddVectoredExceptionHandler(FALSE, ExceptionLogger);
SetUnhandledExceptionFilter([](auto) -> LONG { Terminate(); }); SetUnhandledExceptionFilter([](auto) -> LONG { Terminate(); });
std::wstring exe = Util::GetModuleFileName().value(); QDir::setCurrent(QFileInfo().absolutePath());
while (exe.back() != L'\\') exe.pop_back();
SetCurrentDirectoryW(exe.c_str());
QApplication a(argc, argv); QApplication a(argc, argv);
MainWindow w; MainWindow w;

View File

@ -4,6 +4,7 @@
#include "extenwindow.h" #include "extenwindow.h"
#include "setdialog.h" #include "setdialog.h"
#include "misc.h" #include "misc.h"
#include "host/util.h"
#include <QTimer> #include <QTimer>
#include <QInputDialog> #include <QInputDialog>
@ -35,6 +36,7 @@ MainWindow::MainWindow(QWidget *parent) :
MainWindow::~MainWindow() MainWindow::~MainWindow()
{ {
Host::Shutdown();
settings.setValue(WINDOW, geometry()); settings.setValue(WINDOW, geometry());
settings.sync(); settings.sync();
delete ui; delete ui;
@ -46,7 +48,7 @@ void MainWindow::closeEvent(QCloseEvent*)
} }
void MainWindow::InvokeOnMainThread(std::function<void()>&& f) void MainWindow::InvokeOnMainThread(std::function<void()> f)
{ {
QMetaObject::invokeMethod(this, f); QMetaObject::invokeMethod(this, f);
} }
@ -56,16 +58,15 @@ void MainWindow::ProcessConnected(DWORD processId)
if (processId == 0) return; if (processId == 0) return;
InvokeOnMainThread([&, processId] InvokeOnMainThread([&, processId]
{ {
processCombo->addItem(QString::number(processId, 16).toUpper() + ": " + GetModuleName(processId)); QString process = QString::fromStdWString(Util::GetModuleFileName(processId).value());
QString processName = GetFullModuleName(processId); processCombo->addItem(QString::number(processId, 16).toUpper() + ": " + QFileInfo(process).fileName());
QStringList allProcesses = QString(QAutoFile(HOOK_SAVE_FILE, QIODevice::ReadOnly)->readAll()).split("\r", QString::SkipEmptyParts); QStringList allProcesses = QString(QAutoFile(HOOK_SAVE_FILE, QIODevice::ReadOnly)->readAll()).split("\r", QString::SkipEmptyParts);
for (auto hooks = allProcesses.rbegin(); hooks != allProcesses.rend(); ++hooks) // Can't use QFileInfo::absoluteFilePath since hook save file has '\\' as path separator
if (hooks->contains(processName)) auto hookList = std::find_if(allProcesses.rbegin(), allProcesses.rend(), [&](QString hookList) { return hookList.contains(process); });
{ if (hookList != allProcesses.rend())
for (auto hook : hooks->split(" , ")) for (auto hookCode : hookList->split(" , "))
if (auto hp = ParseCode(hook)) Host::InsertHook(processId, hp.value()); if (auto hp = ParseCode(hookCode)) Host::InsertHook(processId, hp.value());
return;
}
}); });
} }
@ -185,7 +186,7 @@ void MainWindow::on_saveButton_clicked()
ThreadParam tp = ParseTextThreadString(ttCombo->itemText(i)); ThreadParam tp = ParseTextThreadString(ttCombo->itemText(i));
if (tp.processId == GetSelectedProcessId() && !(Host::GetHookParam(tp).type & HOOK_ENGINE)) hookCodes[tp.addr] = GenerateCode(Host::GetHookParam(tp), tp.processId); if (tp.processId == GetSelectedProcessId() && !(Host::GetHookParam(tp).type & HOOK_ENGINE)) hookCodes[tp.addr] = GenerateCode(Host::GetHookParam(tp), tp.processId);
} }
QString hookList = GetFullModuleName(GetSelectedProcessId()); QString hookList = QString::fromStdWString(Util::GetModuleFileName(GetSelectedProcessId()).value());
for (auto hookCode : hookCodes) hookList += " , " + hookCode; for (auto hookCode : hookCodes) hookList += " , " + hookCode;
QAutoFile(HOOK_SAVE_FILE, QIODevice::Append)->write((hookList + "\r\n").toUtf8()); QAutoFile(HOOK_SAVE_FILE, QIODevice::Append)->write((hookList + "\r\n").toUtf8());
} }

View File

@ -32,7 +32,7 @@ private slots:
private: private:
void closeEvent(QCloseEvent*); void closeEvent(QCloseEvent*);
void InvokeOnMainThread(std::function<void()>&& f); void InvokeOnMainThread(std::function<void()> f);
void ProcessConnected(DWORD processId); void ProcessConnected(DWORD processId);
void ProcessDisconnected(DWORD processId); void ProcessDisconnected(DWORD processId);
void ThreadAdded(TextThread* thread); void ThreadAdded(TextThread* thread);

View File

@ -1,32 +1,16 @@
#include "misc.h" #include "misc.h"
#include "const.h" #include "const.h"
#include "host/util.h"
#include <Psapi.h> #include <Psapi.h>
#include <QTextStream> #include <QTextStream>
QString GetFullModuleName(DWORD processId, HMODULE module)
{
HANDLE handle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processId);
wchar_t buffer[MAX_PATH] = {};
GetModuleFileNameExW(handle, module, buffer, MAX_PATH);
CloseHandle(handle);
return QString::fromWCharArray(buffer);
}
QString GetModuleName(DWORD processId, HMODULE module)
{
QString fullName = GetFullModuleName(processId, module);
return fullName.remove(0, fullName.lastIndexOf("\\") + 1);
}
QMultiHash<QString, DWORD> GetAllProcesses() QMultiHash<QString, DWORD> GetAllProcesses()
{ {
DWORD allProcessIds[0x1000]; DWORD allProcessIds[5000] = {}, spaceUsed = 0;
DWORD spaceUsed; EnumProcesses(allProcessIds, sizeof(allProcessIds), &spaceUsed);
QMultiHash<QString, DWORD> ret; QMultiHash<QString, DWORD> ret;
if (!EnumProcesses(allProcessIds, sizeof(allProcessIds), &spaceUsed)) return ret;
for (int i = 0; i < spaceUsed / sizeof(DWORD); ++i) for (int i = 0; i < spaceUsed / sizeof(DWORD); ++i)
if (GetModuleName(allProcessIds[i]).size()) if (auto processName = Util::GetModuleFileName(allProcessIds[i])) ret.insert(QFileInfo(QString::fromStdWString(processName.value())).fileName(), allProcessIds[i]);
ret.insert(GetModuleName(allProcessIds[i]), allProcessIds[i]);
return ret; return ret;
} }
@ -235,19 +219,15 @@ namespace
// Attempt to make the address relative // Attempt to make the address relative
if (!(hp.type & MODULE_OFFSET)) if (!(hp.type & MODULE_OFFSET))
{ if (AutoHandle<> process = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, processId))
HANDLE processHandle; if (MEMORY_BASIC_INFORMATION info = {}; VirtualQueryEx(process, (LPCVOID)hp.address, &info, sizeof(info)))
if (!(processHandle = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, processId))) goto fin; if (auto moduleName = Util::GetModuleFileName(processId, (HMODULE)info.AllocationBase))
MEMORY_BASIC_INFORMATION info; {
if (!VirtualQueryEx(processHandle, (LPCVOID)hp.address, &info, sizeof(info))) goto fin; hp.type |= MODULE_OFFSET;
QString moduleName = GetModuleName(processId, (HMODULE)info.AllocationBase); hp.address -= (uint64_t)info.AllocationBase;
if (moduleName == "") goto fin; wcscpy_s<MAX_MODULE_SIZE>(hp.module, moduleName->c_str() + moduleName->rfind(L'\\') + 1);
hp.type |= MODULE_OFFSET; }
hp.address -= (uint64_t)info.AllocationBase;
wcscpy_s<MAX_MODULE_SIZE>(hp.module, moduleName.toStdWString().c_str());
}
fin:
codeBuilder << "@" << hp.address; codeBuilder << "@" << hp.address;
if (hp.type & MODULE_OFFSET) codeBuilder << ":" << QString::fromWCharArray(hp.module); if (hp.type & MODULE_OFFSET) codeBuilder << ":" << QString::fromWCharArray(hp.module);
if (hp.type & FUNCTION_OFFSET) codeBuilder << ":" << hp.function; if (hp.type & FUNCTION_OFFSET) codeBuilder << ":" << hp.function;

View File

@ -13,8 +13,6 @@ private:
QFile f; QFile f;
}; };
QString GetFullModuleName(DWORD processId, HMODULE module = NULL);
QString GetModuleName(DWORD processId, HMODULE module = NULL);
QMultiHash<QString, DWORD> GetAllProcesses(); QMultiHash<QString, DWORD> GetAllProcesses();
std::optional<HookParam> ParseCode(QString HCode); std::optional<HookParam> ParseCode(QString HCode);
QString GenerateCode(HookParam hp, DWORD processId); QString GenerateCode(HookParam hp, DWORD processId);

View File

@ -7,4 +7,5 @@
#include <QMainWindow> #include <QMainWindow>
#include <QDialog> #include <QDialog>
#include <QFile> #include <QFile>
#include <QFileInfo>
#include <QRegularExpression> #include <QRegularExpression>