Thursday, December 16, 2010

VM Detection by In-The-Wild Malware

Note: This is a reprint of a posting I made for my company, NetWitness (www.netwitness.com). This is unchanged from the original, however that copy can be found at: http://www.networkforensics.com/2010/12/13/vm-detection-by-in-the-wild-malware/

Motivation


A large number of security researchers use Virtual Machines when analyzing malware and/or setting up both active and passive honeynets. There a numerous reasons for this, including: scalability, manageability, configuration and state snapshots, ability to run diverse operating systems, etc..

Malware that attempts to detect if it’s running in a Virtual Machine (then change its behavior accordingly to prevent analysis by security people) is not a subject of academic fancy. A recent search of VirusTotal showed they receive at least 1,000 unique samples a week with VM detection capabilities. (This search was performed by searching for known function import names from non-standard DLLs.) Personally, my first encounter with malware that behaved completely differently inside a Virtual Machine (from a real host) was approximately eight years ago.

VM detection does not apply just to the realm of APT-level malware. Agobot/Gaobot/PhatBot  is a family of massively deployed malware first released in 2004 with the ability to detect if running in either VMware or VirtualPC and changes its behavior accordingly. Considering just this example of how old and low-entry malware (with such a massive deployment) performs these actions, our attention to this subject should be especially keen.

Notes


  1. This post contains a number of techniques for VM detection used by malware, along with code demonstrating how simple these techniques are to implement. Except where noted, all techniques are currently used in the wild.

  1. Most of this post (but not all!) is a summary of other people’s work, not mine – except where noted. References are given and should be accurate. If not, email me and I’ll correct.
  2. Examples where simple code samples could not be produced will not be considered here.
  3. Only techniques that are difficult to mitigate are examined here. I’m sure there are hundreds of other ways to detect VM’s. Of the methods I’m familiar with, these were the ones that stood out in my mind as being difficult to fight.

Types of Virtual Machines


Generally speaking, there are three types of Virtual Machines. They are:
  1. Hardware Assisted – aka: Hypervisors – These VM’s use processor-specific instructions to cause the Host OS to [in effect] “fork,” where the original copy of the OS stays in a suspended state while the newly spawned “Guest copy” continues to run as if nothing happened. The important thing to keep in mind relative to this topic is that when the Guest executes machine level instructions, the actual hardware CPU is used to execute those instructions.
  2. Reduced Privilege – These are the VM’s most people are familiar with and use regularly. Here, the Host takes more of an active “proxy” role for the Guest by virtualizing important data structures and registers, then performing some level of translation services for some machine level instructions. Relative to this topic, the important thing to note here is that the guest – in effect – runs at a lower privilege than if it was truly controlling the CPU.
  3. Pure Software – Software VM’s act as full proxies to the CPU by implementing a truly virtual CPU the Guest interacts with.

Hypervisors (Hardware Assisted VMs)


Xen > 3.x and Virtual Server 2005 are a couple examples of Hardware assisted virtual machines.
Low-level detection of being virtualized in one of these environments is extremely difficult. Many people still call it impossible. While several people have talked publically about proof of concept code developed to detect these environments for years, none has been released or found in wild (that I’m aware of). Because of this, I will not talk about hypervisors any further than describing why detection is so difficult. (Since we have no code to examine how simple it is – the point of this post.)
A Hypervisor “guest” can be launched at any point after the OS has loaded. In preparation for launching a guest copy of the OS, the “host” sets up some basic CPU-specific control structures, then uses a single instruction (opcode) to cause the CPU to place the Host OS in a virtualized state while the Guest is basically a “forked copy” of the originally running OS. Once a Hypervisor has started running, the Guest OS basically has zero knowledge of this fact since all access to hardware is direct access. While the access to hardware is direct, the Hypervisor VM itself still has the ability to intercept interesting events – even before the Host OS has seen them. In this effect, a hypervisor VM is more powerful than both the Host and Guest OS’s because it sees everything before either of them.  Also, once a hypervisor is running, no others can become active. The first hypervisor VM has absolute control.

All methods for detecting the presence of Hypervisors depend on timing functions, however they are only useful techniques in theory because of the infeasibility of creating a good baseline to compare timing results to in order to make a pass/fail decision. Another technique uses context switching to cause Translation Lookaside Buffers filled with a predetermined pattern of data to get flushed when a hypervisor is running. Describing the technique is far beyond the scope of this paper since there is no exploit code to examine, but… Based on my understanding of the following article, I’m not sure the technique is so relevant anymore anyways. http://download.intel.com/technology/itj/2006/v10i3/v10-i3-art01.pdf

VMware


The non-ESX versions of VMware are reduced privilege VMs, and because of that are trivial to detect. Because critical data structures setup by the Operating System in critical regions of memory during OS start-up are already in use by the Host OS, VMware must relocate virtual copies of them for use by the Guest OS. This fact alone presents several powerful opportunities to detect when running inside a VMware image.

The first example simply checks the base address of the Interrupt Descriptor Table, as shown below. If then IDT is at a location much higher than its normal location, the process is likely inside a VM. This technique is generally attributed to Joanna Rutkowska , and is described here.
Code:

Data provided by Pastebin.com - Download Raw
  1. void detectVMwareIDT (void)
  2. {
  3.         unsigned char idtr[6];
  4.         unsigned long idt_base = 0;
  5.  
  6.         _asm sidt idtr
  7.  
  8.         idt_base = *((unsigned long *)&idtr[2]);
  9.  
  10.         if ((idt_base >> 24) == 0xff)
  11.         {
  12.                 printf ("IDT at high relocation – inside VM ");
  13.         }
  14.  
  15.         else
  16.         {
  17.                 printf ("IDT at a normal location ");
  18.         }
  19. }
  In the above example, line #6 is the single line of assembly it takes to get the base address of the IDT, which is then tested a couple lines below that. SIDT is an instruction that stores the contents of the interrupt descriptor table register (IDTR) in the destination operand. It’s important to note this instruction is an unprivileged instruction that can be executed at any privilege level. However, according to the paper, “Detecting the Presence of Virtual Machines Using the Local Data Table,” verifying the IDT on multi-processor systems will fail when there is an IDT for each microprocessor.

If that detection technique wasn’t simple enough, the next one is. VMware builds the Local Descriptor Table in memory, however Windows does not. Therefore, simply checking for a non-zero address for the LDT when running in Windows is enough to identify VMware.

Data provided by Pastebin.com - Download Raw
  1. void detectVMwareLDT (void)
  2. {
  3.         unsigned char  ldtr[4] = {'0xef', '0xbe', '0xad', '0xde'};
  4.         unsigned long  ldt_base  = 0;
  5.  
  6.         _asm sldt ldtr
  7.  
  8.         ldt_base = *((unsigned long *)&ldtr[0]);
  9.  
  10.         if (ldt_base == 0xdead0000)
  11.         {
  12.                 printf ("LDT at normal offset ");
  13.         }
  14.  
  15.         else
  16.         {
  17.                 printf ("LDT found: inside VMware ");
  18.         }
  19. }
On line #6, SLDT is the assembly instruction to store the segment selector from the local descriptor table register (LDTR) in the destination operand. It’s important to note this instruction is also an unprivileged instruction that can be executed at any privilege level!

Another interesting feature of VMware is seen when executing the IN instruction from user-land of common OSs like Linux, Windows, etc (and more accurately, when executing this instruction in ring3). IN is the “Input from Port” instruction. It copies the value from the I/O port specified with the source operand to the destination operand.  The IN instruction is a privileged instruction which cannot be run from ring3 (user-land), therefore when executed, an exception should be thrown. However, when VMware is running, no exception is generated if a special input port is specified. That port is “0×5658,” aka: “VX.” This technique is described in much more detail in the original posting here.

Example code is below, with comments added to explain each step.

Data provided by Pastebin.com - Download Raw
  1. void detectVMwareINcmd (void)
  2. {
  3.   unsigned int a, b;
  4.  
  5.   __try
  6.   {
  7.     __asm
  8.     {
  9.  
  10.         // standard stack setup, nothing special
  11.         push eax  
  12.         push ebx
  13.         push ecx
  14.         push edx
  15.  
  16.         // first, push the value "VMXh" into EAX. If the IN command
  17.         // is successful (meaning we're in VMware), this fingerprint
  18.         // will be placed into the EBX register by VMware.
  19.         mov eax, 'VMXh'
  20.        
  21.         // ECX stores the VMware "backdoor" command we're trying
  22.         // to issue. 0x0a is one of several commands. This one gets
  23.         // the version of VMware running.
  24.         mov ecx, 0Ah
  25.  
  26.         // This is the input operand to the IN instruction and must
  27.         // be "VX" to activate this "backdoor" feature.
  28.         mov dx, 'VX'
  29.  
  30.         // Key instruction is issued
  31.         in eax, dx
  32.  
  33.         // If we got here, then we're in VMware, since an exception
  34.         // would have been thrown otherwise.
  35.         mov a, ebx
  36.         mov b, ecx
  37.  
  38.         // standard stack tear down, nothing special
  39.         pop edx
  40.         pop ecx
  41.         pop ebx
  42.         pop eax
  43.     }
  44.   }
  45.   __except (EXCEPTION_EXECUTE_HANDLER) {}
  46.  
  47.   // if the fingerprint was pushed into the EBX register,
  48.   // we can check the results
  49.   if (a == 'VMXh')
  50.   {
  51.     printf ("VMware found. Version is: ");
  52.  
  53.     // the ECX register actually received the version code, tested next:
  54.     if (b == 1)
  55.       printf ("Express ");
  56.     else if (b == 2)
  57.       printf ("ESX ");
  58.     else if (b == 3)
  59.       printf ("GSX ");
  60.     else if (b == 4)
  61.       printf ("Workstation ");
  62.     else
  63.       printf ("some other version ");
  64.   }
  65.   else
  66.   {
  67.     printf ("fingerprint never made it to EBX, not VMware ");
  68.   }
  69. }
Some people writing for SANS have said that disabling certain configuration option in VMware will defeat this type of detection mechanism. Unfortunately, the real IN instruction would never change any register other than EAX in the first place, so all the other register changes that take place when executing the instruction in VMware are still detectable. Other counter-measures have been proposed, however they are too unstable and unusable in the real world for us to consider here.

VirtualPC

  VirtualPC is also a reduced privilege VM, like non-ESX versions of VMware, and is just as trivial to detect. The IDT and LDT table structures tests described in the VMware section apply to VirtualPC as-is. In fact, those tests apply to all the big-name VMs that people are most familiar with in the Reduced Privilege category of VMs. VirtualPC has functionality similar to VMware’s use of the IN instruction, however it uses illegal instructions to trigger exceptions the kernel will catch. For example, issuing the following machine code would normally cause an exception because it’s an undefined opcode: 0F 3F 0A 00 But, with VirtualPC running, no exception is generated because this is part of VirtualPC’s guest to host communication protocol. Therefore, the code in the VMware example can simply be modified to issue this opcode, then test for a lack of exception. A more interesting feature of VirtualPC is its use of “buffered code emulation.” Buffered code emulation is the practice of copying an instruction from a Guest into a host-controlled buffer and executing it there, then returning the results to the Guest. As VirtualPC is intercepting every instruction and deciding what to return back to the caller, it will sometimes alter or craft its own results – as it does with the CPUID instruction. Normal values retuned are “GenuineIntel” and “AuthenticAMD.” With VirtualPC, the result is “ConnectixCPU.” But, I like this example of VirtualPC detection best since it uses the high-level and easy to use language, C#. Consider the following:

Data provided by Pastebin.com - Download Raw
  1.         private void VirtualPCviaWMI()
  2.         {
  3.             ManagementObjectSearcher objMOS =
  4.                 new ManagementObjectSearcher(
  5.                     "root\CIMV2", "SELECT * FROM  Win32_BaseBoard"
  6.                     );
  7.  
  8.             foreach (ManagementObject objManagemnet in objMOS.Get())
  9.             {
  10.                 if (objManagemnet.GetPropertyValue("Manufacturer").ToString() ==
  11.                     "Microsoft Corporation")
  12.                 {
  13.                     Console.WriteLine("VirtualPC detected");
  14.                 }
  15.             }
  16.         }
The above example pulls the manufacturer name of the motherboard and tests if it’s “Microsoft Corporation.” If it is, then VirtualPC has just been detected.

Software VMs


Examples software VMs include Bochs, Hydra, QEMU, Atlantis, and Norman Sandbox, among many others. Because software VMs try to fully emulate hardware, there are too many techniques to detect them to list here. Because it would be nearly impossible to implement every instruction and match the quirks each instruction has on different families of processors, most of the tests for Software VM’s revolve around testing some of the more arcane instructions.

Sandboxes


I personally love sandboxes because of the sheer volume of work they automate and standardization of data they return. I can’t even imagine life before they existed anymore! :-) However, we need to be realistic about the fact that most (but not all!) are trivial to detect, regardless of hardware platform. This doesn’t mean we should avoid them – it just means we need to ensure we’re compensating for that fact.

A technique I have not seen elsewhere and have used in my own “research” malware in the past is to check the DLLs that have been loaded into my program’s space. To use this technique, you must first create a “fingerprint” of the DLLs your program loads. This is easily accomplished with a couple lines of debug code after you have finished your program. The technique is easy and can be used even in high-level languages like C#. Consider the following:

Data provided by Pastebin.com - Download Raw
  1.         private bool iveBeenInjected()
  2.         {
  3.             bool retVal = false;
  4.  
  5.             Process pp = Process.GetCurrentProcess();
  6.             ProcessModuleCollection pma = pp.Modules;
  7.  
  8.             foreach (ProcessModule pm in pma)
  9.             {
  10.                 // checkName is a simple function that uses a switch() statement
  11.                 // to see if the name is in the “fingerprint” list of DLL's loaded
  12.                 // by my program. (In this case, it's 39 entries.)
  13.                 if (!checkName(pm.ModuleName) && (pm.ModuleName != pp.ProcessName))
  14.                 {
  15.                     retVal = true;
  16.  
  17.                     Console.WriteLine(
  18.                         pm.ModuleName +
  19.                         " is injected from " +
  20.                         pm.BaseAddress.ToString() +
  21.                         " file: " +
  22.                         pm.FileName);
  23.                 }
  24.             }
  25.  
  26.             return retVal;
  27.         }
bool checkName(string nameOfLoadedDLL) is a method that returns true if the dll mapped in the program’s process space is known to belong to the “fingerprint” of this program. If the dll mapped in is unknown to the program, then it returns false and the logic below is executed.

This simple function is enough to catch many sandboxes (again, not all of them), even if you’ve followed their best practices and rename their monitoring dll (typically injected into all new processes).  Other examples of sandbox detection include using the hook detection employed by numerous security programs to find malware (except in this case – used by malware to detect sandboxes), counting hooks, etc..

Unfortunately, in the case of sandboxes, malware doesn’t even need to go through all that trouble to defeat them. The only thing malware needs to do is ensure its persistence through a reboot, then wait for a reboot to take place. That alone is enough to defeat the analysis steps of most analysis!

Summary


In short, you have seen that while many people quibble over VM detection being as simple as looking for registry keys and mac addresses (all easily mitigated from a security perspective), VM detection is actually:
  1. Much easier than programmatically dealing with the registry
  2. Much harder [nearly impossible] to mitigate when behavior of hardware is the target of testing
  3. Happens on a massive scale in malware in the wild

While the use of Virtual Machines has many advantages for research purposes, their selection and limitations should be carefully weighed against your actual objectives.