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,  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, .
In the Offensive Computing experiment , 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:
. Detecting the Presence of Virtual Machines Using the Local Data Table, on OffensiveComputing.net.
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 184.108.40.206/20060624 found [suspicious] Kaspersky 220.127.116.11/20060624 found [Trojan-PSW.Win32.Sinowal.aa] NOD32v2 1.1620/20060624 found [a variant of Win32/Spy.Small.DG] Panda 18.104.22.168/20060623 found [Suspicious file] VBA32 3.11.0/20060623 found [Trojan-PSW.Win32.Sinowal.aa]