image n/a

Using IDT for VMM Detection

I wanted to take a quick moment to review a malware sample that detects the presence of virtual machine monitors (VMMs). The goal is to identify which method it implements and how to circumvent it. A VirusTotal scan of the file is located near the end of this document, excluding the negative hits.

The code begins by making a call to QueryAndCompareIDT() - an internal function that handles locating the base address of the system's Interrupt Descriptor Table and checking if the address is within a certain range.

.text:004010EA       call    QueryAndCompareIDT
.text:004010EF       test    eax, eax
.text:004010F1       jnz     ExitDueToVMM

QueryAndCompareIDT() returns true if a VMM is detected, which subsequently causes the code to exit. More specifically, the function returns true if the IDT location does not exist within memory ranges 80000000h-80FFFFFFh. According to the scoopy doo, [2] source code, 8003F400h is a known constant for the IDT on XP and 2003 Server systems with a native Intel processor. Therefore, the malware will infect most non-VMM implementations of XP and 2003 Server. This malware should also run on Windows 2000 (and potentially others), because the base address there is 80036400h.

The IDT address is stored in the IDTR register, and there is only one IDTR per processor (unless the processor is dual-core, in which case each core has its own IDTR). VMMs relocate the guest's IDT so that it will not conflict with the host's IDT. Programs can detect the presence of VMMs by simply checking the location of the IDT. This is possible because the Intel architecture allows programs running in ring3 to enter ring0 for the purpose of reading this register, [4].

In the Offensive Computing experiment [1], the IDT method was 100% accurate in detecting VMMs when run inside a VMM, however only %50.36 accurate in detecting a native environment (49.64% of the time it falsely detected that it was running on a VMM). This means hng3.exe would probably only infect about half the machines it executes on - a poor choice by the author considering more reliable alternatives such as the Local Descriptor Table (LDT, as described in the same study by OC).

The code to present is composed of two functions - QueryAndCompareIDT() and LocateAddressIDT().The LocateAddressIDT() function issues an sidt instruction (Store Interrupt Descriptor Table) to copy the base address of the IDT into the location specified by the operand. It then moves this value into eax (this is the function's return value) and returns to QueryAndCompareIDT().

.text:004016F1 ; ||||||| S U B R O U T I N E
.text:004016F1
.text:004016F1
.text:004016F1 LocateAddressIDT proc near 
.text:004016F1
.text:004016F1 var_8 = qword ptr -8
.text:004016F1
.text:004016F1       push    ecx
.text:004016F2       push    ecx
.text:004016F3       sidt    [esp+8+var_8]   ; get location of IDT
.text:004016F8       mov     eax, dword ptr [esp+8+var_8+2] 
.text:004016FC       pop     ecx
.text:004016FD       pop     ecx
.text:004016FE       retn
.text:004016FE LocateAddressIDT endp
.text:004016FE
.text:004016FF
.text:004016FF ; ||||||| S U B R O U T I N E
.text:004016FF
.text:004016FF
.text:004016FF QueryAndCompareIDT proc near          
.text:004016FF       call    LocateAddressIDT
.text:00401704       and     eax, 0FF000000h 
.text:00401709       xor     ecx, ecx
.text:0040170B       cmp     eax, 80000000h
.text:00401710       setnz   cl
.text:00401713       mov     eax, ecx
.text:00401715       retn                    
.text:00401715 QueryAndCompareIDT endp
.text:00401715

Now that eax contains the address of IDT, it undergoes an AND operation with 0FF000000h. If IDT is within the range mentioned earlier, 80000000h is left in eax. This makes sense because the next operations compare eax with 80000000h, write 1 to cl if they are not equal (or 0 otherwise), move cl into eax for a return value, and then return to main.

To circumvent the check and be able to run hng3.exe on a VMM, the jnz instruction at 004010F2h (4F2h on disk) can be swapped with a jz. Use a hex editor and change the byte from 85h to 84h. This simply reverses the logic of the check.

The target executable was identified and shared by Adam Thomas of Sunbelt Software. Technical review, additional references, and helpful hints supplied by David Dagon.

Referenced documents and additional sources of information include:

[1]. Detecting the Presence of Virtual Machines Using the Local Data Table, on OffensiveComputing.net.
[2]. The scoopy doo VMware Fingerprint Suite, on trapkit.de.
[3]. Joanna Rutkowska's Red Pill - how to detect VMM using (almost) one CPU instruction
[4]. Intel Virtualization Technology Specification for the IA-32 Intel Architecture
[5]. Analysis of the Intel Pentium's Ability to Support a Secure Virtual Machine Monitor

VirusTotal resuts for hng3.exe:

Avast   4.7.844.0/20060623      found [Win32:Trojano-P]
AVG     386/20060623    found [PSW.Generic2.AXI]
BitDefender     7.2/20060624    found [Trojan.PWS.Sinowal.Y]
eTrust-Vet      12.6.2272/20060623      found [Win32/Anserin!generic]
Ewido   3.5/20060623    found [Trojan.Sinowal.aa]
Fortinet        2.77.0.0/20060624       found [suspicious]
Kaspersky       4.0.2.24/20060624       found [Trojan-PSW.Win32.Sinowal.aa]
NOD32v2 1.1620/20060624 found [a variant of Win32/Spy.Small.DG]
Panda   9.0.0.4/20060623        found [Suspicious file]
VBA32   3.11.0/20060623 found [Trojan-PSW.Win32.Sinowal.aa]
Art of Memory Forensics

Malware Analyst's Cookbook

Site design and layout with umm...a bash shell. Graphic by (Aaron Bieber)
Unless otherwise noted, this work is licensed with (Creative Commons Attribution License).