在linux上检测VMM

时间:2010-09-08 14:33:51

标签: c linux

我正在尝试检测我是否在虚拟环境(vmware,virtualbox等)上运行 在Windows上我使用了几个ASM但我不能在Linux中使用它们,主要是因为代码可以在32位或64位Linux上编译和运行。

以下代码适用于Windows 32和64,并在VMWare,virtualbox和其他虚拟机上进行了测试:

#include <stdio.h> 

int idtCheck () 
{ 
    unsigned char m[2]; 
    __asm sidt m; 
    printf("IDTR: %2.2x %2.2x\n", m[0], m[1]); 
    return (m[1]>0xd0) ? 1 : 0; 
} 

int gdtCheck() 
{ 
    unsigned char m[2]; 
    __asm sgdt m; 
    printf("GDTR: %2.2x %2.2x\n", m[0], m[1]);
    return (m[1]>0xd0) ? 1 : 0; 
} 

int ldtCheck() 
{ 
    unsigned char m[2]; 
    __asm sldt m; 
    printf("LDTR: %2.2x %2.2x\n", m[0], m[1]); 
    return (m[0] != 0x00 && m[1] != 0x00) ? 1 : 0; 
} 

int main(int argc, char * argv[]) 
{ 
    idtCheck(); 
    gdtCheck(); 

    if (ldtCheck()) 
        printf("Virtual Machine detected.\n"); 
    else 
        printf("Native machine detected.\n"); 

    return 0; 
}

现在,海湾合作委员会抱怨所有职能的__asm。我尝试过使用asm(), asm 以及我过去使用的其他形式,但都没有用。有什么想法吗?

1 个答案:

答案 0 :(得分:4)

好吧,我没有在那里反汇编机器代码,但这是一个使用GCC内联汇编程序的版本:

int redpill()
{
        unsigned char idt_addr[(sizeof(long)==8)?10:6];
        __asm__("SIDT (%[ptr])"
                        : "=m" (idt_addr)
                        : [ptr] "r" (&idt_addr));
        // examine high order byte
        return idt_addr[(sizeof(long)==8)?9:5] > 0xd0;
}

这个应该'工作',即使是64位,但我没有在那里测试过。

无论其!这不能保证提供您想要的结果, at all 。首先,它不适用于硬件虚拟化,因为您无法看到真正的IDT。其次,它依赖于VMWare和Virtual PC的实现细节,可能很容易改变。如果您的操作系统决定将其IDT置于高地址,它甚至可能会启动错误警报。所以我根本不推荐这种方法。

对于使用VMX硬件支持的虚拟机,最好的办法是做一些应该在硬件上快速但需要虚拟机中的陷阱的东西,并检查时间。像CPUID之类的东西是个不错的选择;在虚拟机和实际硬件上对其进行基准测试(与执行ADD的虚拟环路或处理不同时钟速率的虚拟环路进行比较),并查看测试机器与哪个配置文件更匹配。由于每个CPUID必须退出VM以询问主机内核它想要呈现的功能,因此它将比实际硬件花费更长的时间。

对于其他类型的虚拟机,您只需加载控制寄存器或调试寄存器即可进行类似的时序检查 - 虚拟机必须捕获它,或用模拟代码替换它。如果您使用的是VMware,它可能会用一些模拟代码替换陷阱 - 在这种情况下,您可以通过定时修改包含控制寄存器的代码或调试寄存器操作来识别它。这将迫使VM使其缓存的代码无效,从而需要一个昂贵的VM陷阱,这将显示在您的基准测试中。

请注意,这两种方法都需要操作系统内核的帮助 - 如果您无法控制模拟,那么很难确定您是否在 中内核至少。如果VM非常复杂,它也可能会伪造时间,此时事情变得非常困难 - 但这往往会破坏性能,并导致时钟漂移(如果您可以连接到互联网并查询时间,则很容易识别服务器某处!),所以大多数商业虚拟机都没有这样做。