Anti VM专题

2、通过进程信息检测虚拟机

一、课程目标

本节课主要学习如何通过检查系统运行的进程信息来检测虚拟机环境。虚拟机软件在运行时会启动特定的后台进程,通过识别这些进程可以判断程序是否运行在虚拟机中。通过本课的学习,你将能够:

  1. 理解虚拟机相关进程的特征和识别方法
  2. 掌握遍历和分析系统进程的技术
  3. 学会识别虚拟机软件特有的进程名称和路径
  4. 实现基于进程信息的虚拟机检测代码
  5. 了解该技术的局限性和绕过方法

二、名词解释表

名词 解释
进程 操作系统中正在运行的程序实例
进程名 进程可执行文件的名称
进程路径 进程可执行文件的完整路径
进程ID Process Identifier,操作系统分配给进程的唯一标识符
CreateToolhelp32Snapshot Windows API函数,用于创建系统信息快照
Process32First Windows API函数,用于获取第一个进程信息
Process32Next Windows API函数,用于获取下一个进程信息
虚拟机进程 虚拟机软件运行时启动的后台进程

三、技术原理

3.1 进程检测概述

进程检测是通过枚举系统中运行的所有进程,检查其中是否包含虚拟机软件特有的进程来判断是否处于虚拟化环境。

3.2 常见虚拟机进程特征

不同虚拟机软件运行时会启动特定的后台进程:

  1. VMware相关进程

    • vmtoolsd.exe
    • vmacthlp.exe
    • vmwaretray.exe
    • vmwareuser.exe
    • vmware.exe
  2. VirtualBox相关进程

    • VBoxService.exe
    • VBoxTray.exe
    • VirtualBox.exe
  3. Hyper-V相关进程

    • vmms.exe
    • vmwp.exe
  4. 其他虚拟化软件

    • parallels.exe
    • qemu-ga.exe

3.3 检测原理

通过创建系统进程快照,遍历所有运行的进程,检查进程名称和路径是否匹配已知的虚拟机软件特征。

四、代码实现

4.1 基础进程检测

#include <windows.h>
#include <tlhelp32.h>
#include <psapi.h>
#include <stdio.h>
#include <vector>
#include <string>

// 虚拟机进程列表
struct VMProcess {
    const char* processName;
    const char* vendor;
};

VMProcess g_vmProcesses[] = {
    {"vmtoolsd.exe", "VMware"},
    {"vmacthlp.exe", "VMware"},
    {"vmwaretray.exe", "VMware"},
    {"vmwareuser.exe", "VMware"},
    {"vmware.exe", "VMware"},
    {"VBoxService.exe", "VirtualBox"},
    {"VBoxTray.exe", "VirtualBox"},
    {"VirtualBox.exe", "VirtualBox"},
    {"vmms.exe", "Hyper-V"},
    {"vmwp.exe", "Hyper-V"},
    {"prl_cc.exe", "Parallels"},
    {"prl_tools.exe", "Parallels"},
    {"qemu-ga.exe", "QEMU"}
};

// 检查进程是否为虚拟机相关进程
BOOL IsVMProcess(const char* processName) {
    for (int i = 0; i < sizeof(g_vmProcesses) / sizeof(g_vmProcesses[0]); i++) {
        if (_stricmp(processName, g_vmProcesses[i].processName) == 0) {
            printf("检测到虚拟机进程: %s (%s)\n", processName, g_vmProcesses[i].vendor);
            return TRUE;
        }
    }
    return FALSE;
}

// 基础进程检测
BOOL DetectVMViaProcesses() {
    printf("=== 基础进程检测 ===\n");
    
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (hSnapshot == INVALID_HANDLE_VALUE) {
        printf("创建进程快照失败。\n");
        return FALSE;
    }
    
    PROCESSENTRY32 pe32;
    pe32.dwSize = sizeof(PROCESSENTRY32);
    
    BOOL vmDetected = FALSE;
    
    // 获取第一个进程
    if (Process32First(hSnapshot, &pe32)) {
        do {
            printf("进程名称: %s, PID: %lu\n", pe32.szExeFile, pe32.th32ProcessID);
            
            // 检查是否为虚拟机进程
            if (IsVMProcess(pe32.szExeFile)) {
                vmDetected = TRUE;
            }
        } while (Process32Next(hSnapshot, &pe32));
    }
    
    CloseHandle(hSnapshot);
    return vmDetected;
}

4.2 改进的进程检测

// 获取进程完整路径
BOOL GetProcessFullPath(DWORD processId, char* fullPath, DWORD bufferSize) {
    HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processId);
    if (hProcess == NULL) {
        return FALSE;
    }
    
    BOOL result = FALSE;
    if (GetModuleFileNameExA(hProcess, NULL, fullPath, bufferSize)) {
        result = TRUE;
    }
    
    CloseHandle(hProcess);
    return result;
}

// 检查进程路径中的虚拟机特征
BOOL CheckProcessPathForVM(const char* processPath) {
    // 转换为小写进行比较
    char lowerPath[MAX_PATH];
    strcpy_s(lowerPath, processPath);
    for (int i = 0; lowerPath[i]; i++) {
        lowerPath[i] = tolower(lowerPath[i]);
    }
    
    // 检查路径中是否包含虚拟机相关关键词
    const char* vmKeywords[] = {
        "vmware", "virtualbox", "vbox", "hyper-v", "hyperv", 
        "parallels", "qemu", "xen", "virtual"
    };
    
    for (int i = 0; i < sizeof(vmKeywords) / sizeof(vmKeywords[0]); i++) {
        if (strstr(lowerPath, vmKeywords[i]) != NULL) {
            printf("进程路径中检测到虚拟机关键词: %s\n", vmKeywords[i]);
            return TRUE;
        }
    }
    
    return FALSE;
}

// 改进的进程检测
BOOL ImprovedVMProcessDetection() {
    printf("=== 改进版进程检测 ===\n");
    
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (hSnapshot == INVALID_HANDLE_VALUE) {
        printf("创建进程快照失败。\n");
        return FALSE;
    }
    
    PROCESSENTRY32 pe32;
    pe32.dwSize = sizeof(PROCESSENTRY32);
    
    BOOL vmDetected = FALSE;
    
    // 获取第一个进程
    if (Process32First(hSnapshot, &pe32)) {
        do {
            // 检查进程名称
            if (IsVMProcess(pe32.szExeFile)) {
                vmDetected = TRUE;
                continue;
            }
            
            // 获取进程完整路径并检查
            char fullPath[MAX_PATH] = {0};
            if (GetProcessFullPath(pe32.th32ProcessID, fullPath, sizeof(fullPath))) {
                printf("进程 %s 路径: %s\n", pe32.szExeFile, fullPath);
                
                if (CheckProcessPathForVM(fullPath)) {
                    printf("检测到可疑进程: %s\n", pe32.szExeFile);
                    vmDetected = TRUE;
                }
            } else {
                printf("进程 %s (PID: %lu) 路径获取失败\n", pe32.szExeFile, pe32.th32ProcessID);
            }
        } while (Process32Next(hSnapshot, &pe32));
    }
    
    CloseHandle(hSnapshot);
    return vmDetected;
}

4.3 高级进程检测技术

// 获取进程命令行参数
BOOL GetProcessCommandLine(DWORD processId, char* cmdLine, DWORD bufferSize) {
    // 这是一个简化的实现,实际应用中可能需要更复杂的方法
    // 可以通过读取进程PEB等方法获取命令行
    
    // 对于演示目的,我们返回FALSE表示无法获取
    return FALSE;
}

// 检查进程特征
BOOL CheckProcessCharacteristics() {
    printf("=== 进程特征检查 ===\n");
    
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (hSnapshot == INVALID_HANDLE_VALUE) {
        return FALSE;
    }
    
    PROCESSENTRY32 pe32;
    pe32.dwSize = sizeof(PROCESSENTRY32);
    
    int processCount = 0;
    int systemProcessCount = 0;
    int userProcessCount = 0;
    
    if (Process32First(hSnapshot, &pe32)) {
        do {
            processCount++;
            
            // 检查进程名长度异常(虚拟机进程通常有特定命名模式)
            size_t nameLen = strlen(pe32.szExeFile);
            if (nameLen >= 4) {
                // 检查是否以vm开头
                if (_strnicmp(pe32.szExeFile, "vm", 2) == 0) {
                    printf("发现vm开头进程: %s\n", pe32.szExeFile);
                }
                
                // 检查是否以vbox开头
                if (_strnicmp(pe32.szExeFile, "vbox", 4) == 0) {
                    printf("发现vbox开头进程: %s\n", pe32.szExeFile);
                }
            }
            
            // 检查进程创建时间(虚拟机相关进程通常运行时间较长)
            HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pe32.th32ProcessID);
            if (hProcess != NULL) {
                FILETIME creationTime, exitTime, kernelTime, userTime;
                if (GetProcessTimes(hProcess, &creationTime, &exitTime, &kernelTime, &userTime)) {
                    // 计算进程运行时间
                    SYSTEMTIME sysTime;
                    GetSystemTime(&sysTime);
                    
                    FILETIME currentFileTime;
                    SystemTimeToFileTime(&sysTime, &currentFileTime);
                    
                    // 简单的时间比较(实际应用中应更精确)
                    ULARGE_INTEGER createTime, currentTime;
                    createTime.LowPart = creationTime.dwLowDateTime;
                    createTime.HighPart = creationTime.dwHighDateTime;
                    currentTime.LowPart = currentFileTime.dwLowDateTime;
                    currentTime.HighPart = currentFileTime.dwHighDateTime;
                    
                    // 如果进程运行时间很长,可能是虚拟机相关进程
                    if (currentTime.QuadPart - createTime.QuadPart > 36000000000ULL) { // 1小时
                        printf("发现长时间运行进程: %s\n", pe32.szExeFile);
                    }
                }
                CloseHandle(hProcess);
            }
        } while (Process32Next(hSnapshot, &pe32));
    }
    
    CloseHandle(hSnapshot);
    
    printf("总进程数: %d\n", processCount);
    
    // 在虚拟机环境中,进程数量通常较少
    if (processCount < 20) {
        printf("进程数量异常少,可能是虚拟机环境。\n");
        return TRUE;
    }
    
    return FALSE;
}

// 综合进程检测
BOOL ComprehensiveProcessDetection() {
    printf("=== 综合进程检测 ===\n");
    
    BOOL result1 = DetectVMViaProcesses();
    BOOL result2 = ImprovedVMProcessDetection();
    BOOL result3 = CheckProcessCharacteristics();
    
    return result1 || result2 || result3;
}

4.4 反虚拟机实现

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

// 多层次进程检测
BOOL MultiLayerProcessDetection() {
    // 第一层:基础检测
    if (DetectVMViaProcesses()) {
        return TRUE;
    }
    
    // 第二层:改进检测
    if (ImprovedVMProcessDetection()) {
        return TRUE;
    }
    
    // 第三层:特征检查
    if (CheckProcessCharacteristics()) {
        return TRUE;
    }
    
    return FALSE;
}

// 增强版反虚拟机检测
VOID EnhancedProcessAntiVM() {
    // 多次检测
    for (int i = 0; i < 3; i++) {
        if (MultiLayerProcessDetection()) {
            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 ProcessObfuscator {
public:
    // 重命名虚拟机进程
    static BOOL RenameVMProcesses() {
        printf("重命名虚拟机进程...\n");
        
        // 实际应用中,这需要管理员权限并且可能被安全软件阻止
        // 这里仅作为概念演示
        
        return FALSE;
    }
    
    // 干扰进程枚举
    static BOOL InterfereWithProcessEnumeration() {
        printf("干扰进程枚举...\n");
        
        // 可以通过Hook相关API来隐藏虚拟机进程
        
        return FALSE;
    }
    
    // 模拟正常进程行为
    static BOOL SimulateNormalProcessBehavior() {
        printf("模拟正常进程行为...\n");
        
        // 可以通过修改进程特征来避免被识别为虚拟机
        
        return FALSE;
    }
};

// 综合绕过方法
VOID ComprehensiveProcessBypass() {
    // 重命名虚拟机进程
    ProcessObfuscator::RenameVMProcesses();
    
    // 干扰进程枚举
    ProcessObfuscator::InterfereWithProcessEnumeration();
    
    // 模拟正常行为
    ProcessObfuscator::SimulateNormalProcessBehavior();
    
    printf("进程检测绕过完成。\n");
}

4.6 完整测试程序

#include <windows.h>
#include <tlhelp32.h>
#include <psapi.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

// 前面实现的函数声明
BOOL DetectVMViaProcesses();
BOOL ImprovedVMProcessDetection();
BOOL CheckProcessCharacteristics();
BOOL MultiLayerProcessDetection();

// 显示系统进程信息
VOID DisplaySystemProcessInfo() {
    printf("=== 系统进程信息 ===\n");
    
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (hSnapshot == INVALID_HANDLE_VALUE) {
        return;
    }
    
    PROCESSENTRY32 pe32;
    pe32.dwSize = sizeof(PROCESSENTRY32);
    int processCount = 0;
    
    if (Process32First(hSnapshot, &pe32)) {
        do {
            processCount++;
            // 只显示前10个进程以避免输出过多
            if (processCount <= 10) {
                printf("进程 %d: %s (PID: %lu)\n", processCount, pe32.szExeFile, pe32.th32ProcessID);
            }
        } while (Process32Next(hSnapshot, &pe32));
    }
    
    printf("总进程数: %d\n\n", processCount);
    CloseHandle(hSnapshot);
}

// 性能测试
VOID PerformanceTest() {
    const int iterations = 5;
    
    printf("=== 性能测试 (%d次调用) ===\n", iterations);
    
    // 测试基础进程检测
    DWORD start = GetTickCount();
    for (int i = 0; i < iterations; i++) {
        DetectVMViaProcesses();
        Sleep(100);
    }
    DWORD basicTime = GetTickCount() - start;
    
    // 测试改进版检测
    start = GetTickCount();
    for (int i = 0; i < iterations; i++) {
        ImprovedVMProcessDetection();
        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");
    
    // 显示系统进程信息
    DisplaySystemProcessInfo();
    
    // 基础进程检测
    DetectVMViaProcesses();
    
    // 改进版检测
    ImprovedVMProcessDetection();
    
    // 特征检查
    CheckProcessCharacteristics();
    
    // 性能测试
    PerformanceTest();
    
    // 实际应用示例
    printf("=== 反虚拟机检测 ===\n");
    if (MultiLayerProcessDetection()) {
        printf("检测到虚拟机环境,执行反虚拟机措施。\n");
        
        // 这里可以执行各种反虚拟机措施
        // 为演示目的,我们只是显示信息而不真正退出
        printf("(演示模式:不实际退出程序)\n");
    } else {
        printf("未检测到虚拟机环境,程序正常运行。\n");
        MessageBoxW(NULL, L"进程检测通过,程序正常运行", L"提示", MB_OK);
    }
    
    // 演示绕过方法
    printf("\n=== 绕过演示 ===\n");
    printf("执行进程绕过...\n");
    // ComprehensiveProcessBypass();  // 注释掉以避免实际修改系统
    
    printf("绕过完成后再次检测:\n");
    if (MultiLayerProcessDetection()) {
        printf("仍然检测到虚拟机环境。\n");
    } else {
        printf("检测结果显示未发现进程异常。\n");
    }
    
    return 0;
}

4.7 高级技巧和注意事项

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

// 综合检测函数
BOOL ComprehensiveProcessDetectionEnhanced() {
    // 抗干扰检测
    if (AntiTamperProcessDetection()) {
        return TRUE;
    }
    
    // 多层检测
    if (MultiLayerProcessDetection()) {
        return TRUE;
    }
    
    return FALSE;
}

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

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

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

五、课后作业

  1. 基础练习

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

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

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

    • 研究Windows进程管理机制
    • 了解进程隐藏技术
    • 学习现代反虚拟机技术