描述
通常在沙箱中运行恶意软件会持续很短的时间,因为沙箱中存储了数千个样本,所以仿真时间很少会超过3-5分钟。因此,恶意软件可以通过在执行恶意代码之前执行长时间的延迟来规避沙箱的检测。
1.延迟执行
恶意软件在执行时延迟,从而规避沙箱或杀毒软件的检测。
以Sleep函数为例:
以Sleep函数为例:
#include <windows.h>
#include <stdio.h>
// 定义恶意代码函数
void MaliciousCode()
{
// 这里插入恶意代码,比如显示一个弹窗消息
MessageBoxA(NULL, "You have been infected by a malware!", "Malware Alert", MB_OK | MB_ICONWARNING);
}
int main()
{
// 设置延迟时间为10秒
DWORD dwDelay = 10000;
// 调用Sleep函数进入休眠状态
Sleep(dwDelay);
// 唤醒后执行恶意代码
MaliciousCode();
return 0;
}
以SleepEx函数为例:
#include <windows.h>
#include <stdio.h>
// 定义恶意代码函数
void MaliciousCode()
{
// 这里插入恶意代码,比如显示一个弹窗消息
MessageBoxA(NULL, "You have been infected by a malware!", "Malware Alert", MB_OK | MB_ICONWARNING);
}
int main()
{
// 设置延迟时间为10秒
DWORD dwDelay = 10000;
// 调用SleepEx函数进入休眠状态,并设置可警报参数为TRUE
SleepEx(dwDelay, TRUE);
// 唤醒后执行恶意代码
MaliciousCode();
return 0;
}
以NtDelayExecution函数为例:
#include <windows.h>
#include <stdio.h>
void MaliciousCode()
{
// 这里插入恶意代码,比如显示一个弹窗消息
MessageBoxA(NULL, "You have been infected by a malware!", "Malware Alert", MB_OK | MB_ICONWARNING);
}
typedef NTSTATUS(WINAPI* NTDELAYEXECUTION)(BOOL, PLARGE_INTEGER);
int main()
{
// 加载ntdll.dll模块
HMODULE hNtdll = LoadLibraryA("ntdll.dll");
// 获取NtDelayExecution函数的地址
NTDELAYEXECUTION NtDelayExecution = (NTDELAYEXECUTION)GetProcAddress(hNtdll, "NtDelayExecution");
// 设置延迟时间为10秒
LARGE_INTEGER liDelay;
liDelay.QuadPart = -100000000;
// 调用NtDelayExecution函数进入休眠状态
NtDelayExecution(FALSE, &liDelay);
// 唤醒后执行恶意代码
MaliciousCode();
return 0;
}
以WaitForSingleObject函数为例:
#include <windows.h>
#include <iostream>
using namespace std;
int main()
{
// 创建一个手动重置事件
HANDLE hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (hEvent == NULL)
{
cout << "CreateEvent error: " << GetLastError() << endl;
return 1;
}
// 等待事件或超时
DWORD dwRet = WaitForSingleObject(hEvent, 10000); // 超时时间为 10 秒
if (dwRet == WAIT_FAILED)
{
cout << "WaitForSingleObject error: " << GetLastError() << endl;
return 1;
}
else if (dwRet == WAIT_TIMEOUT)
{
cout << "WaitForSingleObject timeout" << endl;
}
else if (dwRet == WAIT_OBJECT_0)
{
cout << "WaitForSingleObject signaled" << endl;
}
// 关闭事件的句柄
if (!CloseHandle(hEvent))
{
cout << "CloseHandle error: " << GetLastError() << endl;
return 1;
}
// 这里插入恶意代码,比如显示一个弹窗消息
MessageBoxA(NULL, "You have been infected by a malware!", "Malware Alert", MB_OK | MB_ICONWARNING);
return 0;
}
以WaitForSingleObjectEx函数为例:
#include <windows.h>
#include <stdio.h>
int main()
{
// 创建一个未命名的事件对象
HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (hEvent == NULL)
{
printf("CreateEvent failed (%d)\n", GetLastError());
return 1;
}
// 使用 WaitForSingleObjectEx 函数等待事件对象的信号状态或超时
// 设置 bAlertable 参数为 TRUE,以便在等待期间执行 APC
DWORD dwResult = WaitForSingleObjectEx(hEvent, 10000, TRUE);
switch (dwResult)
{
// 事件对象变为信号状态
case WAIT_OBJECT_0:
printf("Event object signaled\n");
break;
// I/O 完成例程或 APC 排队到线程
case WAIT_IO_COMPLETION:
printf("I/O completion routine or APC queued\n");
break;
// 超时时间到达
case WAIT_TIMEOUT:
printf("Wait timed out\n");
break;
// 函数失败
case WAIT_FAILED:
printf("Wait failed (%d)\n", GetLastError());
break;
}
// 关闭事件对象的句柄
CloseHandle(hEvent);
// 这里插入恶意代码,比如显示一个弹窗消息
MessageBoxA(NULL, "You have been infected by a malware!", "Malware Alert", MB_OK | MB_ICONWARNING);
return 0;
}
以NtWaitForSingleObject函数为例:
#include <windows.h>
#include <winternl.h>
#include <stdio.h>
#include <ntstatus.h>
// 定义 NtWaitForSingleObject 函数的类型
typedef NTSTATUS(NTAPI* NtWaitForSingleObject_t)(
HANDLE Handle,
BOOLEAN Alertable,
PLARGE_INTEGER Timeout
);
int main()
{
// 获取 NTDLL.DLL 的句柄
HMODULE hNtdll = GetModuleHandle(L"ntdll.dll");
if (hNtdll == NULL)
{
printf("GetModuleHandle failed (%d)\n", GetLastError());
return 1;
}
// 获取 NtWaitForSingleObject 函数的地址
NtWaitForSingleObject_t NtWaitForSingleObject = (NtWaitForSingleObject_t)GetProcAddress(hNtdll, "NtWaitForSingleObject");
if (NtWaitForSingleObject == NULL)
{
printf("GetProcAddress failed (%d)\n", GetLastError());
return 1;
}
// 创建一个未命名的事件对象
HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (hEvent == NULL)
{
printf("CreateEvent failed (%d)\n", GetLastError());
return 1;
}
// 使用 NtWaitForSingleObject 函数等待事件对象的信号状态或超时
// 设置 Alertable 参数为 FALSE,以便在等待期间不执行 APC
LARGE_INTEGER Timeout;
Timeout.QuadPart = -100000000; // 10 秒,单位为 100 纳秒
NTSTATUS Status = NtWaitForSingleObject(hEvent, FALSE, &Timeout);
switch (Status)
{
// 事件对象变为信号状态
case STATUS_SUCCESS:
printf("Event object signaled\n");
break;
// 超时时间到达
case STATUS_TIMEOUT:
printf("Wait timed out\n");
break;
// 函数失败
default:
printf("Wait failed (%x)\n", Status);
break;
}
// 关闭事件对象的句柄
CloseHandle(hEvent);
// 这里插入恶意代码,比如显示一个弹窗消息
MessageBoxA(NULL, "You have been infected by a malware!", "Malware Alert", MB_OK | MB_ICONWARNING);
return 0;
}
以WaitForMultipleObjects函数为例:
#include <windows.h>
#include <stdio.h>
int main()
{
// 创建两个未命名的事件对象
HANDLE hEvent1 = CreateEvent(NULL, FALSE, FALSE, NULL);
HANDLE hEvent2 = CreateEvent(NULL, FALSE, FALSE, NULL);
if (hEvent1 == NULL || hEvent2 == NULL)
{
printf("CreateEvent failed (%d)\n", GetLastError());
return 1;
}
// 创建一个对象句柄数组
HANDLE hArray[2];
hArray[0] = hEvent1;
hArray[1] = hEvent2;
// 使用 WaitForMultipleObjects 函数等待两个事件对象的信号状态或超时
// 设置 bWaitAll 参数为 FALSE,以便在任一对象发出信号时返回
DWORD dwResult = WaitForMultipleObjects(2, hArray, FALSE, 10000);
switch (dwResult)
{
// 事件对象 1 变为信号状态
case WAIT_OBJECT_0:
printf("Event object 1 signaled\n");
break;
// 事件对象 2 变为信号状态
case WAIT_OBJECT_0 + 1:
printf("Event object 2 signaled\n");
break;
// 超时时间到达
case WAIT_TIMEOUT:
printf("Wait timed out\n");
break;
// 函数失败
case WAIT_FAILED:
printf("Wait failed (%d)\n", GetLastError());
break;
}
// 关闭事件对象的句柄
CloseHandle(hEvent1);
CloseHandle(hEvent2);
// 这里插入恶意代码,比如显示一个弹窗消息
MessageBoxA(NULL, "You have been infected by a malware!", "Malware Alert", MB_OK | MB_ICONWARNING);
return 0;
}
以WaitForMultipleObjectsEx函数为例:
#include <windows.h>
#include <stdio.h>
int main()
{
// 创建两个未命名的事件对象
HANDLE hEvent1 = CreateEvent(NULL, FALSE, FALSE, NULL);
HANDLE hEvent2 = CreateEvent(NULL, FALSE, FALSE, NULL);
if (hEvent1 == NULL || hEvent2 == NULL)
{
printf("CreateEvent failed (%d)\n", GetLastError());
return 1;
}
// 创建一个对象句柄数组
HANDLE hArray[2];
hArray[0] = hEvent1;
hArray[1] = hEvent2;
// 使用 WaitForMultipleObjectsEx 函数等待两个事件对象的信号状态、I/O 完成例程或 APC 排队或超时
// 设置 bWaitAll 参数为 FALSE,以便在任一对象发出信号时返回
// 设置 bAlertable 参数为 TRUE,以便在等待期间执行 APC
DWORD dwResult = WaitForMultipleObjectsEx(2, hArray, FALSE, 10000, TRUE);
switch (dwResult)
{
// 事件对象 1 变为信号状态
case WAIT_OBJECT_0:
printf("Event object 1 signaled\n");
break;
// 事件对象 2 变为信号状态
case WAIT_OBJECT_0 + 1:
printf("Event object 2 signaled\n");
break;
// 等待由一个或多个用户模式 APC 结束
case WAIT_IO_COMPLETION:
printf("User-mode APC completed\n");
break;
// 超时时间到达
case WAIT_TIMEOUT:
printf("Wait timed out\n");
break;
// 函数失败
case WAIT_FAILED:
printf("Wait failed (%d)\n", GetLastError());
break;
}
// 关闭事件对象的句柄
CloseHandle(hEvent1);
CloseHandle(hEvent2);
// 这里插入恶意代码,比如显示一个弹窗消息
MessageBoxA(NULL, "You have been infected by a malware!", "Malware Alert", MB_OK | MB_ICONWARNING);
return 0;
}
以NtWaitForMultipleObjects函数为例:
#include <windows.h>
#include <winternl.h>
#include <stdio.h>
// 定义 NtWaitForMultipleObjects 函数的原型
typedef NTSTATUS(WINAPI* NtWaitForMultipleObjects_t)(
ULONG Count,
HANDLE Handles[],
BOOLEAN WaitAll,
BOOLEAN Alertable,
PLARGE_INTEGER Timeout
);
int main()
{
// 获取 NtWaitForMultipleObjects 函数的地址
NtWaitForMultipleObjects_t NtWaitForMultipleObjects = (NtWaitForMultipleObjects_t)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtWaitForMultipleObjects");
if (NtWaitForMultipleObjects == NULL)
{
printf("GetProcAddress failed (%d)\n", GetLastError());
return 1;
}
// 创建三个未命名的事件对象
HANDLE hEvent1 = CreateEvent(NULL, FALSE, FALSE, NULL);
HANDLE hEvent2 = CreateEvent(NULL, FALSE, FALSE, NULL);
HANDLE hEvent3 = CreateEvent(NULL, FALSE, FALSE, NULL);
if (hEvent1 == NULL || hEvent2 == NULL || hEvent3 == NULL)
{
printf("CreateEvent failed (%d)\n", GetLastError());
return 1;
}
// 创建一个对象句柄数组
HANDLE hArray[3];
hArray[0] = hEvent1;
hArray[1] = hEvent2;
hArray[2] = hEvent3;
// 使用 NtWaitForMultipleObjects 函数等待三个事件对象的信号状态、I/O 完成例程或 APC 排队或超时
// 设置 WaitAll 参数为 FALSE,以便在任一对象发出信号时返回
// 设置 Alertable 参数为 TRUE,以便在等待期间执行 APC
// 设置 Timeout 参数为 10000 毫秒(负数表示相对时间)
LARGE_INTEGER timeout;
timeout.QuadPart = -10000 * 10000; // 10000 毫秒转换为 100 纳秒单位
NTSTATUS status = NtWaitForMultipleObjects(3, hArray, FALSE, TRUE, &timeout);
switch (status)
{
// 事件对象 1 变为信号状态
case STATUS_WAIT_0:
printf("Event object 1 signaled\n");
break;
// 事件对象 2 变为信号状态
case STATUS_WAIT_0 + 1:
printf("Event object 2 signaled\n");
break;
// 事件对象 3 变为信号状态
case STATUS_WAIT_0 + 2:
printf("Event object 3 signaled\n");
break;
// 等待由一个或多个用户模式 APC 结束
case STATUS_USER_APC:
printf("User-mode APC completed\n");
break;
// 超时时间到达
case STATUS_TIMEOUT:
printf("Wait timed out\n");
break;
// 函数失败
default:
printf("NtWaitForMultipleObjects failed (%x)\n", status);
break;
}
// 关闭事件对象的句柄
CloseHandle(hEvent1);
CloseHandle(hEvent2);
CloseHandle(hEvent3);
// 这里插入恶意代码,比如显示一个弹窗消息
MessageBoxA(NULL, "You have been infected by a malware!", "Malware Alert", MB_OK | MB_ICONWARNING);
return 0;
}
以SetTimer函数为例:
#include <windows.h>
#include <stdio.h>
// 定义 TimerProc 回调函数的原型
VOID CALLBACK TimerProc(
HWND hwnd, // handle of window for timer messages
UINT uMsg, // WM_TIMER message
UINT_PTR idEvent, // timer identifier
DWORD dwTime // current system time
);
// 定义一个全局变量,用来存储事件对象的句柄
HANDLE g_hEvent = NULL;
int main()
{
// 使用 CreateEventEx 函数创建一个私有事件对象,用于同步主线程和计时器线程
// 设置 lpEventAttributes 参数为 NULL,表示使用默认的安全属性
// 设置 dwFlags 参数为 CREATE_EVENT_INITIAL_SET | CREATE_EVENT_MANUAL_RESET,表示创建一个手动重置的事件对象,并且初始状态为有信号
// 设置 lpName 参数为 NULL,表示创建一个未命名的事件对象
g_hEvent = CreateEventEx(NULL, NULL, CREATE_EVENT_INITIAL_SET | CREATE_EVENT_MANUAL_RESET, EVENT_ALL_ACCESS);
if (g_hEvent == NULL)
{
fprintf(stderr, "CreateEventEx failed (%d)\n", GetLastError());
return 1;
}
// 使用 SetTimer 函数创建一个计时器,指定 TimerProc 回调函数
// 设置 hWnd 参数为 NULL,表示不与任何窗口关联
// 设置 nIDEvent 参数为 0,表示生成一个新的计时器 ID
// 设置 uElapse 参数为 10000 毫秒,表示超时值为 10 秒
// 设置 lpTimerFunc 参数为 TimerProc,表示指定回调函数
UINT_PTR timerID = SetTimer(NULL, 0, 10000, TimerProc);
if (timerID == 0)
{
fprintf(stderr, "SetTimer failed (%d)\n", GetLastError());
return 1;
}
// 使用 SetUserObjectInformationW 函数将 UOI_TIMERPROC_EXCEPTION_SUPPRESSION 标志设置为 false
USEROBJECTFLAGS uof;
uof.fInherit = FALSE;
uof.fReserved = FALSE;
uof.dwFlags = UOI_TIMERPROC_EXCEPTION_SUPPRESSION;
if (!SetUserObjectInformationW(GetCurrentProcess(), UOI_FLAGS, &uof, sizeof(uof)))
{
fprintf(stderr, "SetUserObjectInformationW failed (%d)\n", GetLastError());
return 1;
}
// 等待事件对象被设置为有信号状态,表示计时器已经触发
WaitForSingleObject(g_hEvent, INFINITE);
return 0;
}
// 定义 TimerProc 回调函数的实现
VOID CALLBACK TimerProc(
HWND hwnd, // handle of window for timer messages
UINT uMsg, // WM_TIMER message
UINT_PTR idEvent, // timer identifier
DWORD dwTime // current system time
)
{
// 使用 KillTimer 函数销毁计时器
KillTimer(hwnd, idEvent);
// 设置事件对象为有信号状态,通知主线程继续执行
SetEvent(g_hEvent);
// 这里插入恶意代码,比如显示一个弹窗消息
MessageBox(NULL, L"You have been infected by a malware!", L"Malware Alert", MB_OK | MB_ICONWARNING);
}
以SetWaitableTimer函数设置延迟超时,然后循环调用 Sleep 函数直到计时器超时,最后测量经过的时间,如果不同则不运行为例:
#include <windows.h>
#include <stdio.h>
#include <cmath>
// 定义 TimerProc 完成例程的原型
VOID CALLBACK TimerProc(
LPVOID lpArg, // data value
DWORD dwTimerLowValue, // timer low value
DWORD dwTimerHighValue // timer high value
);
int main()
{
// 获取当前系统时间
SYSTEMTIME st;
GetSystemTime(&st);
// 将系统时间转换为 FILETIME 结构
FILETIME ft;
SystemTimeToFileTime(&st, &ft);
// 将 FILETIME 结构转换为 LARGE_INTEGER 结构
LARGE_INTEGER li;
li.LowPart = ft.dwLowDateTime;
li.HighPart = ft.dwHighDateTime;
// 在当前系统时间的基础上增加 10 秒(以 100 纳秒为单位)
li.QuadPart += 10 * 10000000;
// 创建一个未命名的可等待计时器对象
HANDLE hTimer = CreateWaitableTimer(NULL, FALSE, NULL);
if (hTimer == NULL)
{
printf("CreateWaitableTimer failed (%d)\n", GetLastError());
return 1;
}
// 使用 SetWaitableTimer 函数激活计时器,指定 TimerProc 完成例程
// 设置 hTimer 参数为计时器对象的句柄
// 设置 lpDueTime 参数为增加了 10 秒的系统时间,表示绝对时间
// 设置 lPeriod 参数为 0,表示只触发一次计时器
// 设置 pfnCompletionRoutine 参数为 TimerProc,表示指定完成例程
// 设置 lpArgToCompletionRoutine 参数为 NULL,表示不传递任何数据给完成例程
// 设置 fResume 参数为 FALSE,表示不唤醒系统
BOOL result = SetWaitableTimer(hTimer, &li, 0, TimerProc, NULL, FALSE);
if (result == FALSE)
{
printf("SetWaitableTimer failed (%d)\n", GetLastError());
return 1;
}
// 获取开始循环调用 sleep 函数之前的系统时间(以毫秒为单位)
DWORD startTime = GetTickCount();
// 循环调用 sleep 函数直到计时器超时
while (TRUE)
{
Sleep(1000);
if (WaitForSingleObject(hTimer, 0) == WAIT_OBJECT_0)
{
break;
}
printf("Sleeping...\n");
}
// 获取结束循环调用 sleep 函数之后的系统时间(以毫秒为单位)
DWORD endTime = GetTickCount();
// 计算经过的时间(以毫秒为单位)
DWORD elapsedTime = endTime - startTime;
// 关闭计时器对象的句柄
CloseHandle(hTimer);
// 如果经过的时间与预期的时间(10 秒)相差不大,则执行恶意软件的代码
if (std::abs(static_cast<int>(elapsedTime) - static_cast<int>(10000)) < 1000)
{
// 这里插入恶意代码,比如显示一个弹窗消息
MessageBoxA(NULL, "You have been infected by a malware!", "Malware Alert", MB_OK | MB_ICONWARNING);
}
}
// 定义 TimerProc 完成例程的实现
VOID CALLBACK TimerProc(
LPVOID lpArg, // data value
DWORD dwTimerLowValue, // timer low value
DWORD dwTimerHighValue) // timer high value
{
// 什么也不做,只是让计时器超时
}
以CreateTimerQueueTimer函数为例:
#include <windows.h>
#include <stdio.h>
// 定义 WaitOrTimerCallback 回调函数的原型
VOID CALLBACK WaitOrTimerCallback(
PVOID lpParameter, // optional parameter
BOOLEAN TimerOrWaitFired // indicates whether the wait timed out
);
int main()
{
// 创建一个未命名的计时器队列对象
HANDLE hTimerQueue = CreateTimerQueue();
if (hTimerQueue == NULL)
{
printf("CreateTimerQueue failed (%d)\n", GetLastError());
return 1;
}
BOOL result = CreateTimerQueueTimer(NULL, hTimerQueue, WaitOrTimerCallback, NULL, 10000, 0, WT_EXECUTEONLYONCE);
if (result == FALSE)
{
printf("CreateTimerQueueTimer failed (%d)\n", GetLastError());
return 1;
}
// 等待用户按下任意键
printf("Press any key to exit\n");
getchar();
// 删除计时器队列对象及其所有关联的计时器
DeleteTimerQueueEx(hTimerQueue, NULL);
return 0;
}
// 定义 WaitOrTimerCallback 回调函数的实现
VOID CALLBACK WaitOrTimerCallback(
PVOID lpParameter, // optional parameter
BOOLEAN TimerOrWaitFired // indicates whether the wait timed out
)
{
// 这里插入恶意代码,比如显示一个弹窗消息
MessageBoxA(NULL, "You have been infected by a malware!", "Malware Alert", MB_OK | MB_ICONWARNING);
}
以CreateWaitableTimer函数为例:
#include <windows.h>
#include <stdio.h>
// 回调函数
VOID CALLBACK TimerAPCProc(
LPVOID lpArgToCompletionRoutine, // 数据值
DWORD dwTimerLowValue, // 计时器低位值
DWORD dwTimerHighValue // 计时器高位值
)
{
printf("Hello from TimerAPCProc.\n");
}
int main(void)
{
HANDLE hTimer = NULL;
LARGE_INTEGER liDueTime;
// 创建一个无名的手动重置计时器
hTimer = CreateWaitableTimer(NULL, TRUE, NULL);
if (NULL == hTimer)
{
printf("CreateWaitableTimer failed (%d)\n", GetLastError());
return 1;
}
// 设置相对时间为 -10 秒(即从现在起 10 秒后)
liDueTime.QuadPart = -100000000LL;
// 设置计时器,并指定回调函数和参数
if (!SetWaitableTimer(hTimer, &liDueTime, 0, TimerAPCProc, NULL, FALSE))
{
printf("SetWaitableTimer failed (%d)\n", GetLastError());
return 2;
}
// 等待计时器信号
if (WaitForSingleObject(hTimer, INFINITE) != WAIT_OBJECT_0)
printf("WaitForSingleObject failed (%d)\n", GetLastError());
else
// 这里插入恶意代码,比如显示一个弹窗消息
MessageBoxA(NULL, "You have been infected by a malware!", "Malware Alert", MB_OK | MB_ICONWARNING);
// 关闭句柄
CloseHandle(hTimer);
return 0;
}
以timeSetEvent函数为例:
#include <windows.h>
#include <stdio.h>
#include <mmsystem.h>
#pragma comment(lib, "winmm.lib")
// 定义 TIMECALLBACK 回调函数的原型
void CALLBACK TimeProc(
UINT uTimerID, // timer identifier
UINT uMsg, // not used
DWORD_PTR dwUser, // user data
DWORD_PTR dw1, // not used
DWORD_PTR dw2 // not used
);
int main()
{
// 设置定时器事件的间隔为 1000 毫秒
UINT uDelay = 1000;
// 设置定时器事件的精度为 10 毫秒
UINT uResolution = 10;
// 设置定时器事件的类型为 TIME_PERIODIC,表示周期性地触发事件
UINT fuEvent = TIME_PERIODIC;
// 调用 timeSetEvent 函数启动定时器事件,指定 TimeProc 回调函数
// 返回值为定时器标识符,如果为 NULL 表示失败
MMRESULT timerID = timeSetEvent(uDelay, uResolution, TimeProc, NULL, fuEvent);
if (timerID == NULL)
{
printf("timeSetEvent failed\n");
return 1;
}
// 等待用户按下任意键
printf("Press any key to exit\n");
getchar();
// 调用 timeKillEvent 函数停止定时器事件,需要传入定时器标识符
MMRESULT result = timeKillEvent(timerID);
if (result != TIMERR_NOERROR)
{
printf("timeKillEvent failed\n");
return 1;
}
return 0;
}
// 定义 TimeProc 回调函数的实现
void CALLBACK TimeProc(
UINT uTimerID, // timer identifier
UINT uMsg, // not used
DWORD_PTR dwUser, // user data
DWORD_PTR dw1, // not used
DWORD_PTR dw2 // not used
)
{
// 这里插入恶意代码,比如显示一个弹窗消息
MessageBoxA(NULL, "You have been infected by a malware!", "Malware Alert", MB_OK | MB_ICONWARNING);
}
以通过IcmpSendEcho函数发送ICMP 请求到指定的目标地址,并根据响应的状态或延迟时间来判断是否处于沙箱环境中为例:
#include <winsock2.h>
#include <iphlpapi.h>
#include <icmpapi.h>
#include <stdio.h>
#include <ws2tcpip.h>
#pragma comment (lib,"ws2_32.lib")
#pragma comment (lib,"iphlpapi.lib")
// 定义一个阈值,表示回显响应的最大延迟时间(以毫秒为单位)
#define THRESHOLD 1000
// 定义一个超时值,表示等待回显响应的时间(以毫秒为单位)
#define TIMEOUT 10000
int main(int argc, char** argv)
{
// 声明和初始化变量
HANDLE hIcmpFile;
unsigned long ipaddr = INADDR_NONE;
DWORD dwRetVal = 0;
char SendData[32] = "Data Buffer";
LPVOID ReplyBuffer = NULL;
DWORD ReplySize = 0;
IN_ADDR addr; // 声明一个 IN_ADDR 结构体
// 验证参数
if (argc != 2)
{
printf("usage: %s IP address\n", argv[0]);
return 1;
}
// 将 IP 地址转换为无符号长整型
int result = inet_pton(AF_INET, argv[1], &addr);
if (result == 1) // 检查返回值
{
ipaddr = addr.S_un.S_addr; // 获取 IP 地址
}
else if (result == 0)
{
printf("Invalid IP address: %s\n", argv[1]);
return 1;
}
else
{
printf("Error in inet_pton: %ld\n", WSAGetLastError());
return 1;
}
// 调用 IcmpCreateFile 函数创建 ICMP 句柄
hIcmpFile = IcmpCreateFile();
if (hIcmpFile == INVALID_HANDLE_VALUE)
{
printf("\tUnable to open handle.\n");
printf("IcmpCreatefile returned error: %ld\n", GetLastError());
return 1;
}
// 分配回复缓冲区的内存空间
ReplySize = sizeof(ICMP_ECHO_REPLY) + sizeof(SendData);
ReplyBuffer = (VOID*)malloc(ReplySize);
if (ReplyBuffer == NULL)
{
printf("\tUnable to allocate memory\n");
return 1;
}
// 调用 IcmpSendEcho 函数发送 ICMP 回显请求,并指定较大的超时值
dwRetVal = IcmpSendEcho(hIcmpFile, ipaddr, SendData, sizeof(SendData),
NULL, ReplyBuffer, ReplySize, TIMEOUT);
// 检查返回值
if (dwRetVal != 0)
{
// 获取第一个 ICMP_ECHO_REPLY 结构体的指针
PICMP_ECHO_REPLY pEchoReply = (PICMP_ECHO_REPLY)ReplyBuffer;
// 检查回显响应的状态和延迟时间
if (pEchoReply->Status == IP_SUCCESS && pEchoReply->RoundTripTime < THRESHOLD)
{
// 这里插入恶意代码,比如显示一个弹窗消息
MessageBoxA(NULL, "You have been infected by a malware!", "Malware Alert", MB_OK | MB_ICONWARNING);
}
else
{
// 在这里退出程序或执行无害的代码
printf("Malware code skipped\n");
}
}
else
{
printf("\tCall to IcmpSendEcho failed.\n");
printf("\tIcmpSendEcho returned error: %ld\n", GetLastError());
return 1;
}
return 0;
}
以使用select函数通过设置较大的超时数来进行基于时间的规避为例:
#include <winsock2.h>
#include <stdio.h>
#include <ws2tcpip.h>
#pragma comment (lib,"ws2_32.lib")
#pragma comment (lib,"iphlpapi.lib")
// 定义一个超时值,表示等待套接字状态变化的时间(以毫秒为单位)
#define TIMEOUT 10000
int main(int argc, char** argv)
{
// 声明和初始化变量
WSADATA wsaData;
SOCKET sock;
struct sockaddr_in server;
fd_set readfds;
struct timeval tv;
int result;
// 初始化 Winsock
result = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (result != 0)
{
printf("WSAStartup failed: %d\n", result);
return 1;
}
// 创建一个套接字
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock == INVALID_SOCKET)
{
printf("socket failed: %d\n", WSAGetLastError());
WSACleanup();
return 1;
}
// 连接到一个服务器(例如 www.example.com 的 IP 地址)
server.sin_family = AF_INET;
inet_pton(AF_INET, "93.184.216.34", &server.sin_addr.s_addr);
server.sin_port = htons(80);
result = connect(sock, (struct sockaddr*)&server, sizeof(server));
if (result == SOCKET_ERROR)
{
printf("connect failed: %d\n", WSAGetLastError());
closesocket(sock);
WSACleanup();
return 1;
}
// 将套接字添加到读取集合中
FD_ZERO(&readfds);
FD_SET(sock, &readfds);
// 设置超时值
tv.tv_sec = TIMEOUT / 1000; // 秒数
tv.tv_usec = (TIMEOUT % 1000) * 1000; // 微秒数
// 调用 select 函数等待套接字状态变化或超时
result = select(0, &readfds, NULL, NULL, &tv);
// 检查返回值
if (result == SOCKET_ERROR)
{
printf("select failed: %d\n", WSAGetLastError());
closesocket(sock);
WSACleanup();
return 1;
}
if (result == 0)
{
// 超时发生,说明可能在沙箱环境中
// 在这里退出程序或执行无害的代码
printf("select timeout\n");
printf("Malware code skipped\n");
}
if (result > 0)
{
// 套接字状态发生变化,说明可能在正常环境中
printf("select success\n");
// 这里插入恶意代码,比如显示一个弹窗消息
MessageBoxA(NULL, "You have been infected by a malware!", "Malware Alert", MB_OK | MB_ICONWARNING);
}
}
2.使用任务计划程序延迟执行
$tm = (get-date).AddMinutes(10).ToString("HH:mm")
$action = New-ScheduledTaskAction -Execute "some_malicious_app.exe"
$trigger = New-ScheduledTaskTrigger -Once -At $tm
Register-ScheduledTask TaskName -Action $action -Trigger $trigger
3.重新启动
该技术背后的想法是,沙箱在执行恶意样本期间不会重新启动虚拟机。恶意软件可以先建立持久性并静默退出,然后在系统重新启动后才会执行恶意代码。
4.在特定日期运行
恶意软件样本可能会检查当前日期并仅在特定日期执行恶意操作。例如,Sazoora 恶意软件中使用了此技术,该恶意软件检查当前日期并验证该天是否是给定月份的 16 号、17 号或 18 号。
5.使用不同方法的并行延迟
并行执行不同类型的延迟并测量经过的时间以跳过睡眠检测。
DWORD StartingTick, TimeElapsedMs;
LARGE_INTEGER DueTime;
HANDLE hTimer = NULL;
TIMER_BASIC_INFORMATION TimerInformation;
ULONG ReturnLength;
hTimer = CreateWaitableTimer(NULL, TRUE, NULL);
DueTime.QuadPart = Timeout * (-10000LL);
StartingTick = GetTickCount();
SetWaitableTimer(hTimer, &DueTime, 0, NULL, NULL, 0);
do
{
Sleep(Timeout/10);
NtQueryTimer(hTimer, TimerBasicInformation, &TimerInformation, sizeof(TIMER_BASIC_INFORMATION), &ReturnLength);
} while (!TimerInformation.TimerState);
CloseHandle(hTimer);
TimeElapsedMs = GetTickCount() - StartingTick;
printf("Requested delay: %d, elapsed time: %d\n", Timeout, TimeElapsedMs);
if (abs((LONG)(TimeElapsedMs - Timeout)) > Timeout / 2)
printf("Sleep-skipping DETECTED!\n");
6.使用不同的方法测量时间间隔
恶意软件可以通过使用不同的方法测量经过的时间间隔来判断沙箱中是否启动了跳过睡眠检测,如果启动了跳过睡眠检测则证明当前处于虚拟环境中,那么就可以不运行恶意代码。
以GetTickCount64函数先获取系统启动以来的时间,然后通过 Sleep 函数睡眠一段时间,再将睡眠后的时间与其比较,如果时间差异较大则不运行为例:
以GetTickCount64函数先获取系统启动以来的时间,然后通过 Sleep 函数睡眠一段时间,再将睡眠后的时间与其比较,如果时间差异较大则不运行为例:
#include <windows.h>
#include <stdio.h>
// 定义一个超时值,表示等待套接字状态变化的时间(以毫秒为单位)
#define TIMEOUT 10000
int main(int argc, char** argv)
{
// 声明和初始化变量
ULONGLONG start_time, end_time, diff_time;
// 调用 GetTickCount64 函数获取系统启动以来的时间
start_time = GetTickCount64();
// 调用 sleep 函数睡眠一段时间
Sleep(5000);
// 再次调用 GetTickCount64 函数获取系统启动以来的时间
end_time = GetTickCount64();
// 计算两次获取的时间差
diff_time = end_time - start_time;
// 比较时间差是否小于超时值
if (diff_time < TIMEOUT)
{
// 这里插入恶意代码,比如显示一个弹窗消息
MessageBoxA(NULL, "You have been infected by a malware!", "Malware Alert", MB_OK | MB_ICONWARNING);
}
else
{
// 在这里退出程序或执行无害的代码
printf("Malware code skipped\n");
}
return 0;
}
以使用Sleep函数睡眠一段时间,然后使QueryPerformanceFrequency函数获取操作前后的时间,如果时间差异较大则不运行为例:
#include <windows.h>
#include <stdio.h>
// 定义一个超时值,表示等待套接字状态变化的时间(以毫秒为单位)
#define TIMEOUT 10000
int main(int argc, char** argv)
{
// 声明和初始化变量
LARGE_INTEGER freq, start_time, end_time, diff_time;
// 调用 QueryPerformanceFrequency 函数获取性能计数器的频率
QueryPerformanceFrequency(&freq);
// 调用 QueryPerformanceCounter 函数获取操作前的时间
QueryPerformanceCounter(&start_time);
// 调用 sleep 函数睡眠一段时间
Sleep(5000);
// 调用 QueryPerformanceCounter 函数获取操作后的时间
QueryPerformanceCounter(&end_time);
// 计算两次获取的时间差(以毫秒为单位)
diff_time.QuadPart = (end_time.QuadPart - start_time.QuadPart) * 1000 / freq.QuadPart;
// 比较时间差是否小于超时值
if (diff_time.QuadPart < TIMEOUT)
{
// 这里插入恶意代码,比如显示一个弹窗消息
MessageBoxA(NULL, "You have been infected by a malware!", "Malware Alert", MB_OK | MB_ICONWARNING);
}
else
{
// 在这里退出程序或执行无害的代码
printf("Malware code skipped\n");
}
return 0;
}
以使用 Sleep 函数睡眠一段时间,然后通过QueryPerformanceCounter函数获取高精度的时间戳,并与预设的值进行比较,如果时间差异较大则不运行为例:
#include <windows.h>
#include <stdio.h>
// 定义一个超时值,表示等待套接字状态变化的时间(以毫秒为单位)
#define TIMEOUT 10000
int main(int argc, char** argv)
{
// 声明和初始化变量
LARGE_INTEGER freq, start_time, end_time, diff_time;
// 调用 QueryPerformanceFrequency 函数获取性能计数器的频率
QueryPerformanceFrequency(&freq);
// 调用 QueryPerformanceCounter 函数获取操作前的时间
QueryPerformanceCounter(&start_time);
// 调用 sleep 函数睡眠一段时间
Sleep(5000);
// 调用 QueryPerformanceCounter 函数获取操作后的时间
QueryPerformanceCounter(&end_time);
// 计算两次获取的时间差(以毫秒为单位)
diff_time.QuadPart = (end_time.QuadPart - start_time.QuadPart) * 1000 / freq.QuadPart;
// 比较时间差是否小于超时值
if (diff_time.QuadPart < TIMEOUT)
{
// 这里插入恶意代码,比如显示一个弹窗消息
MessageBoxA(NULL, "You have been infected by a malware!", "Malware Alert", MB_OK | MB_ICONWARNING);
}
else
{
// 在这里退出程序或执行无害的代码
printf("Malware code skipped\n");
}
return 0;
}
以使用 NtQuerySystemInformation 函数获取系统中运行的特定进程的信息,包括进程名和 PID,并与预设的值进行比较,如果时间差异较大,则不运行为例:
#include <windows.h>
#include <winternl.h>
#include <ntstatus.h>
// 定义 NtQuerySystemInformation 函数类型
typedef NTSTATUS(WINAPI* NTQUERYSYSTEMINFORMATION) (
SYSTEM_INFORMATION_CLASS SystemInformationClass,
PVOID SystemInformation,
ULONG SystemInformationLength,
PULONG ReturnLength
);
// 定义要检测的进程名和 PID
#define TARGET_PROCESS_NAME L"notepad.exe"
#define TARGET_PROCESS_ID 1234
// 定义要比较的时间差异(毫秒)
#define TIME_DIFFERENCE 1000
int main()
{
// 声明变量
NTQUERYSYSTEMINFORMATION NtQuerySystemInformation; // 函数指针
PSYSTEM_PROCESS_INFORMATION pspi, p; // 进程信息结构指针
DWORD dwSize, dwReturnLength; // 缓冲区大小和返回长度
BOOL bFound; // 是否找到目标进程
LARGE_INTEGER liCurrentTime, liProcessTime; // 当前时间和进程时间
LONGLONG llTimeDifference; // 时间差异
// 获取 NtQuerySystemInformation 函数地址
NtQuerySystemInformation = (NTQUERYSYSTEMINFORMATION)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtQuerySystemInformation");
// 分配缓冲区大小为 1MB
dwSize = 1024 * 1024;
// 循环调用 NtQuerySystemInformation 直到成功或失败
do {
// 分配缓冲区
pspi = (PSYSTEM_PROCESS_INFORMATION)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwSize);
if (pspi == NULL) {
// 分配失败,退出程序
return -1;
}
// 调用 NtQuerySystemInformation 获取系统进程信息
if (NtQuerySystemInformation(SystemProcessInformation, pspi, dwSize, &dwReturnLength) == STATUS_SUCCESS) {
// 调用成功,跳出循环
break;
}
// 调用失败,释放缓冲区
HeapFree(GetProcessHeap(), 0, pspi);
// 如果返回长度大于缓冲区大小,说明缓冲区太小,需要重新分配
if (dwReturnLength > dwSize) {
dwSize = dwReturnLength;
continue;
}
// 如果返回长度小于或等于缓冲区大小,说明发生了其他错误,退出程序
return -1;
} while (TRUE);
// 遍历进程信息结构数组,查找目标进程
p = pspi;
bFound = FALSE;
while (p->NextEntryOffset) {
// 比较进程名和 PID
if (p->ImageName.Buffer && _wcsicmp(p->ImageName.Buffer, TARGET_PROCESS_NAME) == 0 && p->ProcessId == (HANDLE)TARGET_PROCESS_ID) {
// 找到目标进程,设置标志为真
bFound = TRUE;
break;
}
// 移动到下一个进程信息结构
p = (PSYSTEM_PROCESS_INFORMATION)((LPBYTE)p + p->NextEntryOffset);
}
// 如果找到目标进程,比较时间差异
if (bFound) {
// 获取当前时间
GetSystemTimeAsFileTime((LPFILETIME)&liCurrentTime);
// 获取进程时间(用户模式和内核模式的总和)
liProcessTime.QuadPart = p->UserTime.QuadPart + p->KernelTime.QuadPart;
// 计算时间差异(毫秒)
llTimeDifference = (liCurrentTime.QuadPart - liProcessTime.QuadPart) / 10000;
// 如果时间差异大于预设值,不运行恶意代码
if (llTimeDifference > TIME_DIFFERENCE) {
MessageBoxA(NULL, "Malware code skipped", "Info", MB_OK);
return 0;
}
}
// 释放缓冲区
HeapFree(GetProcessHeap(), 0, pspi);
// 运行恶意代码,比如显示一个弹窗消息
MessageBoxA(NULL, "You have been infected by a malware!", "Malware Alert", MB_OK | MB_ICONWARNING);
return 0;
}