mirror of
https://github.com/rapid7/metasploit-payloads
synced 2025-01-08 14:36:22 +01:00
351 lines
9.6 KiB
C
351 lines
9.6 KiB
C
#include "common.h"
|
|
|
|
#ifndef _WIN32
|
|
#include <poll.h>
|
|
#endif
|
|
|
|
typedef struct _WaitableEntry
|
|
{
|
|
Remote * remote;
|
|
#ifdef _WIN32
|
|
HANDLE waitable;
|
|
#else
|
|
int waitable;
|
|
#endif
|
|
EVENT* pause;
|
|
EVENT* resume;
|
|
LPVOID context;
|
|
BOOL running;
|
|
WaitableNotifyRoutine routine;
|
|
WaitableDestroyRoutine destroy;
|
|
} WaitableEntry;
|
|
|
|
/*
|
|
* The list of all currenltly running threads in the scheduler subsystem.
|
|
*/
|
|
LIST * schedulerThreadList = NULL;
|
|
|
|
/*
|
|
* The Remote that is associated with the scheduler subsystem
|
|
*/
|
|
Remote * schedulerRemote = NULL;
|
|
|
|
/*
|
|
* Initialize the scheduler subsystem. Must be called before any calls to scheduler_insert_waitable.
|
|
*/
|
|
DWORD scheduler_initialize( Remote * remote )
|
|
{
|
|
DWORD result = ERROR_SUCCESS;
|
|
|
|
dprintf( "[SCHEDULER] entering scheduler_initialize." );
|
|
|
|
if( remote == NULL )
|
|
return ERROR_INVALID_HANDLE;
|
|
|
|
schedulerThreadList = list_create();
|
|
if( schedulerThreadList == NULL )
|
|
return ERROR_INVALID_HANDLE;
|
|
|
|
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;
|
|
WaitableEntry * entry = NULL;
|
|
|
|
dprintf( "[SCHEDULER] entering scheduler_destroy." );
|
|
|
|
lock_acquire( schedulerThreadList->lock );
|
|
|
|
count = list_count( schedulerThreadList );
|
|
|
|
for( index=0 ; index < count ; index++ )
|
|
{
|
|
thread = (THREAD *)list_get( schedulerThreadList, index );
|
|
if( thread == NULL )
|
|
continue;
|
|
|
|
list_push( jlist, thread );
|
|
|
|
entry = (WaitableEntry *)thread->parameter1;
|
|
|
|
if( !entry->running )
|
|
event_signal( entry->resume );
|
|
|
|
thread_sigterm( thread );
|
|
}
|
|
|
|
lock_release( schedulerThreadList->lock );
|
|
|
|
dprintf( "[SCHEDULER] scheduler_destroy, joining all waitable threads..." );
|
|
|
|
while( TRUE )
|
|
{
|
|
dprintf( "[SCHEDULER] scheduler_destroy, popping off another item from thread list..." );
|
|
|
|
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 entryContext, LPVOID threadContext, WaitableNotifyRoutine routine, WaitableDestroyRoutine destroy )
|
|
{
|
|
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, 0x%08X, 0x%08X )",
|
|
waitable, entryContext, threadContext, routine, destroy );
|
|
|
|
memset( entry, 0, sizeof( WaitableEntry ) );
|
|
|
|
entry->remote = schedulerRemote;
|
|
entry->waitable = waitable;
|
|
entry->destroy = destroy;
|
|
entry->context = entryContext;
|
|
entry->routine = routine;
|
|
entry->pause = event_create();
|
|
entry->resume = event_create();
|
|
|
|
swt = thread_create( scheduler_waitable_thread, entry, threadContext, NULL );
|
|
if( swt != NULL )
|
|
{
|
|
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;
|
|
}
|
|
|
|
/*
|
|
* Signal a waitable object.
|
|
*/
|
|
DWORD scheduler_signal_waitable( HANDLE waitable, SchedularSignal signal )
|
|
{
|
|
DWORD index = 0;
|
|
DWORD count = 0;
|
|
THREAD * thread = NULL;
|
|
WaitableEntry * entry = NULL;
|
|
DWORD result = ERROR_NOT_FOUND;
|
|
|
|
dprintf( "[SCHEDULER] entering scheduler_signal_waitable( 0x%08X )", waitable );
|
|
|
|
if( schedulerThreadList == NULL || !waitable )
|
|
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;
|
|
|
|
if( entry->waitable == waitable )
|
|
{
|
|
dprintf( "[SCHEDULER] scheduler_signal_waitable: signaling waitable = 0x%08X, thread = 0x%08X", waitable, thread );
|
|
if( signal == Pause )
|
|
{
|
|
if( entry->running ) {
|
|
dprintf( "[SCHEDULER] scheduler_signal_waitable: thread running, pausing. waitable = 0x%08X, thread = 0x%08X, handle = 0x%X", waitable, thread, entry->pause->handle );
|
|
event_signal( entry->pause );
|
|
} else {
|
|
dprintf( "[SCHEDULER] scheduler_signal_waitable: thread already paused. waitable = 0x%08X, thread = 0x%08X", waitable, thread );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( !entry->running ) {
|
|
dprintf( "[SCHEDULER] scheduler_signal_waitable: thread paused, resuming. waitable = 0x%08X, thread = 0x%08X, handle = 0x%X", waitable, thread, entry->resume->handle );
|
|
event_signal( entry->resume );
|
|
}
|
|
|
|
if( signal == Stop ) {
|
|
dprintf( "[SCHEDULER] scheduler_signal_waitable: stopping thread. waitable = 0x%08X, thread = 0x%08X, handle = 0x%X", waitable, thread, thread->sigterm->handle );
|
|
thread_sigterm( thread );
|
|
} else {
|
|
dprintf( "[SCHEDULER] scheduler_signal_waitable: thread already running. waitable = 0x%08X, thread = 0x%08X", waitable, thread );
|
|
}
|
|
}
|
|
|
|
result = ERROR_SUCCESS;
|
|
break;
|
|
}
|
|
}
|
|
|
|
lock_release( schedulerThreadList->lock );
|
|
|
|
dprintf( "[SCHEDULER] leaving scheduler_signal_waitable" );
|
|
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* 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 THREADCALL scheduler_waitable_thread( THREAD * thread )
|
|
{
|
|
#ifdef _WIN32
|
|
HANDLE waitableHandles[3] = {0};
|
|
#else
|
|
struct pollfd pollDetail = {0};
|
|
#endif
|
|
|
|
WaitableEntry * entry = NULL;
|
|
DWORD result = 0;
|
|
BOOL terminate = FALSE;
|
|
UINT signalIndex = 0;
|
|
|
|
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 );
|
|
|
|
#ifdef _WIN32
|
|
waitableHandles[0] = thread->sigterm->handle;
|
|
waitableHandles[1] = entry->pause->handle;
|
|
waitableHandles[2] = entry->waitable;
|
|
#else
|
|
pollDetail.fd = entry->waitable;
|
|
pollDetail.events = POLLRDNORM;
|
|
pollDetail.revents = 0;
|
|
#endif
|
|
|
|
dprintf( "[SCHEDULER] entering scheduler_waitable_thread( 0x%08X )", thread );
|
|
|
|
entry->running = TRUE;
|
|
while( !terminate )
|
|
{
|
|
#ifdef _WIN32
|
|
dprintf( "[SCHEDULER] About to wait ( 0x%08X )", thread );
|
|
result = WaitForMultipleObjects( 3, waitableHandles, FALSE, INFINITE );
|
|
dprintf( "[SCHEDULER] Wait returned ( 0x%08X )", thread );
|
|
signalIndex = result - WAIT_OBJECT_0;
|
|
switch( signalIndex )
|
|
{
|
|
case 0:
|
|
dprintf( "[SCHEDULER] scheduler_waitable_thread( 0x%08X ), signaled to terminate...", thread );
|
|
terminate = TRUE;
|
|
break;
|
|
case 1:
|
|
dprintf( "[SCHEDULER] scheduler_waitable_thread( 0x%08X ), signaled to pause...", thread );
|
|
entry->running = FALSE;
|
|
event_poll( entry->resume, INFINITE );
|
|
entry->running = TRUE;
|
|
dprintf( "[SCHEDULER] scheduler_waitable_thread( 0x%08X ), signaled to resume...", thread );
|
|
case 2:
|
|
//dprintf( "[SCHEDULER] scheduler_waitable_thread( 0x%08X ), signaled on waitable...", thread );
|
|
entry->routine( entry->remote, entry->context, thread->parameter2 );
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
#else
|
|
if( event_poll( thread->sigterm, 0 ) ) {
|
|
dprintf( "[SCHEDULER] scheduler_waitable_thread( 0x%08X ), signaled to terminate...", thread );
|
|
terminate = TRUE;
|
|
}
|
|
else if( event_poll( entry->pause, 0 ) ) {
|
|
dprintf( "[SCHEDULER] scheduler_waitable_thread( 0x%08X ), signaled to pause...", thread );
|
|
entry->running = FALSE;
|
|
while( !event_poll( entry->resume, 1000 ) );
|
|
entry->running = TRUE;
|
|
dprintf( "[SCHEDULER] scheduler_waitable_thread( 0x%08X ), signaled to resume...", thread );
|
|
}
|
|
else if( poll( &pollDetail, 1, 100 ) == POLLIN ) {
|
|
//dprintf( "[SCHEDULER] scheduler_waitable_thread( 0x%08X ), signaled on waitable...", thread );
|
|
entry->routine( entry->remote, entry->context, (LPVOID)thread->parameter2 );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
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_signal_waitable for this thread (unlikely but best practice).
|
|
lock_acquire( schedulerThreadList->lock );
|
|
if( list_remove( schedulerThreadList, thread ) )
|
|
{
|
|
if( entry->destroy ) {
|
|
entry->destroy( entry->waitable, entry->context, thread->parameter2 );
|
|
}
|
|
else if( entry->waitable ) {
|
|
dprintf( "[SCHEDULER] scheduler_waitable_thread( 0x%08X ) closing handle 0x%08X", thread, entry->waitable);
|
|
#ifdef _WIN32
|
|
CloseHandle( entry->waitable );
|
|
#else
|
|
close( entry->waitable );
|
|
#endif
|
|
}
|
|
|
|
event_destroy( entry->resume );
|
|
event_destroy( entry->pause );
|
|
thread_destroy( thread );
|
|
free( entry );
|
|
}
|
|
lock_release( schedulerThreadList->lock );
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|