我正在尝试检测我是否在虚拟环境(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 和 Virtual PC 的实现细节,这可能很容易改变。如果您的操作系统决定将其 IDT 放在高地址,它甚至可能会发出错误警报。所以我完全不推荐这种方法。
对于使用 VMX 硬件支持的虚拟机,您最好的选择可能是做一些在硬件上应该很快但需要在虚拟机中捕获的东西,并检查时间。像 CPUID
这样的东西是个不错的选择;在 VM 和真实硬件上对其进行基准测试(与执行 ADD 或处理不同时钟速率的虚拟循环相比),并查看测试机器更接近哪个配置文件。由于每个 CPUID
都必须退出 VM 以询问主机内核它想要呈现什么功能,因此这将比在真实硬件上花费更长的时间。
对于其他类型的虚拟机,您可以简单地通过加载控制寄存器或调试寄存器来进行类似的时序检查——VM 必须捕获它,或用模拟代码替换它。如果您使用的是像 VMware 这样的东西,它可能会用一些模拟代码替换陷阱 - 在这种情况下,您可以通过定时修改包含控制寄存器或调试寄存器操作的代码来识别它.这将迫使 VM 使其缓存的代码无效,从而对 VM 造成昂贵的陷阱,这将显示在您的基准测试中。
请注意,这两种方法都需要操作系统内核的帮助 - 如果您无法控制模拟的内容,将非常难以确定您是否在虚拟机中内核至少。如果 VM 真的很复杂,它也可能会伪造计时,这时事情会变得非常困难 - 但这往往会降低性能,并导致时钟漂移(很容易识别您是否可以连接到互联网并查询时间服务器某处!),因此大多数商业虚拟机不这样做。
关于c - 在 Linux 上检测 VMM,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3668804/