From time to time it is handy to be able to detect whether your binary / script / program / software is running under a virtual machine and / or a similar controlled environment. For example:
- You have a network of nodes where you want your binary to run but you want it to behave differently under a VM.
- You wrote a virus / worm / boogie-woogie and you want to make the life of the AV vendors or the sec researchers harder when they run your binary in a controlled VM.
- At work we make an extensive use of software simulators (in VMs off course) for testing of the products on different hardwares ( Card Readers / RFIDs / BNAs / SNAs and / or machine simulators ), the behaviors of whom are obviously different in many cases than the real Self Service Terminals.
- Any other reason that you can think of and is not listed here.
I googled around for other VM Detection algorithms and found some really interesting solutions. The most impressive one was Joanna Rutkowska’s swallowing the Red Pill. I wouldn’t had gone any further after reading his article but somehow I couldn’t manage to make his code produce a convincing result under XEN when I ran it on my BSD. I also found a very good USENIX paper describing the problems of implementing VMs on Intel processors. Some other cool ways for VMWare fingerprinting can also be found at http://www.trapkit.de/.
I. Detecting Microsoft VMs:
The easiest way to detect that you are inside a VM is to use hardware fingerprinting. Look for a hardware that is always present in a particular VM. For e.g. in Microsoft based VMs (Virtual Server / Virtual PC) a clear indicator is that the “Motherboard” is manufactured by Microsoft Corporation : ).
use strict;
use Win32::OLE('in');
use constant wbemFlagReturnImmediately => 0×10;
use constant wbemFlagForwardOnly => 0×20;
my @computers = ("localhost");
foreach my $computer (@computers) {
print "\n";
print "==========================================\n";
print "Computer: $computer\n";
print "==========================================\n";
my $objWMIService = Win32::OLE->GetObject("winmgmts:\\\\$computer\\root\\CIMV2") or die "WMI connection failed.\n";
my $colItems = $objWMIService->ExecQuery("SELECT * FROM Win32_BaseBoard", "WQL",
wbemFlagReturnImmediately | wbemFlagForwardOnly);
foreach my $objItem (in $colItems) {
print "Caption: $objItem->{Caption}\n";
print "ConfigOptions: " . join(",", (in $objItem->{ConfigOptions})) . "\n";
print "CreationClassName: $objItem->{CreationClassName}\n";
print "Depth: $objItem->{Depth}\n";
print "Description: $objItem->{Description}\n";
print "Height: $objItem->{Height}\n";
print "HostingBoard: $objItem->{HostingBoard}\n";
print "HotSwappable: $objItem->{HotSwappable}\n";
print "InstallDate: $objItem->{InstallDate}\n";
print "Manufacturer: $objItem->{Manufacturer}\n";
print "Model: $objItem->{Model}\n";
print "Name: $objItem->{Name}\n";
print "OtherIdentifyingInfo: $objItem->{OtherIdentifyingInfo}\n";
print "PartNumber: $objItem->{PartNumber}\n";
print "PoweredOn: $objItem->{PoweredOn}\n";
print "Product: $objItem->{Product}\n";
print "Removable: $objItem->{Removable}\n";
print "Replaceable: $objItem->{Replaceable}\n";
print "RequirementsDescription: $objItem->{RequirementsDescription}\n";
print "RequiresDaughterBoard: $objItem->{RequiresDaughterBoard}\n";
print "SerialNumber: $objItem->{SerialNumber}\n";
print "SKU: $objItem->{SKU}\n";
print "SlotLayout: $objItem->{SlotLayout}\n";
print "SpecialRequirements: $objItem->{SpecialRequirements}\n";
print "Status: $objItem->{Status}\n";
print "Tag: $objItem->{Tag}\n";
print "Version: $objItem->{Version}\n";
print "Weight: $objItem->{Weight}\n";
print "Width: $objItem->{Width}\n";
print "\n";
}
}sub WMIDateStringToDate(strDate)
{
return "blah";
}
The above script uses WMI to find out the motherboard manufacturer information. If the motherboard is made by "Microsoft Corporation" then you’re inside one of Micorosft VMs.( The script requires Active State Perl 5.6 or greater and WMI enabled ).
Going to the Lower Level:
When an invalid instruction (that is not present in the Instruction Set Architecture) is encountered, the machine raises an exception of the type "Invalid Opcode". The software can either handle the exception (using the usual try/catch mechanism), let the operating system handle the exception, or worse: if the programmer is like me: crash the machine.
Virtual Server uses a bunch of invalid instructions to allow the interfacing between the virtual machine and the Virtual Server software.
Here's what happens when Virtual Server's VM wants to talk with Virtual Server:
- The program sets exception handlers (try/catch blocks).
- Set needed parameters before calling the VM software.
- Issue a special "Invalid Opcode" instruction.
- VM software will recognize this invalid opcode and act accordingly, causing no exception if VirtualServer was present, and an exception if Virtual Server isn't present.
- The program's "catch" block will handle the exception and examine the returned parameters for the presence/absence of VM software.
- In short, Virtual Server uses the "Invalid Opcode" mechanism as a backdoor.
The following code shows how to detect Virtual Server's presence:
DWORD __forceinline IsInsideVPC_exceptionFilter(LPEXCEPTION_POINTERS ep)
{
PCONTEXT ctx = ep->ContextRecord;
ctx->Ebx = -1; // Not running VPC
ctx->Eip += 4; // skip past the "call VPC" opcodes
return EXCEPTION_CONTINUE_EXECUTION;
// we can safely resume execution since we skipped faulty instruction
}
// High level language friendly version of IsInsideVPC()
bool IsInsideVPC()
{
bool rc = false;
__try
{
_asm push ebx
_asm mov ebx, 0 // It will stay ZERO if VPC is running
_asm mov eax, 1 // VPC function number
// call VPC
_asm __emit 0Fh
_asm __emit 3Fh
_asm __emit 07h
_asm __emit 0Bh
_asm test ebx, ebx
_asm setz [rc]
_asm pop ebx
}
// The except block shouldn't get triggered if VPC is running!!
__except(IsInsideVPC_exceptionFilter(GetExceptionInformation()))
{
}
return rc;
}
Code Details:
- Install exception handlers.
- Prepare input registers "eax" & "ebx".
- Issue invalid instruction 0x0F 0x3F 0×07 0x0B. This invalid instruction is like a function designator, it tells Virtual Server what to DO exactly. For other functionality, Virtual Server uses another invalid instruction.
- Inside the exception handler -> modify registers so to mark VM's absence (EBX is set to -1 if exception is triggered -> VM is absent).
- Return from exception and resume execution (only if VM was absent).
- Inspect returned registers accordingly.
II. Detecting VMWare:
VMware contains a program called "VMware Command Line Tools". These tools communicate with the host system via the VMware virtual machine (the same method is used by the official VMware-Tools).
Also both Virtual Server and VMWare allows you to install "add-ins" to accelerate emulation, allow drag-n-drop from your real desktop to your virtual desktop, and allow file sharing between your real machine and the virtual machine.
In order to accomplish this , a communication mechanism between the virtual machine software and the virtual machine itself must exist. This sort of interfacing is called a "backdoor interfacing", since, using a special/undocumented mechanism, certain commands can be carried and interpreted in a different manner (by the virtual machine software) unlike having them interpreted by the real machine.
The basic idea is that the communication is done through a special I/O port specific to the VMware virtual machines.
The following sequence is used to call VMwares environment:
MOV EAX, 564D5868h ; Magic Number
MOV EBX, COMMAND_SPECIFIC_PARAMETER
MOV ECX, BACKDOOR_COMMAND_NUMBER
MOV DX, 5658h ; Port Number
IN EAX, DX
Though it may appear to be an ordinary I/O access routine at first glance, several VMware specific mechanisms are involved in this. What is not apparent from this example is that data can be transferred to both directions with this routine. As shown in the example, the Magic number (0x564D5868 or 'VMXh') is stored in EAX and other certain values are stored in EBX and ECX prior to executing IN instruction. Although values in these registers have no effect on IN instruction in real machines, VMwares environments use these values as their input parameters. Also some functions return their results in EBX, ECX and EDX as well as in EAX. Consequently, you can not use C library functions to access this Backdoor port (e.g. _inp() function in MSVC runtime library), because those functions never expect these registers to be changed by IN instruction.
As the above I/O port doesn't exist on non-VMware environments, a malicious code can detect whether it runs under VMware or not, and act accordingly.
Code Description:
The Intel x86 provides two instructions to allow you to carry I/O operations, the "IN" and "OUT". These instructions are privileged instructions and cannot be used in a user-mode (while in protected mode) process unless the necessary privileges are enabled, so using them in normal cases will cause an exception of the type: "EXCEPTION_PRIV_INSTRUCTION". VMWare uses the "IN" instruction to read from a special port. This port does not effectively exist, however when VMWare is present, that port will be the interface between the virtual machine and VMWare.
bool IsInsideVMWare()
{
bool rc = true;
__try
{
__asm
{
push edx
push ecx
push ebx
mov eax, 'VMXh'
mov ebx, 0 // any value except the MAGIC
mov ecx, 10 // get VMWare version
mov edx, 'VX' // port number
in eax, dx // read port
// on return EAX returns the VERSION
cmp ebx, 'VMXh' // is it VMWare?
setz [rc] // set return value
pop ebx
pop ecx
pop edx
}
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
rc = false;
}
return rc;
}
The so called algorithm:
- The program sets exception handlers (in case VMWare isn't present, just discard its presence).
- Set up into the EAX register the magic number 0x564D5868 (or 'VMXh').
- Set EBX register to any value but the magic number.
- Set ECX register to the "function number" value. The value 10 means Get VMWare version, other codes means other functionality.
- Set into DX the magic port number 0×5658 (or 'VX'), this special port number allows interfacing with VMWare when it is present.
- Read from that port into EAX.
- When VMWare is not present, an exception will occur so VMWare's presence is discarded
- Otherwise, the code flow continues.
- EBX should now read the magic number value.
- If so, then VMWare is present.