沙箱检测与绕过:反调试、虚拟机检测与父进程伪造实战
字数 2085 2025-12-16 12:17:48
沙箱检测与绕过技术详解:反调试、虚拟机检测与父进程伪造实战
一、前言
当前绝大多数免杀思路主要围绕混淆shellcode展开,通过多层加密可以有效绕过静态检测。然而,动态检测的绕过才是真正的挑战,因为它会对程序行为进行深度分析。动态检测通常在沙箱或虚拟机环境中进行,如果我们能够识别这些运行环境并避免在其中执行恶意代码,就能有效绕过动态检测。
本文系统梳理了主流沙箱平台的共性特征,并围绕六大关键维度展开深入实践:
- 用户名与主机名敏感词检测
- 调试器存在性验证
- 虚拟化痕迹识别(注册表、进程、MAC地址)
- 系统资源异常判断(CPU、内存、启动时间)
- 父进程合法性校验
- 父进程伪造技术
二、常见云沙箱平台
以下为目前主流的云沙箱分析平台:
- 微步沙箱:https://s.threatbook.com/
- VirusTotal:https://www.virustotal.com/
- Any.Run:https://any.run/(可交互式沙箱)
- Joe Sandbox:https://www.joesandbox.com/#windows
- Hybrid Analysis:https://www.hybrid-analysis.com/
- 安恒云沙箱:https://sandbox.dbappsecurity.com.cn/
- 奇安信沙箱:https://sandbox.ti.qianxin.com/sandbox/page
- FreeBuf沙箱:https://sandbox.freebuf.com/
- 360云沙箱:https://ata.360.net/
- 哈勃沙箱:https://habo.qq.com/
三、注册表检测技术
原理分析
通过检查Windows注册表中特定路径下是否存在与虚拟化平台相关的字符串,来判断当前是否运行在虚拟机环境。
关键注册表路径
HKLM\HARDWARE\DEVICETREE\SYSTEMHKLM\HARDWARE\DESCRIPTION\SystemHKLM\SYSTEM\CurrentControlSet\Services\Disk\Enum
实现代码
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 虚拟机相关关键词列表
const char* vm_keywords[] = {
"vbox", "vmware", "virtual", "qemu", "xen",
"hyper-v", "parallels", "vmw", "vbox"
};
const int VM_KEYWORD_COUNT = sizeof(vm_keywords) / sizeof(vm_keywords[0]);
// 宽字符转UTF-8辅助函数
char* wchar_to_utf8(const wchar_t* wstr) {
int size = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL);
if (size <= 0) return NULL;
char* utf8_str = (char*)malloc(size);
if (!utf8_str) return NULL;
WideCharToMultiByte(CP_UTF8, 0, wstr, -1, utf8_str, size, NULL, NULL);
return utf8_str;
}
// 检查字符串是否包含虚拟机关键词
int contains_virt_keyword(const char* str) {
if (!str) return 0;
char lower_str[1024];
strncpy(lower_str, str, sizeof(lower_str) - 1);
lower_str[sizeof(lower_str) - 1] = '\0';
// 转换为小写进行不区分大小写的比较
for (size_t i = 0; lower_str[i]; i++) {
lower_str[i] = (char)tolower((unsigned char)lower_str[i]);
}
for (int i = 0; i < VM_KEYWORD_COUNT; i++) {
if (strstr(lower_str, vm_keywords[i]) != NULL) {
return 1;
}
}
return 0;
}
// 检查指定注册表项
int check_registry_key(HKEY hRoot, const wchar_t* subKey) {
HKEY hKey;
LONG ret = RegOpenKeyExW(hRoot, subKey, 0, KEY_READ, &hKey);
if (ret != ERROR_SUCCESS) {
return 0;
}
// 获取值信息
DWORD maxValueLen = 0, maxDataLen = 0;
RegQueryInfoKeyW(hKey, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
&maxValueLen, &maxDataLen, NULL, NULL);
wchar_t* valueName = (wchar_t*)malloc((maxValueLen + 1) * sizeof(wchar_t));
BYTE* valueData = (BYTE*)malloc(maxDataLen + 1);
if (!valueName || !valueData) {
free(valueName);
free(valueData);
RegCloseKey(hKey);
return 0;
}
// 枚举值
DWORD index = 0;
while (1) {
DWORD valueNameSize = maxValueLen + 1;
DWORD dataSize = maxDataLen;
DWORD type;
ret = RegEnumValueW(hKey, index, valueName, &valueNameSize,
NULL, &type, valueData, &dataSize);
if (ret != ERROR_SUCCESS) break;
// 检查值名称
char* nameStr = wchar_to_utf8(valueName);
if (nameStr) {
if (contains_virt_keyword(nameStr)) {
printf("[+] Found virtualization keyword in value name: %s\n", nameStr);
free(nameStr);
free(valueName);
free(valueData);
RegCloseKey(hKey);
return 1;
}
free(nameStr);
}
// 检查值数据(字符串类型)
if (type == REG_SZ || type == REG_EXPAND_SZ || type == REG_MULTI_SZ) {
char* dataStr = wchar_to_utf8((wchar_t*)valueData);
if (dataStr) {
if (contains_virt_keyword(dataStr)) {
printf("[+] Found virtualization keyword in value data: %s\n", dataStr);
free(dataStr);
free(valueName);
free(valueData);
RegCloseKey(hKey);
return 1;
}
free(dataStr);
}
}
index++;
}
// 枚举子键
DWORD maxSubKeyLen = 0;
RegQueryInfoKeyW(hKey, NULL, NULL, NULL, NULL, &maxSubKeyLen, NULL,
NULL, NULL, NULL, NULL, NULL);
wchar_t* subKeyName = (wchar_t*)malloc((maxSubKeyLen + 1) * sizeof(wchar_t));
if (subKeyName) {
DWORD subIndex = 0;
while (1) {
DWORD size = maxSubKeyLen + 1;
ret = RegEnumKeyExW(hKey, subIndex, subKeyName, &size, NULL, NULL, NULL, NULL);
if (ret != ERROR_SUCCESS) break;
char* keyNameStr = wchar_to_utf8(subKeyName);
if (keyNameStr) {
if (contains_virt_keyword(keyNameStr)) {
printf("[+] Found virtualization keyword in subkey name: %s\n", keyNameStr);
free(keyNameStr);
free(subKeyName);
free(valueName);
free(valueData);
RegCloseKey(hKey);
return 1;
}
free(keyNameStr);
}
subIndex++;
}
free(subKeyName);
}
free(valueName);
free(valueData);
RegCloseKey(hKey);
return 0;
}
int main() {
const wchar_t* paths[] = {
L"HARDWARE\\DEVICETREE\\SYSTEM",
L"HARDWARE\\DESCRIPTION\\System",
L"SYSTEM\\CurrentControlSet\\Services\\Disk\\Enum"
};
int pathCount = sizeof(paths) / sizeof(paths[0]);
printf("[*] Checking registry for virtualization artifacts...\n");
int detected = 0;
for (int i = 0; i < pathCount; i++) {
printf("[*] Checking: HKLM\\%ls\n", paths[i]);
if (check_registry_key(HKEY_LOCAL_MACHINE, paths[i])) {
detected = 1;
break;
}
}
if (detected) {
printf("\n[!] Virtual machine detected via registry!\n");
} else {
printf("\n[*] No virtualization keywords found in registry.\n");
}
return 0;
}
四、虚拟机进程检测
原理分析
通过枚举当前系统中运行的进程列表,检查是否存在与主流虚拟化平台相关的特定进程名。
常见虚拟机进程
- "vboxservice", "vboxtray" (VirtualBox)
- "vmwaretray", "vmwareuser", "vmacthlp" (VMware)
- "vmsrvc", "vmusrvc" (VMware)
- "prl_cc", "prl_tools" (Parallels)
实现代码
#include <windows.h>
#include <tlhelp32.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
const char* vm_processes[] = {
"vboxservice", "vboxtray", "vmwaretray", "vmwareuser",
"vmacthlp", "vmsrvc", "vmusrvc", "prl_cc", "prl_tools"
};
const int VM_PROCESS_COUNT = sizeof(vm_processes) / sizeof(vm_processes[0]);
// 字符串转小写
void to_lower_str(char* str) {
for (size_t i = 0; str[i]; i++) {
str[i] = (char)tolower((unsigned char)str[i]);
}
}
// 检查是否为虚拟机进程
int is_vm_process(const char* process_name) {
if (!process_name) return 0;
char lower_name[MAX_PATH];
strncpy(lower_name, process_name, MAX_PATH - 1);
lower_name[MAX_PATH - 1] = '\0';
to_lower_str(lower_name);
// 去掉".exe"后缀
size_t len = strlen(lower_name);
if (len > 4 && strcmp(lower_name + len - 4, ".exe") == 0) {
lower_name[len - 4] = '\0';
}
for (int i = 0; i < VM_PROCESS_COUNT; i++) {
if (strcmp(lower_name, vm_processes[i]) == 0) {
return 1;
}
}
return 0;
}
// 检测虚拟机进程
int detect_vm_processes() {
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnapshot == INVALID_HANDLE_VALUE) {
return 0;
}
PROCESSENTRY32 pe;
pe.dwSize = sizeof(PROCESSENTRY32);
int detected = 0;
if (Process32First(hSnapshot, &pe)) {
do {
if (is_vm_process(pe.szExeFile)) {
printf("[+] Found VM process: %s (PID: %d)\n", pe.szExeFile, pe.th32ProcessID);
detected = 1;
}
} while (Process32Next(hSnapshot, &pe));
}
CloseHandle(hSnapshot);
return detected;
}
int main() {
printf("[*] Checking for VM processes...\n");
if (detect_vm_processes()) {
printf("\n[!] Virtual machine detected via process analysis!\n");
} else {
printf("\n[*] No VM processes found.\n");
}
return 0;
}
五、虚拟网卡MAC地址检测
原理分析
通过检查本机网络接口的MAC地址前缀(OUI,组织唯一标识符)是否属于已知的虚拟化厂商。
常见虚拟化MAC地址前缀
- VMware: "00:05:69", "00:0C:29", "00:1C:14", "00:50:56"
- VirtualBox: "08:00:27", "0A:00:27"
- Parallels: "00:1C:42"
- Hyper-V: "00:03:FF"
实现代码
#define _WIN32_WINNT 0x0600
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winsock2.h>
#include <iphlpapi.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 虚拟机MAC地址前缀(OUI)
const unsigned char vm_ouis[][3] = {
{0x00, 0x05, 0x69}, // VMware
{0x00, 0x0C, 0x29}, // VMware
{0x00, 0x1C, 0x14}, // VMware
{0x00, 0x50, 0x56}, // VMware
{0x08, 0x00, 0x27}, // VirtualBox
{0x0A, 0x00, 0x27}, // VirtualBox
{0x00, 0x1C, 0x42}, // Parallels
{0x00, 0x03, 0xFF} // Hyper-V
};
const int VM_OUI_COUNT = sizeof(vm_ouis) / sizeof(vm_ouis[0]);
// 检查MAC地址是否为虚拟机
int is_vm_mac(const unsigned char mac[6]) {
if (!mac) return 0;
for (int i = 0; i < VM_OUI_COUNT; i++) {
if (mac[0] == vm_ouis[i][0] &&
mac[1] == vm_ouis[i][1] &&
mac[2] == vm_ouis[i][2]) {
return 1;
}
}
return 0;
}
// 检测虚拟网卡
int detect_vm_network() {
ULONG outBufLen = 0;
DWORD ret = GetAdaptersAddresses(AF_UNSPEC, 0, NULL, NULL, &outBufLen);
if (ret != ERROR_BUFFER_OVERFLOW) {
return 0;
}
PIP_ADAPTER_ADDRESSES pAddresses = (PIP_ADAPTER_ADDRESSES)malloc(outBufLen);
if (!pAddresses) {
return 0;
}
ret = GetAdaptersAddresses(AF_UNSPEC, 0, NULL, pAddresses, &outBufLen);
if (ret != NO_ERROR) {
free(pAddresses);
return 0;
}
int detected = 0;
PIP_ADAPTER_ADDRESSES pCurrAddresses = pAddresses;
while (pCurrAddresses) {
if (pCurrAddresses->PhysicalAddressLength == 6) {
unsigned char mac[6];
memcpy(mac, pCurrAddresses->PhysicalAddress, 6);
if (is_vm_mac(mac)) {
printf("[+] Found VM network adapter: MAC=%02X:%02X:%02X:%02X:%02X:%02X\n",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
detected = 1;
}
}
pCurrAddresses = pCurrAddresses->Next;
}
free(pAddresses);
return detected;
}
int main() {
printf("[*] Checking network adapters for VM artifacts...\n");
if (detect_vm_network()) {
printf("\n[!] Virtual machine detected via network adapter analysis!\n");
} else {
printf("\n[*] No VM network adapters found.\n");
}
return 0;
}
六、系统资源检测
原理分析
真实用户设备通常具备至少2个CPU核心和≥2GB内存,而许多自动化分析沙箱为了节省资源,会使用1核CPU + 1GB内存的虚拟机配置。
实现代码
#define WIN32_LEAN_AND_MEAN
#define _WIN32_WINNT 0x0501
#include <windows.h>
#include <stdio.h>
#include <stdint.h>
int main() {
printf("[*] Checking system resources for sandbox/virtual machine artifacts...\n");
// 检测CPU核心数
SYSTEM_INFO sysInfo;
GetSystemInfo(&sysInfo);
DWORD cpuCores = sysInfo.dwNumberOfProcessors;
printf("[*] CPU logical cores: %lu\n", cpuCores);
// 检测物理内存
MEMORYSTATUSEX memInfo;
memInfo.dwLength = sizeof(memInfo);
GlobalMemoryStatusEx(&memInfo);
uint64_t totalMem = memInfo.ullTotalPhys; // 字节单位
uint64_t memMB = totalMem / (1024 * 1024);
printf("[*] Physical memory: %llu MB (%llu bytes)\n", memMB, totalMem);
// 判断是否符合沙箱特征
int isLowCpu = (cpuCores < 2);
int isLowMem = (totalMem < 2147483648ULL); // 2GB
if (isLowCpu || isLowMem) {
printf("\n[!] Suspicious environment detected:\n");
if (isLowCpu) {
printf(" - CPU cores < 2\n");
}
if (isLowMem) {
printf(" - Memory < 2 GB\n");
}
printf("\n[!] Likely running in a sandbox or low-resource VM!\n");
return 1;
} else {
printf("\n[*] System resources appear normal.\n");
return 0;
}
}
七、系统启动时间检测
原理分析
真实用户系统通常已运行较长时间,而大多数沙箱在启动样本前才刚开机,系统运行时间往往<120秒。
实现代码
#define WIN32_LEAN_AND_MEAN
#define _WIN32_WINNT 0x0600
#include <windows.h>
#include <stdio.h>
int main() {
printf("[*] Checking system uptime for sandbox artifacts...\n");
// 获取系统启动以来的毫秒数
ULONGLONG uptime_ms = GetTickCount64();
ULONGLONG uptime_sec = uptime_ms / 1000;
printf("[*] System uptime: %llu seconds (%llu ms)\n", uptime_sec, uptime_ms);
if (uptime_sec < 120) {
printf("\n[!] Suspicious: System uptime < 120 seconds!\n");
printf("[!] Likely running in a freshly started sandbox or VM.\n");
return 1;
} else {
printf("\n[*] System uptime appears normal (>= 120 seconds).\n");
return 0;
}
}
八、用户名检测
原理分析
许多自动化恶意软件分析平台为了便于识别,会使用包含"sandbox"、"test"、"malware"等关键词的用户名或主机名。
实现代码
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
// 敏感关键词列表(小写)
const char* suspicious_keywords[] = {
"sandbox", "malware", "analy", "virus", "test"
};
const int KEYWORD_COUNT = sizeof(suspicious_keywords) / sizeof(suspicious_keywords[0]);
// 字符串转小写
void to_lower_str(char* str) {
for (size_t i = 0; str[i]; i++) {
str[i] = (char)tolower((unsigned char)str[i]);
}
}
// 检查用户名是否包含敏感词
int is_suspicious_username(const char* username) {
if (!username || !*username) return 0;
char lower_name[256];
strncpy(lower_name, username, sizeof(lower_name) - 1);
lower_name[sizeof(lower_name) - 1] = '\0';
to_lower_str(lower_name);
for (int i = 0; i < KEYWORD_COUNT; i++) {
if (strstr(lower_name, suspicious_keywords[i]) != NULL) {
return 1;
}
}
return 0;
}
int main() {
char username[256];
DWORD size = sizeof(username);
if (GetUserNameA(username, &size)) {
printf("[*] Current username: %s\n", username);
if (is_suspicious_username(username)) {
printf("\n[!] Suspicious username detected!\n");
printf("[!] Likely running in analysis environment.\n");
return 1;
} else {
printf("\n[*] Username appears normal.\n");
return 0;
}
} else {
printf("[-] Failed to get username\n");
return 0;
}
}
九、反调试技术
原理分析
检测当前进程是否被调试是恶意软件和受保护程序中非常常见的技术。Windows提供了多种API和系统机制可用于实现这一目的。
检测方法
IsDebuggerPresent()- 检查PEB中的BeingDebugged标志CheckRemoteDebuggerPresent()- 检查是否有远程调试器附加NtQueryInformationProcess+ProcessDebugPort- 查询进程调试端口OutputDebugString异常检测 - 若存在调试器,不会触发异常CloseHandle无效句柄触发异常 - 调试器会拦截异常
实现代码
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdio.h>
#include <winternl.h>
// 手动声明NtQueryInformationProcess
typedef LONG (NTAPI *PNtQueryInformationProcess)(
HANDLE ProcessHandle,
ULONG ProcessInformationClass,
PVOID ProcessInformation,
ULONG ProcessInformationLength,
PULONG ReturnLength
);
// 方法1: IsDebuggerPresent
int check_debugger_IsDebuggerPresent() {
return IsDebuggerPresent();
}
// 方法2: CheckRemoteDebuggerPresent
int check_debugger_CheckRemoteDebuggerPresent() {
BOOL isRemote = FALSE;
CheckRemoteDebuggerPresent(GetCurrentProcess(), &isRemote);
return isRemote;
}
// 方法3: NtQueryInformationProcess
int check_debugger_NtQueryInfo() {
HMODULE hNtdll = GetModuleHandleA("ntdll.dll");
if (!hNtdll) return 0;
PNtQueryInformationProcess NtQueryInfo = (PNtQueryInformationProcess)
GetProcAddress(hNtdll, "NtQueryInformationProcess");
if (!NtQueryInfo) return 0;
DWORD debugPort = 0;
NTSTATUS status = NtQueryInfo(
GetCurrentProcess(),
7, // ProcessDebugPort
&debugPort,
sizeof(debugPort),
NULL
);
return (status == 0 && debugPort != 0);
}
// 方法4: OutputDebugString异常检测
int check_debugger_OutputDebugString() {
__try {
OutputDebugStringA("Test");
return 0; // 无异常 = 有调试器
}
__except (EXCEPTION_EXECUTE_HANDLER) {
return 1; // 有异常 = 无调试器
}
}
// 方法5: CloseHandle异常检测
int check_debugger_CloseHandle() {
__try {
CloseHandle((HANDLE)0x12345678); // 无效句柄
return 1; // 无异常 = 有调试器
}
__except (EXCEPTION_EXECUTE_HANDLER) {
return 0; // 有异常 = 无调试器
}
}
int main() {
printf("[*] Starting anti-debug checks...\n");
int debug_detected = 0;
if (check_debugger_IsDebuggerPresent()) {
printf("[!] Debugger detected via IsDebuggerPresent\n");
debug_detected = 1;
}
if (check_debugger_CheckRemoteDebuggerPresent()) {
printf("[!] Debugger detected via CheckRemoteDebuggerPresent\n");
debug_detected = 1;
}
if (check_debugger_NtQueryInfo()) {
printf("[!] Debugger detected via NtQueryInformationProcess\n");
debug_detected = 1;
}
if (!check_debugger_OutputDebugString()) {
printf("[!] Debugger detected via OutputDebugString\n");
debug_detected = 1;
}
if (check_debugger_CloseHandle()) {
printf("[!] Debugger detected via CloseHandle\n");
debug_detected = 1;
}
if (debug_detected) {
printf("\n[!] Debugger detected! Exiting...\n");
return 1;
} else {
printf("\n[*] No debugger detected. Continuing...\n");
return 0;
}
}
十、父进程检测技术
原理分析
在正常用户场景下,双击运行.exe文件时,其父进程是explorer.exe;而在沙箱、自动化脚本等非交互式环境中,父进程通常是其他进程(如python.exe、analyzer.exe、cmd.exe等)。
检测流程
- 获取自身进程PID
- 遍历进程快照,读取父PID
- 根据父PID查找父进程名
- 与explorer.exe比较
获取当前进程的父进程PID
DWORD get_current_process_parent_pid() {
DWORD currentPid = GetCurrentProcessId(); // 获取当前进程PID
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnapshot == INVALID_HANDLE_VALUE) {
return 0;
}
PROCESSENTRY32 pe = {0};
pe.dwSize = sizeof(PROCESSENTRY32);
DWORD parentPid = 0;
if (Process32First(hSnapshot, &pe)) {
do {
if (pe.th32ProcessID == currentPid) {
parentPid = pe.th32ParentProcessID;
break;
}
} while (Process32Next(hSnapshot, &pe));
}
CloseHandle(hSnapshot);
return parentPid;
}
根据PID获取进程名
bool get_process_name_by_pid(DWORD pid, char* name_out, size_t name_size) {
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32