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;
QString extenName = fileName.mid(fileName.lastIndexOf("/") + 1);
QFile::copy(fileName, extenName);
Load(extenName.split(".dll")[0]);
if (extenFile.suffix() != "dll") return;
QFile::copy(extenFile.fileName(), extenFile.absoluteFilePath());
Load(extenFile.completeBaseName());
Sync();
}

View File

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

View File

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

View File

@ -5,17 +5,15 @@
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 Shutdown();
bool InjectProcess(DWORD processId, DWORD timeout = 5000);
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);
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); }
HookParam GetHookParam(ThreadParam tp);
std::shared_ptr<TextThread> GetThread(ThreadParam tp);
void AddConsoleOutput(std::wstring text);

View File

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

View File

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

View File

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

View File

@ -4,6 +4,7 @@
#include "extenwindow.h"
#include "setdialog.h"
#include "misc.h"
#include "host/util.h"
#include <QTimer>
#include <QInputDialog>
@ -35,6 +36,7 @@ MainWindow::MainWindow(QWidget *parent) :
MainWindow::~MainWindow()
{
Host::Shutdown();
settings.setValue(WINDOW, geometry());
settings.sync();
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);
}
@ -56,16 +58,15 @@ void MainWindow::ProcessConnected(DWORD processId)
if (processId == 0) return;
InvokeOnMainThread([&, processId]
{
processCombo->addItem(QString::number(processId, 16).toUpper() + ": " + GetModuleName(processId));
QString processName = GetFullModuleName(processId);
QString process = QString::fromStdWString(Util::GetModuleFileName(processId).value());
processCombo->addItem(QString::number(processId, 16).toUpper() + ": " + QFileInfo(process).fileName());
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;
}
// Can't use QFileInfo::absoluteFilePath since hook save file has '\\' as path separator
auto hookList = std::find_if(allProcesses.rbegin(), allProcesses.rend(), [&](QString hookList) { return hookList.contains(process); });
if (hookList != allProcesses.rend())
for (auto hookCode : hookList->split(" , "))
if (auto hp = ParseCode(hookCode)) Host::InsertHook(processId, hp.value());
});
}
@ -185,7 +186,7 @@ void MainWindow::on_saveButton_clicked()
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);
}
QString hookList = GetFullModuleName(GetSelectedProcessId());
QString hookList = QString::fromStdWString(Util::GetModuleFileName(GetSelectedProcessId()).value());
for (auto hookCode : hookCodes) hookList += " , " + hookCode;
QAutoFile(HOOK_SAVE_FILE, QIODevice::Append)->write((hookList + "\r\n").toUtf8());
}

View File

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

View File

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

View File

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

View File

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