mirror of
https://github.com/Artikash/Textractor.git
synced 2024-12-24 01:14:12 +08:00
409 lines
10 KiB
C++
409 lines
10 KiB
C++
// host.cc
|
|
// 8/24/2013 jichi
|
|
// Branch IHF/main.cpp, rev 111
|
|
// 8/24/2013 TODO: Clean up this file
|
|
|
|
//#ifdef _MSC_VER
|
|
//# pragma warning(disable:4800) // C4800: forcing value to bool (performance warning)
|
|
//#endif // _MSC_VER
|
|
|
|
//#include "customfilter.h"
|
|
#include "growl.h"
|
|
#include "host.h"
|
|
#include "host_p.h"
|
|
#include "settings.h"
|
|
#include "vnrhook/include/const.h"
|
|
#include "vnrhook/include/defs.h"
|
|
#include "vnrhook/include/types.h"
|
|
#include "ithsys/ithsys.h"
|
|
#include "ccutil/ccmacro.h"
|
|
#include <commctrl.h>
|
|
|
|
//#define ITH_WINE
|
|
//#define ITH_USE_UX_DLLS IthIsWine()
|
|
//#define ITH_USE_XP_DLLS (IthIsWindowsXp() && !IthIsWine())
|
|
|
|
#define DEBUG "vnrhost/host.cc"
|
|
#include "sakurakit/skdebug.h"
|
|
|
|
namespace
|
|
{ // unnamed
|
|
|
|
//enum { HOOK_TIMEOUT = -50000000 }; // in nanoseconds = 5 seconds
|
|
|
|
CRITICAL_SECTION hostCs;
|
|
//WCHAR exist[] = ITH_PIPEEXISTS_EVENT;
|
|
//WCHAR mutex[] = L"ITH_RUNNING";
|
|
//WCHAR EngineName[] = ITH_ENGINE_DLL;
|
|
//WCHAR EngineNameXp[] = ITH_ENGINE_XP_DLL;
|
|
//WCHAR DllName[] = ITH_CLIENT_DLL;
|
|
//WCHAR DllNameXp[] = ITH_CLIENT_XP_DLL;
|
|
HANDLE preventDuplicationMutex; // jichi 9/28/2013: used to guard pipe
|
|
HANDLE hookMutex; // jichi 9/28/2013: used to guard hook modification
|
|
} // unnamed namespace
|
|
|
|
//extern LPWSTR current_dir;
|
|
extern CRITICAL_SECTION detachCs;
|
|
|
|
Settings *settings;
|
|
HWND dummyWindow;
|
|
HANDLE pipeExistsEvent;
|
|
BOOL running;
|
|
|
|
#define ITH_SYNC_HOOK IthMutexLocker locker(::hookMutex)
|
|
|
|
namespace
|
|
{ // unnamed
|
|
|
|
void GetDebugPrivileges()
|
|
{
|
|
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 sendCommand(HANDLE commandPipe, HostCommandType command)
|
|
{
|
|
DWORD unused;
|
|
return commandPipe && WriteFile(commandPipe, &command, sizeof(command), &unused, nullptr);
|
|
}
|
|
|
|
} // unnamed namespace
|
|
|
|
void CreateNewPipe();
|
|
|
|
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID unused)
|
|
{
|
|
switch (fdwReason)
|
|
{
|
|
case DLL_PROCESS_ATTACH:
|
|
DisableThreadLibraryCalls(hinstDLL);
|
|
InitializeCriticalSection(&::hostCs);
|
|
IthInitSystemService();
|
|
GetDebugPrivileges();
|
|
// jichi 12/20/2013: Since I already have a GUI, I don't have to InitCommonControls()
|
|
//Used by timers.
|
|
InitCommonControls();
|
|
// 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:
|
|
if (::running)
|
|
CloseHost();
|
|
DeleteCriticalSection(&::hostCs);
|
|
IthCloseSystemService();
|
|
DestroyWindow(dummyWindow);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
enum { IHS_SIZE = 0x80 };
|
|
enum { IHS_BUFF_SIZE = IHS_SIZE - sizeof(HookParam) };
|
|
|
|
struct InsertHookStruct
|
|
{
|
|
SendParam sp;
|
|
BYTE name_buffer[IHS_SIZE];
|
|
};
|
|
|
|
IHFSERVICE bool IHFAPI OpenHost()
|
|
{
|
|
bool success;
|
|
EnterCriticalSection(&::hostCs);
|
|
|
|
preventDuplicationMutex = CreateMutexW(nullptr, TRUE, ITH_SERVER_MUTEX);
|
|
if (GetLastError() == ERROR_ALREADY_EXISTS || ::running)
|
|
{
|
|
GROWL_WARN(L"I am sorry that this game is attached by some other VNR ><\nPlease restart the game and try again!");
|
|
success = false;
|
|
}
|
|
else
|
|
{
|
|
::running = true;
|
|
::settings = new Settings;
|
|
::man = new HookManager;
|
|
InitializeCriticalSection(&detachCs);
|
|
::hookMutex = CreateMutexW(nullptr, FALSE, ITH_SERVER_HOOK_MUTEX);
|
|
success = true;
|
|
}
|
|
LeaveCriticalSection(&::hostCs);
|
|
return success;
|
|
}
|
|
|
|
IHFSERVICE void IHFAPI StartHost()
|
|
{
|
|
CreateNewPipe();
|
|
::pipeExistsEvent = CreateEventW(nullptr, TRUE, TRUE, ITH_PIPEEXISTS_EVENT);
|
|
}
|
|
|
|
IHFSERVICE void IHFAPI CloseHost()
|
|
{
|
|
EnterCriticalSection(&::hostCs);
|
|
if (::running)
|
|
{
|
|
::running = FALSE;
|
|
ResetEvent(::pipeExistsEvent);
|
|
delete man;
|
|
delete settings;
|
|
CloseHandle(::hookMutex);
|
|
CloseHandle(preventDuplicationMutex);
|
|
CloseHandle(::pipeExistsEvent);
|
|
DeleteCriticalSection(&detachCs);
|
|
}
|
|
LeaveCriticalSection(&::hostCs);
|
|
}
|
|
|
|
IHFSERVICE bool IHFAPI InjectProcessById(DWORD processId, DWORD timeout)
|
|
{
|
|
bool success = true;
|
|
|
|
if (processId == GetCurrentProcessId())
|
|
{
|
|
success = false;
|
|
}
|
|
|
|
CloseHandle(CreateMutexW(nullptr, FALSE, CONCAT_STR_NUM(ITH_HOOKMAN_MUTEX_, processId)));
|
|
if (GetLastError() == ERROR_ALREADY_EXISTS)
|
|
{
|
|
man->AddConsoleOutput(L"already locked");
|
|
success = false;
|
|
}
|
|
|
|
HMODULE textHooker = LoadLibraryExW(L"vnrhook", nullptr, DONT_RESOLVE_DLL_REFERENCES);
|
|
if (textHooker == nullptr)
|
|
{
|
|
success = false;
|
|
}
|
|
wchar_t textHookerPath[MAX_PATH];
|
|
unsigned int textHookerPathSize = GetModuleFileNameW(textHooker, textHookerPath, MAX_PATH) * 2 + 2;
|
|
FreeLibrary(textHooker);
|
|
|
|
HANDLE processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processId);
|
|
if (processHandle == INVALID_HANDLE_VALUE || processHandle == nullptr)
|
|
{
|
|
success = false;
|
|
}
|
|
|
|
void* loadLibraryStartRoutine = GetProcAddress(GetModuleHandleA("kernel32.dll"), "LoadLibraryW");
|
|
|
|
if (success)
|
|
{
|
|
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)loadLibraryStartRoutine, remoteData, 0, nullptr))
|
|
{
|
|
WaitForSingleObject(thread, timeout);
|
|
CloseHandle(thread);
|
|
}
|
|
else
|
|
{
|
|
success = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
success = false;
|
|
}
|
|
VirtualFreeEx(processHandle, remoteData, textHookerPathSize, MEM_RELEASE);
|
|
}
|
|
else
|
|
{
|
|
success = false;
|
|
}
|
|
}
|
|
|
|
if (!success)
|
|
{
|
|
man->AddConsoleOutput(L"error: could not inject");
|
|
}
|
|
|
|
CloseHandle(processHandle);
|
|
return success;
|
|
}
|
|
|
|
// jichi 7/16/2014: Test if process is valid before creating remote threads
|
|
// See: http://msdn.microsoft.com/en-us/library/ms687032.aspx
|
|
static bool isProcessTerminated(HANDLE processHandle)
|
|
{
|
|
return WAIT_OBJECT_0 == ::WaitForSingleObject(processHandle, 0);
|
|
}
|
|
|
|
IHFSERVICE bool IHFAPI DetachProcessById(DWORD pid) // Todo: clean this up
|
|
{
|
|
ITH_SYNC_HOOK;
|
|
|
|
//man->LockHookman();
|
|
ProcessRecord *pr = man->GetProcessRecord(pid);
|
|
HANDLE hCmd = man->GetCmdHandleByPID(pid);
|
|
if (pr == 0 || hCmd == 0)
|
|
return false;
|
|
HANDLE hProc;
|
|
//hProc = pr->process_handle; //This handle may be closed(thus invalid) during the detach process.
|
|
NtDuplicateObject(NtCurrentProcess(), pr->process_handle,
|
|
NtCurrentProcess(), &hProc, 0, 0, DUPLICATE_SAME_ACCESS); // Make a copy of the process handle.
|
|
HANDLE hModule = (HANDLE)pr->module_register;
|
|
if (!hModule) {
|
|
DOUT("process module not found");
|
|
return false;
|
|
}
|
|
|
|
// jichi 7/15/2014: Process already closed
|
|
if (isProcessTerminated(hProc)) {
|
|
DOUT("process has terminated");
|
|
return false;
|
|
}
|
|
|
|
// jichi 10/19/2014: Disable the second dll
|
|
//engine = pr->engine_register;
|
|
//engine &= ~0xff;
|
|
|
|
DOUT("send detach command");
|
|
bool ret = sendCommand(hCmd, HOST_COMMAND_DETACH);
|
|
|
|
// jichi 7/15/2014: Process already closed
|
|
//if (isProcessTerminated(hProc)) {
|
|
// DOUT("process has terminated");
|
|
// return false;
|
|
//}
|
|
//WinDbg::ejectDll(hModule, 0, hProc); // eject in case module has not loaded yet
|
|
|
|
//cmdq->AddRequest(sp, pid);
|
|
////#ifdef ITH_WINE // Nt series crash on wine
|
|
//// hThread = IthCreateThread(FreeLibrary, engine, hProc);
|
|
////#else
|
|
// hThread = IthCreateThread(LdrUnloadDll, engine, hProc);
|
|
////#endif // ITH_WINE
|
|
// if (hThread == 0 || hThread == INVALID_HANDLE_VALUE)
|
|
// return FALSE;
|
|
// // jichi 10/22/2013: Timeout might crash vnrsrv
|
|
// //const LONGLONG timeout = HOOK_TIMEOUT;
|
|
// //NtWaitForSingleObject(hThread, 0, (PLARGE_INTEGER)&timeout);
|
|
// NtWaitForSingleObject(hThread, 0, nullptr);
|
|
// NtClose(hThread);
|
|
NtClose(hProc);
|
|
return ret;
|
|
}
|
|
|
|
IHFSERVICE DWORD IHFAPI Host_GetHookManager(HookManager** hookman)
|
|
{
|
|
if (::running) {
|
|
*hookman = man;
|
|
return 0;
|
|
}
|
|
else
|
|
return 1;
|
|
}
|
|
|
|
IHFSERVICE bool IHFAPI Host_GetSettings(Settings **p)
|
|
{
|
|
if (::running) {
|
|
*p = settings;
|
|
return true;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
IHFSERVICE DWORD IHFAPI Host_InsertHook(DWORD pid, HookParam *hp, LPCSTR name)
|
|
{
|
|
ITH_SYNC_HOOK;
|
|
|
|
HANDLE hCmd = man->GetCmdHandleByPID(pid);
|
|
if (hCmd == 0)
|
|
return -1;
|
|
|
|
InsertHookStruct s;
|
|
s.sp.type = HOST_COMMAND_NEW_HOOK;
|
|
s.sp.hp = *hp;
|
|
size_t len;
|
|
if (name)
|
|
len = ::strlen(name);
|
|
else
|
|
len = 0;
|
|
if (len) {
|
|
if (len >= IHS_BUFF_SIZE) len = IHS_BUFF_SIZE - 1;
|
|
memcpy(s.name_buffer, name, len);
|
|
}
|
|
s.name_buffer[len] = 0;
|
|
IO_STATUS_BLOCK ios;
|
|
NtWriteFile(hCmd, 0,0,0, &ios, &s, IHS_SIZE, 0, 0);
|
|
|
|
//memcpy(&sp.hp,hp,sizeof(HookParam));
|
|
//cmdq->AddRequest(sp, pid);
|
|
return 0;
|
|
}
|
|
|
|
IHFSERVICE DWORD IHFAPI Host_ModifyHook(DWORD pid, HookParam *hp)
|
|
{
|
|
ITH_SYNC_HOOK;
|
|
|
|
HANDLE hCmd = GetCmdHandleByPID(pid);
|
|
if (hCmd == 0)
|
|
return -1;
|
|
HANDLE hModify = IthCreateEvent(ITH_MODIFYHOOK_EVENT);
|
|
SendParam sp;
|
|
sp.type = HOST_COMMAND_MODIFY_HOOK;
|
|
sp.hp = *hp;
|
|
IO_STATUS_BLOCK ios;
|
|
if (NT_SUCCESS(NtWriteFile(hCmd, 0,0,0, &ios, &sp, sizeof(SendParam), 0, 0)))
|
|
// jichi 9/28/2013: no wait timeout
|
|
//const LONGLONG timeout = HOOK_TIMEOUT;
|
|
NtWaitForSingleObject(hModify, 0, nullptr);
|
|
NtClose(hModify);
|
|
man->RemoveSingleHook(pid, sp.hp.address);
|
|
return 0;
|
|
}
|
|
|
|
IHFSERVICE DWORD IHFAPI Host_RemoveHook(DWORD pid, DWORD addr)
|
|
{
|
|
ITH_SYNC_HOOK;
|
|
|
|
HANDLE hRemoved,hCmd;
|
|
hCmd = GetCmdHandleByPID(pid);
|
|
if (hCmd == 0)
|
|
return -1;
|
|
hRemoved = IthCreateEvent(ITH_REMOVEHOOK_EVENT);
|
|
SendParam sp = {};
|
|
IO_STATUS_BLOCK ios;
|
|
sp.type = HOST_COMMAND_REMOVE_HOOK;
|
|
sp.hp.address = addr;
|
|
//cmdq -> AddRequest(sp, pid);
|
|
NtWriteFile(hCmd, 0,0,0, &ios, &sp, sizeof(SendParam),0,0);
|
|
// jichi 10/22/2013: Timeout might crash vnrsrv
|
|
//const LONGLONG timeout = HOOK_TIMEOUT;
|
|
//NtWaitForSingleObject(hRemoved, 0, (PLARGE_INTEGER)&timeout);
|
|
NtWaitForSingleObject(hRemoved, 0, nullptr);
|
|
NtClose(hRemoved);
|
|
man -> RemoveSingleHook(pid, sp.hp.address);
|
|
return 0;
|
|
}
|
|
|
|
// 4/30/2015: Removed as not needed. Going to change to json
|
|
IHFSERVICE DWORD IHFAPI Host_AddLink(DWORD from, DWORD to)
|
|
{
|
|
man->AddLink(from & 0xffff, to & 0xffff);
|
|
return 0;
|
|
}
|
|
|
|
IHFSERVICE DWORD IHFAPI Host_UnLink(DWORD from)
|
|
{
|
|
man->UnLink(from & 0xffff);
|
|
return 0;
|
|
}
|
|
|
|
IHFSERVICE DWORD IHFAPI Host_UnLinkAll(DWORD from)
|
|
{
|
|
man->UnLinkAll(from & 0xffff);
|
|
return 0;
|
|
}
|
|
|
|
// EOF
|