There are, of course, many ways to detect x86 processor virtual machines. It is a truism, often ignored by operating system developers and others, that x86 virtual machines don't set out to exactly duplicate real hardware. Indeed, the paper by Garfinkel et al.1 not only points out this truism but describes some of the many unofficial ways in which it is possible to detect that one is running in a virtual machine.
Unofficial detection mechanisms rely upon one of several factors:
Virtual x86 machines, as said, don't actually set out to exactly duplicate real hardware, and so often appear to be machines that wouldn't exist as real hardware (such as a combination of processor, RAM, and support chips that would not interoperate in reality).
Virtual x86 machine implementations are either incomplete or buggy.
The infamous SIDT
detection mechanism relies upon this, for
example.
Virtual x86 machine implementations don't run as fast as, or as uniformly in terms of instruction execution speed, as real hardware.
There are plenty of unofficial x86 virtual machine detection mechanisms around, often passed on by word of mouth. Equally as often, these mechanisms are highly specific to one virtual machine, or even to one version of the virtualization software (and its concomitant bugs). There are, however, official detection mechanisms for many x86 virtual machines. It is best to use those.
The official detection method for detecting whether one's code is running
in a Virtual DOS Machine (or a Virtual Machine Boot, for that matter) in
OS/2 is to issue INT 0x2F
with the AX
register set to 0x4010
. If the code is executing in a VDM,
the AX
register value after the interrupt will be zero. (The
default action for an unrecognized INT 0x2F
call is to
leave AX
unchanged, of course. But the proper test is to
test for equality to zero, not to test for inequality to
0x4010
.) As a bonus, the BX
register, if
AX
is zero, contains an encoding of the version number of
OS/2.
The official detection method for detecting whether one's code is running
in a DOS Box in DOS-Windows 3.1
is to issue INT 0x2F
with the AX
register set to 0x160A
. If the code is executing in a DOS Box,
the AX
register value after the interrupt will be zero. (The
same applies here as discussed for the OS/2 VDM check.) As a bonus, the BX
register, if
AX
is zero, contains an encoding of the version number of
DOS-Windows, and the CX
register indicates either Standard
mode (with value 0x0002
) or 386 Enhanced mode (with value
0x0003
).
The official detection method for detecting whether one's code is running
in a xen, VMWare, KVM, or Microsoft Hypervisor virtual machine is to use
the CPUID
instruction. In addition to supporting either the
behaviour of an
Intel2
or an
AMD3
processor, such virtual machine softwares explicitly adhere to a common
extension protocol to both.
According to Microsoft4,
a flag bit in the ECX
register (bit
#31, "Hypervisor present"), after executing CPUID
with the
EAX
register set to 0x000000001
, will be set to
1 in a (Microsoft) virtual machine and set to 0 on real hardware.
This is indeed the official Hypervisor detection mechanism.
It's also the official detection mechanism for
VMWare6.
But here Microsoft and VMWare are
incorrectly relying upon an accident of hardware implementation.
Both
Intel's2
and
AMD's3
CPUID specifications state that bit #31 of the ECX
register
is reserved. Intel's specification even explicitly states that one
should not count on the value of the bit. That includes not
counting on the fact of it being zero on real hardware. As such,
Microsoft's "official" detection mechanism is bogus.
The proper official detection mechanism is to follow the
bit #31 check by executing CPUID
with the EAX
register set to
0x40000000
5.
The output EAX
register should be a number between
0x40000001
and 0x400000FF
(as all
protocol-compliant virtual machines are required to implement at least
functions 0x40000000
and 0x40000001
).
Even this is slightly bogus, because the real hardware
specifications don't explicitly guarantee that on real hardware
EAX
will be 0x40000000
. Formally, function
0x40000000
is in the "standard function" range of the
CPUID
instruction but outside of the subset of that range
returned by function 0x00000000
and hence the behaviour of
which is, as AMD's specification puts it, "undefined and should not be
relied upon". But at least it is more than just one bit for the
real hardware to accidentally happen to yield the same values as virtual
machines do.
There is most definitely no guarantee that CPUID
function 0x40000000
will be a no-operation on real hardware.
Indeed, on the contrary, it is far more likely that it will not be. A
hardware designer can legitimately note that all software that adheres to
its part of the protocol will always set zeroes in some bits of the
EAX
register; and so can, whilst still remaining conformant
with the specification, arrange not to decode those bits. So it
is legitimately possible for function 0x40000000
to decode to
the same as function 0x00000000
, which is not a no-operation.
It's equally possible, with yet another hardware design choice, that
function 0x40000000
will pull some meaningless bit patterns
out of a ROM, that may well accidentally happen to satisfy the virtual
machine presence check. The behaviour on real hardware of function
0x40000000
really is, simply, "undefined and should not be
relied upon".
One of these days, perhaps, the processor hardware manufacturers and the
virtualization software vendors will all start singing from the same
songsheet about CPUID
's "software functions" range.
Tal Garfinkel, Keith Adams, Andrew Warfield, and Jason Franklin (2007-05). Compatibility is Not Transparency: VMM Detection Myths and Realities; Proceedings of the 11th Workshop on Hot Topics in Operating Systems (HotOS-XI), May 2007.
Intel Corporation (2009-08). Application Note 485: Intel Processor Identification and the CPUID Instruction. 241618-036.
Advanced Micro Devices (2008-04). Technical Document 25481: CPUID Specification (Revision 2.28).
Microsoft Corporation (2009-10). Determining if Hypervisor is installed. Microsoft Developer Network: Windows Driver Kit.
Microsoft Corporation (2009-10). Standard Hypervisor CPUID leaves Microsoft Developer Network: Windows Driver Kit.
VMWare Incorporated (2011-03). Mechanisms to determine if software is running in VMWare virtual machine 1009458. VMWare KnowledgeBase