2016-01-05 23:01:17 +08:00
// host.cc
// 8/24/2013 jichi
// Branch IHF/main.cpp, rev 111
# include "host.h"
2018-07-19 12:46:52 +08:00
# include "pipe.h"
2018-07-24 03:25:02 +08:00
# include "winmutex.h"
# include <atlbase.h>
2018-07-21 04:26:27 +08:00
# include "../vnrhook/include/const.h"
# include "../vnrhook/include/defs.h"
# include "../vnrhook/include/types.h"
2018-07-24 03:25:02 +08:00
# include <unordered_map>
2016-01-05 23:01:17 +08:00
2018-07-18 05:01:56 +08:00
HANDLE preventDuplicationMutex ;
2016-01-05 23:01:17 +08:00
2018-07-24 03:25:02 +08:00
std : : unordered_map < ThreadParameter , TextThread * , ThreadParameterHasher > textThreadsByParams ;
std : : unordered_map < DWORD , ProcessRecord > processRecordsByIds ;
CRITICAL_SECTION hostCs ;
ThreadEventCallback onCreate , onRemove ;
ProcessEventCallback onAttach , onDetach ;
WORD nextThreadNumber ;
2018-05-12 04:46:05 +08:00
HWND dummyWindow ;
2016-01-05 23:01:17 +08:00
2018-07-24 03:25:02 +08:00
# define HOST_LOCK CriticalSectionLocker hostLocker(hostCs) // Synchronized scope for accessing private data
2016-01-05 23:01:17 +08:00
2018-07-24 03:25:02 +08:00
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 ) ;
}
2016-01-05 23:01:17 +08:00
2018-05-12 04:46:05 +08:00
BOOL WINAPI DllMain ( HINSTANCE hinstDLL , DWORD fdwReason , LPVOID unused )
2016-01-05 23:01:17 +08:00
{
2018-05-12 04:46:05 +08:00
switch ( fdwReason )
{
case DLL_PROCESS_ATTACH :
DisableThreadLibraryCalls ( hinstDLL ) ;
// 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 ;
default :
break ;
}
return true ;
2016-01-05 23:01:17 +08:00
}
2018-07-24 03:25:02 +08:00
namespace Host
2016-01-05 23:01:17 +08:00
{
2018-07-24 03:25:02 +08:00
DLLEXPORT bool Start ( )
{
preventDuplicationMutex = CreateMutexW ( nullptr , TRUE , ITH_SERVER_MUTEX ) ;
2018-07-25 10:00:10 +08:00
if ( GetLastError ( ) = = ERROR_ALREADY_EXISTS )
2018-07-24 03:25:02 +08:00
{
MessageBoxW ( nullptr , L " I am sorry that this game is attached by some other VNR >< \n Please restart the game and try again! " , L " Error " , MB_ICONERROR ) ;
return false ;
}
else
{
GetDebugPrivileges ( ) ;
InitializeCriticalSection ( & hostCs ) ;
onAttach = onDetach = nullptr ;
onCreate = onRemove = nullptr ;
nextThreadNumber = 0 ;
return true ;
}
}
DLLEXPORT void Open ( )
2018-05-12 04:46:05 +08:00
{
2018-07-24 13:57:54 +08:00
TextThread * console = textThreadsByParams [ { 0 , - 1UL , - 1UL , - 1UL } ] = new TextThread ( { 0 , - 1UL , - 1UL , - 1UL } , nextThreadNumber + + ) ;
console - > Status ( ) | = USING_UNICODE ;
if ( onCreate ) onCreate ( console ) ;
2018-07-24 03:25:02 +08:00
CreateNewPipe ( ) ;
}
DLLEXPORT void Close ( )
{
2018-07-25 16:11:23 +08:00
// Artikash 7/25/2018: This is only called when NextHooker is closed, at which point Windows should free everything itself...right?
//EnterCriticalSection(&hostCs);
//DestroyWindow(dummyWindow);
//RemoveThreads([](auto one, auto two) { return true; }, {});
////for (auto i : processRecordsByIds) UnregisterProcess(i.first); // Artikash 7/24/2018 FIXME: This segfaults since UnregisterProcess invalidates the iterator
//LeaveCriticalSection(&hostCs);
//DeleteCriticalSection(&hostCs);
//CloseHandle(preventDuplicationMutex);
2018-07-24 03:25:02 +08:00
}
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 " ) ;
2018-07-18 05:01:56 +08:00
return false ;
2018-05-12 04:46:05 +08:00
}
2018-07-24 03:25:02 +08:00
DLLEXPORT bool DetachProcess ( DWORD processId )
2018-05-12 04:46:05 +08:00
{
2018-07-24 03:25:02 +08:00
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 } ) ;
2018-07-18 05:01:56 +08:00
return true ;
2018-05-12 04:46:05 +08:00
}
2016-01-05 23:01:17 +08:00
2018-07-24 03:25:02 +08:00
DLLEXPORT HookParam GetHookParam ( DWORD pid , DWORD addr )
{
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 ;
}
2016-01-05 23:01:17 +08:00
2018-07-24 03:25:02 +08:00
DLLEXPORT std : : wstring GetHookName ( DWORD pid , DWORD addr )
2018-05-12 04:46:05 +08:00
{
2018-07-24 13:57:54 +08:00
if ( pid = = 0 ) return L " Console " ;
2018-07-24 03:25:02 +08:00
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 ( ) ) ) ;
2018-05-12 04:46:05 +08:00
}
2016-01-05 23:01:17 +08:00
2018-07-24 03:25:02 +08:00
DLLEXPORT TextThread * GetThread ( DWORD number )
{
HOST_LOCK ;
for ( auto i : textThreadsByParams )
if ( i . second - > Number ( ) = = number )
return i . second ;
return nullptr ;
}
2018-05-12 04:46:05 +08:00
2018-07-24 03:25:02 +08:00
DLLEXPORT void AddConsoleOutput ( std : : wstring text )
2018-05-12 04:46:05 +08:00
{
2018-07-24 03:25:02 +08:00
HOST_LOCK ;
textThreadsByParams [ { 0 , - 1UL , - 1UL , - 1UL } ] - > AddSentence ( std : : wstring ( text ) ) ;
2018-05-12 04:46:05 +08:00
}
2018-07-24 03:25:02 +08:00
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 ; }
2016-01-05 23:01:17 +08:00
}
2018-07-24 03:25:02 +08:00
void DispatchText ( DWORD pid , DWORD hook , DWORD retn , DWORD split , const BYTE * text , int len )
2018-05-12 04:46:05 +08:00
{
2018-07-25 16:11:23 +08:00
// jichi 2/27/2013: When PID is zero, the text comes from console, which I don't need
2018-07-24 03:25:02 +08:00
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 ) ;
2018-05-12 04:46:05 +08:00
}
2016-01-05 23:01:17 +08:00
2018-07-24 03:25:02 +08:00
void RemoveThreads ( bool ( * RemoveIf ) ( ThreadParameter , ThreadParameter ) , ThreadParameter cmp )
2016-01-05 23:01:17 +08:00
{
2018-07-24 03:25:02 +08:00
HOST_LOCK ;
std : : vector < ThreadParameter > removedThreads ;
for ( auto i : textThreadsByParams )
if ( RemoveIf ( i . first , cmp ) )
{
if ( onRemove ) onRemove ( i . second ) ;
2018-07-25 01:39:02 +08:00
//delete i.second; // Artikash 7/24/2018: FIXME: Qt GUI updates on another thread, so I can't delete this yet.
2018-07-26 01:46:59 +08:00
i . second - > Clear ( ) ; // Temp workaround to free some memory.
2018-07-24 03:25:02 +08:00
removedThreads . push_back ( i . first ) ;
}
for ( auto i : removedThreads ) textThreadsByParams . erase ( i ) ;
2016-01-05 23:01:17 +08:00
}
2018-07-24 03:25:02 +08:00
void RegisterProcess ( DWORD pid , HANDLE hostPipe )
2016-01-05 23:01:17 +08:00
{
2018-07-24 03:25:02 +08:00
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 ) ;
2016-01-05 23:01:17 +08:00
}
2018-07-24 03:25:02 +08:00
void UnregisterProcess ( DWORD pid )
2016-01-05 23:01:17 +08:00
{
2018-07-24 03:25:02 +08:00
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 ) ;
2016-01-05 23:01:17 +08:00
}
2018-07-25 01:39:02 +08:00
// EOF