Swallowing the Red Pill is more or less equivalent to the following code (returns non zero when in Matrix):
int swallow_redpill () { unsigned char m[2+4], rpill[] = "\x0f\x01\x0d\x00\x00\x00\x00\xc3"; *((unsigned*)&rpill;[3]) = (unsigned)m; ((void(*)())&rpill;)(); return (m[5]>0xd0) ? 1 : 0; }
The heart of this code is actually the SIDT instruction (encoded as
0F010D[addr]
), which stores the contents of the interrupt
descriptor table register (IDTR) in the destination operand, which is
actually a memory location. What is special and interesting about SIDT
instruction is that, it can be executed in non privileged mode (ring3) but it
returns the contents of the sensitive register, used internally by operating
system.
Because there is only one IDTR register, but there are at least two OS
running concurrently (i.e. the host and the guest OS), VMM needs to relocate
the guest's IDTR in a safe place, so that it will not conflict with a host's
one. Unfortunately, VMM cannot know if (and when) the process running in
guest OS executes SIDT instruction, since it is not privileged (and it
doesn't generate exception). Thus the process gets the relocated address of
IDT table. It was observed that on VMWare, the relocated address of IDT is at
address 0xffXXXXXX
, whereas on Virtual PC it is
0xe8XXXXXX
. This was tested on VMWare Workstation 4 and
Virtual PC 2004, both running on Windows XP host OS.
BTW, I would be more then happy if anyone test it on different VMMs, running on different host OSes and send me the results. I could then create a table of known addresses of IDTR, which could potentially be used to not only detect the VMM presence but also its version and the host OS...
I came across this strange behavior of SIDT instruction a few years ago,
when I was testing Suckit
rootkit on VMWare. I noticed that it failed to load on VMWare whereas it
seemed to work fine on the same distribution ran outside VM. After
spending many hours I figured out that the problematic instruction was
actually SIDT, which was used by Suckit to get the address of the IDT table,
and to hook its 0x80 entry through /dev/kmem
device.
However, I was not the first one who discovered this trick. Shortly after my adventure with Suckit I found a very good USENIX paper about problems when implementing Virtual Machines on Intel processors, discussing of course SIDT problem, as well as many others. This paper is really worth reading!
So now, here is the simple code, written in C, which should compile on any all Intel based OS. Just in case you don't have the C compiler for Windows, there is also a binary version attached.
NOTE: this program will fail on systems with
PAX/X^W/grsecurity, protection (as it was pointed out by Brad Spengler) since
the rpill
variable is not marked as executable. To make it run in such
systems, mprotect()
should be used to mark rpill
with PROT_EXEC
attribute. Another solution would be to just use
asm()
keyword instead of shellcode-like buffer. However, this
program should be rather considered as a skeleton to build into your own
shellcode, rather then standalone production class tool;) My goal was to make
it as simple and portable as possible. That's why I didn't use
asm()
nor mprotect()
since they are system or compiler
dependent.
I am also aware of another implementation of this technique, as well as some other tricks to fingerprint VMWare, which can be found at http://www.trapkit.de/.
Update: For an intersting follow up research, you might want to check this paper by Offensive Computing.