Anti VM专题

3、通过文件夹信息检测虚拟机

一、课程目标

本节课主要学习如何通过检查系统文件夹和文件路径信息来检测虚拟机环境。虚拟机软件在安装和运行时会在系统中创建特定的文件夹和文件,通过识别这些特征可以判断程序是否运行在虚拟机中。通过本课的学习,你将能够:

  1. 理解虚拟机相关文件夹和文件的特征
  2. 掌握检查文件系统路径的技术
  3. 学会识别虚拟机软件特有的文件夹结构
  4. 实现基于文件夹信息的虚拟机检测代码
  5. 了解该技术的局限性和绕过方法

二、名词解释表

名词 解释
文件夹 文件系统中的目录结构
路径 文件或文件夹在文件系统中的位置
GetFileAttributes Windows API函数,用于获取文件或文件夹属性
FindFirstFile Windows API函数,用于查找文件
FindNextFile Windows API函数,用于继续查找文件
虚拟机文件夹 虚拟机软件安装或运行时创建的特定文件夹
安装路径 软件安装的根目录路径

三、技术原理

3.1 文件夹检测概述

文件夹检测是通过检查文件系统中是否存在虚拟机软件特有的文件夹和文件来判断是否处于虚拟化环境。

3.2 常见虚拟机文件夹特征

不同虚拟机软件在安装和运行时会创建特定的文件夹:

  1. VMware相关文件夹

    • C:\Program Files\VMware\
    • C:\Program Files (x86)\VMware\
    • C:\Users*\Documents\Virtual Machines\
    • C:\Users*\Documents\VMware\
  2. VirtualBox相关文件夹

    • C:\Program Files\Oracle\VirtualBox\
    • C:\Program Files (x86)\Oracle\VirtualBox\
    • C:\Users*\VirtualBox VMs\
  3. Hyper-V相关文件夹

    • C:\ProgramData\Microsoft\Windows\Hyper-V\
    • C:\Users\Public\Documents\Hyper-V\
  4. 其他虚拟化软件

    • C:\Program Files\Parallels\
    • C:\Program Files\QEMU\

3.3 检测原理

通过检查特定路径是否存在、文件夹名称是否匹配已知的虚拟机软件特征来判断是否处于虚拟机环境。

四、代码实现

4.1 基础文件夹检测

#include <windows.h>
#include <stdio.h>
#include <shlobj.h>
#include <vector>
#include <string>

// 虚拟机文件夹列表
struct VMFolder {
    const char* folderPath;
    const char* vendor;
};

VMFolder g_vmFolders[] = {
    {"C:\\Program Files\\VMware\\", "VMware"},
    {"C:\\Program Files (x86)\\VMware\\", "VMware"},
    {"C:\\Program Files\\Oracle\\VirtualBox\\", "VirtualBox"},
    {"C:\\Program Files (x86)\\Oracle\\VirtualBox\\", "VirtualBox"},
    {"C:\\Program Files\\Parallels\\", "Parallels"},
    {"C:\\Program Files\\QEMU\\", "QEMU"}
};

// 检查文件夹是否存在
BOOL FolderExists(const char* folderPath) {
    DWORD attributes = GetFileAttributesA(folderPath);
    return (attributes != INVALID_FILE_ATTRIBUTES && (attributes & FILE_ATTRIBUTE_DIRECTORY));
}

// 基础文件夹检测
BOOL DetectVMViaFolders() {
    printf("=== 基础文件夹检测 ===\n");
    
    BOOL vmDetected = FALSE;
    
    // 检查已知的虚拟机文件夹
    for (int i = 0; i < sizeof(g_vmFolders) / sizeof(g_vmFolders[0]); i++) {
        if (FolderExists(g_vmFolders[i].folderPath)) {
            printf("检测到虚拟机文件夹: %s (%s)\n", g_vmFolders[i].folderPath, g_vmFolders[i].vendor);
            vmDetected = TRUE;
        }
    }
    
    return vmDetected;
}

4.2 改进的文件夹检测

// 获取用户文档文件夹路径
BOOL GetUserDocumentsPath(char* path, DWORD bufferSize) {
    HRESULT hr = SHGetFolderPathA(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, path);
    return SUCCEEDED(hr);
}

// 获取公共文档文件夹路径
BOOL GetPublicDocumentsPath(char* path, DWORD bufferSize) {
    HRESULT hr = SHGetFolderPathA(NULL, CSIDL_COMMON_DOCUMENTS, NULL, SHGFP_TYPE_CURRENT, path);
    return SUCCEEDED(hr);
}

// 检查用户特定的虚拟机文件夹
BOOL CheckUserSpecificFolders() {
    printf("=== 用户特定文件夹检查 ===\n");
    
    char documentsPath[MAX_PATH];
    if (!GetUserDocumentsPath(documentsPath, sizeof(documentsPath))) {
        printf("无法获取用户文档路径。\n");
        return FALSE;
    }
    
    printf("用户文档路径: %s\n", documentsPath);
    
    BOOL vmDetected = FALSE;
    
    // 检查VMware用户文件夹
    char vmwarePath[MAX_PATH];
    sprintf_s(vmwarePath, sizeof(vmwarePath), "%s\\Virtual Machines\\", documentsPath);
    if (FolderExists(vmwarePath)) {
        printf("检测到VMware用户文件夹: %s\n", vmwarePath);
        vmDetected = TRUE;
    }
    
    sprintf_s(vmwarePath, sizeof(vmwarePath), "%s\\VMware\\", documentsPath);
    if (FolderExists(vmwarePath)) {
        printf("检测到VMware用户文件夹: %s\n", vmwarePath);
        vmDetected = TRUE;
    }
    
    // 检查VirtualBox用户文件夹
    char vboxPath[MAX_PATH];
    sprintf_s(vboxPath, sizeof(vboxPath), "%s\\VirtualBox VMs\\", documentsPath);
    if (FolderExists(vboxPath)) {
        printf("检测到VirtualBox用户文件夹: %s\n", vboxPath);
        vmDetected = TRUE;
    }
    
    return vmDetected;
}

// 检查公共文件夹
BOOL CheckPublicFolders() {
    printf("=== 公共文件夹检查 ===\n");
    
    char publicPath[MAX_PATH];
    if (!GetPublicDocumentsPath(publicPath, sizeof(publicPath))) {
        printf("无法获取公共文档路径。\n");
        return FALSE;
    }
    
    printf("公共文档路径: %s\n", publicPath);
    
    BOOL vmDetected = FALSE;
    
    // 检查Hyper-V公共文件夹
    char hyperVPath[MAX_PATH];
    sprintf_s(hyperVPath, sizeof(hyperVPath), "%s\\Hyper-V\\", publicPath);
    if (FolderExists(hyperVPath)) {
        printf("检测到Hyper-V公共文件夹: %s\n", hyperVPath);
        vmDetected = TRUE;
    }
    
    return vmDetected;
}

// 改进的文件夹检测
BOOL ImprovedVMFolderDetection() {
    printf("=== 改进版文件夹检测 ===\n");
    
    BOOL result1 = DetectVMViaFolders();
    BOOL result2 = CheckUserSpecificFolders();
    BOOL result3 = CheckPublicFolders();
    
    return result1 || result2 || result3;
}

4.3 高级文件夹检测技术

// 递归搜索文件夹
BOOL RecursiveFolderSearch(const char* basePath, const char* targetFolderName) {
    char searchPath[MAX_PATH];
    sprintf_s(searchPath, sizeof(searchPath), "%s\\*", basePath);
    
    WIN32_FIND_DATAA findData;
    HANDLE hFind = FindFirstFileA(searchPath, &findData);
    
    if (hFind == INVALID_HANDLE_VALUE) {
        return FALSE;
    }
    
    BOOL found = FALSE;
    
    do {
        // 跳过.和..
        if (strcmp(findData.cFileName, ".") == 0 || strcmp(findData.cFileName, "..") == 0) {
            continue;
        }
        
        // 检查是否为目录
        if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
            // 检查目录名是否匹配
            if (_stricmp(findData.cFileName, targetFolderName) == 0) {
                printf("在 %s 中找到目标文件夹: %s\n", basePath, targetFolderName);
                found = TRUE;
                break;
            }
            
            // 递归搜索子目录(限制深度以避免过长时间)
            char subPath[MAX_PATH];
            sprintf_s(subPath, sizeof(subPath), "%s\\%s", basePath, findData.cFileName);
            
            // 为了避免无限递归,我们限制搜索深度
            // 这里简化处理,只搜索一层
        }
    } while (FindNextFileA(hFind, &findData));
    
    FindClose(hFind);
    return found;
}

// 检查文件夹特征
BOOL CheckFolderCharacteristics() {
    printf("=== 文件夹特征检查 ===\n");
    
    // 检查Program Files目录
    const char* programFilesPaths[] = {
        "C:\\Program Files\\",
        "C:\\Program Files (x86)\\"
    };
    
    const char* vmKeywords[] = {
        "vmware", "virtualbox", "vbox", "hyper-v", "hyperv",
        "parallels", "qemu", "xen", "virtual"
    };
    
    BOOL vmDetected = FALSE;
    
    for (int i = 0; i < sizeof(programFilesPaths) / sizeof(programFilesPaths[0]); i++) {
        WIN32_FIND_DATAA findData;
        char searchPath[MAX_PATH];
        sprintf_s(searchPath, sizeof(searchPath), "%s*", programFilesPaths[i]);
        
        HANDLE hFind = FindFirstFileA(searchPath, &findData);
        if (hFind != INVALID_HANDLE_VALUE) {
            do {
                // 跳过.和..
                if (strcmp(findData.cFileName, ".") == 0 || strcmp(findData.cFileName, "..") == 0) {
                    continue;
                }
                
                // 检查是否为目录
                if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
                    // 转换为小写进行比较
                    char lowerName[MAX_PATH];
                    strcpy_s(lowerName, findData.cFileName);
                    for (int j = 0; lowerName[j]; j++) {
                        lowerName[j] = tolower(lowerName[j]);
                    }
                    
                    // 检查是否包含虚拟机关键词
                    for (int k = 0; k < sizeof(vmKeywords) / sizeof(vmKeywords[0]); k++) {
                        if (strstr(lowerName, vmKeywords[k]) != NULL) {
                            printf("在 %s 中发现可疑文件夹: %s (包含关键词: %s)\n", 
                                   programFilesPaths[i], findData.cFileName, vmKeywords[k]);
                            vmDetected = TRUE;
                        }
                    }
                }
            } while (FindNextFileA(hFind, &findData));
            
            FindClose(hFind);
        }
    }
    
    return vmDetected;
}

// 检查最近使用的文件夹
BOOL CheckRecentFolders() {
    printf("=== 最近使用文件夹检查 ===\n");
    
    // 检查最近使用的文件夹(通过注册表或其他方式)
    // 这里简化实现
    
    return FALSE;
}

// 综合文件夹检测
BOOL ComprehensiveFolderDetection() {
    printf("=== 综合文件夹检测 ===\n");
    
    BOOL result1 = DetectVMViaFolders();
    BOOL result2 = CheckUserSpecificFolders();
    BOOL result3 = CheckPublicFolders();
    BOOL result4 = CheckFolderCharacteristics();
    BOOL result5 = CheckRecentFolders();
    
    return result1 || result2 || result3 || result4 || result5;
}

4.4 反虚拟机实现

// 简单的文件夹反虚拟机检测
VOID SimpleFolderAntiVM() {
    if (ComprehensiveFolderDetection()) {
        printf("通过文件夹信息检测到虚拟机环境!程序即将退出。\n");
        ExitProcess(1);
    }
}

// 多层次文件夹检测
BOOL MultiLayerFolderDetection() {
    // 第一层:基础检测
    if (DetectVMViaFolders()) {
        return TRUE;
    }
    
    // 第二层:用户特定文件夹检测
    if (CheckUserSpecificFolders()) {
        return TRUE;
    }
    
    // 第三层:公共文件夹检测
    if (CheckPublicFolders()) {
        return TRUE;
    }
    
    // 第四层:特征检查
    if (CheckFolderCharacteristics()) {
        return TRUE;
    }
    
    // 第五层:最近文件夹检查
    if (CheckRecentFolders()) {
        return TRUE;
    }
    
    return FALSE;
}

// 增强版反虚拟机检测
VOID EnhancedFolderAntiVM() {
    // 多次检测
    for (int i = 0; i < 3; i++) {
        if (MultiLayerFolderDetection()) {
            printf("第%d次文件夹检测发现虚拟机环境!\n", i + 1);
            
            // 随机化响应
            int response = rand() % 4;
            switch (response) {
            case 0:
                ExitProcess(0);
            case 1:
                printf("发生未知错误。\n");
                Sleep(5000);
                exit(1);
            case 2:
                // 执行错误指令
                __debugbreak();
            case 3:
                // 进入无限循环
                while (1) {
                    Sleep(1000);
                }
            }
        }
        
        // 随机延迟
        Sleep(rand() % 100 + 50);
    }
    
    printf("文件夹反虚拟机检测通过。\n");
}

4.5 绕过文件夹检测的方法

// 文件夹检测绕过技术
class FolderObfuscator {
public:
    // 重命名虚拟机文件夹
    static BOOL RenameVMFolders() {
        printf("重命名虚拟机文件夹...\n");
        
        // 实际应用中,这需要管理员权限并且可能被安全软件阻止
        // 这里仅作为概念演示
        
        return FALSE;
    }
    
    // 移动虚拟机文件夹
    static BOOL MoveVMFolders() {
        printf("移动虚拟机文件夹...\n");
        
        // 可以将虚拟机文件夹移动到不常见的位置
        
        return FALSE;
    }
    
    // 创建虚假文件夹
    static BOOL CreateFakeFolders() {
        printf("创建虚假文件夹...\n");
        
        // 创建看起来像正常软件的文件夹来混淆检测
        
        return FALSE;
    }
};

// 综合绕过方法
VOID ComprehensiveFolderBypass() {
    // 重命名虚拟机文件夹
    FolderObfuscator::RenameVMFolders();
    
    // 移动虚拟机文件夹
    FolderObfuscator::MoveVMFolders();
    
    // 创建虚假文件夹
    FolderObfuscator::CreateFakeFolders();
    
    printf("文件夹检测绕过完成。\n");
}

4.6 完整测试程序

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <shlobj.h>

// 前面实现的函数声明
BOOL DetectVMViaFolders();
BOOL CheckUserSpecificFolders();
BOOL CheckPublicFolders();
BOOL CheckFolderCharacteristics();
BOOL MultiLayerFolderDetection();

// 显示系统文件夹信息
VOID DisplaySystemFolderInfo() {
    printf("=== 系统文件夹信息 ===\n");
    
    // 显示Program Files路径
    char programFilesPath[MAX_PATH];
    if (GetEnvironmentVariableA("ProgramFiles", programFilesPath, sizeof(programFilesPath))) {
        printf("Program Files路径: %s\n", programFilesPath);
    }
    
    // 显示用户文档路径
    char documentsPath[MAX_PATH];
    if (SUCCEEDED(SHGetFolderPathA(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, documentsPath))) {
        printf("用户文档路径: %s\n", documentsPath);
    }
    
    // 显示公共文档路径
    char publicPath[MAX_PATH];
    if (SUCCEEDED(SHGetFolderPathA(NULL, CSIDL_COMMON_DOCUMENTS, NULL, SHGFP_TYPE_CURRENT, publicPath))) {
        printf("公共文档路径: %s\n", publicPath);
    }
    
    printf("\n");
}

// 性能测试
VOID PerformanceTest() {
    const int iterations = 5;
    
    printf("=== 性能测试 (%d次调用) ===\n", iterations);
    
    // 测试基础文件夹检测
    DWORD start = GetTickCount();
    for (int i = 0; i < iterations; i++) {
        DetectVMViaFolders();
        Sleep(100);
    }
    DWORD basicTime = GetTickCount() - start;
    
    // 测试改进版检测
    start = GetTickCount();
    for (int i = 0; i < iterations; i++) {
        ImprovedVMFolderDetection();
        Sleep(100);
    }
    DWORD improvedTime = GetTickCount() - start;
    
    printf("基础文件夹检测耗时: %lu ms\n", basicTime);
    printf("改进版文件夹检测耗时: %lu ms\n", improvedTime);
    
    printf("\n");
}

// 主程序
int main() {
    srand((unsigned int)time(NULL));
    
    printf("通过文件夹信息检测虚拟机演示程序\n");
    printf("===========================\n\n");
    
    // 显示系统文件夹信息
    DisplaySystemFolderInfo();
    
    // 基础文件夹检测
    DetectVMViaFolders();
    
    // 用户特定文件夹检查
    CheckUserSpecificFolders();
    
    // 公共文件夹检查
    CheckPublicFolders();
    
    // 特征检查
    CheckFolderCharacteristics();
    
    // 性能测试
    PerformanceTest();
    
    // 实际应用示例
    printf("=== 反虚拟机检测 ===\n");
    if (MultiLayerFolderDetection()) {
        printf("检测到虚拟机环境,执行反虚拟机措施。\n");
        
        // 这里可以执行各种反虚拟机措施
        // 为演示目的,我们只是显示信息而不真正退出
        printf("(演示模式:不实际退出程序)\n");
    } else {
        printf("未检测到虚拟机环境,程序正常运行。\n");
        MessageBoxW(NULL, L"文件夹检测通过,程序正常运行", L"提示", MB_OK);
    }
    
    // 演示绕过方法
    printf("\n=== 绕过演示 ===\n");
    printf("执行文件夹绕过...\n");
    // ComprehensiveFolderBypass();  // 注释掉以避免实际修改系统
    
    printf("绕过完成后再次检测:\n");
    if (MultiLayerFolderDetection()) {
        printf("仍然检测到虚拟机环境。\n");
    } else {
        printf("检测结果显示未发现文件夹异常。\n");
    }
    
    return 0;
}

4.7 高级技巧和注意事项

// 抗干扰版本(防止简单的Hook)
BOOL AntiTamperFolderDetection() {
    // 多次调用并验证
    BOOL results[3];
    
    for (int i = 0; i < 3; i++) {
        results[i] = MultiLayerFolderDetection();
        Sleep(10);  // 简短延迟
    }
    
    // 检查结果一致性
    for (int i = 1; i < 3; i++) {
        if (results[i] != results[0]) {
            // 结果不一致,可能是被干扰了
            return TRUE;  // 假设存在虚拟机环境
        }
    }
    
    return results[0];
}

// 综合检测函数
BOOL ComprehensiveFolderDetectionEnhanced() {
    // 抗干扰检测
    if (AntiTamperFolderDetection()) {
        return TRUE;
    }
    
    // 多层检测
    if (MultiLayerFolderDetection()) {
        return TRUE;
    }
    
    return FALSE;
}

// 动态获取文件API地址(避免静态导入)
FARPROC GetDynamicFileAPIAddress(LPCSTR functionName) {
    // 动态加载kernel32.dll
    HMODULE hKernel32 = GetModuleHandle(L"kernel32.dll");
    if (hKernel32 == NULL) {
        return NULL;
    }
    
    // 获取函数地址
    FARPROC pfn = GetProcAddress(hKernel32, functionName);
    
    return pfn;
}

// 检查文件API调用的完整性
BOOL ValidateFileAPICall() {
    // 可以通过检查相关函数代码的完整性来验证未被修改
    // 这需要更高级的技术,如代码校验和检查
    
    return TRUE;
}

// 多线程环境下的文件夹检测
BOOL MultiThreadFolderDetection() {
    printf("=== 多线程文件夹检测 ===\n");
    
    // 在多线程环境中进行检测可以增加检测的可靠性
    
    return FALSE;
}

五、课后作业

  1. 基础练习

    • 在不同的虚拟机软件中测试文件夹检测的准确性
    • 研究更多虚拟机软件的文件夹特征
    • 实现对文件夹信息的完整验证
  2. 进阶练习

    • 实现一个完整的文件夹行为监控器
    • 研究如何通过API Hook绕过文件夹检测
    • 设计一个多层检测机制,结合文件夹信息和其他反虚拟机技术
  3. 思考题

    • 文件夹检测方法有哪些明显的局限性?
    • 如何提高文件夹检测的准确性和隐蔽性?
    • 现代虚拟机采用了哪些技术来对抗文件夹检测?
  4. 扩展阅读

    • 研究Windows文件系统结构
    • 了解文件夹隐藏技术
    • 学习现代反虚拟机技术