start implementing new GUI
This commit is contained in:
parent
ffeb4e2dad
commit
fe30b77a44
@ -35,6 +35,9 @@ FORMS += \
|
||||
win32: LIBS += \
|
||||
-L$$PWD/../Builds/Debug/Debug/ -lvnrhost
|
||||
|
||||
QMAKE_CXXFLAGS_RELEASE += \
|
||||
/MT
|
||||
|
||||
# Default rules for deployment.
|
||||
qnx: target.path = /tmp/$${TARGET}/bin
|
||||
else: unix:!android: target.path = /opt/$${TARGET}/bin
|
||||
|
@ -1,17 +1,44 @@
|
||||
#include "mainwindow.h"
|
||||
#include "ui_mainwindow.h"
|
||||
#include "QMessageBox"
|
||||
#include "qlineedit.h"
|
||||
#include "QLineEdit"
|
||||
#include "QTableWidget"
|
||||
#include "QInputDialog"
|
||||
#include <Windows.h>
|
||||
#include <qdebug.h>
|
||||
#include <Psapi.h>
|
||||
#include "../texthook/host.h"
|
||||
|
||||
QTableWidget* processList;
|
||||
|
||||
QString GetModuleName(DWORD processId, HMODULE module = NULL)
|
||||
{
|
||||
HANDLE handle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processId);
|
||||
wchar_t buffer[MAX_PATH];
|
||||
GetModuleFileNameExW(handle, module, buffer, MAX_PATH);
|
||||
return QString::fromWCharArray(wcsrchr(buffer, L'\\') + 1);
|
||||
}
|
||||
|
||||
void OnProcessAttach(DWORD processId)
|
||||
{
|
||||
processList->setItem(processList->rowCount(), 0, new QTableWidgetItem(QString::number(processId)));
|
||||
}
|
||||
|
||||
MainWindow::MainWindow(QWidget *parent) :
|
||||
QMainWindow(parent),
|
||||
ui(new Ui::MainWindow)
|
||||
{
|
||||
Host::Start();
|
||||
ui->setupUi(this);
|
||||
connect(ui->centralWidget->children().at(0), SIGNAL(returnPressed()), this, SLOT(onCommand()));
|
||||
StartHost();
|
||||
|
||||
processList = this->findChild<QTableWidget*>("processList");
|
||||
Host::RegisterProcessAttachCallback([](DWORD processId)
|
||||
{
|
||||
processList->insertRow(processList->rowCount());
|
||||
processList->setItem(processList->rowCount() - 1, 0, new QTableWidgetItem(QString::number(processId)));
|
||||
processList->setItem(processList->rowCount() - 1, 1, new QTableWidgetItem(GetModuleName(processId)));
|
||||
});
|
||||
Host::Open();
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
@ -19,8 +46,9 @@ MainWindow::~MainWindow()
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void MainWindow::onCommand()
|
||||
void MainWindow::on_attachButton_clicked()
|
||||
{
|
||||
QLineEdit* lineEdit = (QLineEdit*)sender();
|
||||
QMessageBox::information(this, "called", lineEdit->text());
|
||||
//processList->insertRow(processList->rowCount());
|
||||
//processList->setItem(processList->rowCount() - 1, 0, new QTableWidgetItem(QString::number(6000)));
|
||||
Host::InjectProcess(QInputDialog::getInt(this, "Process ID?", ""));
|
||||
}
|
||||
|
@ -3,8 +3,9 @@
|
||||
|
||||
#include <QMainWindow>
|
||||
|
||||
namespace Ui {
|
||||
class MainWindow;
|
||||
namespace Ui
|
||||
{
|
||||
class MainWindow;
|
||||
}
|
||||
|
||||
class MainWindow : public QMainWindow
|
||||
@ -15,8 +16,10 @@ public:
|
||||
explicit MainWindow(QWidget *parent = nullptr);
|
||||
~MainWindow();
|
||||
|
||||
public slots:
|
||||
void onCommand();
|
||||
private slots:
|
||||
|
||||
void on_attachButton_clicked();
|
||||
|
||||
private:
|
||||
Ui::MainWindow *ui;
|
||||
};
|
||||
|
@ -6,46 +6,110 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>496</width>
|
||||
<height>376</height>
|
||||
<width>800</width>
|
||||
<height>600</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>NextHooker</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralWidget">
|
||||
<widget class="QLineEdit" name="lineEdit">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>252</x>
|
||||
<y>0</y>
|
||||
<width>241</width>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPlainTextEdit" name="plainTextEdit">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>3</x>
|
||||
<y>40</y>
|
||||
<width>491</width>
|
||||
<height>291</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QTextBrowser" name="textBrowser">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>5</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QFrame" name="processManager">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>2</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="attachButton">
|
||||
<property name="text">
|
||||
<string>Attach to game</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Currently attached to:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTableWidget" name="processList">
|
||||
<property name="columnCount">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<attribute name="horizontalHeaderDefaultSectionSize">
|
||||
<number>50</number>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderStretchLastSection">
|
||||
<bool>true</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>ID</string>
|
||||
</property>
|
||||
<property name="textAlignment">
|
||||
<set>AlignCenter</set>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Name</string>
|
||||
</property>
|
||||
<property name="textAlignment">
|
||||
<set>AlignCenter</set>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QMenuBar" name="menuBar">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>496</width>
|
||||
<width>800</width>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusBar"/>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
<resources/>
|
||||
|
@ -1,12 +1,10 @@
|
||||
project(host)
|
||||
|
||||
set(vnrhost_src
|
||||
hookman.h
|
||||
host.h
|
||||
pipe.h
|
||||
textthread.h
|
||||
winmutex.h
|
||||
hookman.cc
|
||||
host.cc
|
||||
pipe.cc
|
||||
textthread.cc
|
||||
|
@ -1,155 +0,0 @@
|
||||
// hookman.cc
|
||||
// 8/24/2013 jichi
|
||||
// Branch IHF/HookManager.cpp, rev 133
|
||||
// 8/24/2013 TODO: Clean up this file
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# pragma warning (disable:4100) // C4100: unreference formal parameter
|
||||
# pragma warning (disable:4146) // C4146: unary minus operator applied to unsigned type
|
||||
#endif // _MSC_VER
|
||||
|
||||
#include "hookman.h"
|
||||
#include "../vnrhook/include/const.h"
|
||||
#include "../vnrhook/include/defs.h"
|
||||
#include "../vnrhook/include/types.h"
|
||||
#include "winmutex.h"
|
||||
#include <atlbase.h>
|
||||
|
||||
#define HM_LOCK CriticalSectionLocker hmLocker(hmCs) // Synchronized scope for accessing private data
|
||||
|
||||
HookManager::HookManager() :
|
||||
create(nullptr),
|
||||
remove(nullptr),
|
||||
attach(nullptr),
|
||||
detach(nullptr),
|
||||
nextThreadNumber(0),
|
||||
splitDelay(250),
|
||||
textThreadsByParams(),
|
||||
processRecordsByIds()
|
||||
{
|
||||
InitializeCriticalSection(&hmCs);
|
||||
// Console text thread
|
||||
(textThreadsByParams[{ 0, -1UL, -1UL, -1UL }] = new TextThread({ 0, -1UL, -1UL, -1UL }, nextThreadNumber++, splitDelay))->Status() |= USING_UNICODE;
|
||||
}
|
||||
|
||||
HookManager::~HookManager()
|
||||
{
|
||||
EnterCriticalSection(&hmCs);
|
||||
RemoveThreads([](auto one, auto two) { return true; }, {});
|
||||
for (auto i : processRecordsByIds) UnRegisterProcess(i.first);
|
||||
LeaveCriticalSection(&hmCs);
|
||||
DeleteCriticalSection(&hmCs);
|
||||
}
|
||||
|
||||
TextThread* HookManager::FindSingle(DWORD number)
|
||||
{
|
||||
HM_LOCK;
|
||||
for (auto i : textThreadsByParams)
|
||||
if (i.second->Number() == number)
|
||||
return i.second;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void HookManager::RemoveThreads(bool(*RemoveIf)(ThreadParameter, ThreadParameter), ThreadParameter cmp)
|
||||
{
|
||||
HM_LOCK;
|
||||
std::vector<ThreadParameter> removedThreads;
|
||||
for (auto i : textThreadsByParams)
|
||||
if (RemoveIf(i.first, cmp))
|
||||
{
|
||||
if (remove) remove(i.second);
|
||||
delete i.second;
|
||||
removedThreads.push_back(i.first);
|
||||
}
|
||||
for (auto i : removedThreads) textThreadsByParams.erase(i);
|
||||
}
|
||||
|
||||
void HookManager::RegisterProcess(DWORD pid, HANDLE hostPipe)
|
||||
{
|
||||
HM_LOCK;
|
||||
ProcessRecord record;
|
||||
record.hostPipe = hostPipe;
|
||||
record.hookman_section = OpenFileMappingW(FILE_MAP_READ, FALSE, (ITH_SECTION_ + std::to_wstring(pid)).c_str());
|
||||
record.hookman_map = MapViewOfFile(record.hookman_section, FILE_MAP_READ, 0, 0, HOOK_SECTION_SIZE / 2); // jichi 1/16/2015: Changed to half to hook section size
|
||||
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 (attach) attach(pid);
|
||||
}
|
||||
|
||||
void HookManager::UnRegisterProcess(DWORD pid)
|
||||
{
|
||||
HM_LOCK;
|
||||
ProcessRecord pr = processRecordsByIds[pid];
|
||||
CloseHandle(pr.hookman_mutex);
|
||||
UnmapViewOfFile(pr.hookman_map);
|
||||
CloseHandle(pr.process_handle);
|
||||
CloseHandle(pr.hookman_section);
|
||||
processRecordsByIds.erase(pid);
|
||||
RemoveThreads([](auto one, auto two) { return one.pid == two.pid; }, { pid, 0, 0, 0 });
|
||||
if (detach) detach(pid);
|
||||
}
|
||||
|
||||
void HookManager::DispatchText(DWORD pid, DWORD hook, DWORD retn, DWORD spl, const BYTE *text, int len)
|
||||
{
|
||||
// jichi 20/27/2013: When PID is zero, the text comes from console, which I don't need
|
||||
if (!text || !pid || len <= 0) return;
|
||||
HM_LOCK;
|
||||
ThreadParameter tp = { pid, hook, retn, spl };
|
||||
TextThread *it;
|
||||
if ((it = textThreadsByParams[tp]) == nullptr)
|
||||
{
|
||||
it = textThreadsByParams[tp] = new TextThread(tp, nextThreadNumber++, splitDelay);
|
||||
if (GetHookParam(pid, hook).type & USING_UNICODE) it->Status() |= USING_UNICODE;
|
||||
if (create) create(it);
|
||||
}
|
||||
it->AddText(text, len);
|
||||
}
|
||||
|
||||
void HookManager::AddConsoleOutput(std::wstring text)
|
||||
{
|
||||
HM_LOCK;
|
||||
textThreadsByParams[{ 0, -1UL, -1UL, -1UL }]->AddSentence(std::wstring(text));
|
||||
}
|
||||
|
||||
HANDLE HookManager::GetHostPipe(DWORD pid)
|
||||
{
|
||||
HM_LOCK;
|
||||
return processRecordsByIds[pid].hostPipe;
|
||||
}
|
||||
|
||||
HookParam HookManager::GetHookParam(DWORD pid, DWORD addr)
|
||||
{
|
||||
HM_LOCK;
|
||||
HookParam ret = {};
|
||||
ProcessRecord pr = processRecordsByIds[pid];
|
||||
if (pr.hookman_map == nullptr) return ret;
|
||||
MutexLocker locker(pr.hookman_mutex);
|
||||
const Hook* hooks = (const Hook*)pr.hookman_map;
|
||||
for (int i = 0; i < MAX_HOOK; ++i)
|
||||
if (hooks[i].Address() == addr)
|
||||
ret = hooks[i].hp;
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::wstring HookManager::GetHookName(DWORD pid, DWORD addr)
|
||||
{
|
||||
HM_LOCK;
|
||||
std::string buffer = "";
|
||||
ProcessRecord pr = processRecordsByIds[pid];
|
||||
if (pr.hookman_map == nullptr) return L"";
|
||||
MutexLocker locker(pr.hookman_mutex);
|
||||
const Hook* hooks = (const Hook*)pr.hookman_map;
|
||||
for (int i = 0; i < MAX_HOOK; ++i)
|
||||
{
|
||||
if (hooks[i].Address() == addr)
|
||||
{
|
||||
buffer.resize(hooks[i].NameLength());
|
||||
ReadProcessMemory(pr.process_handle, hooks[i].Name(), &buffer[0], hooks[i].NameLength(), nullptr);
|
||||
}
|
||||
}
|
||||
USES_CONVERSION;
|
||||
return std::wstring(A2W(buffer.c_str()));
|
||||
}
|
||||
|
||||
// EOF
|
@ -1,70 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// hookman.h
|
||||
// 8/23/2013 jichi
|
||||
// Branch: ITH/HookManager.h, rev 133
|
||||
|
||||
#include <Windows.h>
|
||||
#include "textthread.h"
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include "../vnrhook/include/types.h"
|
||||
|
||||
struct ProcessRecord
|
||||
{
|
||||
HANDLE process_handle;
|
||||
HANDLE hookman_mutex;
|
||||
HANDLE hookman_section;
|
||||
LPVOID hookman_map;
|
||||
HANDLE hostPipe;
|
||||
};
|
||||
|
||||
typedef void(*ProcessEventCallback)(DWORD pid);
|
||||
typedef void(*ThreadEventCallback)(TextThread*);
|
||||
|
||||
struct ThreadParameterHasher
|
||||
{
|
||||
size_t operator()(const ThreadParameter& tp) const
|
||||
{
|
||||
return std::hash<DWORD>()(tp.pid << 6) + std::hash<DWORD>()(tp.hook) + std::hash<DWORD>()(tp.retn) + std::hash<DWORD>()(tp.spl);
|
||||
}
|
||||
};
|
||||
|
||||
// Artikash 7/19/2018: This should probably be broken up into 2-4 classes...
|
||||
class __declspec(dllexport) HookManager
|
||||
{
|
||||
public:
|
||||
HookManager();
|
||||
~HookManager();
|
||||
|
||||
TextThread* FindSingle(DWORD number);
|
||||
void AddConsoleOutput(std::wstring text);
|
||||
void DispatchText(DWORD pid, DWORD hook, DWORD retn, DWORD split, const BYTE *text, int len);
|
||||
void RemoveThreads(bool(*RemoveIf)(ThreadParameter, ThreadParameter), ThreadParameter cmp);
|
||||
void RegisterProcess(DWORD pid, HANDLE hostPipe);
|
||||
void UnRegisterProcess(DWORD pid);
|
||||
HANDLE GetHostPipe(DWORD pid);
|
||||
HookParam GetHookParam(DWORD pid, DWORD addr);
|
||||
std::wstring GetHookName(DWORD pid, DWORD addr);
|
||||
|
||||
void RegisterThreadCreateCallback(ThreadEventCallback cf) { create = cf; }
|
||||
void RegisterThreadRemoveCallback(ThreadEventCallback cf) { remove = cf; }
|
||||
void RegisterProcessAttachCallback(ProcessEventCallback cf) { attach = cf; }
|
||||
void RegisterProcessDetachCallback(ProcessEventCallback cf) { detach = cf; }
|
||||
|
||||
void SetSplitInterval(unsigned int splitDelay) { this->splitDelay = splitDelay; }
|
||||
|
||||
private:
|
||||
std::unordered_map<ThreadParameter, TextThread*, ThreadParameterHasher> textThreadsByParams;
|
||||
std::unordered_map<DWORD, ProcessRecord> processRecordsByIds;
|
||||
|
||||
CRITICAL_SECTION hmCs;
|
||||
|
||||
ThreadEventCallback create, remove;
|
||||
ProcessEventCallback attach, detach;
|
||||
|
||||
WORD nextThreadNumber;
|
||||
unsigned int splitDelay;
|
||||
};
|
||||
|
||||
// EOF
|
310
texthook/host.cc
310
texthook/host.cc
@ -4,28 +4,37 @@
|
||||
|
||||
#include "host.h"
|
||||
#include "pipe.h"
|
||||
#include "winmutex.h"
|
||||
#include <atlbase.h>
|
||||
#include "../vnrhook/include/const.h"
|
||||
#include "../vnrhook/include/defs.h"
|
||||
#include "../vnrhook/include/types.h"
|
||||
#include <unordered_map>
|
||||
|
||||
HANDLE preventDuplicationMutex;
|
||||
|
||||
HookManager* man;
|
||||
std::unordered_map<ThreadParameter, TextThread*, ThreadParameterHasher> textThreadsByParams;
|
||||
std::unordered_map<DWORD, ProcessRecord> processRecordsByIds;
|
||||
|
||||
CRITICAL_SECTION hostCs;
|
||||
|
||||
ThreadEventCallback onCreate, onRemove;
|
||||
ProcessEventCallback onAttach, onDetach;
|
||||
|
||||
WORD nextThreadNumber;
|
||||
HWND dummyWindow;
|
||||
bool running;
|
||||
|
||||
namespace
|
||||
{ // unnamed
|
||||
void GetDebugPrivileges() // Artikash 5/19/2018: Is it just me or is this function 100% superfluous?
|
||||
{
|
||||
HANDLE processToken;
|
||||
TOKEN_PRIVILEGES Privileges = { 1, {0x14, 0, SE_PRIVILEGE_ENABLED} };
|
||||
#define HOST_LOCK CriticalSectionLocker hostLocker(hostCs) // Synchronized scope for accessing private data
|
||||
|
||||
OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &processToken);
|
||||
AdjustTokenPrivileges(processToken, FALSE, &Privileges, 0, nullptr, nullptr);
|
||||
CloseHandle(processToken);
|
||||
}
|
||||
} // unnamed namespace
|
||||
void GetDebugPrivileges() // Artikash 5/19/2018: Is it just me or is this function 100% superfluous?
|
||||
{
|
||||
HANDLE processToken;
|
||||
TOKEN_PRIVILEGES Privileges = { 1, {0x14, 0, SE_PRIVILEGE_ENABLED} };
|
||||
OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &processToken);
|
||||
AdjustTokenPrivileges(processToken, FALSE, &Privileges, 0, nullptr, nullptr);
|
||||
CloseHandle(processToken);
|
||||
}
|
||||
|
||||
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID unused)
|
||||
{
|
||||
@ -33,12 +42,11 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID unused)
|
||||
{
|
||||
case DLL_PROCESS_ATTACH:
|
||||
DisableThreadLibraryCalls(hinstDLL);
|
||||
GetDebugPrivileges();
|
||||
// jichi 8/24/2013: Create hidden window so that ITH can access timer and events
|
||||
dummyWindow = CreateWindowW(L"Button", L"InternalWindow", 0, 0, 0, 0, 0, 0, 0, hinstDLL, 0);
|
||||
break;
|
||||
case DLL_PROCESS_DETACH:
|
||||
CloseHost();
|
||||
Host::Close();
|
||||
DestroyWindow(dummyWindow);
|
||||
break;
|
||||
default:
|
||||
@ -47,112 +55,222 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID unused)
|
||||
return true;
|
||||
}
|
||||
|
||||
DLLEXPORT bool StartHost()
|
||||
namespace Host
|
||||
{
|
||||
preventDuplicationMutex = CreateMutexW(nullptr, TRUE, ITH_SERVER_MUTEX);
|
||||
if (GetLastError() == ERROR_ALREADY_EXISTS || ::running)
|
||||
DLLEXPORT bool Start()
|
||||
{
|
||||
MessageBoxW(nullptr, L"I am sorry that this game is attached by some other VNR ><\nPlease restart the game and try again!", L"Error", MB_ICONERROR);
|
||||
preventDuplicationMutex = CreateMutexW(nullptr, TRUE, ITH_SERVER_MUTEX);
|
||||
if (GetLastError() == ERROR_ALREADY_EXISTS || running)
|
||||
{
|
||||
MessageBoxW(nullptr, L"I am sorry that this game is attached by some other VNR ><\nPlease restart the game and try again!", L"Error", MB_ICONERROR);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
running = true;
|
||||
GetDebugPrivileges();
|
||||
InitializeCriticalSection(&hostCs);
|
||||
onAttach = onDetach = nullptr;
|
||||
onCreate = onRemove = nullptr;
|
||||
nextThreadNumber = 0;
|
||||
// Console text thread
|
||||
(textThreadsByParams[{ 0, -1UL, -1UL, -1UL }] = new TextThread({ 0, -1UL, -1UL, -1UL }, nextThreadNumber++))->Status() |= USING_UNICODE;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
DLLEXPORT void Open()
|
||||
{
|
||||
CreateNewPipe();
|
||||
}
|
||||
|
||||
DLLEXPORT void Close()
|
||||
{
|
||||
if (running)
|
||||
{
|
||||
EnterCriticalSection(&hostCs);
|
||||
running = false;
|
||||
RemoveThreads([](auto one, auto two) { return true; }, {});
|
||||
for (auto i : processRecordsByIds) UnregisterProcess(i.first);
|
||||
LeaveCriticalSection(&hostCs);
|
||||
DeleteCriticalSection(&hostCs);
|
||||
CloseHandle(preventDuplicationMutex);
|
||||
}
|
||||
}
|
||||
|
||||
DLLEXPORT bool InjectProcess(DWORD processId, DWORD timeout)
|
||||
{
|
||||
if (processId == GetCurrentProcessId()) return false;
|
||||
|
||||
CloseHandle(CreateMutexW(nullptr, FALSE, (ITH_HOOKMAN_MUTEX_ + std::to_wstring(processId)).c_str()));
|
||||
if (GetLastError() == ERROR_ALREADY_EXISTS)
|
||||
{
|
||||
AddConsoleOutput(L"already locked");
|
||||
return false;
|
||||
}
|
||||
|
||||
HMODULE textHooker = LoadLibraryExW(ITH_DLL, nullptr, DONT_RESOLVE_DLL_REFERENCES);
|
||||
wchar_t textHookerPath[MAX_PATH];
|
||||
unsigned int textHookerPathSize = GetModuleFileNameW(textHooker, textHookerPath, MAX_PATH) * 2 + 2;
|
||||
FreeLibrary(textHooker);
|
||||
|
||||
if (HANDLE processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processId))
|
||||
if (LPVOID remoteData = VirtualAllocEx(processHandle, nullptr, textHookerPathSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE))
|
||||
if (WriteProcessMemory(processHandle, remoteData, textHookerPath, textHookerPathSize, nullptr))
|
||||
if (HANDLE thread = CreateRemoteThread(processHandle, nullptr, 0, (LPTHREAD_START_ROUTINE)LoadLibraryW, remoteData, 0, nullptr))
|
||||
{
|
||||
WaitForSingleObject(thread, timeout);
|
||||
CloseHandle(thread);
|
||||
VirtualFreeEx(processHandle, remoteData, 0, MEM_RELEASE);
|
||||
CloseHandle(processHandle);
|
||||
return true;
|
||||
}
|
||||
|
||||
AddConsoleOutput(L"couldn't inject dll");
|
||||
return false;
|
||||
}
|
||||
else
|
||||
|
||||
DLLEXPORT bool DetachProcess(DWORD processId)
|
||||
{
|
||||
::running = true;
|
||||
::man = new HookManager;
|
||||
DWORD command = HOST_COMMAND_DETACH;
|
||||
DWORD unused;
|
||||
return WriteFile(processRecordsByIds[processId].hostPipe, &command, sizeof(command), &unused, nullptr);
|
||||
}
|
||||
|
||||
DLLEXPORT bool InsertHook(DWORD pid, HookParam hp, std::string name)
|
||||
{
|
||||
BYTE buffer[PIPE_BUFFER_SIZE] = {};
|
||||
*(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);
|
||||
}
|
||||
|
||||
DLLEXPORT bool RemoveHook(DWORD pid, DWORD addr)
|
||||
{
|
||||
HANDLE hostPipe = processRecordsByIds[pid].hostPipe;
|
||||
if (hostPipe == nullptr) return false;
|
||||
HANDLE hookRemovalEvent = CreateEventW(nullptr, TRUE, FALSE, ITH_REMOVEHOOK_EVENT);
|
||||
BYTE buffer[sizeof(DWORD) * 2] = {};
|
||||
*(DWORD*)buffer = HOST_COMMAND_REMOVE_HOOK;
|
||||
*(DWORD*)(buffer + sizeof(DWORD)) = addr;
|
||||
DWORD unused;
|
||||
WriteFile(hostPipe, buffer, sizeof(DWORD) * 2, &unused, nullptr);
|
||||
WaitForSingleObject(hookRemovalEvent, 1000);
|
||||
CloseHandle(hookRemovalEvent);
|
||||
RemoveThreads([](auto one, auto two) { return one.pid == two.pid && one.hook == two.hook; }, { pid, addr, 0, 0 });
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
DLLEXPORT void OpenHost()
|
||||
{
|
||||
CreateNewPipe();
|
||||
}
|
||||
|
||||
DLLEXPORT void CloseHost()
|
||||
{
|
||||
if (::running)
|
||||
DLLEXPORT HookParam GetHookParam(DWORD pid, DWORD addr)
|
||||
{
|
||||
::running = false;
|
||||
delete man;
|
||||
CloseHandle(preventDuplicationMutex);
|
||||
}
|
||||
}
|
||||
|
||||
DLLEXPORT bool InjectProcess(DWORD processId, DWORD timeout)
|
||||
{
|
||||
if (processId == GetCurrentProcessId()) return false;
|
||||
|
||||
CloseHandle(CreateMutexW(nullptr, FALSE, (ITH_HOOKMAN_MUTEX_ + std::to_wstring(processId)).c_str()));
|
||||
if (GetLastError() == ERROR_ALREADY_EXISTS)
|
||||
{
|
||||
man->AddConsoleOutput(L"already locked");
|
||||
return false;
|
||||
HOST_LOCK;
|
||||
HookParam ret = {};
|
||||
ProcessRecord pr = processRecordsByIds[pid];
|
||||
if (pr.hookman_map == nullptr) return ret;
|
||||
MutexLocker locker(pr.hookman_mutex);
|
||||
const Hook* hooks = (const Hook*)pr.hookman_map;
|
||||
for (int i = 0; i < MAX_HOOK; ++i)
|
||||
if (hooks[i].Address() == addr)
|
||||
ret = hooks[i].hp;
|
||||
return ret;
|
||||
}
|
||||
|
||||
HMODULE textHooker = LoadLibraryExW(ITH_DLL, nullptr, DONT_RESOLVE_DLL_REFERENCES);
|
||||
wchar_t textHookerPath[MAX_PATH];
|
||||
unsigned int textHookerPathSize = GetModuleFileNameW(textHooker, textHookerPath, MAX_PATH) * 2 + 2;
|
||||
FreeLibrary(textHooker);
|
||||
DLLEXPORT std::wstring GetHookName(DWORD pid, DWORD addr)
|
||||
{
|
||||
HOST_LOCK;
|
||||
std::string buffer = "";
|
||||
ProcessRecord pr = processRecordsByIds[pid];
|
||||
if (pr.hookman_map == nullptr) return L"";
|
||||
MutexLocker locker(pr.hookman_mutex);
|
||||
const Hook* hooks = (const Hook*)pr.hookman_map;
|
||||
for (int i = 0; i < MAX_HOOK; ++i)
|
||||
if (hooks[i].Address() == addr)
|
||||
{
|
||||
buffer.resize(hooks[i].NameLength());
|
||||
ReadProcessMemory(pr.process_handle, hooks[i].Name(), &buffer[0], hooks[i].NameLength(), nullptr);
|
||||
}
|
||||
USES_CONVERSION;
|
||||
return std::wstring(A2W(buffer.c_str()));
|
||||
}
|
||||
|
||||
if (HANDLE processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processId))
|
||||
if (LPVOID remoteData = VirtualAllocEx(processHandle, nullptr, textHookerPathSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE))
|
||||
if (WriteProcessMemory(processHandle, remoteData, textHookerPath, textHookerPathSize, nullptr))
|
||||
if (HANDLE thread = CreateRemoteThread(processHandle, nullptr, 0, (LPTHREAD_START_ROUTINE)LoadLibraryW, remoteData, 0, nullptr))
|
||||
{
|
||||
WaitForSingleObject(thread, timeout);
|
||||
CloseHandle(thread);
|
||||
VirtualFreeEx(processHandle, remoteData, 0, MEM_RELEASE);
|
||||
CloseHandle(processHandle);
|
||||
return true;
|
||||
}
|
||||
DLLEXPORT TextThread* GetThread(DWORD number)
|
||||
{
|
||||
HOST_LOCK;
|
||||
for (auto i : textThreadsByParams)
|
||||
if (i.second->Number() == number)
|
||||
return i.second;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
man->AddConsoleOutput(L"couldn't inject dll");
|
||||
return false;
|
||||
DLLEXPORT void AddConsoleOutput(std::wstring text)
|
||||
{
|
||||
HOST_LOCK;
|
||||
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 bool DetachProcess(DWORD processId)
|
||||
void DispatchText(DWORD pid, DWORD hook, DWORD retn, DWORD split, const BYTE * text, int len)
|
||||
{
|
||||
DWORD command = HOST_COMMAND_DETACH;
|
||||
DWORD unused;
|
||||
return WriteFile(man->GetHostPipe(processId), &command, sizeof(command), &unused, nullptr);
|
||||
// jichi 20/27/2013: When PID is zero, the text comes from console, which I don't need
|
||||
if (!text || !pid || 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++);
|
||||
if (Host::GetHookParam(pid, hook).type & USING_UNICODE) it->Status() |= USING_UNICODE;
|
||||
if (onCreate) onCreate(it);
|
||||
}
|
||||
it->AddText(text, len);
|
||||
}
|
||||
|
||||
DLLEXPORT HookManager* GetHostHookManager()
|
||||
void RemoveThreads(bool(*RemoveIf)(ThreadParameter, ThreadParameter), ThreadParameter cmp)
|
||||
{
|
||||
return man;
|
||||
HOST_LOCK;
|
||||
std::vector<ThreadParameter> removedThreads;
|
||||
for (auto i : textThreadsByParams)
|
||||
if (RemoveIf(i.first, cmp))
|
||||
{
|
||||
if (onRemove) onRemove(i.second);
|
||||
delete i.second;
|
||||
removedThreads.push_back(i.first);
|
||||
}
|
||||
for (auto i : removedThreads) textThreadsByParams.erase(i);
|
||||
}
|
||||
|
||||
DLLEXPORT bool InsertHook(DWORD pid, HookParam hp, std::string name)
|
||||
void RegisterProcess(DWORD pid, HANDLE hostPipe)
|
||||
{
|
||||
HANDLE commandPipe = man->GetHostPipe(pid);
|
||||
if (commandPipe == nullptr) return false;
|
||||
|
||||
BYTE buffer[PIPE_BUFFER_SIZE] = {};
|
||||
*(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(commandPipe, buffer, sizeof(DWORD) + sizeof(HookParam) + name.size(), &unused, nullptr);
|
||||
HOST_LOCK;
|
||||
ProcessRecord record;
|
||||
record.hostPipe = hostPipe;
|
||||
record.hookman_section = OpenFileMappingW(FILE_MAP_READ, FALSE, (ITH_SECTION_ + std::to_wstring(pid)).c_str());
|
||||
record.hookman_map = MapViewOfFile(record.hookman_section, FILE_MAP_READ, 0, 0, HOOK_SECTION_SIZE / 2); // jichi 1/16/2015: Changed to half to hook section size
|
||||
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);
|
||||
}
|
||||
|
||||
DLLEXPORT bool RemoveHook(DWORD pid, DWORD addr)
|
||||
void UnregisterProcess(DWORD pid)
|
||||
{
|
||||
HANDLE commandPipe = man->GetHostPipe(pid);
|
||||
if (commandPipe == nullptr) return false;
|
||||
|
||||
HANDLE hookRemovalEvent = CreateEventW(nullptr, TRUE, FALSE, ITH_REMOVEHOOK_EVENT);
|
||||
|
||||
BYTE buffer[sizeof(DWORD) * 2] = {};
|
||||
*(DWORD*)buffer = HOST_COMMAND_REMOVE_HOOK;
|
||||
*(DWORD*)(buffer + sizeof(DWORD)) = addr;
|
||||
DWORD unused;
|
||||
WriteFile(commandPipe, buffer, sizeof(DWORD) * 2, &unused, nullptr);
|
||||
|
||||
WaitForSingleObject(hookRemovalEvent, 1000);
|
||||
CloseHandle(hookRemovalEvent);
|
||||
|
||||
man->RemoveThreads([](auto one, auto two) { return one.pid == two.pid && one.hook == two.hook; }, { pid, addr, 0, 0 });
|
||||
return true;
|
||||
HOST_LOCK;
|
||||
ProcessRecord pr = processRecordsByIds[pid];
|
||||
if (!pr.hostPipe) return;
|
||||
CloseHandle(pr.hookman_mutex);
|
||||
UnmapViewOfFile(pr.hookman_map);
|
||||
CloseHandle(pr.process_handle);
|
||||
CloseHandle(pr.hookman_section);
|
||||
processRecordsByIds.erase(pid);
|
||||
RemoveThreads([](auto one, auto two) { return one.pid == two.pid; }, { pid, 0, 0, 0 });
|
||||
if (onDetach) onDetach(pid);
|
||||
}
|
||||
|
||||
// EOF
|
||||
// EOF
|
@ -5,17 +5,57 @@
|
||||
// Branch: ITH/IHF.h, rev 105
|
||||
|
||||
#define DLLEXPORT __declspec(dllexport)
|
||||
#include "hookman.h"
|
||||
#include "../vnrhook/include/types.h"
|
||||
#include <string>
|
||||
|
||||
DLLEXPORT void OpenHost();
|
||||
DLLEXPORT bool StartHost();
|
||||
DLLEXPORT void CloseHost();
|
||||
DLLEXPORT HookManager* GetHostHookManager();
|
||||
DLLEXPORT bool InjectProcess(DWORD pid, DWORD timeout = 5000);
|
||||
DLLEXPORT bool DetachProcess(DWORD pid);
|
||||
DLLEXPORT bool InsertHook(DWORD pid, HookParam hp, std::string name = "");
|
||||
DLLEXPORT bool RemoveHook(DWORD pid, DWORD addr);
|
||||
#include <Windows.h>
|
||||
#include "textthread.h"
|
||||
#include <string>
|
||||
#include "../vnrhook/include/types.h"
|
||||
|
||||
struct ProcessRecord
|
||||
{
|
||||
HANDLE process_handle;
|
||||
HANDLE hookman_mutex;
|
||||
HANDLE hookman_section;
|
||||
LPVOID hookman_map;
|
||||
HANDLE hostPipe;
|
||||
};
|
||||
|
||||
typedef void(*ProcessEventCallback)(DWORD pid);
|
||||
typedef void(*ThreadEventCallback)(TextThread*);
|
||||
|
||||
struct ThreadParameterHasher
|
||||
{
|
||||
size_t operator()(const ThreadParameter& tp) const
|
||||
{
|
||||
return std::hash<DWORD>()(tp.pid << 6) + std::hash<DWORD>()(tp.hook) + std::hash<DWORD>()(tp.retn) + std::hash<DWORD>()(tp.spl);
|
||||
}
|
||||
};
|
||||
|
||||
namespace Host
|
||||
{
|
||||
DLLEXPORT void Open();
|
||||
DLLEXPORT bool Start();
|
||||
DLLEXPORT void Close();
|
||||
DLLEXPORT bool InjectProcess(DWORD pid, DWORD timeout = 5000);
|
||||
DLLEXPORT bool DetachProcess(DWORD pid);
|
||||
|
||||
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 std::wstring GetHookName(DWORD pid, DWORD addr);
|
||||
|
||||
DLLEXPORT TextThread* GetThread(DWORD number);
|
||||
DLLEXPORT void AddConsoleOutput(std::wstring text);
|
||||
|
||||
DLLEXPORT void RegisterThreadCreateCallback(ThreadEventCallback cf);
|
||||
DLLEXPORT void RegisterThreadRemoveCallback(ThreadEventCallback cf);
|
||||
DLLEXPORT void RegisterProcessAttachCallback(ProcessEventCallback cf);
|
||||
DLLEXPORT void RegisterProcessDetachCallback(ProcessEventCallback cf);
|
||||
}
|
||||
|
||||
void DispatchText(DWORD pid, DWORD hook, DWORD retn, DWORD split, const BYTE *text, int len);
|
||||
void RemoveThreads(bool(*RemoveIf)(ThreadParameter, ThreadParameter), ThreadParameter cmp);
|
||||
void RegisterProcess(DWORD pid, HANDLE hostPipe);
|
||||
void UnregisterProcess(DWORD pid);
|
||||
|
||||
// EOF
|
||||
|
@ -8,8 +8,6 @@
|
||||
#include "../vnrhook/include/const.h"
|
||||
#include <atlbase.h>
|
||||
|
||||
extern HookManager* man;
|
||||
|
||||
struct Pipes
|
||||
{
|
||||
HANDLE hookPipe;
|
||||
@ -34,7 +32,7 @@ DWORD WINAPI TextReceiver(LPVOID lpThreadParameter)
|
||||
BYTE buffer[PIPE_BUFFER_SIZE] = {};
|
||||
DWORD bytesRead, processId;
|
||||
ReadFile(pipes->hookPipe, &processId, sizeof(processId), &bytesRead, nullptr);
|
||||
man->RegisterProcess(processId, pipes->hostPipe);
|
||||
RegisterProcess(processId, pipes->hostPipe);
|
||||
|
||||
// jichi 9/27/2013: why recursion?
|
||||
// Artikash 5/20/2018: To create a new pipe for another process
|
||||
@ -55,13 +53,13 @@ DWORD WINAPI TextReceiver(LPVOID lpThreadParameter)
|
||||
case HOST_NOTIFICATION_NEWHOOK: // Artikash 7/18/2018: Useless for now, but could be used to implement smth later
|
||||
break;
|
||||
case HOST_NOTIFICATION_TEXT:
|
||||
man->AddConsoleOutput(A2W((LPCSTR)(buffer + sizeof(DWORD) * 2))); // Text
|
||||
Host::AddConsoleOutput(A2W((LPCSTR)(buffer + sizeof(DWORD) * 2))); // Text
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
man->DispatchText(processId,
|
||||
DispatchText(processId,
|
||||
*(DWORD*)buffer, // Hook address
|
||||
*(DWORD*)(buffer + sizeof(DWORD)), // Return address
|
||||
*(DWORD*)(buffer + sizeof(DWORD) * 2), // Split
|
||||
@ -73,7 +71,7 @@ DWORD WINAPI TextReceiver(LPVOID lpThreadParameter)
|
||||
|
||||
DisconnectNamedPipe(pipes->hookPipe);
|
||||
DisconnectNamedPipe(pipes->hostPipe);
|
||||
man->UnRegisterProcess(processId);
|
||||
UnregisterProcess(processId);
|
||||
CloseHandle(pipes->hookPipe);
|
||||
CloseHandle(pipes->hostPipe);
|
||||
delete pipes;
|
||||
|
@ -5,12 +5,10 @@
|
||||
# pragma warning (disable:4100) // C4100: unreference formal parameter
|
||||
#endif // _MSC_VER
|
||||
|
||||
#include "host.h"
|
||||
#include "textthread.h"
|
||||
#include "../vnrhook/include/const.h"
|
||||
#include "winmutex.h"
|
||||
|
||||
extern HookManager* man;
|
||||
extern HWND dummyWindow;
|
||||
|
||||
#define TT_LOCK CriticalSectionLocker ttLocker(ttCs) // Synchronized scope for accessing private data
|
||||
|
@ -30,7 +30,7 @@ typedef std::wstring(*ThreadOutputCallback)(TextThread*, std::wstring data);
|
||||
class TextThread
|
||||
{
|
||||
public:
|
||||
TextThread(ThreadParameter tp, unsigned int threadNumber, unsigned int splitDelay);
|
||||
TextThread(ThreadParameter tp, unsigned int threadNumber, unsigned int splitDelay = 250);
|
||||
~TextThread();
|
||||
|
||||
void Reset();
|
||||
@ -42,6 +42,7 @@ public:
|
||||
DWORD &Status() { return status; }
|
||||
WORD Number() const { return threadNumber; }
|
||||
ThreadParameter GetThreadParameter() { return tp; }
|
||||
void SetSplitDelay(unsigned int splitDelay) { this->splitDelay = splitDelay; }
|
||||
|
||||
void RegisterOutputCallBack(ThreadOutputCallback cb) { output = cb; }
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user