2016-01-05 23:01:17 +08:00
# include "host.h"
2018-08-23 23:53:23 +08:00
# include "defs.h"
2018-11-23 04:53:32 +08:00
# include "util.h"
2019-03-13 23:54:19 +08:00
# include "../texthook/texthook.h"
2016-01-05 23:01:17 +08:00
2019-02-28 00:33:17 +08:00
extern const wchar_t * ALREADY_INJECTED ;
2019-04-22 22:02:59 +08:00
extern const wchar_t * NEED_32_BIT ;
extern const wchar_t * NEED_64_BIT ;
2019-02-28 00:33:17 +08:00
extern const wchar_t * INJECT_FAILED ;
extern const wchar_t * CONSOLE ;
extern const wchar_t * CLIPBOARD ;
2018-08-25 00:50:20 +08:00
namespace
2018-08-23 23:53:23 +08:00
{
2018-11-01 00:04:32 +08:00
class ProcessRecord
2018-08-25 00:50:20 +08:00
{
2018-11-01 00:04:32 +08:00
public :
2018-11-28 04:54:04 +08:00
ProcessRecord ( DWORD processId , HANDLE pipe ) :
pipe ( pipe ) ,
2018-12-02 04:55:32 +08:00
mappedFile ( OpenFileMappingW ( FILE_MAP_READ , FALSE , ( ITH_SECTION_ + std : : to_wstring ( processId ) ) . c_str ( ) ) ) ,
2019-01-11 10:47:16 +08:00
view ( * ( const TextHook ( * ) [ MAX_HOOK ] ) MapViewOfFile ( mappedFile , FILE_MAP_READ , 0 , 0 , HOOK_SECTION_SIZE / 2 ) ) , // jichi 1/16/2015: Changed to half to hook section size
2018-12-02 04:55:32 +08:00
viewMutex ( ITH_HOOKMAN_MUTEX_ + std : : to_wstring ( processId ) )
2019-02-05 04:18:47 +08:00
{ }
2018-11-23 04:53:32 +08:00
2018-11-01 00:04:32 +08:00
~ ProcessRecord ( )
{
2018-12-02 04:55:32 +08:00
UnmapViewOfFile ( view ) ;
2018-11-01 00:04:32 +08:00
}
TextHook GetHook ( uint64_t addr )
{
2019-06-17 05:15:47 +08:00
if ( ! view ) return { } ;
2019-01-10 11:35:01 +08:00
std : : scoped_lock lock ( viewMutex ) ;
2019-01-11 10:47:16 +08:00
for ( auto hook : view )
if ( hook . address = = addr ) return hook ;
2018-11-01 00:04:32 +08:00
return { } ;
}
2018-11-28 04:54:04 +08:00
template < typename T >
2019-02-17 11:51:10 +08:00
void Send ( T data )
2018-11-28 04:54:04 +08:00
{
2019-02-17 11:51:10 +08:00
static_assert ( sizeof ( data ) < PIPE_BUFFER_SIZE ) ;
2019-01-11 10:47:16 +08:00
std : : thread ( [ = ]
{
2019-06-04 05:58:30 +08:00
WriteFile ( pipe , & data , sizeof ( data ) , DUMMY , nullptr ) ;
2019-01-11 10:47:16 +08:00
} ) . detach ( ) ;
2018-11-28 04:54:04 +08:00
}
2018-11-01 00:04:32 +08:00
2019-06-10 13:49:11 +08:00
Host : : HookEventHandler OnHookFound = [ ] ( HookParam hp , const std : : wstring & text )
2019-06-02 14:09:17 +08:00
{
Host : : AddConsoleOutput ( Util : : GenerateCode ( hp , 0 ) + L " : " + text ) ;
} ;
2018-11-01 00:04:32 +08:00
private :
2018-11-28 04:54:04 +08:00
HANDLE pipe ;
2018-12-02 04:55:32 +08:00
AutoHandle < > mappedFile ;
2019-01-11 10:47:16 +08:00
const TextHook ( & view ) [ MAX_HOOK ] ;
2018-12-02 04:55:32 +08:00
WinMutex viewMutex ;
2018-08-25 00:50:20 +08:00
} ;
2019-06-10 13:49:11 +08:00
size_t HashThreadParam ( ThreadParam tp ) { return std : : hash < int64_t > ( ) ( tp . processId + tp . addr ) + std : : hash < int64_t > ( ) ( tp . ctx + tp . ctx2 ) ; }
2019-08-20 04:15:08 +08:00
Synchronized < std : : unordered_map < ThreadParam , TextThread , Functor < HashThreadParam > > > textThreadsByParams ;
2019-06-17 10:57:41 +08:00
Synchronized < std : : unordered_map < DWORD , ProcessRecord > > processRecordsByIds ;
2018-08-23 09:31:15 +08:00
2019-02-05 04:18:47 +08:00
Host : : ProcessEventHandler OnConnect , OnDisconnect ;
Host : : ThreadEventHandler OnCreate , OnDestroy ;
2018-08-25 00:50:20 +08:00
void RemoveThreads ( std : : function < bool ( ThreadParam ) > removeIf )
{
2019-02-05 04:18:47 +08:00
std : : vector < TextThread * > threadsToRemove ;
2019-08-20 04:15:08 +08:00
{
auto textThreadsByParams = : : textThreadsByParams . Acquire ( ) ;
std : : for_each ( textThreadsByParams - > begin ( ) , textThreadsByParams - > end ( ) , [ & ] ( auto & it ) { if ( removeIf ( it . first ) ) threadsToRemove . push_back ( & it . second ) ; } ) ;
}
2019-02-05 04:18:47 +08:00
for ( auto thread : threadsToRemove )
{
OnDestroy ( * thread ) ;
textThreadsByParams - > erase ( thread - > tp ) ;
}
2018-08-25 00:50:20 +08:00
}
2018-11-04 14:34:49 +08:00
void CreatePipe ( )
2018-08-25 00:50:20 +08:00
{
2018-09-02 02:11:48 +08:00
std : : thread ( [ ]
2018-08-25 00:50:20 +08:00
{
2019-01-10 11:35:01 +08:00
struct PipeCloser { void operator ( ) ( HANDLE h ) { DisconnectNamedPipe ( h ) ; CloseHandle ( h ) ; } } ;
AutoHandle < PipeCloser >
2019-02-17 11:51:10 +08:00
hookPipe = CreateNamedPipeW ( HOOK_PIPE , PIPE_ACCESS_INBOUND , PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE , PIPE_UNLIMITED_INSTANCES , 0 , PIPE_BUFFER_SIZE , MAXDWORD , & allAccess ) ,
hostPipe = CreateNamedPipeW ( HOST_PIPE , PIPE_ACCESS_OUTBOUND , PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE , PIPE_UNLIMITED_INSTANCES , PIPE_BUFFER_SIZE , 0 , MAXDWORD , & allAccess ) ;
static AutoHandle < > pipeAvailableEvent = CreateEventW ( & allAccess , FALSE , FALSE , PIPE_AVAILABLE_EVENT ) ;
SetEvent ( pipeAvailableEvent ) ;
2018-08-25 00:50:20 +08:00
ConnectNamedPipe ( hookPipe , nullptr ) ;
2018-11-23 04:53:32 +08:00
BYTE buffer [ PIPE_BUFFER_SIZE ] = { } ;
2018-08-25 00:50:20 +08:00
DWORD bytesRead , processId ;
ReadFile ( hookPipe , & processId , sizeof ( processId ) , & bytesRead , nullptr ) ;
2019-01-10 15:00:39 +08:00
processRecordsByIds - > try_emplace ( processId , processId , hostPipe ) ;
2019-02-05 04:18:47 +08:00
OnConnect ( processId ) ;
2018-08-25 00:50:20 +08:00
2018-11-04 14:34:49 +08:00
CreatePipe ( ) ;
2018-09-10 10:37:48 +08:00
2018-08-25 00:50:20 +08:00
while ( ReadFile ( hookPipe , buffer , PIPE_BUFFER_SIZE , & bytesRead , nullptr ) )
2018-11-11 12:29:12 +08:00
switch ( * ( HostNotificationType * ) buffer )
2018-08-25 00:50:20 +08:00
{
2019-06-02 14:09:17 +08:00
case HOST_NOTIFICATION_FOUND_HOOK :
{
auto info = * ( HookFoundNotif * ) buffer ;
2019-06-17 10:57:41 +08:00
auto OnHookFound = processRecordsByIds - > at ( processId ) . OnHookFound ;
2019-06-09 12:48:30 +08:00
std : : wstring wide = info . text ;
2019-06-10 13:49:11 +08:00
if ( wide . size ( ) > STRING ) OnHookFound ( info . hp , info . text ) ;
2019-06-17 03:28:59 +08:00
info . hp . type & = ~ USING_UNICODE ;
2019-08-20 04:15:08 +08:00
if ( auto converted = Util : : StringToWideString ( ( char * ) info . text , info . hp . codepage ) )
2019-06-10 13:49:11 +08:00
if ( converted - > size ( ) > STRING ) OnHookFound ( info . hp , converted . value ( ) ) ;
2019-08-20 04:15:08 +08:00
if ( auto converted = Util : : StringToWideString ( ( char * ) info . text , info . hp . codepage = CP_UTF8 ) )
2019-06-10 13:49:11 +08:00
if ( converted - > size ( ) > STRING ) OnHookFound ( info . hp , converted . value ( ) ) ;
2019-06-02 14:09:17 +08:00
}
break ;
2018-08-25 00:50:20 +08:00
case HOST_NOTIFICATION_RMVHOOK :
{
auto info = * ( HookRemovedNotif * ) buffer ;
2018-11-05 09:48:46 +08:00
RemoveThreads ( [ & ] ( ThreadParam tp ) { return tp . processId = = processId & & tp . addr = = info . address ; } ) ;
2018-08-25 00:50:20 +08:00
}
break ;
case HOST_NOTIFICATION_TEXT :
{
auto info = * ( ConsoleOutputNotif * ) buffer ;
2018-11-26 05:23:41 +08:00
Host : : AddConsoleOutput ( Util : : StringToWideString ( info . message ) . value ( ) ) ;
2018-08-25 00:50:20 +08:00
}
break ;
default :
{
2018-11-19 21:17:00 +08:00
auto tp = * ( ThreadParam * ) buffer ;
2019-06-05 11:12:45 +08:00
auto textThreadsByParams = : : textThreadsByParams . Acquire ( ) ;
2019-06-10 13:49:11 +08:00
auto textThread = textThreadsByParams - > find ( tp ) ;
if ( textThread = = textThreadsByParams - > end ( ) )
2019-02-05 04:18:47 +08:00
{
2019-06-17 10:57:41 +08:00
try { textThread = textThreadsByParams - > try_emplace ( tp , tp , processRecordsByIds - > at ( tp . processId ) . GetHook ( tp . addr ) . hp ) . first ; }
2019-06-10 13:49:11 +08:00
catch ( std : : out_of_range ) { continue ; } // probably garbage data in pipe, try again
OnCreate ( textThread - > second ) ;
2019-02-05 04:18:47 +08:00
}
2019-06-10 13:49:11 +08:00
textThread - > second . Push ( buffer + sizeof ( tp ) , bytesRead - sizeof ( tp ) ) ;
2018-08-25 00:50:20 +08:00
}
break ;
}
2018-08-23 06:05:45 +08:00
2018-11-28 04:54:04 +08:00
RemoveThreads ( [ & ] ( ThreadParam tp ) { return tp . processId = = processId ; } ) ;
2019-02-05 04:18:47 +08:00
OnDisconnect ( processId ) ;
2018-11-28 04:54:04 +08:00
processRecordsByIds - > erase ( processId ) ;
2018-08-25 00:50:20 +08:00
} ) . detach ( ) ;
}
}
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
{
2019-02-05 04:18:47 +08:00
void Start ( ProcessEventHandler Connect , ProcessEventHandler Disconnect , ThreadEventHandler Create , ThreadEventHandler Destroy , TextThread : : OutputCallback Output )
2018-11-28 04:54:04 +08:00
{
2019-02-05 04:18:47 +08:00
OnConnect = Connect ;
OnDisconnect = Disconnect ;
OnCreate = [ Create ] ( TextThread & thread ) { Create ( thread ) ; thread . Start ( ) ; } ;
OnDestroy = [ Destroy ] ( TextThread & thread ) { thread . Stop ( ) ; Destroy ( thread ) ; } ;
2018-11-28 04:54:04 +08:00
TextThread : : Output = Output ;
2019-02-05 04:18:47 +08:00
textThreadsByParams - > try_emplace ( console , console , HookParam { } , CONSOLE ) ;
OnCreate ( GetThread ( console ) ) ;
textThreadsByParams - > try_emplace ( clipboard , clipboard , HookParam { } , CLIPBOARD ) ;
OnCreate ( GetThread ( clipboard ) ) ;
2018-11-04 14:34:49 +08:00
CreatePipe ( ) ;
2019-01-20 22:52:35 +08:00
2019-02-16 13:33:38 +08:00
static AutoHandle < > clipboardUpdate = CreateEventW ( nullptr , FALSE , TRUE , NULL ) ;
2019-01-20 22:52:35 +08:00
SetWindowsHookExW ( WH_GETMESSAGE , [ ] ( int statusCode , WPARAM wParam , LPARAM lParam )
{
2019-02-16 13:33:38 +08:00
if ( statusCode = = HC_ACTION & & wParam = = PM_REMOVE & & ( ( MSG * ) lParam ) - > message = = WM_CLIPBOARDUPDATE ) SetEvent ( clipboardUpdate ) ;
2019-01-20 22:52:35 +08:00
return CallNextHookEx ( NULL , statusCode , wParam , lParam ) ;
} , NULL , GetCurrentThreadId ( ) ) ;
2019-02-16 13:33:38 +08:00
std : : thread ( [ ]
{
while ( WaitForSingleObject ( clipboardUpdate , INFINITE ) = = WAIT_OBJECT_0 )
2019-06-19 08:18:42 +08:00
{
2019-08-13 09:01:35 +08:00
std : : optional < std : : wstring > clipboardText ;
for ( int retry = 0 ; ! clipboardText & & retry < 3 ; + + retry ) // retry loop in case something else is using the clipboard
2019-06-19 08:18:42 +08:00
{
Sleep ( 10 ) ;
2019-08-13 09:01:35 +08:00
if ( clipboardText = Util : : GetClipboardText ( ) ) GetThread ( clipboard ) . AddSentence ( std : : move ( clipboardText . value ( ) ) ) ;
2019-06-19 08:18:42 +08:00
}
}
2019-02-16 13:33:38 +08:00
throw ;
} ) . detach ( ) ;
2018-07-24 03:25:02 +08:00
}
2019-02-16 13:33:38 +08:00
void InjectProcess ( DWORD processId )
2018-07-24 03:25:02 +08:00
{
2019-02-16 13:33:38 +08:00
std : : thread ( [ processId ]
2018-07-24 03:25:02 +08:00
{
2019-02-16 13:33:38 +08:00
if ( processId = = GetCurrentProcessId ( ) ) return ;
2018-07-24 03:25:02 +08:00
2019-02-16 13:33:38 +08:00
WinMutex ( ITH_HOOKMAN_MUTEX_ + std : : to_wstring ( processId ) ) ;
if ( GetLastError ( ) = = ERROR_ALREADY_EXISTS ) return AddConsoleOutput ( ALREADY_INJECTED ) ;
2018-07-24 03:25:02 +08:00
2019-02-16 13:33:38 +08:00
if ( AutoHandle < > process = OpenProcess ( PROCESS_ALL_ACCESS , FALSE , processId ) )
2018-08-23 03:11:58 +08:00
{
2019-02-16 13:33:38 +08:00
# ifdef _WIN64
BOOL invalidProcess = FALSE ;
IsWow64Process ( process , & invalidProcess ) ;
2019-04-22 22:02:59 +08:00
if ( invalidProcess ) return AddConsoleOutput ( NEED_32_BIT ) ;
2018-08-23 03:11:58 +08:00
# endif
2019-02-17 11:51:10 +08:00
static std : : wstring location = Util : : GetModuleFilename ( LoadLibraryExW ( ITH_DLL , nullptr , DONT_RESOLVE_DLL_REFERENCES ) ) . value ( ) ;
2019-02-16 13:33:38 +08:00
if ( LPVOID remoteData = VirtualAllocEx ( process , nullptr , ( location . size ( ) + 1 ) * sizeof ( wchar_t ) , MEM_RESERVE | MEM_COMMIT , PAGE_READWRITE ) )
2018-08-23 03:11:58 +08:00
{
2019-02-16 13:33:38 +08:00
WriteProcessMemory ( process , remoteData , location . c_str ( ) , ( location . size ( ) + 1 ) * sizeof ( wchar_t ) , nullptr ) ;
if ( AutoHandle < > thread = CreateRemoteThread ( process , nullptr , 0 , ( LPTHREAD_START_ROUTINE ) LoadLibraryW , remoteData , 0 , nullptr ) ) WaitForSingleObject ( thread , INFINITE ) ;
2019-04-22 22:02:59 +08:00
else if ( GetLastError ( ) = = ERROR_ACCESS_DENIED ) AddConsoleOutput ( NEED_64_BIT ) ; // https://stackoverflow.com/questions/16091141/createremotethread-access-denied
2018-11-28 04:54:04 +08:00
VirtualFreeEx ( process , remoteData , 0 , MEM_RELEASE ) ;
2019-02-16 13:33:38 +08:00
return ;
2018-08-23 03:11:58 +08:00
}
}
2018-07-24 03:25:02 +08:00
2019-02-16 13:33:38 +08:00
AddConsoleOutput ( INJECT_FAILED ) ;
} ) . detach ( ) ;
2018-05-12 04:46:05 +08:00
}
2018-07-24 03:25:02 +08:00
2018-08-25 02:04:23 +08:00
void DetachProcess ( DWORD processId )
2018-05-12 04:46:05 +08:00
{
2019-02-22 02:09:44 +08:00
processRecordsByIds - > at ( processId ) . Send ( HOST_COMMAND_DETACH ) ;
2018-07-24 03:25:02 +08:00
}
2018-12-02 04:55:32 +08:00
void InsertHook ( DWORD processId , HookParam hp )
2018-07-24 03:25:02 +08:00
{
2018-12-18 10:03:42 +08:00
processRecordsByIds - > at ( processId ) . Send ( InsertHookCmd ( hp ) ) ;
2018-07-24 03:25:02 +08:00
}
2016-01-05 23:01:17 +08:00
2019-06-10 13:49:11 +08:00
void RemoveHook ( DWORD processId , uint64_t address )
{
processRecordsByIds - > at ( processId ) . Send ( RemoveHookCmd ( address ) ) ;
}
2019-06-02 14:09:17 +08:00
void FindHooks ( DWORD processId , SearchParam sp , HookEventHandler HookFound )
{
if ( HookFound ) processRecordsByIds - > at ( processId ) . OnHookFound = HookFound ;
processRecordsByIds - > at ( processId ) . Send ( FindHookCmd ( sp ) ) ;
}
2019-06-17 10:57:41 +08:00
TextThread & GetThread ( ThreadParam tp )
2018-05-12 04:46:05 +08:00
{
2019-06-17 10:57:41 +08:00
return textThreadsByParams - > at ( tp ) ;
2018-05-12 04:46:05 +08:00
}
2016-01-05 23:01:17 +08:00
2019-06-17 10:57:41 +08:00
TextThread * GetThread ( int64_t handle )
2018-07-24 03:25:02 +08:00
{
2019-06-17 10:57:41 +08:00
auto textThreadsByParams = : : textThreadsByParams . Acquire ( ) ;
auto thread = std : : find_if ( textThreadsByParams - > begin ( ) , textThreadsByParams - > end ( ) , [ & ] ( const auto & thread ) { return thread . second . handle = = handle ; } ) ;
return thread ! = textThreadsByParams - > end ( ) ? & thread - > second : nullptr ;
2018-07-24 03:25:02 +08:00
}
2018-05-12 04:46:05 +08:00
2018-12-29 01:14:56 +08:00
void AddConsoleOutput ( std : : wstring text )
{
2019-02-09 13:30:38 +08:00
GetThread ( console ) . AddSentence ( std : : move ( text ) ) ;
2018-11-02 03:03:30 +08:00
}
2016-01-05 23:01:17 +08:00
}