Anti VM专题

6、通过IO端口检测虚拟机

一、课程目标

本节课主要学习如何通过检查IO端口访问来检测虚拟机环境。虚拟机软件在模拟硬件时会对某些特殊的IO端口做出特定响应,通过向这些端口发送特定的指令并检查返回值可以判断程序是否运行在虚拟机中。通过本课的学习,你将能够:

  1. 理解IO端口机制和虚拟机相关端口特征
  2. 掌握直接IO端口访问的技术
  3. 学会识别虚拟机软件特有的端口响应模式
  4. 实现基于IO端口的虚拟机检测代码
  5. 了解该技术的局限性和绕过方法

二、名词解释表

名词 解释
IO端口 Input/Output Port,CPU与外设通信的接口
端口访问 通过IN/OUT指令访问硬件端口
VMware端口 VMware虚拟机特有的IO端口
VirtualBox端口 VirtualBox虚拟机特有的IO端口
__inbyte 内联汇编函数,用于从端口读取字节
__outbyte 内联汇编函数,用于向端口写入字节
端口响应 硬件或虚拟机对端口访问的响应

三、技术原理

3.1 IO端口检测概述

IO端口检测是通过直接访问特定的IO端口,检查虚拟机软件特有的响应模式来判断是否处于虚拟化环境。

3.2 常见虚拟机IO端口特征

不同虚拟机软件在模拟硬件时会响应特定的IO端口:

  1. VMware相关端口

    • 0x5658 - VMware检测端口(“VX”)
    • 0x5659 - VMware检测端口(“VY”)
    • 0x564D5868 - VMware魔术端口
  2. VirtualBox相关端口

    • 0x564D5868 - VirtualBox也使用类似端口
    • 0x1808 - VirtualBox检测端口
  3. 其他虚拟化软件

    • 0x4F50 - Parallels检测端口
    • 0x2000 - QEMU检测端口

3.3 检测原理

通过向特定的IO端口发送魔数或指令,检查返回值是否符合虚拟机软件的预期响应模式。

四、代码实现

4.1 基础IO端口检测

#include <windows.h>
#include <stdio.h>

#ifdef _X86_
// x86架构下的端口访问函数
__declspec(naked) BYTE __inbyte(WORD port) {
    __asm {
        push ebp
        mov ebp, esp
        mov edx, [ebp + 8]  // port
        xor eax, eax
        in al, dx
        pop ebp
        ret
    }
}

__declspec(naked) VOID __outbyte(WORD port, BYTE data) {
    __asm {
        push ebp
        mov ebp, esp
        mov edx, [ebp + 8]   // port
        mov eax, [ebp + 12]  // data
        out dx, al
        pop ebp
        ret
    }
}

__declspec(naked) WORD __inword(WORD port) {
    __asm {
        push ebp
        mov ebp, esp
        mov edx, [ebp + 8]  // port
        xor eax, eax
        in ax, dx
        pop ebp
        ret
    }
}

__declspec(naked) DWORD __indword(WORD port) {
    __asm {
        push ebp
        mov ebp, esp
        mov edx, [ebp + 8]  // port
        in eax, dx
        pop ebp
        ret
    }
}
#endif

#ifdef _WIN64
// x64架构下需要使用Windows API
#include <intrin.h>
#define __inbyte(port) __inbyte(port)
#define __outbyte(port, data) __outbyte(port, data)
#define __inword(port) __inword(port)
#define __indword(port) __indword(port)
#endif

// VMware检测端口
BOOL DetectVMwareViaIOPort() {
    printf("=== VMware IO端口检测 ===\n");
    
    // 方法1:使用0x5658和0x5659端口
    __try {
        // 向端口0x5658写入0x564D ('V'和'M')
        __outbyte(0x5658, 0x56);
        __outbyte(0x5659, 0x4D);
        
        // 从端口0x5658和0x5659读取数据
        BYTE value1 = __inbyte(0x5658);
        BYTE value2 = __inbyte(0x5659);
        
        printf("端口0x5658读取值: 0x%02X\n", value1);
        printf("端口0x5659读取值: 0x%02X\n", value2);
        
        // VMware通常会返回特定的值
        if (value1 == 0x56 && value2 == 0x4D) {
            printf("检测到VMware特征响应。\n");
            return TRUE;
        }
    } __except (EXCEPTION_EXECUTE_HANDLER) {
        printf("端口访问异常,可能是权限不足或不在虚拟机中。\n");
        return FALSE;
    }
    
    return FALSE;
}

4.2 改进的IO端口检测

// VMware魔术端口检测
BOOL DetectVMwareMagicPort() {
    printf("=== VMware魔术端口检测 ===\n");
    
    __try {
        // 使用VMware魔术端口0x564D5868
        // EAX=0x564D5868, EBX=0, ECX=10, EDX=0x5658646D
        DWORD eax = 0x564D5868;
        DWORD ebx = 0;
        DWORD ecx = 10;  // 获取版本信息的命令
        DWORD edx = 0x5658646D;
        
#ifdef _X86_
        __asm {
            push eax
            push ebx
            push ecx
            push edx
            
            mov eax, [esp + 12]  // eax
            mov ebx, [esp + 8]   // ebx
            mov ecx, [esp + 4]   // ecx
            mov edx, [esp]       // edx
            
            in eax, dx           // 执行端口访问
            
            mov [esp + 12], eax
            mov [esp + 8], ebx
            mov [esp + 4], ecx
            mov [esp], edx
            
            pop edx
            pop ecx
            pop ebx
            pop eax
        }
        
        printf("VMware魔术端口返回值: EAX=0x%08X, EBX=0x%08X, ECX=0x%08X, EDX=0x%08X\n", 
               eax, ebx, ecx, edx);
        
        // 检查返回值是否符合VMware特征
        if (eax == 0x564D5868) {
            printf("检测到VMware魔术端口响应。\n");
            return TRUE;
        }
#endif
        
#ifdef _WIN64
        // x64下使用内联汇编替代方案
        // 由于x64不支持内联汇编,这里简化处理
        printf("x64架构下跳过魔术端口检测。\n");
#endif
        
    } __except (EXCEPTION_EXECUTE_HANDLER) {
        printf("魔术端口访问异常。\n");
        return FALSE;
    }
    
    return FALSE;
}

// VirtualBox检测
BOOL DetectVirtualBoxViaIOPort() {
    printf("=== VirtualBox IO端口检测 ===\n");
    
    __try {
        // VirtualBox也使用类似的端口机制
        DWORD eax = 0x564D5868;
        DWORD ebx = 0;
        DWORD ecx = 10;  // 获取版本信息
        DWORD edx = 0x5658646D;
        
#ifdef _X86_
        __asm {
            push eax
            push ebx
            push ecx
            push edx
            
            mov eax, [esp + 12]
            mov ebx, [esp + 8]
            mov ecx, [esp + 4]
            mov edx, [esp]
            
            in eax, dx
            
            mov [esp + 12], eax
            mov [esp + 8], ebx
            mov [esp + 4], ecx
            mov [esp], edx
            
            pop edx
            pop ecx
            pop ebx
            pop eax
        }
        
        printf("VirtualBox端口返回值: EAX=0x%08X, EBX=0x%08X, ECX=0x%08X, EDX=0x%08X\n", 
               eax, ebx, ecx, edx);
        
        // VirtualBox也有类似的响应模式
        if (eax == 0x564D5868 && ebx != 0) {
            printf("检测到VirtualBox端口响应。\n");
            return TRUE;
        }
#endif
        
    } __except (EXCEPTION_EXECUTE_HANDLER) {
        printf("VirtualBox端口访问异常。\n");
        return FALSE;
    }
    
    return FALSE;
}

4.3 高级IO端口检测技术

// 检查端口访问时间
BOOL CheckPortAccessTiming() {
    printf("=== 端口访问时间检测 ===\n");
    
    // 真实硬件和虚拟机在端口访问时间上可能有差异
    DWORD start = GetTickCount();
    
    __try {
        for (int i = 0; i < 1000; i++) {
            // 访问不存在的端口
            __inbyte(0x80);  // 传统DMA端口,通常无响应
        }
    } __except (EXCEPTION_EXECUTE_HANDLER) {
        // 异常处理
    }
    
    DWORD end = GetTickCount();
    DWORD elapsed = end - start;
    
    printf("1000次端口访问耗时: %lu ms\n", elapsed);
    
    // 虚拟机中的端口访问时间可能与真实硬件不同
    // 这是一个粗略的检测方法
    if (elapsed > 1000) {  // 如果耗时过长
        printf("端口访问时间异常,可能是虚拟机环境。\n");
        return TRUE;
    }
    
    return FALSE;
}

// 检查端口响应一致性
BOOL CheckPortResponseConsistency() {
    printf("=== 端口响应一致性检测 ===\n");
    
    __try {
        // 多次访问同一个端口,检查响应是否一致
        BYTE values[10];
        
        for (int i = 0; i < 10; i++) {
            values[i] = __inbyte(0x80);  // 访问DMA端口
            Sleep(1);  // 短暂延迟
        }
        
        // 检查值是否一致
        BOOL consistent = TRUE;
        for (int i = 1; i < 10; i++) {
            if (values[i] != values[0]) {
                consistent = FALSE;
                break;
            }
        }
        
        if (!consistent) {
            printf("端口响应不一致,可能是虚拟机环境。\n");
            return TRUE;
        }
        
        printf("端口响应一致。\n");
        
    } __except (EXCEPTION_EXECUTE_HANDLER) {
        printf("端口访问异常。\n");
        return FALSE;
    }
    
    return FALSE;
}

// 检查特殊端口序列
BOOL CheckSpecialPortSequences() {
    printf("=== 特殊端口序列检测 ===\n");
    
    __try {
        // VMware特定的端口序列检测
        // 发送特定的命令序列到VMware端口
        
        // 命令1:获取VMware版本
        DWORD eax = 0x564D5868;
        DWORD ebx = 0;
        DWORD ecx = 10;  // 获取版本命令
        DWORD edx = 0x5658646D;
        
#ifdef _X86_
        __asm {
            mov eax, 0x564D5868
            mov ebx, 0
            mov ecx, 10
            mov edx, 0x5658646D
            in eax, dx
            mov eax, eax
        }
        
        if (eax == 0x564D5868) {
            printf("检测到VMware版本命令响应。\n");
            
            // 命令2:获取内存大小
            eax = 0x564D5868;
            ebx = 0;
            ecx = 14;  // 获取内存大小命令
            edx = 0x5658646D;
            
            __asm {
                mov eax, 0x564D5868
                mov ebx, 0
                mov ecx, 14
                mov edx, 0x5658646D
                in eax, dx
                mov eax, eax
            }
            
            if (eax == 0x564D5868) {
                printf("检测到VMware内存命令响应。\n");
                return TRUE;
            }
        }
#endif
        
    } __except (EXCEPTION_EXECUTE_HANDLER) {
        printf("特殊端口序列访问异常。\n");
        return FALSE;
    }
    
    return FALSE;
}

// 综合IO端口检测
BOOL ComprehensiveIOPortDetection() {
    printf("=== 综合IO端口检测 ===\n");
    
    BOOL result1 = DetectVMwareViaIOPort();
    BOOL result2 = DetectVMwareMagicPort();
    BOOL result3 = DetectVirtualBoxViaIOPort();
    BOOL result4 = CheckPortAccessTiming();
    BOOL result5 = CheckPortResponseConsistency();
    BOOL result6 = CheckSpecialPortSequences();
    
    return result1 || result2 || result3 || result4 || result5 || result6;
}

4.4 反虚拟机实现

// 简单的IO端口反虚拟机检测
VOID SimpleIOPortAntiVM() {
    if (ComprehensiveIOPortDetection()) {
        printf("通过IO端口检测到虚拟机环境!程序即将退出。\n");
        ExitProcess(1);
    }
}

// 多层次IO端口检测
BOOL MultiLayerIOPortDetection() {
    // 第一层:基础VMware检测
    if (DetectVMwareViaIOPort()) {
        return TRUE;
    }
    
    // 第二层:VMware魔术端口检测
    if (DetectVMwareMagicPort()) {
        return TRUE;
    }
    
    // 第三层:VirtualBox检测
    if (DetectVirtualBoxViaIOPort()) {
        return TRUE;
    }
    
    // 第四层:时间检测
    if (CheckPortAccessTiming()) {
        return TRUE;
    }
    
    // 第五层:一致性检测
    if (CheckPortResponseConsistency()) {
        return TRUE;
    }
    
    // 第六层:特殊序列检测
    if (CheckSpecialPortSequences()) {
        return TRUE;
    }
    
    return FALSE;
}

// 增强版反虚拟机检测
VOID EnhancedIOPortAntiVM() {
    // 多次检测
    for (int i = 0; i < 3; i++) {
        if (MultiLayerIOPortDetection()) {
            printf("第%d次IO端口检测发现虚拟机环境!\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("IO端口反虚拟机检测通过。\n");
}

4.5 绕过IO端口检测的方法

// IO端口检测绕过技术
class IOPortObfuscator {
public:
    // 模拟真实硬件端口响应
    static BOOL SimulateRealHardwareResponse() {
        printf("模拟真实硬件端口响应...\n");
        
        // 可以通过修改虚拟机配置或使用Hypervisor功能来模拟真实硬件响应
        
        return FALSE;
    }
    
    // 干扰端口访问
    static BOOL InterfereWithPortAccess() {
        printf("干扰端口访问...\n");
        
        // 可以通过Hook相关API来干扰端口访问检测
        
        return FALSE;
    }
    
    // 修改端口响应时间
    static BOOL ModifyPortResponseTime() {
        printf("修改端口响应时间...\n");
        
        // 调整虚拟机配置使端口访问时间更接近真实硬件
        
        return FALSE;
    }
};

// 综合绕过方法
VOID ComprehensiveIOPortBypass() {
    // 模拟真实硬件响应
    IOPortObfuscator::SimulateRealHardwareResponse();
    
    // 干扰端口访问
    IOPortObfuscator::InterfereWithPortAccess();
    
    // 修改响应时间
    IOPortObfuscator::ModifyPortResponseTime();
    
    printf("IO端口检测绕过完成。\n");
}

4.6 完整测试程序

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

// 前面实现的函数声明
BOOL DetectVMwareViaIOPort();
BOOL DetectVMwareMagicPort();
BOOL DetectVirtualBoxViaIOPort();
BOOL CheckPortAccessTiming();
BOOL CheckPortResponseConsistency();
BOOL MultiLayerIOPortDetection();

// 显示系统架构信息
VOID DisplaySystemArchitectureInfo() {
    printf("=== 系统架构信息 ===\n");
    
#ifdef _WIN64
    printf("系统架构: x64\n");
#else
    printf("系统架构: x86\n");
#endif
    
    // 检查是否支持硬件虚拟化
    int cpuInfo[4];
    __cpuid(cpuInfo, 1);
    
    if (cpuInfo[2] & (1 << 5)) {
        printf("支持硬件虚拟化 (VT-x/AMD-V)\n");
    } else {
        printf("不支持硬件虚拟化\n");
    }
    
    printf("\n");
}

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

// 主程序
int main() {
    srand((unsigned int)time(NULL));
    
    printf("通过IO端口检测虚拟机演示程序\n");
    printf("========================\n\n");
    
    // 显示系统架构信息
    DisplaySystemArchitectureInfo();
    
    // 基础VMware检测
    DetectVMwareViaIOPort();
    
    // VMware魔术端口检测
    DetectVMwareMagicPort();
    
    // VirtualBox检测
    DetectVirtualBoxViaIOPort();
    
    // 时间检测
    CheckPortAccessTiming();
    
    // 一致性检测
    CheckPortResponseConsistency();
    
    // 特殊序列检测
    CheckSpecialPortSequences();
    
    // 性能测试
    PerformanceTest();
    
    // 实际应用示例
    printf("=== 反虚拟机检测 ===\n");
    if (MultiLayerIOPortDetection()) {
        printf("检测到虚拟机环境,执行反虚拟机措施。\n");
        
        // 这里可以执行各种反虚拟机措施
        // 为演示目的,我们只是显示信息而不真正退出
        printf("(演示模式:不实际退出程序)\n");
    } else {
        printf("未检测到虚拟机环境,程序正常运行。\n");
        MessageBoxW(NULL, L"IO端口检测通过,程序正常运行", L"提示", MB_OK);
    }
    
    // 演示绕过方法
    printf("\n=== 绕过演示 ===\n");
    printf("执行IO端口绕过...\n");
    // ComprehensiveIOPortBypass();  // 注释掉以避免实际修改
    
    printf("绕过完成后再次检测:\n");
    if (MultiLayerIOPortDetection()) {
        printf("仍然检测到虚拟机环境。\n");
    } else {
        printf("检测结果显示未发现IO端口异常。\n");
    }
    
    return 0;
}

4.7 高级技巧和注意事项

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

// 综合检测函数
BOOL ComprehensiveIOPortDetectionEnhanced() {
    // 抗干扰检测
    if (AntiTamperIOPortDetection()) {
        return TRUE;
    }
    
    // 多层检测
    if (MultiLayerIOPortDetection()) {
        return TRUE;
    }
    
    return FALSE;
}

// 检查IO权限
BOOL CheckIOPrivileges() {
    printf("=== IO权限检查 ===\n");
    
    // 检查当前进程是否具有IO端口访问权限
    HANDLE hToken;
    if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) {
        TOKEN_PRIVILEGES tp;
        DWORD tpSize = sizeof(tp);
        
        if (GetTokenInformation(hToken, TokenPrivileges, &tp, tpSize, &tpSize)) {
            for (DWORD i = 0; i < tp.PrivilegeCount; i++) {
                char privilegeName[256];
                DWORD nameSize = sizeof(privilegeName);
                
                if (LookupPrivilegeNameA(NULL, &tp.Privileges[i].Luid, privilegeName, &nameSize)) {
                    if (strcmp(privilegeName, "SeLockMemoryPrivilege") == 0) {
                        printf("具有内存锁定权限,可能可以访问IO端口。\n");
                        CloseHandle(hToken);
                        return TRUE;
                    }
                }
            }
        }
        
        CloseHandle(hToken);
    }
    
    printf("可能缺少IO端口访问权限。\n");
    return FALSE;
}

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

五、课后作业

  1. 基础练习

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

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

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

    • 研究x86/x64架构的IO端口机制
    • 了解硬件虚拟化技术
    • 学习现代反虚拟机技术