开启辅助访问
 找回密码
 立即注册

操作系统能否知道自个处于虚拟机中?

aaaa1018 回答数5 浏览数1000
一直在用虚拟机学习linux,突然来了个这个问题
使用道具 举报
| 来自北京
中华先锋 | 来自广东
目前虚拟机环境检测有两个“金标准”,分别是Al-khaser和Pafish。这两个开源项目几乎一网打尽了所有公开常见的VM检测技术。下面简要分析一下它们的技术原理。
一、硬件信息检测
首先大概说说操作系统是怎么知道这台计算机安了哪些设备的。计算机启动的时候,主板固件会给OS传两个信息表,分别是ACPI和SMBIOS。ACPI表有很多部分,其中硬件信息主要集中在DSDT和SSDT这两部分。
ACPI表的每个部分开头都有一个OEM ID和OEM Table ID,这是第一个容易露馅的地方,例如QEMU默认将OEM ID写为BOCHS,将OEM Table ID写为BXPC + 部分名称,如DSDT部分就写成“BXPCDSDT”。
虚拟机的ACPI表中往往会存在一些现实中不存在的硬件,用于主客机间通讯,这是第二个容易露馅的地方,例如QEMU的DSDT表中会有DBUG和FWCF这两个硬件。
计算机中大部分设备的信息并不写在ACPI和SMBIOS表中。像显卡、声卡、网卡、USB这些都属于PCI设备,与PCI控制器相连。PCI控制器本身提供一个接口,可以列出所有检测到的PCI设备。每个PCI设备有四个用来亮明身份的代号,分别是Vendor ID, Device ID, Subsystem ID, Class ID。一般来说,OS检测到PCI设备时,会首先根据Vendor ID和Device ID搜索驱动;如果搜不到驱动,则会根据Class ID查找是否有这一类设备的通用驱动。
虚拟机模拟出的PCI设备,其身份代号往往采用特定数字,这是第三个容易露馅的地方,例如QEMU模拟出的VirtIO设备,其Vendor ID多为0x1AF4。
相比于ACPI,SMBIOS对系统的正常运行没有那么重要。但是Windows的“系统信息”工具显示的内容,多半都来自SMBIOS表,这是第四个容易露馅的地方,例如目前主流的虚拟机固件是OVMF,那么虚拟机里面“系统信息”就会显示出OVMF的信息。
有些恶意软件会通过检测系统是否有风扇和热区域(Thermal Zone)来判断是否处于虚拟环境。目前所有版本的Windows,都是从SMBIOS表中读取风扇信息,从ACPI表中读取Thermal Zone信息。但是正常的商业软件一般不使用此方法判定,因为很多正常的笔记本电脑也没有在SMBIOS表中写入风扇信息。
此外还有硬盘产品名、序列号、声卡ID、网卡MAC地址等容易带有虚拟机特征的地方。
二、CPU信息检测
x86 CPU本身有一条指令叫CPUID,用于探测该CPU所支持的功能,例如是否支持SSE指令集等。有些功能虚拟机无法模拟,就会屏蔽掉相关功能的信息,这是第五个容易露馅的地方
早期虚拟化技术不完善的时候,虚拟机软件需要挪动一些重要数据结构的位置,例如中断表(IDT)等。著名的Red Pill程序就是靠读取这些结构的地址来判定虚拟环境。但是后来有了Intel VT-x等硬件虚拟化技术,以及KVM以后,这些检测方法就基本被淘汰了。
基于KVM的客机,如果将EAX寄存器置为0x40000000,并执行CPUID指令,会在EBX、ECX、EDX寄存器中读取到字符串“KVMKVMKVM”,这是第六个容易露馅的地方
三、驱动信息检测
在有Linux KVM之前,各虚拟机软件用的几乎都是半虚拟化(Paravirtualization),也就是必须对客机软件做一定修改才能在虚拟机中正常运行。例如VMWare虚拟机需要在客机中安装一些驱动程序,这些驱动的信息中都带有VMWare标识,这是第七个容易露馅的地方
KVM实现的是全虚拟化,不需要对客机做任何修改,所以不必担心这些问题。但是,按默认配置的QEMU虚拟机会带有很多VirtIO接口的设备,这些设备的驱动也会留下虚拟机的痕迹,需要当心。
四、计时检测
x86 CPU中有一个精度极高的计时器,称为TSC计时器,可以精确到CPU时钟周期数。那么可以执行一段CPU指令,并将消耗的时间与正常CPU上消耗的时间进行对比,如果明显高于正常值,就可判定处于虚拟机环境。
目前最常用的方法是,在两次读取TSC计时器之间,执行一次CPUID指令。前文说到,虚拟机软件一般会特殊处理CPUID指令,屏蔽掉一些无法模拟的功能的信息,执行这些操作所需的时间远多于正常CPU上执行一次CPUID所需的时间,这是第八个容易露馅的地方
目前没有简单的办法可以骗过计时检测,所有已知方案都需要用特制的Linux内核和特制的QEMU软件配合。
<hr/>但是近几年来,虚拟机环境检测已经没那么重要了。这是因为微软在Windows 10上大力推广Hyper-V技术,有相当数量的用户自己都不知道自己处于虚拟机环境。例如,只要在Windows 10 Home的Windows Defender中开启Memory Integrity或Core Isolation功能,就等同于开启Hyper-V,并让Windows运行在虚拟机环境下。
于是有很多网游反作弊系统,只要检测到开启了Hyper-V,就放弃检测虚拟机环境。于是在Linux界就有了一种神奇的操作,先用KVM开Windows虚拟机,然后在Windows中开启Hyper-V,这样就能愉快地玩各种3A巨作了。
当然这种操作需要CPU和Hypervisor支持Nested Virtualization,然而Windows的Hyper-V长期以来不支持AMD的Nesting,会在启动时卡死。直到2020年6月,微软才宣布Windows 10 Insider的Hyper-V开始支持嵌套虚拟化。最近国外Reddit上有人报告使用5.11.6版本的Linux内核(无需打补丁),配合4.2版本的QEMU,可以在AMD系统上正常启动Windows 10 20H2并开启Hyper-V,可能是Linux KVM那边做了改进。
<hr/>既然这么多人看我就再写一写怎么绕开这些虚拟机检测方法。我日常电脑装的是Linux,平时用Linux KVM + QEMU方案跑Windows虚拟机。QEMU是开源的虚拟机Hypervisor,命令行参数非常灵活,有另一个开源项目libvirt专门帮助配置QEMU的参数。下面的内容都是基于KVM + QEMU + libvirt。我从简单的操作写起,比较难隐藏的东西放到后面。
1)CPU信息:打开libvirt的XML配置,找到<cpu>段落,将mode设为host-passthrough,然后段落里面添加一行
<feature policy="disable" name="hypervisor"/>找到<os>段落,里面添加一行
<smbios mode="host"/>2)KVM Hypervisor信息:XML配置中找到<features>段落,里面添加几行
<hyperv>
  <vendor_id state="on" value="random"/>
</hyperv>
<kvm>
  <hidden state="on"/>
</kvm>3)硬盘产品名、序列号:将Disk bus设为SCSI,Serial随便填写,添加一个SCSI Controller,Model填为saslsi1068,然后在XML配置中找到<disk>段落,在里面添加几行
<vendor>Samsung</vendor>
<product>20GB Harddisk</product>此处Vendor和Product可随便填写
4)网卡:NIC Device Model选rtl8139,然后用这个网站随机生成一个MAC地址填进去。
5)QEMU硬编码的ID信息:修改这些ID有两种方法,一种是下载QEMU源代码,修改硬编码ID后重新编译;另一种是直接用radare2等二进制修改工具,在QEMU可执行文件上打补丁。下面列出应当隐藏的硬编码ID对应的代码位置。(QEMU源代码见github)
ACPI OEM ID:见hw/acpi/aml-build.c中的build_header函数
ACPI DSDT中的FWCF设备:见hw/i386/acpi-build.c中的build_dsdt函数
ACPI DSDT中的DBUG设备:见hw/i386/acpi-build.c中的build_dbg_aml函数
常规PCI设备ID:见hw/pci/pci.c中的pci_set_default_subsystem_id函数
VGA设备ID:见hw/display/vga-pci.c中的vga_pci_class_init函数
声卡ID:见hw/audio/hda-codec-common.h中所有含有QEMU_HDA_ID_*宏的数据结构
VirtIO Serial设备ID:见hw/virtio/virtio-serial-pci.c中的virtio_serial_pci_class_init函数
6)风扇和热区域:利用QEMU的-acpitable参数,伪造一个ACPI SSDT表,里面填入一个Thermal Zone的信息;再利用QEMU的-smbios参数,伪造一个SMBIOS Entry Type 27,里面填入一个风扇的信息。
ACPI标准第11.7节提供了ACPI Thermal Zone的例子
SMBIOS标准第7.28节提供了SMBIOS Entry Type 27的格式说明
7)计时检测:这个绕过很麻烦,需要重新编译Linux内核,主要原理是每当遇到CPUID这种需要Hypervisor专门处理的指令时,都把TSC计时器的当前值减掉一定量,这样就抵消掉处理这些指令花掉的时间。
详见Reddit上相关讨论:
https://www.reddit.com/r/VFIO/comments/g4pqkq/rdtsc_detection_workarounds/
https://www.reddit.com/r/VFIO/comments/i071qx/spoof_and_make_your_vm_undetectable_no_more/
https://www.reddit.com/r/VFIO/comments/i1xbue/here_is_my_take_on_rdtsc_offsetting/
回复
使用道具 举报
冷眼看烟花 | 来自北京
可以,常规情况下Hypervisor会通知操作系统"你在虚拟化环境里"
但是,即使是刻意隐藏,也是有破绽的
有一对很知名的项目:Blue Pill和Red Pill (后来还有个New Red Pill),其中蓝色药片着重于使用虚拟化技术对操作系统,安全软件,反外挂,反调试系统进行欺骗,来进行一些底层操作,而红色药片着重于识别当前是否处于被虚拟化的环境中,一些小众的分支还会尝试对虚拟化环境进行攻击,他一般常见于各种反外挂程序中。
具体他们的原理都很简单,BP会尽可能的隐藏自己和正常环境的差别,而RP则利用一些虚拟化无法处理的事件或者故意预留的后门,并且对比当前机器的行为和已知的虚拟机或者真实系统的差异点来判断自己是否在虚拟化环境中
回复
使用道具 举报
天使乱步 | 来自北京
IBM 曾经有一种黑科技产品:https://www.ebay.com/itm/IBM-2890-9406-Integrated-Netfinity-700Mhz-Sub-23L4306/293119110344?hash=item443f42acc8:g:PNYAAOSwOSddATga


在400小型机里加上一块 x86的cpu卡,它就是把真实的windows运行在真实的x86的cpu卡上。它有全部的PC物理硬件、包括显示器、键盘、鼠标和网卡,从硬件看没有破绽。但是它又小型机的宿主操作系统中作为硬分区存在。
不要想买来自己玩了。驱动这个需要有特定型号的小机(都是洗衣机那么大的,没有苗条的,超贵)、还要独占一块全功能IOP卡、和软件lic(很难搞) 。

说实话我不太理解这个硬件有什么实际意义?为什么客户非要花一百倍的钱买台小型机去执行PC机的exe程序?
回复
使用道具 举报
sammy_leung | 来自北京
有能让 Guest 完全区分不出来的虚拟机——即 Cycle-accurate Emulator,不过这个太慢了,正常软件不会做到这么极端
回复
使用道具 举报
globbs | 未知
虚拟化分为完全虚拟化(full virtualization)和准虚拟化(para-virtualization)两种,完全虚拟化意思是,guest OS不需要进行任何定制,就可以在hypervisor中运行,例如KVM、VirtualBox;准虚拟化则表示guest OS必须进行定制,把一些硬件操作替换成对VMM的调用,例如XEN。
准虚拟化环境中,guest OS一定知道自己在虚拟机中,如果不知道就没法正常运行了。在x86引入硬件虚拟化之前,XEN一直是x86虚拟化的唯一方案。
而在完全虚拟化环境中,并没有100%可靠的办法,因为这种虚拟化的目的就是模拟真实机器,让guest OS感觉不到差别。
但是,也存在一些办法可以进行猜测。例如OS可以探测pci总线上的外设,如果发现某些外设的vendor是VMware、VirtualBox这类,那么很有可能是在虚拟机里面。此外还可以获取硬盘驱动器的product string。
在x86架构下,还可以使用cpuid(eax=1)辅助判断。Intel文档中说,ecx最高比特“not used, always returns 0”并没有规定edx最高位比特的含义,但是微软将这个空闲的bit利用了起来,HyperV环境可以用这个比特来判断。
上面这些方法都是虚拟机故意留下一些痕迹,让OS可以发现。如果虚拟机不想被发现,就只能借助外部时钟源进行timing attack。例如,有一种rootkit叫做Blue Pill(蓝色药丸),本身就是一个轻量级的hypervisor,处在硬件和操作系统之间,可以拦截操作系统的全部硬件交互。检测这类的恶意软件,常规方法显然是不行的。
回复
使用道具 举报
快速回复
您需要登录后才可以回帖 登录 | 立即注册

当贝投影