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 += \
-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

View File

@ -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?", ""));
}

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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