在 WebHostingTalk 论坛上有些国外奸商会把虚拟机当作独立服务器卖,去年7月份的时候就有一位中国同胞上当受骗,并在 WHT 上发帖声讨,证据确凿,甚至连服务商自己也承认,回帖达355篇。这家独立服务器/VPS 提供商 HostATree.com 居然大胆的把 OpenVZ VPS 这种一看就知道是虚拟机的虚拟机当作独立服务器卖,晕,至少也要弄个 VMWare/KVM/Xen HVM 吧(更难发现是虚拟机),用 OpenVZ 这种容器也太欺负人了:)昨天恰好收到网友一封邮件问到了如何判断自己买的是独立服务器还是虚拟机的问题。这里 VPSee 简单介绍一下市面上常用虚拟技术(包括容器技术)的判别小技巧。
判断 OpenVZ/Xen PV/UML
判断 OpenVZ/Xen PV/UML 是最容易的,直接检查 /proc 下的相关目录和文件就可以知道,比如 OpenVZ VPS 上会有 /proc/vz 这个文件;Xen PV 虚拟机上会有 /proc/xen/ 这个目录,并且目录下有一些东西;UML 上打印 /proc/cpuinfo 会找到 UML 标志。写了一个简单的 Python 脚本来检测:
#!/usr/bin/python # check if a linux system running on a virtual machine (openvz/xen pv/uml) # written by http://www.vpsee.com import sys, os def main(): if os.getuid() != 0: print "must be run as root" sys.exit(0) # check OpenVZ/Virtuozzo if os.path.exists("/proc/vz"): if not os.path.exists("/proc/bc"): print "openvz container" else: print "openvz node" # check Xen if os.path.exists("/proc/xen/capabilities"): if (os.path.getsize("/proc/xen/capabilities") > 0): print "xen dom0" else: print "xen domU" # check User Mode Linux (UML) f = open("/proc/cpuinfo", "r"); t = f.read(); f.close() if (t.find("UML") > 0): print "uml" if __name__=="__main__": main()
判断 VMware/Xen HVM/KVM
如果使用的是 VMware/Xen HVM/KVM 这样的全虚拟就更难判断一些,最准确的办法是读取 CPUID 来判断,Xen 源代码下面有一段检测是否是 Xen 的 C 语言代码 tools/misc/xen-detect.c,这段代码提供了一个很好的例子,VPSee 重写了代码,用宏替代了函数,增加了对 VMware 和 KVM 的识别,用 gcc 编译后就可以运行:
/****************************************************************************** | |
* xen_detect.c | |
* | |
* Simple GNU C / POSIX application to detect execution on Xen VMM platform. | |
* | |
* Copyright (c) 2007, XenSource Inc. | |
* | |
* Permission is hereby granted, free of charge, to any person obtaining a copy | |
* of this software and associated documentation files (the "Software"), to | |
* deal in the Software without restriction, including without limitation the | |
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | |
* sell copies of the Software, and to permit persons to whom the Software is | |
* furnished to do so, subject to the following conditions: | |
* | |
* The above copyright notice and this permission notice shall be included in | |
* all copies or substantial portions of the Software. | |
* | |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |
* DEALINGS IN THE SOFTWARE. | |
*/ | |
#include <stdint.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <sys/types.h> | |
#include <sys/wait.h> | |
#include <unistd.h> | |
static int pv_context; | |
static void cpuid(uint32_t idx, | |
uint32_t *eax, | |
uint32_t *ebx, | |
uint32_t *ecx, | |
uint32_t *edx) | |
{ | |
asm volatile ( | |
"test %1,%1 ; jz 1f ; ud2a ; .ascii \"xen\" ; 1: cpuid" | |
: "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx) | |
: "0" (idx), "1" (pv_context) ); | |
} | |
static int check_for_xen(void) | |
{ | |
uint32_t eax, ebx, ecx, edx; | |
char signature[13]; | |
uint32_t base; | |
for ( base = 0x40000000; base < 0x40001000; base += 0x100 ) | |
{ | |
cpuid(base, &eax, &ebx, &ecx, &edx); | |
*(uint32_t *)(signature + 0) = ebx; | |
*(uint32_t *)(signature + 4) = ecx; | |
*(uint32_t *)(signature + 8) = edx; | |
signature[12] = '\0'; | |
if ( !strcmp("XenVMMXenVMM", signature) && (eax >= (base + 2)) ) | |
goto found; | |
} | |
return 0; | |
found: | |
cpuid(base + 1, &eax, &ebx, &ecx, &edx); | |
printf("Running in %s context on Xen v%d.%d.\n", | |
pv_context ? "PV" : "HVM", (uint16_t)(eax >> 16), (uint16_t)eax); | |
return 1; | |
} | |
int main(void) | |
{ | |
pid_t pid; | |
int status; | |
uint32_t dummy; | |
/* Check for execution in HVM context. */ | |
if ( check_for_xen() ) | |
return 0; | |
/* Now we check for execution in PV context. */ | |
pv_context = 1; | |
/* | |
* Fork a child to test the paravirtualised CPUID instruction. | |
* If executed outside Xen PV context, the extended opcode will fault. | |
*/ | |
pid = fork(); | |
switch ( pid ) | |
{ | |
case 0: | |
/* Child: test paravirtualised CPUID opcode and then exit cleanly. */ | |
cpuid(0x40000000, &dummy, &dummy, &dummy, &dummy); | |
exit(0); | |
case -1: | |
fprintf(stderr, "Fork failed.\n"); | |
return 0; | |
} | |
/* | |
* Parent waits for child to terminate and checks for clean exit. | |
* Only if the exit is clean is it safe for us to try the extended CPUID. | |
*/ | |
waitpid(pid, &status, 0); | |
if ( WIFEXITED(status) && check_for_xen() ) | |
return 0; | |
printf("Not running on Xen.\n"); | |
return 0; | |
} |
判断 VirtualBox/Virtual PC
什么?这种家用桌面虚拟机自己装的还会不知道?!如果不知道的话也有办法,在 Linux 下运行 dmidecode 工具然后查找 Manufacturer: innotek GmbH, Manufacturer: Microsoft Corporation 关键字就能对应上 VirtualBox 和 Virtual PC.