我正在尝试检测我是否在虚拟环境(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;
}

现在,gcc抱怨所有函数上的asm。我试过asm()、asm和其他我过去用过的表单,但都没有成功。有什么想法吗?

最佳答案

好吧,我还没有在那里反汇编机器代码,但这里有一个使用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位的,这也应该是有效的,但我还没有在那里测试过。
然而!这根本不能保证给你想要的结果。首先,它不适用于硬件虚拟化,因为您看不到真正的idt。其次,它依赖于vmware和虚拟pc的一个实现细节,这可能很容易改变。如果你的操作系统决定把它的idt放在一个高地址,它甚至可能触发错误警报。所以我完全不推荐这种方法。
对于使用vmx硬件支持的虚拟机,您最好的选择可能是做一些在硬件上应该很快但需要在虚拟机中设置陷阱的事情,并检查时机。像CPUID这样的方法是个不错的选择;在虚拟机和实际硬件上对其进行基准测试(与执行加法或处理不同时钟速率的虚拟循环相比),并查看测试机器的哪个配置文件更接近匹配。由于每个CPUID必须退出虚拟机,以询问主机内核想要呈现什么能力,这将比实际硬件花费更长的时间。
对于其他类型的虚拟机,只需加载控制寄存器或调试寄存器就可以执行类似的计时检查—虚拟机将不得不捕获它,或者用模拟代码替换它。如果您使用的是类似于vmware的软件,它可能会用一些模拟代码替换陷阱——在本例中,您可以通过计时修改包含控制寄存器或调试寄存器操作的代码来识别它。这将迫使vm使其缓存的代码失效,这就需要vm有一个昂贵的陷阱,它将出现在您的基准测试中。
请注意,这两种方法都需要操作系统内核的帮助-如果您至少没有对模拟内核的控制权,将很难确定您是否在vm中。如果虚拟机真的很复杂,它可能也会伪造时间,在这一点上事情变得非常困难-但这往往会扼杀性能,并导致时钟漂移(如果您可以连接到Internet并在某处查询时间服务器,则很容易识别!),所以大多数商业虚拟机不会这样做。

关于c - 在Linux上检测VMM,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/3668804/

10-13 06:18