有没有办法从VM内识别您的代码是否在VM内运行?
我猜大概有一些简单的方法可以识别特定的VM系统,尤其是在VM已安装了提供程序的扩展名(例如VirtualBox或VMWare)的情况下。 但是,是否有一种通用方法可以确定您不是直接在CPU上运行?
关于此的大量研究致力于检测所谓的"蓝色药丸"攻击,即恶意的管理程序正在积极尝试逃避检测。
检测VM的经典技巧是填充ITLB,运行必须虚拟化的指令(当它将对虚拟机管理程序的控制权交给虚拟机时,必须清除此类处理器状态),然后运行更多代码以检测ITLB是否仍在填充。它的第一篇论文位于这里,研究人员的博客和替代Wayback Machine对该博客文章的链接进行了相当丰富的解释(图像已损坏)。
讨论的底线是,总有一种方法可以检测到恶意的虚拟机管理程序,而检测未隐藏的虚拟机管理程序要简单得多。
红帽有一个程序可以检测正在哪个虚拟化产品下运行(virt-what)。
从长远来看,使用这种第三方维护的工具比尝试使用自己的检测逻辑(例如,更多的眼睛(针对更多的虚拟化产品进行测试)等等)是一种更好的策略。
一种更经验的方法是检查已知的VM设备驱动程序。您可以编写WMI查询来定位VMware显示适配器,磁盘驱动器,网络适配器等。如果您只需要担心环境中已知的VM主机类型,那么这将是合适的。这是在Perl中执行此操作的示例,可以将其移植到您选择的语言中。
这取决于您所追求的:
-
如果虚拟机不是故意向您隐藏,则可以使用一些已知的挂钩。喜欢寻找VmWare驱动程序或内存中是否存在某些字符串或某些其他迹象。
-
如果VM确实希望您为它做一些特殊的事情,它将有一些明显的挂钩,例如修改处理器的ID或添加一些您可以访问以检测它的特殊寄存器。或者在内存中一个已知的位置放置一个特殊的设备(假定您可以原始访问世界的物理内存空间)。注意,像IBM Power6和Sun UltraSparc T1 / T2这样的现代机器设计总是设计为运行虚拟机监控程序,而不是直接在原始硬件上运行。操作系统使用的"硬件"接口实际上是虚拟机管理程序软件层的接口,无法绕开它。在这种情况下,检测是微不足道的,因为它是恒定的"是"。对于所有负担得起的计算机系统来说,这是未来的可能方向,例如,以飞思卡尔QorIQ P4080芯片等最新设计的支持为例(www.freescale.com/qoriq)。
-
如果虚拟机是有意试图隐藏的,而您正在追逐它的存在,那它是一种猫捉老鼠的游戏,其中,虚拟机的时序干扰和不同的性能特征几乎总是会把它丢掉。显然,这取决于VM的实现方式以及体系结构中有多少硬件支持(我认为zSeries大型机比常规的x86更好地隐藏了特定OS下的VM或VM堆栈的存在)例如,机器)。有关此主题的一些讨论,请参见http://jakob.engbloms.se/archives/97。可以尝试将其隐藏为VM,但是如果尝试足够努力,检测很可能总是会获胜。
这是一个(java + Windows)解决方案,用于识别基础计算机是物理的还是虚拟的。
虚拟机示例:
制造商
-
en
-
微软公司
-
innotek GmbH
-
红色的帽子
-
VMware,Inc.
模型
-
HVM domU
-
虚拟机
-
虚拟盒子
-
虚拟机
-
VMware虚拟平台
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
public abstract class OSUtil {
public static final List<String> readCmdOutput(String command) {
List<String> result = new ArrayList<>();
try {
Process p=Runtime.getRuntime().exec("cmd /c" + command);
p.waitFor();
BufferedReader reader=new BufferedReader(
new InputStreamReader(p.getInputStream())
);
String line;
while((line = reader.readLine()) != null) {
if(line != null && !line.trim().isEmpty()) {
result.add(line);
}
}
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
public static final String readCmdOutput(String command, int lineNumber) {
List<String> result = readCmdOutput(command);
if(result.size() < lineNumber) {
return null;
}
return result.get(lineNumber - 1);
}
public static final String getBiosSerial() {
return readCmdOutput("WMIC BIOS GET SERIALNUMBER", 2);
}
public static final String getHardwareModel() {
return readCmdOutput("WMIC COMPUTERSYSTEM GET MODEL", 2);
}
public static final String getHardwareManufacturer() {
return readCmdOutput("WMIC COMPUTERSYSTEM GET MANUFACTURER", 2);
}
public static void main(String[] args) {
System.out.println("BIOS Serial:" + getBiosSerial());
System.out.println("Hardware Model:" + getHardwareModel());
System.out.println("Hardware Manufacturer:" + getHardwareManufacturer());
}
} |
您可以使用输出来确定它是VM还是物理机:
物理机输出:
BIOS序列号:2HC3J12
硬件型号:Inspiron 7570
硬件制造商:Dell Inc.
虚拟机输出:
BIOS序列号:0
硬件型号:Innotec GmBH
硬件制造商:虚拟盒子
我曾经遇到一个汇编代码片段,该片段告诉您您是否在VM中。...我用Google搜索,但找不到原始文章。
我确实发现了这一点:检测您的程序是否在虚拟机中运行。
希望能帮助到你。
在大多数情况下,您不应该尝试这样做。除了少数特定情况外,您无需关心是否有人在VM中运行您的代码。
如果需要,在Linux中,最常见的方法是查看/sys/devices/virtual/dmi/id/product_name,它将在大多数实际系统上列出笔记本电脑/主板的名称,并在大多数虚拟系统上列出虚拟机监控程序。 dmidecode | grep Product是另一种常用方法,但是我认为这需要root访问。
在Linux系统中,您可以尝试在/ proc上搜索公用文件。
例如,存在的/ proc / vz /告诉您是一个OpenVZ。
这是完整的指南,用于检测Linux下VM的环境,而无需"喝药" :)
TrapKIT提供了ScoopyNG(一种用于VMware识别的工具),它试图解决规避技术,但不一定要针对VMware以外的任何虚拟化软件。源代码和二进制文件均可用。
通过查看网络连接的MAC地址,您也许可以识别您是否在虚拟机中。例如,Xen通常建议使用特定范围的地址00:16:3e:xx:xx:xx。
这不能保证,因为系统管理员可以指定他们喜欢的MAC地址。
一个很好的例子是,显然是在对主板制造商进行WMI查询,如果返回" Microsoft",则表示您在VM中。以为我相信这仅适用于VMWare。每种VM主机软件可能有不同的判断方法。
本文http://blogs.technet.com/jhoward/archive/2005/07/26/407958.aspx提供了一些不错的建议,并提供了一些链接来检测您是否在VM中(VMWare和VirtualPC位于最小)。
如果VM能够很好地完成工作,则客户端应该看不到它已被虚拟化。但是,可以看看其他线索。
我可以想象,寻找特定于VM环境的已知驱动程序或软件将是最好的方法。
例如,在运行Windows的VMWare客户端上,vmxnet.sys将是网络驱动程序,显示为VMware加速的AMD PCNet适配器。