2016-01-06 00:01:17 +09:00
# include "host.h"
2018-08-23 11:53:23 -04:00
# include "const.h"
2018-11-22 15:53:32 -05:00
# include "text.h"
2018-08-23 11:53:23 -04:00
# include "defs.h"
2018-11-22 15:53:32 -05:00
# include "util.h"
2018-11-10 23:29:12 -05:00
# include "../vnrhook/texthook.h"
2016-01-06 00:01:17 +09:00
2018-08-24 12:50:20 -04:00
namespace
2018-08-23 11:53:23 -04:00
{
2018-10-31 12:04:32 -04:00
class ProcessRecord
2018-08-24 12:50:20 -04:00
{
2018-10-31 12:04:32 -04:00
public :
2018-11-27 15:54:04 -05:00
inline static Host : : ProcessEventCallback OnConnect , OnDisconnect ;
2018-10-31 12:04:32 -04:00
2018-11-27 15:54:04 -05:00
ProcessRecord ( DWORD processId , HANDLE pipe ) :
processId ( processId ) ,
pipe ( pipe ) ,
2018-12-01 15:55:32 -05:00
mappedFile ( OpenFileMappingW ( FILE_MAP_READ , FALSE , ( ITH_SECTION_ + std : : to_wstring ( processId ) ) . c_str ( ) ) ) ,
2019-01-10 21:47:16 -05: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-01 15:55:32 -05:00
viewMutex ( ITH_HOOKMAN_MUTEX_ + std : : to_wstring ( processId ) )
2018-11-27 15:54:04 -05:00
{
OnConnect ( processId ) ;
}
2018-11-22 15:53:32 -05:00
2018-10-31 12:04:32 -04:00
~ ProcessRecord ( )
{
2018-11-27 15:54:04 -05:00
OnDisconnect ( processId ) ;
2018-12-01 15:55:32 -05:00
UnmapViewOfFile ( view ) ;
2018-10-31 12:04:32 -04:00
}
TextHook GetHook ( uint64_t addr )
{
2018-12-01 15:55:32 -05:00
if ( view = = nullptr ) return { } ;
2019-01-09 22:35:01 -05:00
std : : scoped_lock lock ( viewMutex ) ;
2019-01-10 21:47:16 -05:00
for ( auto hook : view )
if ( hook . address = = addr ) return hook ;
2018-10-31 12:04:32 -04:00
return { } ;
}
2018-11-27 15:54:04 -05:00
template < typename T >
void Send ( T data )
{
2019-01-10 21:47:16 -05:00
std : : thread ( [ = ]
{
std : : enable_if_t < sizeof ( data ) < PIPE_BUFFER_SIZE , DWORD > DUMMY ;
WriteFile ( pipe , & data , sizeof ( data ) , & DUMMY , nullptr ) ;
} ) . detach ( ) ;
2018-11-27 15:54:04 -05:00
}
2018-10-31 12:04:32 -04:00
private :
2018-11-27 15:54:04 -05:00
DWORD processId ;
HANDLE pipe ;
2018-12-01 15:55:32 -05:00
AutoHandle < > mappedFile ;
2019-01-10 21:47:16 -05:00
const TextHook ( & view ) [ MAX_HOOK ] ;
2018-12-01 15:55:32 -05:00
WinMutex viewMutex ;
2018-08-24 12:50:20 -04:00
} ;
2019-01-09 22:35:01 -05:00
size_t HashThreadParam ( ThreadParam tp )
{
std : : hash < int64_t > hash ;
return hash ( hash ( tp . processId ) + hash ( tp . addr ) + hash ( tp . ctx ) + hash ( tp . ctx2 ) ) ;
}
ThreadSafe < std : : unordered_map < ThreadParam , std : : unique_ptr < TextThread > , Functor < HashThreadParam > > , std : : recursive_mutex > textThreadsByParams ;
2019-01-06 02:57:52 -05:00
ThreadSafe < std : : unordered_map < DWORD , ProcessRecord > , std : : recursive_mutex > processRecordsByIds ;
2018-08-22 21:31:15 -04:00
2018-08-24 12:50:20 -04:00
void RemoveThreads ( std : : function < bool ( ThreadParam ) > removeIf )
{
2019-01-09 22:35:01 -05:00
std : : vector < std : : unique_ptr < TextThread > > removedThreads ; // delay destruction until after lock is released
2018-12-01 15:55:32 -05:00
auto [ lock , textThreadsByParams ] = : : textThreadsByParams . operator - > ( ) ;
2019-01-06 02:57:52 -05:00
for ( auto it = textThreadsByParams - > begin ( ) ; it ! = textThreadsByParams - > end ( ) ; removeIf ( it - > first ) ? it = textThreadsByParams - > erase ( it ) : + + it )
if ( removeIf ( it - > first ) ) removedThreads . emplace_back ( std : : move ( it - > second ) ) ;
2018-08-24 12:50:20 -04:00
}
2018-11-04 01:34:49 -05:00
void CreatePipe ( )
2018-08-24 12:50:20 -04:00
{
2018-09-01 14:11:48 -04:00
std : : thread ( [ ]
2018-08-24 12:50:20 -04:00
{
2018-09-20 23:04:11 -04:00
SECURITY_DESCRIPTOR pipeSD = { } ;
InitializeSecurityDescriptor ( & pipeSD , SECURITY_DESCRIPTOR_REVISION ) ;
SetSecurityDescriptorDacl ( & pipeSD , TRUE , NULL , FALSE ) ; // Allow non-admin processes to connect to pipe created by admin host
2019-01-09 22:35:01 -05:00
SECURITY_ATTRIBUTES pipeSA = { sizeof ( pipeSA ) , & pipeSD , FALSE } ;
struct PipeCloser { void operator ( ) ( HANDLE h ) { DisconnectNamedPipe ( h ) ; CloseHandle ( h ) ; } } ;
AutoHandle < PipeCloser >
2018-11-27 15:54:04 -05:00
hookPipe = CreateNamedPipeW ( HOOK_PIPE , PIPE_ACCESS_INBOUND , PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE , PIPE_UNLIMITED_INSTANCES , 0 , PIPE_BUFFER_SIZE , MAXDWORD , & pipeSA ) ,
hostPipe = CreateNamedPipeW ( HOST_PIPE , PIPE_ACCESS_OUTBOUND , PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE , PIPE_UNLIMITED_INSTANCES , PIPE_BUFFER_SIZE , 0 , MAXDWORD , & pipeSA ) ;
2018-08-24 12:50:20 -04:00
ConnectNamedPipe ( hookPipe , nullptr ) ;
2018-11-22 15:53:32 -05:00
BYTE buffer [ PIPE_BUFFER_SIZE ] = { } ;
2018-08-24 12:50:20 -04:00
DWORD bytesRead , processId ;
ReadFile ( hookPipe , & processId , sizeof ( processId ) , & bytesRead , nullptr ) ;
2019-01-10 02:00:39 -05:00
processRecordsByIds - > try_emplace ( processId , processId , hostPipe ) ;
2018-08-24 12:50:20 -04:00
2018-11-04 01:34:49 -05:00
CreatePipe ( ) ;
2018-09-09 22:37:48 -04:00
2018-08-24 12:50:20 -04:00
while ( ReadFile ( hookPipe , buffer , PIPE_BUFFER_SIZE , & bytesRead , nullptr ) )
2018-11-10 23:29:12 -05:00
switch ( * ( HostNotificationType * ) buffer )
2018-08-24 12:50:20 -04:00
{
case HOST_NOTIFICATION_RMVHOOK :
{
auto info = * ( HookRemovedNotif * ) buffer ;
2018-11-04 20:48:46 -05:00
RemoveThreads ( [ & ] ( ThreadParam tp ) { return tp . processId = = processId & & tp . addr = = info . address ; } ) ;
2018-08-24 12:50:20 -04:00
}
break ;
case HOST_NOTIFICATION_TEXT :
{
auto info = * ( ConsoleOutputNotif * ) buffer ;
2018-11-25 16:23:41 -05:00
Host : : AddConsoleOutput ( Util : : StringToWideString ( info . message ) . value ( ) ) ;
2018-08-24 12:50:20 -04:00
}
break ;
default :
{
2018-11-19 08:17:00 -05:00
auto tp = * ( ThreadParam * ) buffer ;
2019-01-06 02:57:52 -05:00
if ( textThreadsByParams - > count ( tp ) = = 0 ) textThreadsByParams - > insert ( { tp , std : : make_unique < TextThread > ( tp , Host : : GetHookParam ( tp ) ) } ) ;
2018-11-27 15:54:04 -05:00
textThreadsByParams - > at ( tp ) - > Push ( buffer + sizeof ( tp ) , bytesRead - sizeof ( tp ) ) ;
2018-08-24 12:50:20 -04:00
}
break ;
}
2018-08-22 18:05:45 -04:00
2018-11-27 15:54:04 -05:00
RemoveThreads ( [ & ] ( ThreadParam tp ) { return tp . processId = = processId ; } ) ;
processRecordsByIds - > erase ( processId ) ;
2018-08-24 12:50:20 -04:00
} ) . detach ( ) ;
}
2018-11-01 19:51:23 -04:00
void StartCapturingClipboard ( )
{
2018-12-28 11:13:02 -05:00
SetWindowsHookExW ( WH_GETMESSAGE , [ ] ( int statusCode , WPARAM wParam , LPARAM lParam )
2018-11-01 19:51:23 -04:00
{
2018-12-28 11:13:02 -05:00
if ( statusCode = = HC_ACTION & & wParam = = PM_REMOVE & & ( ( MSG * ) lParam ) - > message = = WM_CLIPBOARDUPDATE )
2019-01-09 22:35:01 -05:00
if ( auto text = Util : : GetClipboardText ( ) ) Host : : GetThread ( Host : : clipboard ) - > AddSentence ( text . value ( ) ) ;
2018-12-28 11:13:02 -05:00
return CallNextHookEx ( NULL , statusCode , wParam , lParam ) ;
} , NULL , GetCurrentThreadId ( ) ) ;
2018-11-01 19:51:23 -04:00
}
2018-08-24 12:50:20 -04:00
}
2016-01-06 00:01:17 +09:00
2018-07-23 12:25:02 -07:00
namespace Host
2016-01-06 00:01:17 +09:00
{
2018-11-27 15:54:04 -05:00
void Start ( ProcessEventCallback OnConnect , ProcessEventCallback OnDisconnect , TextThread : : EventCallback OnCreate , TextThread : : EventCallback OnDestroy , TextThread : : OutputCallback Output )
{
ProcessRecord : : OnConnect = OnConnect ;
ProcessRecord : : OnDisconnect = OnDisconnect ;
TextThread : : OnCreate = OnCreate ;
TextThread : : OnDestroy = OnDestroy ;
TextThread : : Output = Output ;
2019-01-10 02:00:39 -05:00
processRecordsByIds - > try_emplace ( console . processId , console . processId , INVALID_HANDLE_VALUE ) ;
2019-01-09 22:35:01 -05:00
textThreadsByParams - > insert ( { console , std : : make_unique < TextThread > ( console , HookParam { } , CONSOLE ) } ) ;
textThreadsByParams - > insert ( { Host : : clipboard , std : : make_unique < TextThread > ( Host : : clipboard , HookParam { } , CLIPBOARD ) } ) ;
2018-11-01 19:51:23 -04:00
StartCapturingClipboard ( ) ;
2018-11-04 01:34:49 -05:00
CreatePipe ( ) ;
2018-07-23 12:25:02 -07:00
}
2018-08-24 12:50:20 -04:00
bool InjectProcess ( DWORD processId , DWORD timeout )
2018-07-23 12:25:02 -07:00
{
if ( processId = = GetCurrentProcessId ( ) ) return false ;
2018-11-27 15:54:04 -05:00
WinMutex ( ITH_HOOKMAN_MUTEX_ + std : : to_wstring ( processId ) ) ;
2018-07-23 12:25:02 -07:00
if ( GetLastError ( ) = = ERROR_ALREADY_EXISTS )
{
2018-11-04 02:13:51 -05:00
AddConsoleOutput ( ALREADY_INJECTED ) ;
2018-07-23 12:25:02 -07:00
return false ;
}
2018-11-27 15:54:04 -05:00
static HMODULE vnrhook = LoadLibraryExW ( ITH_DLL , nullptr , DONT_RESOLVE_DLL_REFERENCES ) ;
2018-12-22 13:05:01 -05:00
static std : : wstring location = Util : : GetModuleFilename ( vnrhook ) . value ( ) ;
2018-07-23 12:25:02 -07:00
2018-11-27 15:54:04 -05:00
if ( AutoHandle < > process = OpenProcess ( PROCESS_ALL_ACCESS , FALSE , processId ) )
2018-08-22 15:11:58 -04:00
{
# ifdef _WIN64
BOOL invalidProcess = FALSE ;
2018-11-27 15:54:04 -05:00
IsWow64Process ( process , & invalidProcess ) ;
2018-08-22 15:11:58 -04:00
if ( invalidProcess )
{
2018-11-04 02:13:51 -05:00
AddConsoleOutput ( ARCHITECTURE_MISMATCH ) ;
2018-08-22 15:11:58 -04:00
return false ;
}
# endif
2019-01-09 22:35:01 -05:00
if ( LPVOID remoteData = VirtualAllocEx ( process , nullptr , ( location . size ( ) + 1 ) * sizeof ( wchar_t ) , MEM_RESERVE | MEM_COMMIT , PAGE_READWRITE ) )
2018-08-22 15:11:58 -04:00
{
2018-11-27 15:54:04 -05:00
WriteProcessMemory ( process , remoteData , location . c_str ( ) , location . size ( ) * 2 + 2 , nullptr ) ;
if ( AutoHandle < > thread = CreateRemoteThread ( process , nullptr , 0 , ( LPTHREAD_START_ROUTINE ) LoadLibraryW , remoteData , 0 , nullptr ) )
2018-08-22 15:11:58 -04:00
{
WaitForSingleObject ( thread , timeout ) ;
2018-11-27 15:54:04 -05:00
VirtualFreeEx ( process , remoteData , 0 , MEM_RELEASE ) ;
2018-08-22 15:11:58 -04:00
return true ;
}
2018-11-27 15:54:04 -05:00
VirtualFreeEx ( process , remoteData , 0 , MEM_RELEASE ) ;
2018-08-22 15:11:58 -04:00
}
}
2018-07-23 12:25:02 -07:00
2018-11-04 02:13:51 -05:00
AddConsoleOutput ( INJECT_FAILED ) ;
2018-07-17 17:01:56 -04:00
return false ;
2018-05-11 16:46:05 -04:00
}
2018-07-23 12:25:02 -07:00
2018-08-24 14:04:23 -04:00
void DetachProcess ( DWORD processId )
2018-05-11 16:46:05 -04:00
{
2018-12-17 21:03:42 -05:00
processRecordsByIds - > at ( processId ) . Send ( HostCommandType ( HOST_COMMAND_DETACH ) ) ;
2018-07-23 12:25:02 -07:00
}
2018-12-01 15:55:32 -05:00
void InsertHook ( DWORD processId , HookParam hp )
2018-07-23 12:25:02 -07:00
{
2018-12-17 21:03:42 -05:00
processRecordsByIds - > at ( processId ) . Send ( InsertHookCmd ( hp ) ) ;
2018-07-23 12:25:02 -07:00
}
2016-01-06 00:01:17 +09:00
2018-12-01 15:55:32 -05:00
HookParam GetHookParam ( ThreadParam tp )
2018-05-11 16:46:05 -04:00
{
2018-12-17 21:03:42 -05:00
return processRecordsByIds - > at ( tp . processId ) . GetHook ( tp . addr ) . hp ;
2018-05-11 16:46:05 -04:00
}
2016-01-06 00:01:17 +09:00
2019-01-06 02:57:52 -05:00
TextThread * GetThread ( ThreadParam tp )
2018-07-23 12:25:02 -07:00
{
2019-01-06 02:57:52 -05:00
return textThreadsByParams - > at ( tp ) . get ( ) ;
2018-07-23 12:25:02 -07:00
}
2018-05-11 16:46:05 -04:00
2018-12-28 12:14:56 -05:00
void AddConsoleOutput ( std : : wstring text )
{
2019-01-09 22:35:01 -05:00
GetThread ( console ) - > AddSentence ( text ) ;
2018-11-01 15:03:30 -04:00
}
2016-01-06 00:01:17 +09:00
}