start implementing new GUI

This commit is contained in:
Akash Mozumdar 2018-07-23 12:25:02 -07:00
parent ffeb4e2dad
commit fe30b77a44
12 changed files with 403 additions and 377 deletions

View File

@ -35,6 +35,9 @@ FORMS += \
win32: LIBS += \ win32: LIBS += \
-L$$PWD/../Builds/Debug/Debug/ -lvnrhost -L$$PWD/../Builds/Debug/Debug/ -lvnrhost
QMAKE_CXXFLAGS_RELEASE += \
/MT
# Default rules for deployment. # Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin else: unix:!android: target.path = /opt/$${TARGET}/bin

View File

@ -1,17 +1,44 @@
#include "mainwindow.h" #include "mainwindow.h"
#include "ui_mainwindow.h" #include "ui_mainwindow.h"
#include "QMessageBox" #include "QMessageBox"
#include "qlineedit.h" #include "QLineEdit"
#include "QTableWidget"
#include "QInputDialog"
#include <Windows.h> #include <Windows.h>
#include <qdebug.h>
#include <Psapi.h>
#include "../texthook/host.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) : MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent), QMainWindow(parent),
ui(new Ui::MainWindow) ui(new Ui::MainWindow)
{ {
Host::Start();
ui->setupUi(this); 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() MainWindow::~MainWindow()
@ -19,8 +46,9 @@ MainWindow::~MainWindow()
delete ui; delete ui;
} }
void MainWindow::onCommand() void MainWindow::on_attachButton_clicked()
{ {
QLineEdit* lineEdit = (QLineEdit*)sender(); //processList->insertRow(processList->rowCount());
QMessageBox::information(this, "called", lineEdit->text()); //processList->setItem(processList->rowCount() - 1, 0, new QTableWidgetItem(QString::number(6000)));
Host::InjectProcess(QInputDialog::getInt(this, "Process ID?", ""));
} }

View File

@ -3,8 +3,9 @@
#include <QMainWindow> #include <QMainWindow>
namespace Ui { namespace Ui
class MainWindow; {
class MainWindow;
} }
class MainWindow : public QMainWindow class MainWindow : public QMainWindow
@ -15,8 +16,10 @@ public:
explicit MainWindow(QWidget *parent = nullptr); explicit MainWindow(QWidget *parent = nullptr);
~MainWindow(); ~MainWindow();
public slots: private slots:
void onCommand();
void on_attachButton_clicked();
private: private:
Ui::MainWindow *ui; Ui::MainWindow *ui;
}; };

View File

@ -6,46 +6,110 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>496</width> <width>800</width>
<height>376</height> <height>600</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>NextHooker</string> <string>NextHooker</string>
</property> </property>
<widget class="QWidget" name="centralWidget"> <widget class="QWidget" name="centralWidget">
<widget class="QLineEdit" name="lineEdit"> <layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="geometry"> <item>
<rect> <widget class="QTextBrowser" name="textBrowser">
<x>252</x> <property name="sizePolicy">
<y>0</y> <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<width>241</width> <horstretch>5</horstretch>
<height>20</height> <verstretch>0</verstretch>
</rect> </sizepolicy>
</property> </property>
</widget> </widget>
<widget class="QPlainTextEdit" name="plainTextEdit"> </item>
<property name="geometry"> <item>
<rect> <widget class="QFrame" name="processManager">
<x>3</x> <property name="sizePolicy">
<y>40</y> <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<width>491</width> <horstretch>2</horstretch>
<height>291</height> <verstretch>0</verstretch>
</rect> </sizepolicy>
</property> </property>
</widget> <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>
<widget class="QMenuBar" name="menuBar"> <widget class="QMenuBar" name="menuBar">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>496</width> <width>800</width>
<height>21</height> <height>21</height>
</rect> </rect>
</property> </property>
</widget> </widget>
<widget class="QStatusBar" name="statusBar"/>
</widget> </widget>
<layoutdefault spacing="6" margin="11"/> <layoutdefault spacing="6" margin="11"/>
<resources/> <resources/>

View File

@ -1,12 +1,10 @@
project(host) project(host)
set(vnrhost_src set(vnrhost_src
hookman.h
host.h host.h
pipe.h pipe.h
textthread.h textthread.h
winmutex.h winmutex.h
hookman.cc
host.cc host.cc
pipe.cc pipe.cc
textthread.cc textthread.cc

View File

@ -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

View File

@ -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

View File

@ -4,28 +4,37 @@
#include "host.h" #include "host.h"
#include "pipe.h" #include "pipe.h"
#include "winmutex.h"
#include <atlbase.h>
#include "../vnrhook/include/const.h" #include "../vnrhook/include/const.h"
#include "../vnrhook/include/defs.h" #include "../vnrhook/include/defs.h"
#include "../vnrhook/include/types.h" #include "../vnrhook/include/types.h"
#include <unordered_map>
HANDLE preventDuplicationMutex; 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; HWND dummyWindow;
bool running; bool running;
namespace #define HOST_LOCK CriticalSectionLocker hostLocker(hostCs) // Synchronized scope for accessing private data
{ // 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} };
OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &processToken); void GetDebugPrivileges() // Artikash 5/19/2018: Is it just me or is this function 100% superfluous?
AdjustTokenPrivileges(processToken, FALSE, &Privileges, 0, nullptr, nullptr); {
CloseHandle(processToken); HANDLE processToken;
} TOKEN_PRIVILEGES Privileges = { 1, {0x14, 0, SE_PRIVILEGE_ENABLED} };
} // unnamed namespace 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) 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: case DLL_PROCESS_ATTACH:
DisableThreadLibraryCalls(hinstDLL); DisableThreadLibraryCalls(hinstDLL);
GetDebugPrivileges();
// jichi 8/24/2013: Create hidden window so that ITH can access timer and events // 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); dummyWindow = CreateWindowW(L"Button", L"InternalWindow", 0, 0, 0, 0, 0, 0, 0, hinstDLL, 0);
break; break;
case DLL_PROCESS_DETACH: case DLL_PROCESS_DETACH:
CloseHost(); Host::Close();
DestroyWindow(dummyWindow); DestroyWindow(dummyWindow);
break; break;
default: default:
@ -47,112 +55,222 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID unused)
return true; return true;
} }
DLLEXPORT bool StartHost() namespace Host
{ {
preventDuplicationMutex = CreateMutexW(nullptr, TRUE, ITH_SERVER_MUTEX); DLLEXPORT bool Start()
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); 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; return false;
} }
else
DLLEXPORT bool DetachProcess(DWORD processId)
{ {
::running = true; DWORD command = HOST_COMMAND_DETACH;
::man = new HookManager; 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; return true;
} }
}
DLLEXPORT void OpenHost() DLLEXPORT HookParam GetHookParam(DWORD pid, DWORD addr)
{
CreateNewPipe();
}
DLLEXPORT void CloseHost()
{
if (::running)
{ {
::running = false; HOST_LOCK;
delete man; HookParam ret = {};
CloseHandle(preventDuplicationMutex); ProcessRecord pr = processRecordsByIds[pid];
} if (pr.hookman_map == nullptr) return ret;
} MutexLocker locker(pr.hookman_mutex);
const Hook* hooks = (const Hook*)pr.hookman_map;
DLLEXPORT bool InjectProcess(DWORD processId, DWORD timeout) for (int i = 0; i < MAX_HOOK; ++i)
{ if (hooks[i].Address() == addr)
if (processId == GetCurrentProcessId()) return false; ret = hooks[i].hp;
return ret;
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;
} }
HMODULE textHooker = LoadLibraryExW(ITH_DLL, nullptr, DONT_RESOLVE_DLL_REFERENCES); DLLEXPORT std::wstring GetHookName(DWORD pid, DWORD addr)
wchar_t textHookerPath[MAX_PATH]; {
unsigned int textHookerPathSize = GetModuleFileNameW(textHooker, textHookerPath, MAX_PATH) * 2 + 2; HOST_LOCK;
FreeLibrary(textHooker); 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)) DLLEXPORT TextThread* GetThread(DWORD number)
if (LPVOID remoteData = VirtualAllocEx(processHandle, nullptr, textHookerPathSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE)) {
if (WriteProcessMemory(processHandle, remoteData, textHookerPath, textHookerPathSize, nullptr)) HOST_LOCK;
if (HANDLE thread = CreateRemoteThread(processHandle, nullptr, 0, (LPTHREAD_START_ROUTINE)LoadLibraryW, remoteData, 0, nullptr)) for (auto i : textThreadsByParams)
{ if (i.second->Number() == number)
WaitForSingleObject(thread, timeout); return i.second;
CloseHandle(thread); return nullptr;
VirtualFreeEx(processHandle, remoteData, 0, MEM_RELEASE); }
CloseHandle(processHandle);
return true;
}
man->AddConsoleOutput(L"couldn't inject dll"); DLLEXPORT void AddConsoleOutput(std::wstring text)
return false; {
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; // jichi 20/27/2013: When PID is zero, the text comes from console, which I don't need
DWORD unused; if (!text || !pid || len <= 0) return;
return WriteFile(man->GetHostPipe(processId), &command, sizeof(command), &unused, nullptr); 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); HOST_LOCK;
if (commandPipe == nullptr) return false; ProcessRecord record;
record.hostPipe = hostPipe;
BYTE buffer[PIPE_BUFFER_SIZE] = {}; record.hookman_section = OpenFileMappingW(FILE_MAP_READ, FALSE, (ITH_SECTION_ + std::to_wstring(pid)).c_str());
*(DWORD*)buffer = HOST_COMMAND_NEW_HOOK; 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
*(HookParam*)(buffer + sizeof(DWORD)) = hp; record.process_handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
if (name.size()) strcpy((char*)buffer + sizeof(DWORD) + sizeof(HookParam), name.c_str()); record.hookman_mutex = OpenMutexW(MUTEX_ALL_ACCESS, FALSE, (ITH_HOOKMAN_MUTEX_ + std::to_wstring(pid)).c_str());
DWORD unused; processRecordsByIds[pid] = record;
return WriteFile(commandPipe, buffer, sizeof(DWORD) + sizeof(HookParam) + name.size(), &unused, nullptr); if (onAttach) onAttach(pid);
} }
DLLEXPORT bool RemoveHook(DWORD pid, DWORD addr) void UnregisterProcess(DWORD pid)
{ {
HANDLE commandPipe = man->GetHostPipe(pid); HOST_LOCK;
if (commandPipe == nullptr) return false; ProcessRecord pr = processRecordsByIds[pid];
if (!pr.hostPipe) return;
HANDLE hookRemovalEvent = CreateEventW(nullptr, TRUE, FALSE, ITH_REMOVEHOOK_EVENT); CloseHandle(pr.hookman_mutex);
UnmapViewOfFile(pr.hookman_map);
BYTE buffer[sizeof(DWORD) * 2] = {}; CloseHandle(pr.process_handle);
*(DWORD*)buffer = HOST_COMMAND_REMOVE_HOOK; CloseHandle(pr.hookman_section);
*(DWORD*)(buffer + sizeof(DWORD)) = addr; processRecordsByIds.erase(pid);
DWORD unused; RemoveThreads([](auto one, auto two) { return one.pid == two.pid; }, { pid, 0, 0, 0 });
WriteFile(commandPipe, buffer, sizeof(DWORD) * 2, &unused, nullptr); if (onDetach) onDetach(pid);
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;
} }
// EOF // EOF

View File

@ -5,17 +5,57 @@
// Branch: ITH/IHF.h, rev 105 // Branch: ITH/IHF.h, rev 105
#define DLLEXPORT __declspec(dllexport) #define DLLEXPORT __declspec(dllexport)
#include "hookman.h"
#include "../vnrhook/include/types.h"
#include <string>
DLLEXPORT void OpenHost(); #include <Windows.h>
DLLEXPORT bool StartHost(); #include "textthread.h"
DLLEXPORT void CloseHost(); #include <string>
DLLEXPORT HookManager* GetHostHookManager(); #include "../vnrhook/include/types.h"
DLLEXPORT bool InjectProcess(DWORD pid, DWORD timeout = 5000);
DLLEXPORT bool DetachProcess(DWORD pid); struct ProcessRecord
DLLEXPORT bool InsertHook(DWORD pid, HookParam hp, std::string name = ""); {
DLLEXPORT bool RemoveHook(DWORD pid, DWORD addr); 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 // EOF

View File

@ -8,8 +8,6 @@
#include "../vnrhook/include/const.h" #include "../vnrhook/include/const.h"
#include <atlbase.h> #include <atlbase.h>
extern HookManager* man;
struct Pipes struct Pipes
{ {
HANDLE hookPipe; HANDLE hookPipe;
@ -34,7 +32,7 @@ DWORD WINAPI TextReceiver(LPVOID lpThreadParameter)
BYTE buffer[PIPE_BUFFER_SIZE] = {}; BYTE buffer[PIPE_BUFFER_SIZE] = {};
DWORD bytesRead, processId; DWORD bytesRead, processId;
ReadFile(pipes->hookPipe, &processId, sizeof(processId), &bytesRead, nullptr); ReadFile(pipes->hookPipe, &processId, sizeof(processId), &bytesRead, nullptr);
man->RegisterProcess(processId, pipes->hostPipe); RegisterProcess(processId, pipes->hostPipe);
// jichi 9/27/2013: why recursion? // jichi 9/27/2013: why recursion?
// Artikash 5/20/2018: To create a new pipe for another process // 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 case HOST_NOTIFICATION_NEWHOOK: // Artikash 7/18/2018: Useless for now, but could be used to implement smth later
break; break;
case HOST_NOTIFICATION_TEXT: case HOST_NOTIFICATION_TEXT:
man->AddConsoleOutput(A2W((LPCSTR)(buffer + sizeof(DWORD) * 2))); // Text Host::AddConsoleOutput(A2W((LPCSTR)(buffer + sizeof(DWORD) * 2))); // Text
break; break;
} }
} }
else else
{ {
man->DispatchText(processId, DispatchText(processId,
*(DWORD*)buffer, // Hook address *(DWORD*)buffer, // Hook address
*(DWORD*)(buffer + sizeof(DWORD)), // Return address *(DWORD*)(buffer + sizeof(DWORD)), // Return address
*(DWORD*)(buffer + sizeof(DWORD) * 2), // Split *(DWORD*)(buffer + sizeof(DWORD) * 2), // Split
@ -73,7 +71,7 @@ DWORD WINAPI TextReceiver(LPVOID lpThreadParameter)
DisconnectNamedPipe(pipes->hookPipe); DisconnectNamedPipe(pipes->hookPipe);
DisconnectNamedPipe(pipes->hostPipe); DisconnectNamedPipe(pipes->hostPipe);
man->UnRegisterProcess(processId); UnregisterProcess(processId);
CloseHandle(pipes->hookPipe); CloseHandle(pipes->hookPipe);
CloseHandle(pipes->hostPipe); CloseHandle(pipes->hostPipe);
delete pipes; delete pipes;

View File

@ -5,12 +5,10 @@
# pragma warning (disable:4100) // C4100: unreference formal parameter # pragma warning (disable:4100) // C4100: unreference formal parameter
#endif // _MSC_VER #endif // _MSC_VER
#include "host.h"
#include "textthread.h" #include "textthread.h"
#include "../vnrhook/include/const.h" #include "../vnrhook/include/const.h"
#include "winmutex.h" #include "winmutex.h"
extern HookManager* man;
extern HWND dummyWindow; extern HWND dummyWindow;
#define TT_LOCK CriticalSectionLocker ttLocker(ttCs) // Synchronized scope for accessing private data #define TT_LOCK CriticalSectionLocker ttLocker(ttCs) // Synchronized scope for accessing private data

View File

@ -30,7 +30,7 @@ typedef std::wstring(*ThreadOutputCallback)(TextThread*, std::wstring data);
class TextThread class TextThread
{ {
public: public:
TextThread(ThreadParameter tp, unsigned int threadNumber, unsigned int splitDelay); TextThread(ThreadParameter tp, unsigned int threadNumber, unsigned int splitDelay = 250);
~TextThread(); ~TextThread();
void Reset(); void Reset();
@ -42,6 +42,7 @@ public:
DWORD &Status() { return status; } DWORD &Status() { return status; }
WORD Number() const { return threadNumber; } WORD Number() const { return threadNumber; }
ThreadParameter GetThreadParameter() { return tp; } ThreadParameter GetThreadParameter() { return tp; }
void SetSplitDelay(unsigned int splitDelay) { this->splitDelay = splitDelay; }
void RegisterOutputCallBack(ThreadOutputCallback cb) { output = cb; } void RegisterOutputCallBack(ThreadOutputCallback cb) { output = cb; }