反沙箱(sanbox)多种技术-上篇
一、通过硬件规格实现反虚拟化
一般来说,虚拟化环境无法完全访问主机的硬件。恶意软件可以利用对硬件缺乏完全访问权限来检测它是否在虚拟环境或沙箱中执行。请记住,无法保证完全准确,因为机器可能只是以较低的硬件规格运行。将检查的硬件规格如下:
CPU — 检查处理器数量是否少于 2 个。
RAM — 检查是否少于 2 GB。
先前安装的 USB 数量 - 检查 USB 数量是否少于 2 个。
CPU 检查
可以使用 GetSystemInfo WinAPI 进行 CPU 检查。此函数返回一个 SYSTEM_INFO 结构,其中包含有关系统的信息,包括处理器数量。
SYSTEM_INFO SysInfo = { 0 };
GetSystemInfo(&SysInfo);
if (SysInfo.dwNumberOfProcessors < 2){
possibly a virtualized environment
}
RAM 检查
请注意,这 2 * 1073741824 是 2 GB 的字节大小。
BOOL CheckRamNum() {
MEMORYSTATUSEX MemStatus;
MemStatus.dwLength = sizeof(MEMORYSTATUSEX);
if (!GlobalMemoryStatusEx(&MemStatus)) {
printf("\n\t[!] GlobalMemoryStatusEx Failed With Error : %d \n", GetLastError());
}
if ((DWORD)MemStatus.ullTotalPhys <= (DWORD)(2 * 1073741824)) {
return TRUE;
}
}
检查之前安装的 USB
HKEY hKey = NULL;
DWORD dwUsbNumber = NULL;
DWORD dwRegErr = NULL;
if ((dwRegErr = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SYSTEM\\ControlSet001\\Enum\\USBSTOR", NULL, KEY_READ, &hKey)) != ERROR_SUCCESS) {
printf("\n\t[!] RegOpenKeyExA Failed With Error : %d | 0x%0.8X \n", dwRegErr, dwRegErr);
}
if ((dwRegErr = RegQueryInfoKeyA(hKey, NULL, NULL, NULL, &dwUsbNumber, NULL, NULL, NULL, NULL, NULL, NULL, NULL)) != ERROR_SUCCESS) {
printf("\n\t[!] RegQueryInfoKeyA Failed With Error : %d | 0x%0.8X \n", dwRegErr, dwRegErr);
}
// Less than 2 USBs previously mounted
if (dwUsbNumber < 2) {
// possibly a virtualized environment
}
完整代码
前面的代码片段被合并成一个函数 如果检测到虚拟化环境,IsVenvByHardwareCheck此函数将返回 。TRUE
BOOL IsVenvByHardwareCheck() {
SYSTEM_INFO SysInfo = { 0 };
MEMORYSTATUSEX MemStatus;
MemStatus.dwLength = sizeof(MEMORYSTATUSEX);
HKEY hKey = NULL;
DWORD dwUsbNumber = NULL;
DWORD dwRegErr = NULL;
// CPU CHECK
GetSystemInfo(&SysInfo);
// Less than 2 processors
if (SysInfo.dwNumberOfProcessors < 2) {
return TRUE;
}
// RAM CHECK
if (!GlobalMemoryStatusEx(&MemStatus)) {
printf("\n\t[!] GlobalMemoryStatusEx Failed With Error : %d \n", GetLastError());
return FALSE;
}
// Less than 2 gb of ram
if ((DWORD)MemStatus.ullTotalPhys < (DWORD)(2 * 1073741824)) {
return TRUE;
}
// NUMBER OF USBs PREVIOUSLY MOUNTED
if ((dwRegErr = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SYSTEM\\ControlSet001\\Enum\\USBSTOR", NULL, KEY_READ, &hKey)) != ERROR_SUCCESS) {
printf("\n\t[!] RegOpenKeyExA Failed With Error : %d | 0x%0.8X \n", dwRegErr, dwRegErr);
return FALSE;
}
if ((dwRegErr = RegQueryInfoKeyA(hKey, NULL, NULL, NULL, &dwUsbNumber, NULL, NULL, NULL, NULL, NULL, NULL, NULL)) != ERROR_SUCCESS) {
printf("\n\t[!] RegQueryInfoKeyA Failed With Error : %d | 0x%0.8X \n", dwRegErr, dwRegErr);
return FALSE;
}
// Less than 2 usbs previously mounted
if (dwUsbNumber < 2) {
return TRUE;
}
RegCloseKey(hKey);
return FALSE;
}
二、 通过机器解析实现反虚拟化
在沙盒环境中,机器的分辨率和显示属性通常会设置为一个标准化的、一致的值,与真实世界的机器的分辨率和显示属性可能会有所不同。因此,可以将分辨率较低的机器作为虚拟化环境的指标。
EnumDisplayMonitors 函数要求为其检测到的每个显示器执行一个回调函数,在此回调函数中, 必须调用GetMonitorInfoW WinAPI。此函数检索显示器的分辨率
获取的信息以MONITORINFO 结构的形式 返回 GetMonitorInfoW,如下所示。
typedef struct tagMONITORINFO {
DWORD cbSize; // The size of the structure
RECT rcMonitor; // Display monitor rectangle, expressed in virtual-screen coordinates
RECT rcWork; // Work area rectangle of the display monitor, expressed in virtual-screen coordinates
DWORD dwFlags; // Represents attributes of the display monito
} MONITORINFO, *LPMONITORINFO;
该 rcMonitor 成员包含所需的信息。该成员也是一个 RECT类型的结构 ,通过其左上角和右下角的 X 和 Y 坐标定义一个矩形。检索结构的值后 RECT ,进行一些计算来确定显示器的实际坐标:
该 CheckMachineResolution 函数使用所描述的过程,通过执行回调来计算机器的分辨率 ResolutionCallback 。
#include <iostream>
#include <windows.h>
// The callback function called whenever 'EnumDisplayMonitors' detects an display
BOOL CALLBACK ResolutionCallback(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lpRect, LPARAM ldata) {
int X = 0,Y = 0;
MONITORINFO MI;
MI.cbSize = sizeof(MONITORINFO);
if (!GetMonitorInfoW(hMonitor, &MI)) {
printf("\n\t[!] GetMonitorInfoW Failed With Error : %d \n", GetLastError());
return FALSE;
}
// Calculating the X coordinates of the desplay
X = MI.rcMonitor.right - MI.rcMonitor.left;
// Calculating the Y coordinates of the desplay
Y = MI.rcMonitor.top - MI.rcMonitor.bottom;
// If numbers are in negative value, reverse them
if (X < 0)
X = -X;
if (Y < 0)
Y = -Y;
if ((X != 1920 && X != 2560 && X != 1440) || (Y != 1080 && Y != 1200 && Y != 1600 && Y != 900))
*((BOOL*)ldata) = TRUE; // sandbox is detected
return TRUE;
}
BOOL CheckMachineResolution() {
BOOL SANDBOX = FALSE;
// SANDBOX will be set to TRUE by 'EnumDisplayMonitors' if a sandbox is detected
EnumDisplayMonitors(NULL, NULL, (MONITORENUMPROC)ResolutionCallback, (LPARAM)(&SANDBOX));
return SANDBOX;
}
int main()
{
BOOL isSandbox = CheckMachineResolution();
if (isSandbox) {
printf("Sandbox environment detected!\n");
}
else {
printf("No sandbox detected.\n");
}
return 0;
}
三、通过文件名进行反虚拟化
沙盒通常会重命名文件作为分类的一种方法(例如将其重命名为其 MD5 哈希值)。此过程通常会导致文件名任意,包含字母和数字的混合。
下面显示的函数 ExeDigitsInNameCheck 用于计算当前文件名的位数。它使用 GetModuleFileNameA 获取文件名(包括路径),然后使用 PathFindFileNameA 将文件名与路径分开。
最后, isdigit 函数用于判断文件名中的字符是否为数字。如果文件名中的数字超过 3 位,则将 ExeDigitsInNameCheck 假定它在沙盒中并返回 TRUE
BOOL ExeDigitsInNameCheck() {
CHAR Path [MAX_PATH * 3];
CHAR cName [MAX_PATH];
DWORD dwNumberOfDigits = NULL;
// Getting the current filename (with the full path)
if (!GetModuleFileNameA(NULL, Path, MAX_PATH * 3)) {
printf("\n\t[!] GetModuleFileNameA Failed With Error : %d \n", GetLastError());
return FALSE;
}
// Prevent a buffer overflow - getting the filename from the full path
if (lstrlenA(PathFindFileNameA(Path)) < MAX_PATH)
lstrcpyA(cName, PathFindFileNameA(Path));
// Counting number of digits
for (int i = 0; i < lstrlenA(cName); i++){
if (isdigit(cName[i]))
dwNumberOfDigits++;
}
// Max digits allowed: 3
if (dwNumberOfDigits > 3){
return TRUE;
}
return FALSE;
}
四、通过正在运行的进程数量进行反虚拟化
检测虚拟化环境的另一种方法是检查系统上运行的进程数。沙盒通常不会安装太多应用程序,因此运行的进程较少。与之前的方法类似,这不是保证系统是沙盒的灵丹妙药。Windows 系统至少应该有 60-70 个进程在运行。
BOOL CheckMachineProcesses() {
DWORD adwProcesses [1024];
DWORD dwReturnLen = NULL,
DWORD dwNmbrOfPids = NULL;
if (!EnumProcesses(adwProcesses, sizeof(adwProcesses), &dwReturnLen)) {
printf("\n\t[!] EnumProcesses Failed With Error : %d \n", GetLastError());
return FALSE;
}
dwNmbrOfPids = dwReturnLen / sizeof(DWORD);
// If less than 50 process, it's possibly a sandbox
if (dwNmbrOfPids < 50)
return TRUE;
return FALSE;
}
五、通过用户交互实现反虚拟化
沙盒通常在无头环境中运行,这意味着没有显示器或外围设备,例如键盘和鼠标。无头环境通常也是自动化的,由脚本或其他工具触发。缺乏用户交互可能是沙盒环境的一个指标。例如,恶意软件可以检查环境是否在一段时间内没有收到任何鼠标点击或按键。
SetWindowsHookExW 和 CallNextHookEx WinAPI 用于跟踪鼠标点击。下面的函数中应用了相同的技术。 MouseClicksLogger如果它在 20 秒内没有收到超过 5 次鼠标点击,那么它将假定它处于沙盒环境中。
// ConsoleApplication70.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <windows.h>
// Monitor mouse clicks for 20 seconds
#define MONITOR_TIME 20000 // Global hook handle variable
HHOOK g_hMouseHook = NULL;
// Global mouse clicks counter
DWORD g_dwMouseClicks = NULL;
// The callback function that will be executed whenever the user clicked a mouse button
LRESULT CALLBACK HookEvent(int nCode, WPARAM wParam, LPARAM lParam) {
// WM_RBUTTONDOWN : "Right Mouse Click"
// WM_LBUTTONDOWN : "Left Mouse Click"
// WM_MBUTTONDOWN : "Middle Mouse Click"
if (wParam == WM_LBUTTONDOWN || wParam == WM_RBUTTONDOWN || wParam == WM_MBUTTONDOWN) {
printf("[+] Mouse Click Recorded \n");
g_dwMouseClicks++;
}
return CallNextHookEx(g_hMouseHook, nCode, wParam, lParam);
}
BOOL MouseClicksLogger() {
MSG Msg = { 0 };
// Installing hook
g_hMouseHook = SetWindowsHookExW(
WH_MOUSE_LL,
(HOOKPROC)HookEvent,
NULL,
NULL
);
if (!g_hMouseHook) {
printf("[!] SetWindowsHookExW Failed With Error : %d \n", GetLastError());
}
// Process unhandled events
while (GetMessageW(&Msg, NULL, NULL, NULL)) {
DefWindowProcW(Msg.hwnd, Msg.message, Msg.wParam, Msg.lParam);
}
return TRUE;
}
int main() {
HANDLE hThread = NULL;
DWORD dwThreadId = NULL;
// running the hooking function in a seperate thread for 'MONITOR_TIME' ms
hThread = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)MouseClicksLogger, NULL, NULL, &dwThreadId);
if (hThread) {
printf("\t\t<<>> Thread %d Is Created To Monitor Mouse Clicks For %d Seconds <<>>\n\n", dwThreadId, (MONITOR_TIME / 1000));
WaitForSingleObject(hThread, MONITOR_TIME);
}
// unhooking
if (g_hMouseHook && !UnhookWindowsHookEx(g_hMouseHook)) {
printf("[!] UnhookWindowsHookEx Failed With Error : %d \n", GetLastError());
}
// the test
printf("[i] Monitored User's Mouse Clicks : %d ... ", g_dwMouseClicks);
// if less than 5 clicks - its a sandbox
if (g_dwMouseClicks > 5)
printf("[+] Passed The Test \n");
else
printf("[-] Posssibly A Virtual Environment \n");
printf("[#] Press <Enter> To Quit ... ");
getchar();
return 0;
}
本文来自博客园,作者:aoaoaoao,转载请注明原文链接:https://www.cnblogs.com/websecyw/p/18710217
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异