有没有办法从 VM 中识别您的代码在 VM 中运行?
我想有或多或少简单的方法来识别特定的 VM 系统,特别是如果 VM 安装了提供程序的扩展(例如 VirtualBox 或 VMWare)。但是有没有一种通用的方法来识别你没有直接在 CPU 上运行?
有没有办法从 VM 中识别您的代码在 VM 中运行?
我想有或多或少简单的方法来识别特定的 VM 系统,特别是如果 VM 安装了提供程序的扩展(例如 VirtualBox 或 VMWare)。但是有没有一种通用的方法来识别你没有直接在 CPU 上运行?
很多这方面的研究都致力于检测所谓的“蓝色药丸”攻击,即积极试图逃避检测的恶意管理程序。
检测 VM 的经典技巧是填充 ITLB,运行必须虚拟化的指令(当它将控制权交给管理程序时,这必然会清除此类处理器状态),然后运行更多代码来检测 ITLB 是否仍然填充. 关于它的第一篇论文位于此处,研究人员的博客和替代Wayback Machine 链接到博客文章的一个相当丰富多彩的解释(图像损坏) 。
对此讨论的底线是,总有一种方法可以检测到恶意虚拟机管理程序,而且检测不试图隐藏的虚拟机管理程序要简单得多。
Red Hat 有一个程序可以检测它在哪个(如果有的话)虚拟化产品下运行:virt-what
.
从长远来看,使用第三方维护的工具比尝试推出自己的检测逻辑更好:更多的眼睛(针对更多的虚拟化产品进行测试)等。
一种更具经验性的方法是检查已知的 VM 设备驱动程序。您可以编写 WMI 查询来定位 VMware 显示适配器、磁盘驱动器、网络适配器等。如果您知道您只需要担心环境中已知的 VM 主机类型,这将是合适的。这是在 Perl 中执行此操作的示例,可以将其移植到您选择的语言。
这取决于你追求的是什么:
如果虚拟机不是故意隐藏的,你可以使用一些已知的钩子。比如寻找 VmWare 驱动程序或内存中是否存在某些字符串或某些其他迹象。
如果虚拟机真的希望你为它做一些特殊的事情,它会有一些明显的钩子,比如修改处理器的 ID 或添加一些你可以访问的特殊寄存器来检测它。或者是内存中已知位置的特殊设备(假设您可以原始访问您的世界的物理内存空间)。请注意,IBM Power6 和 Sun UltraSparc T1/T2 等现代机器设计旨在始终运行虚拟机管理程序,而不是直接在原始硬件上运行。操作系统使用的“硬件”接口实际上是管理程序软件层的接口,无法绕过它。在这种情况下,检测是微不足道的,因为它是一个恒定的“是”。对于所有能够承受开销的计算机系统来说,这可能是未来的方向,
如果 VM 故意试图隐藏,而您正在追逐它的存在,那么这就是一场猫捉老鼠的游戏,VM 的时间干扰和不同的性能配置文件几乎总是会泄露它。显然,这取决于 VM 是如何实现的,以及架构中有多少硬件支持(我认为 zSeries 大型机在隐藏特定操作系统下的 VM 或 VM 堆栈方面比常规 x86 更好)机器是,例如)。有关此主题的一些讨论,请参见http://jakob.engbloms.se/archives/97。可以尝试隐藏为 VM,但如果足够努力,检测很可能总是获胜。
在大多数情况下,您不应该尝试。您不应该关心是否有人在 VM 中运行您的代码,除非在少数特定情况下。
如果需要,在 Linux 中最常见的方法是查看/sys/devices/virtual/dmi/id/product_name
,它将列出大多数真实系统上的笔记本电脑/主板的名称,以及大多数虚拟系统上的虚拟机管理程序。dmidecode | grep Product
是另一种常用方法,但我认为这需要 root 访问权限。
我曾经遇到过一个汇编代码片段,它告诉你是否在虚拟机中……我用谷歌搜索但找不到原始文章。
我确实发现了这一点:Detect if your program is running inside a Virtual Machine。
希望能帮助到你。
这是一个(java + windows)解决方案,用于识别底层机器是物理机还是虚拟机。
虚拟机示例:
制造商
模型
VMware 虚拟平台
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());
}
}
您可以使用输出来确定它是虚拟机还是物理机:
物理机输出:
BIOS 序列号:2HC3J12
硬件型号:Inspiron 7570
硬件制造商:Dell Inc.
虚拟机输出:
BIOS 序列号:0
硬件型号:Innotec GmBH
硬件制造商:Virtual Box
如果它的虚拟机做得很好,它应该对客户端不可见,它正在被虚拟化。但是,可以查看其他线索。
我想寻找特定于 VM 环境的已知驱动程序或软件将是最好的方法。
例如,在运行 Windows 的 VMWare 客户端上,vmxnet.sys 将是网络驱动程序,显示为 VMware 加速 AMD PCNet 适配器。
一个很好的例子是,显然对主板制造商进行了 WMI 查询,如果它返回“Microsoft”,那么你就在 VM 中。以为我相信这仅适用于VMWare。对于每个 VM 主机软件,可能有不同的方法来判断。
这篇文章在这里http://blogs.technet.com/jhoward/archive/2005/07/26/407958.aspx有一些很好的建议和链接到几种方法来检测你是否在虚拟机中(VMWare 和 VirtualPC 在至少)。
您可以通过查看网络连接的 MAC 地址来确定您是否在虚拟机中。例如,Xen 通常建议使用特定范围的地址 00:16:3e:xx:xx:xx。
这不能保证,因为系统管理员可以指定他们喜欢的 MAC 地址。
TrapKIT 提供了 ScoopyNG,这是一种 VMware 识别工具——它试图绕过规避技术,但不一定针对 VMware 以外的任何虚拟化软件。源代码和二进制文件都可用。