ڼС
梦回起点
做你害怕做的事,你会发现:不过如此
本站基于WordPress—主题by 设计窝
冀ICP备15003737号
梦回起点
Copyright © 2015-2024 All rights reserved.

虚拟机和沙箱检测及绕过的方法

沙箱检测

硬件检测

包括:
1.CPU核心数检测

//检查处理器核心数
BOOL CheckNumberOfProcessor()
{
    SYSTEM_INFO sysInfo;
    GetSystemInfo(&sysInfo);
    if (sysInfo.dwNumberOfProcessors <= 2) {
        return FALSE;
    }

    return TRUE;
}

2.硬盘空间检测
2024年了,主流PC上最小硬盘也在120+,如果系统盘只有40G(部分云服务器可能是40G),或者是更小,显然不正常,所以可以通过检测硬盘的大小来判断是不是沙箱环境。
3.检测Mac地址
用途很少,但也算一个方法,Mac地址一般是按规则分配给各个厂商的,可以通过判断Mac地址来确定程序是否在沙箱或虚拟机环境运行。
4.检测物理内存

//检查内存大小,
//如果小于2G,那么返回FALSE
//否则返回TRUE
BOOL CheckMemorySize()
{
    _MEMORYSTATUSEX stMem;
    stMem.dwLength = sizeof(stMem);
    GlobalMemoryStatusEx(&stMem);
    if (stMem.ullTotalPhys < (1024ULL * 1024 * 1024 * 2))
    {
        return FALSE;
    }

    return TRUE;
}

环境检测

1.进程数量检测

#include <windows.h>
#include <tlhelp32.h>
#include <iostream>

int GetProcessCount() {
    int processCount = 0;
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_PROCESS, 0);

    if (hSnapshot == INVALID_HANDLE_VALUE) {
        std::cerr << "无法创建进程快照。" << std::endl;
        return -1;
    }

    PROCESSENTRY32 processEntry;
    processEntry.dwSize = sizeof(PROCESSENTRY32);

    if (Process32First(hSnapshot, &processEntry)) {
        do {
            processCount++;
        } while (Process32Next(hSnapshot, &processEntry));
    } else {
        std::cerr << "无法获取进程信息。" << std::endl;
    }

    CloseHandle(hSnapshot);
    return processCount;
}

int main() {
    int processCount = GetProcessCount();
    if (processCount >= 0) {
        std::cout << "当前系统进程数量: " << processCount << std::endl;
    }
    return 0;
}

2.特定进程检测

#include <windows.h>
#include <tlhelp32.h>
#include <iostream>

bool IsDwmProcessRunning() {
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_PROCESS, 0);
    if (hSnapshot == INVALID_HANDLE_VALUE) {
        std::cerr << "无法创建进程快照。" << std::endl;
        return false;
    }

    PROCESSENTRY32 processEntry;
    processEntry.dwSize = sizeof(PROCESSENTRY32);

    // 遍历进程
    if (Process32First(hSnapshot, &processEntry)) {
        do {
            if (strcmp(processEntry.szExeFile, "dwm.exe") == 0) {
                CloseHandle(hSnapshot);
                return true; // 找到 dwm.exe 进程
            }
        } while (Process32Next(hSnapshot, &processEntry));
    }

    CloseHandle(hSnapshot);
    return false; // 未找到 dwm.exe 进程
}

int main() {
    if (IsDwmProcessRunning()) {
        std::cout << "dwm.exe 进程正在运行。" << std::endl;
    } else {
        std::cout << "dwm.exe 进程未运行。" << std::endl;
    }
    return 0;
}

3.临时目录的文件数量

BOOL CheckCountTempPathFile()
{
    int nCount = 0;
    WCHAR szTmpPath[MAX_PATH];
    GetTempPath(MAX_PATH, szTmpPath);
    wcscat(szTmpPath, L"\\*.*");
    WIN32_FIND_DATA findData;
    HANDLE hFile = FindFirstFile(szTmpPath, &findData);
    if (INVALID_HANDLE_VALUE == hFile) {
        return FALSE;
    }
    do {
        nCount++;
    } while (FindNextFile(hFile, &findData));
    if (nCount < 30) {
        return FALSE;
    }

    return TRUE;
}

4.检测开机时间

//检测开机时间
BOOL CheckTickCount()
{
    DWORD dwTickCount = GetTickCount();
    if (dwTickCount < (10 * 60 * 1000)) {
        return FALSE;
    }

    return TRUE;
}

5.安装的软件数量
一般正常使用的PC不可能只有一两个软件,所以可以通过统计系统中注册的软件数量来判断是不是沙箱环境。

#include <windows.h>
#include <tlhelp32.h>
#include <iostream>

bool IsDwmProcessRunning() {
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_PROCESS, 0);
    if (hSnapshot == INVALID_HANDLE_VALUE) {
        std::cerr << "无法创建进程快照。" << std::endl;
        return false;
    }

    PROCESSENTRY32 processEntry;
    processEntry.dwSize = sizeof(PROCESSENTRY32);

    // 遍历进程
    if (Process32First(hSnapshot, &processEntry)) {
        do {
            if (strcmp(processEntry.szExeFile, "dwm.exe") == 0) {
                CloseHandle(hSnapshot);
                return true; // 找到 dwm.exe 进程
            }
        } while (Process32Next(hSnapshot, &processEntry));
    }

    CloseHandle(hSnapshot);
    return false; // 未找到 dwm.exe 进程
}

int main() {
    if (IsDwmProcessRunning()) {
        std::cout << "dwm.exe 进程正在运行。" << std::endl;
    } else {
        std::cout << "dwm.exe 进程未运行。" << std::endl;
    }
    return 0;
}

6.检测特定函数是否被挂钩
沙箱一般会处理特定的函数,例如:时间函数、进程相关函数、内存分配等,可以通过检测这些函数是否被HOOK来判断,但是正常情况存在安全软件等也可能会有这种情况。

//下面的代码可以检测IAT HOOK
#include <windows.h>
#include <iostream>
#include <psapi.h>

void CheckVirtualAllocHook() {
    HMODULE hModule = GetModuleHandleA("kernel32.dll");
    FARPROC pVirtualAlloc = GetProcAddress(hModule, "VirtualAlloc");

    // 获取 VirtualAlloc 的真实地址
    // 这里可以使用一些已知的地址来对比
    // 例如: 0x7C801D7C 在某些系统上是 VirtualAlloc 的地址

    // 检查地址
    if (pVirtualAlloc != (FARPROC)0x7C801D7C) { // 这里的地址需要根据实际情况调整
        std::cout << "VirtualAlloc may be hooked!" << std::endl;
    } else {
        std::cout << "VirtualAlloc is not hooked." << std::endl;
    }
}

int main() {
    CheckVirtualAllocHook();
    return 0;
}

7.操作系统语言检测,用在已知目标终端机器操作系统语言的情况下。

#include <windows.h>
#include <iostream>
#include <locale>

void CheckSystemLanguage() {
    // 获取当前线程的用户界面语言
    LANGID langId = GetUserDefaultUILanguage();

    // 获取语言名称
    wchar_t langName[LOCALE_NAME_MAX_LENGTH];
    if (LCIDToLocaleName(langId, langName, LOCALE_NAME_MAX_LENGTH, 0)) {
        std::wcout << L"当前系统语言: " << langName << std::endl;
    } else {
        std::cerr << "获取语言名称失败。" << std::endl;
    }

    // 获取区域设置
    wchar_t localeName[LOCALE_NAME_MAX_LENGTH];
    if (GetLocaleInfoEx(nullptr, LOCALE_SISO639LANGNAME, localeName, LOCALE_NAME_MAX_LENGTH) > 0) {
        std::wcout << L"区域设置: " << localeName << std::endl;
    } else {
        std::cerr << "获取区域设置失败。" << std::endl;
    }
}

int main() {
    CheckSystemLanguage();
    return 0;
}

8.用户名、计算机名检测
如果已知特定沙箱的用户名、计算机名等特征,可以通过检测该特征。
9.CPU温度

#include <windows.h>
#include <iostream>
#include <comdef.h>
#include <wbemidl.h>

#pragma comment(lib, "wbemuuid.lib")

void CheckCpuTemperature() {
    HRESULT hres;

    // 初始化 COM
    hres = CoInitializeEx(0, COINIT_MULTITHREADED);
    if (FAILED(hres)) {
        std::cerr << "COM 初始化失败。" << std::endl;
        return;
    }

    hres = CoInitializeSecurity(
        NULL,
        -1,
        NULL,
        NULL,
        RPC_C_AUTHN_LEVEL_DEFAULT,
        RPC_C_IMP_LEVEL_IMPERSONATE,
        NULL,
        EOAC_NONE,
        NULL
    );

    if (FAILED(hres)) {
        std::cerr << "安全初始化失败。" << std::endl;
        CoUninitialize();
        return;
    }

    // 创建 WMI 连接
    IWbemLocator *pLoc = NULL;
    hres = CoCreateInstance(
        CLSID_WbemLocator,
        0,
        CLSCTX_INPROC_SERVER,
        IID_IWbemLocator,
        (LPVOID *)&pLoc
    );

    if (FAILED(hres)) {
        std::cerr << "WMI Locator 创建失败。" << std::endl;
        CoUninitialize();
        return;
    }

    IWbemServices *pSvc = NULL;
    hres = pLoc->ConnectServer(
        _bstr_t(L"ROOT\\CIMV2"),
        NULL,
        NULL,
        0,
        NULL,
        0,
        0,
        &pSvc
    );

    if (FAILED(hres)) {
        std::cerr << "连接 WMI 服务失败。" << std::endl;
        pLoc->Release();
        CoUninitialize();
        return;
    }

    // 设置安全级别
    hres = CoInitializeSecurity(
        NULL,
        -1,
        NULL,
        NULL,
        RPC_C_AUTHN_LEVEL_DEFAULT,
        RPC_C_IMP_LEVEL_IMPERSONATE,
        NULL,
        EOAC_NONE,
        NULL
    );

    // 查询 CPU 温度
    IEnumWbemClassObject* pEnumerator = NULL;
    hres = pSvc->ExecQuery(
        bstr_t("WQL"),
        bstr_t("SELECT * FROM Win32_PerfFormattedData_Counters_ThermalZone"),
        WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
        NULL,
        &pEnumerator
    );

    if (FAILED(hres)) {
        std::cerr << "查询失败。" << std::endl;
        pSvc->Release();
        pLoc->Release();
        CoUninitialize();
        return;
    }

    IWbemClassObject *pclsObj = NULL;
    ULONG uReturn = 0;

    while (pEnumerator) {
        HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);
        if (0 == uReturn) {
            break;
        }

        // 获取 CPU 温度
        VARIANT vtTemperature;
        VariantInit(&vtTemperature);
        hr = pclsObj->Get(L"CurrentTemperature", 0, &vtTemperature, 0, 0);
        if (SUCCEEDED(hr)) {
            // 温度单位为开尔文,转换为摄氏度
            double temperatureCelsius = (vtTemperature.dblVal - 273.15);
            std::cout << "CPU 温度: " << temperatureCelsius << " °C" << std::endl;
        }
        VariantClear(&vtTemperature);
        pclsObj->Release();
    }

    // 清理
    pSvc->Release();
    pLoc->Release();
    pEnumerator->Release();
    CoUninitialize();
}

int main() {
    CheckCpuTemperature();
    return 0;
}

10.软件数量检测
软件数量一般要检测注册表,下面的代码只检测了64位的软件数量

#include <windows.h>
#include <iostream>

int GetInstalledSoftwareCount() {
    HKEY hKey;
    int softwareCount = 0;

    // 打开注册表路径以获取已安装软件的信息
    if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall", 0, KEY_READ, &hKey) == ERROR_SUCCESS) {
        char softwareName[256];
        DWORD nameLength = sizeof(softwareName);
        DWORD index = 0;

        // 遍历注册表项
        while (RegEnumKeyExA(hKey, index, softwareName, &nameLength, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) {
            softwareCount++;
            index++;
            nameLength = sizeof(softwareName); // 重置名称长度
        }

        RegCloseKey(hKey);
    } else {
        std::cerr << "无法打开注册表项。" << std::endl;
    }

    return softwareCount;
}

int main() {
    int softwareCount = GetInstalledSoftwareCount();
    std::cout << "当前系统安装的软件数量: " << softwareCount << std::endl;
    return 0;
}

用户操作检测

1.特定窗口检测
2.鼠标移动检测
3.键盘按键检测(慎用)

#include <windows.h>
#include <iostream>

bool IsKeyboardInputDetected() {
    // 检查当前线程的消息队列
    MSG msg;
    while (PeekMessage(&msg, NULL, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE)) {
        if (msg.message >= WM_KEYFIRST && msg.message <= WM_KEYLAST) {
            return true; // 检测到键盘输入
        }
    }
    return false; // 未检测到键盘输入
}

bool IsMouseMoved(POINT& lastPosition) {
    POINT currentPosition;
    GetCursorPos(&currentPosition); // 获取当前鼠标位置

    // 检查当前鼠标位置是否与上次记录的位置不同
    if (currentPosition.x != lastPosition.x || currentPosition.y != lastPosition.y) {
        lastPosition = currentPosition; // 更新最后的位置
        return true; // 鼠标已移动
    }
    return false; // 鼠标未移动
}

int main() {
    POINT lastMousePosition;
    GetCursorPos(&lastMousePosition); // 初始化上次鼠标位置

    while (true) {
        // 检测键盘输入
        if (IsKeyboardInputDetected()) {
            std::cout << "检测到键盘输入。" << std::endl;
        }

        // 检测鼠标移动
        if (IsMouseMoved(lastMousePosition)) {
            std::cout << "鼠标已移动。" << std::endl;
        }

        Sleep(100); // 暂停一段时间以避免过于频繁的检测
    }

    return 0;
}

时间检测

1.通过GetTickCount和Time函数的结果对比
2.Sleep和GetTickCount的结果去对比
3.判断特定时间范围

沙箱绕过

延时运行

1.通过长时间计算任务(计算pi、遍历特定的目录等)
2.通过时间判断(这种不太准,可能存在被沙箱将API HOOK导致函数失效的问题)

环境检测绕过

1.临时目录中文件数量检测,如果不符合条件退出或者延长程序的功能执行时间

#include <windows.h>
#include <iostream>
#include <string>
#include <dirent.h>

int CountFilesInTempDirectory() {
    // 获取临时目录路径
    char tempPath[MAX_PATH];
    GetTempPathA(MAX_PATH, tempPath);

    // 确保路径结尾有反斜杠
    std::string path(tempPath);
    if (path.back() != '\\') {
        path += '\\';
    }

    // 计数文件数量
    int fileCount = 0;
    WIN32_FIND_DATAA findFileData;
    HANDLE hFind = FindFirstFileA((path + "*").c_str(), &findFileData);

    if (hFind != INVALID_HANDLE_VALUE) {
        do {
            // 忽略目录
            if (!(findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
                fileCount++;
            }
        } while (FindNextFileA(hFind, &findFileData) != 0);
        FindClose(hFind);
    } else {
        std::cerr << "无法访问临时目录。" << std::endl;
    }

    return fileCount;
}

int main() {
    int fileCount = CountFilesInTempDirectory();
    std::cout << "临时目录中的文件数量: " << fileCount << std::endl;
    return 0;
}

2.特定进程检测、进程数量检测
正常系统Windows 10一般情况下会有50+以上个进程,而沙箱中为了性能,不会有太多进程,所以可以通过检测系统进程数量来判断进程是否处于沙箱中。同理,软件、服务等等都可以作为特征。

虚拟机检测

文件检测

VMware

C:\windows\System32\Drivers\Vmmouse.sys
C:\windows\System32\Drivers\vmtray.dll
C:\windows\System32\Drivers\VMToolsHook.dll
C:\windows\System32\Drivers\vmmousever.dll
C:\windows\System32\Drivers\vmhgfs.dll
C:\windows\System32\Drivers\vmGuestLib.dll

VirtualBox

C:\windows\System32\Drivers\VBoxMouse.sys
C:\windows\System32\Drivers\VBoxGuest.sys
C:\windows\System32\Drivers\VBoxSF.sys
C:\windows\System32\Drivers\VBoxVideo.sys
C:\windows\System32\vboxdisp.dll
C:\windows\System32\vboxhook.dll
C:\windows\System32\vboxoglerrorspu.dll
C:\windows\System32\vboxoglpassthroughspu.dll
C:\windows\System32\vboxservice.exe
C:\windows\System32\vboxtray.exe
C:\windows\System32\VBoxControl.exe

Mac地址检测

虚拟机Mac地址特点

00:05:69 (Vmware)
00:0C:29 (Vmware)
00:1C:14 (Vmware)
00:50:56 (Vmware)
08:00:27 (VirtualBox)

服务检测

VMTools
Vmrawdsk
Vmusbmouse
Vmvss
Vmscsi
Vmxnet
vmx_svga
Vmware Tools

使用CPUID

#include <iostream>
#include <intrin.h>

bool IsRunningInVM() {
    int cpuInfo[4];
    __cpuid(cpuInfo, 1); // 获取处理器信息

    // 检查虚拟化标志
    bool isVM = (cpuInfo[2] & (1 << 31)) != 0; // 检查 Bit 31

    // 进一步检查 VMWare 和 VirtualBox 等虚拟化平台的特征
    __cpuid(cpuInfo, 0x40000000); // 获取制造商信息
    if (cpuInfo[0] >= 0x40000000) {
        // 获取虚拟化厂商的字符串
        char vendor[13];
        memcpy(vendor, &cpuInfo[1], 4);
        memcpy(vendor + 4, &cpuInfo[2], 4);
        memcpy(vendor + 8, &cpuInfo[3], 4);
        vendor[12] = '\0';

        if (strcmp(vendor, "VMware") == 0 || strcmp(vendor, "VirtualBox") == 0) {
            isVM = true;
        }
    }

    return isVM;
}

int main() {
    if (IsRunningInVM()) {
        std::cout << "程序正在虚拟机中运行。" << std::endl;
    } else {
        std::cout << "程序不在虚拟机中运行。" << std::endl;
    }
    return 0;
}

进程名

Vmware:

Vmtoolsd.exe
Vmwaretrat.exe
Vmwareuser.exe
Vmacthlp.exe

VirtualBox:

vboxservice.exe
vboxtray.exe

注册表检测

以下是vbox和vmware存在的一些注册表:

HKLM\SOFTWARE\Vmware Inc\Vmware Tools
HKLM\HARDWARE\DEVICEMAP\Scsi\Scsi Port 2\Scsi Bus 0\Target Id 0\Logical Unit Id 0\Identifier
HKEY_CLASSES_ROOT\Applications\VMwareHostOpen.exe
HKEY_LOCAL_MACHINE\SOFTWARE\Oracle\VirtualBox Guest Additions

参考文档:
https://www.freebuf.com/articles/system/202717.html
部分代码使用Chatgpt4生成,未验证是否可正常通过编译,如果报错,请依据实际情况修改。

2024-04-10
                         
暂无评论

发表回复