using raii for processrecords and qfileinfo for files and other refactors
This commit is contained in:
parent
0f903940bc
commit
5ef1ff6352
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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()
|
||||
{
|
||||
SetEvent(deletionEvent);
|
||||
flushThread.join();
|
||||
OnDestroy(this);
|
||||
if (flushThread.joinable())
|
||||
{
|
||||
SetEvent(deletionEvent);
|
||||
flushThread.join();
|
||||
OnDestroy(this);
|
||||
}
|
||||
}
|
||||
|
||||
std::wstring TextThread::GetStorage()
|
||||
|
@ -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();
|
||||
|
21
GUI/main.cpp
21
GUI/main.cpp
@ -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;
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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);
|
||||
|
44
GUI/misc.cpp
44
GUI/misc.cpp
@ -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))
|
||||
{
|
||||
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());
|
||||
}
|
||||
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))
|
||||
{
|
||||
hp.type |= MODULE_OFFSET;
|
||||
hp.address -= (uint64_t)info.AllocationBase;
|
||||
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;
|
||||
|
@ -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);
|
||||
|
@ -7,4 +7,5 @@
|
||||
#include <QMainWindow>
|
||||
#include <QDialog>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QRegularExpression>
|
||||
|
Loading…
Reference in New Issue
Block a user