Just Another VM Detection (was "VM Detection Combo")
Tuesday, August 18, 2009 4:41:46 PM
[edited article follows]
Here it is. Some of the long overdue result of the research for the remaining VM softwares I mentioned a year ago (here). But first of all, I would like to note that this result is several months old since now I have PC upgraded and use newer software versions. I would also like to apologize and correct the "Red Pill" method variant "Scoopy". In the previous blog, I misspelled its name as "Scooby". My bad... The Scooby Doo's picture in the "On the Cutting Edge: Thwarting Virtual Machine Detection" white paper sure made me do the mistake. The cartoon image shouldn't be there in the first place. The white paper is a serious resource and the picture makes "Scoopy" a triffling matter. Not to mention that those pictures are copyrighted and the paper's author don't mention their copyright notice. Nuff said... Let's go back to the main topic.
To make things simple, have a look at the table below. The bright red colored rows of the "VM Software" column indicates that the VM softwares in that configuration do not run at optimum speed or in compatibility mode. Please keep in mind that my standard for VM detection is that it must be runable under Microsoft Windows 9x/2000 and above in USER mode (ring 3) ONLY and does not require any third party software including device driver. Administrator privilege requirement is acceptable, but should be avoided. Safe mode Windows environment is fine with me. DOS based VM detection is acceptable (runnable in a DOS box) but is not the preferred method. Above all, the method should be able to detect at least the release build of the VM software. Detecting only a debug build of VM softwares are not acceptable since debug build are not available to all end-users.
|VM Software||Backdoor||Long Opcode||TF+CF Bug|
|Parallels (normal/high accel.)||Yes||Yes||No|
|Parallels (no accel.)||Yes||Yes||Yes|
|Qemu (with KQEMU)||No||Yes||No|
|Qemu (no KQEMU)||No||Yes||Yes|
|VMware (with directexec)||Yes||No||No|
|VMware (no directexec)||Yes||No||Yes|
VM Software Versions Tested:
1. Bochs x86 2.3.6 (December 24, 2007) and x86-64 2.2.6 (January 29, 2006).
2. Parallels Workstation 2.2 build 2112.
3. Qemu 0.9.1 with and without KQEMU 1.3.0pre11. QVM86 is not tested since it's obsolete.
4. innotek VirtualBox 1.5.2.
5. Microsoft Virtual PC 2007 126.96.36.199.
Guest Operating Systems Used With Testings:
1. Microsoft Windows 98 SE.
2. Microsoft Windows XP SP2 32-Bit.
Host System Configuration Used With Testings:
1. Iwill P4S with Intel 845 chipset.
2. Intel Pentium 4 1.6GHz Willamette.
3. 512MB of SDRAM.
|1||. Backdoor Detection|
Most backdoor detections are based from what I gathered from the internet except for the Parallels backdoor. The "Attacks on More Virtual Machine Emulators" white paper does include Parallels backdoor detection method, but it requires authentication code, or the paper doesn't provide sufficient information to create a working code. The Parallels backdoor I used here is based from reverse engineering the Parallels VM Tools and it's the first one that is used by the VM Tools. As far as I can understand, it retrieves the build number of the Parallels software. The backdoor uses port based method to communicate with the host system - the Parallels software. Take a look at below Delphi function code for the backdoors.
function ParallelsBackdoorCheck(var BuildNumber: integer): boolean; begin result:= false; try asm push ebx push esi mov eax, $13 //function code? mov ebx, $3141 //magic number mov esi, 1 //sub function code? mov dx, $e4 in eax, dx cmp eax, $3141 //make sure eax is set to magic number jne @done cmp ebx, $3141 //make sure ebx is changed je @done //i assume build number 12609 will not exists or ebx, ebx //make sure build number is non-zero setnz @result mov eax, BuildNumber mov [eax], ebx @done: pop esi pop ebx end; except //not in VM if exception occured end; end;
Since I use Parallels 2.2 build number 2112, the above function will returns TRUE and the BuildNumber will be set to 2112. The above function will work regardless of the VM acceleration setting. There should be more backdoor functions that use port access, but I'm not interested in them since I already got what it needs to detect Parallels VM.
For Virtual PC backdoor detection code, I use a two part detection code in a single function. One with the invalid opcode bytes 0F,3F,07,0B and one with the 0F,C7,C8,01,00 opcode. If any one of the part detects Virtual PC VM, the function will returns TRUE.
function VirtualPCBackdoorCheck: boolean; begin try asm push ebx mov eax, 1 xor ebx, ebx //zero or non-zero? db $0f,$3f,$07,$0b test ebx, ebx setz @result pop ebx end; except result:= false; end; if result then exit; try asm push ebx xor eax, eax xor ebx, ebx xor ecx, ecx xor edx, edx db $0f,$c7,$c8,$01,$00 pop ebx end; result:= true; except result:= false; end; end;
For VMware backdoor detection, I use the one I mentioned in my previous blog. That is the "GetMemSize" backdoor function since it shouldn't be disabled, otherwise the BIOS won't be able to load the bootstrap code from the bootable media (the media indicator won't even blink).
function VMwareBackdoorCheck: boolean; const VMWARE_MAGIC = $564d5868; //"VMXh" VMWARE_PORT_NUMBER = $5658; //"VX" VMWARE_CMD_GETMEMSIZE= $14; begin result:= false; try asm push ebx mov eax, VMWARE_MAGIC xor ebx, ebx //zero parameter mov ecx, VMWARE_CMD_GETMEMSIZE mov dx, VMWARE_PORT_NUMBER in eax, dx pop ebx cmp Win32Platform, VER_PLATFORM_WIN32_NT je @winnt cmp eax, 16 //16mb minimum, else os won't run jl @done cmp eax, 4096 //1024mb maximum, else os won't run jg @done //4096mb maximum with unofficial patch jmp @detected @winnt: cmp eax, 32 //32mb minimum, else os won't run jl @done cmp eax, 2048 // 2tb maximum statistically jg @done //32tb maximum theorically @detected: mov @result, 1 @done: end; except end; end;
As for VirtualBox backdoor... I have to say that I was frustated when trying to find its backdoor. It's difficult since VirtualBox Guest Additions doesn't complain when installed and run in a real hardware or under non VirtualBox VMs AND VirtualBox installation won't complain when installed and run under its own VM (VirtualBox in a VirtualBox). At this point I gave up finding it. One of anonymous person pointed out that I can use the SYSENTER instruction for the backdoor. I'm yet to research this and don't know yet if the method conforms to my standard of VM detection.
Other remaining VM softwares - which are Bochs and Qemu, don't seem to have any backdoor at all. However, for Qemu, as far as I know, there can be a backdoor, but only if it's a debug build. This conclusion is supported by the fact that these two don't have any guest addition nor guest VM tool, so I guess that they don't have any backdoor yet. But I'm sure they'll have one when they are matured enough.
|2||. Long Opcode Instruction Limit Detection|
This method is more like a CPU compatibility check rather than a VM detection. This method is about checking the CPU limitation against executing a CPU instruction which has long opcode bytes. On Intel x86 compatible CPUs, the maximum opcode bytes for a single CPU instruction is 15 - that is 15 bytes per instruction. However, several VM softwares don't have this limitation, thus its virtual CPU is not 100% compatible even though the unlimited opcode length per instruction seems to be an advantage. When a real CPU executes an instruction whose length is more than 15 bytes, it will raise an exception, but under VMs that don't have this limit will not raise an exception. For this method, I also use two parts of code. One with a 16 bytes instruction and one with a 20 bytes instruction. When any one of them doesn't raise an exception, then it's run under a VM.
function GenericLongOpcodeCheck: boolean; begin try asm //uses 11 DS segment prefix opcodes. 16-bytes opcode db $3e,$3e,$3e,$3e,$3e,$3e,$3e,$3e,$3e,$3e,$3e mov eax, exitcode end; result:= true; except on eaccessviolation do result:= false; else result:= true; end; if not result then exit; try asm //uses 15 DS segment prefix opcodes. 20-bytes opcode db $3e,$3e,$3e,$3e,$3e,$3e,$3e,$3e,$3e,$3e,$3e,$3e,$3e,$3e,$3e mov eax, exitcode end; result:= true; except on eexternal do result:= false; else result:= true; end; end;
|3||. Trap & Carry Flags Bug Detection|
The Trap & Carry Flags bug detection that I use here is a ported version of the "Bochs CMPS Demo" code taken from the "Attacks on More Virtual Machine Emulators" white paper. I was forced to port it to 32-bit Windows code since the original one is for DOS platform. Since the method is simple and efficient, there is no need for significant modification.
function GenericTfCfBugCheck: boolean; begin try asm mov esi, offset GenericTfCfBugCheck mov edi, esi mov ecx, 2 pushfd or word ptr [esp], $101 popfd repe cmpsb end; result:= false; except asm mov eax, [esp+$c] //get os exception pointer (pexceptionrecord) mov ecx, [eax+$10] //get number of parameters lea eax, [ecx*4+eax+$14] //advance to context record mov eax, _CONTEXT([eax]).eflags //get eflags and eax, 1 //filter cf xor eax, 1 //invert cf mov @result, al //save as result. TRUE if CF is zero end; end; end;
|4||. BIOS Pattern Detection|
After I completed the above five detection methods, the combination of backdoor and IDTR & LDTR modification detections can detect all but two cases. They are Bochs detection and Qemu (without KQEMU). These two softwares don't have any backdoor and don't modify the IDTR. For the sake for a complete VM software detection and after some considerations, I came out with a BIOS pattern detection method. This method is simply a text finder that scans the BIOS at memory address 0xF0000 to 0xEFFE0 for a specific text in length of 11 to 32 characters. The text that will berecognized by this detection are listed below.
Since this method requires access to the BIOS memory, I make use of the PhysicalMemory device which I hate to say that it requires the administrator privilege. You can get a C++ source code example here. Other alternative method used is with the help of NTVDM to read the BIOS memory. This is possible since NTVDM maps (and/or creates copies of) the lowest 1 MB of physical memory. All I have to do is to execute a DOS program (the COMMAND.COM in this case), use the ReadProcessMemory Windows API, then terminate the process.
I kinda dislike this BIOS pattern detection method since it's a lame detection and requires special privilege, so hopefully I'll be able to have other method that doesn't need special privilege. If anyone have a better idea, I'll be very interested on hearing about it. But please note that I'm not interested on information that directly or indirectly came from the Windows registry nor DMI.
There are still other VM softwares and/or versions which I don't have in my system yet. A couple of those are the latest VirtualBox 1.5.6 and Virtual Simics. Also in my list are all VM softwares that require hardware assisted virtualization such as Virtual Iron (at least when I'm able to upgrade my 5 years old Pentium 4 to AMD64 X2 or Core2 Duo). All other VM softwares that don't provide full VM (full system virtualization) such as SandBox are not and will not be in my list. Other things I might do when I finally have a 64-bit dual core system is to research on VM softwares that supports 64-bit platform (except for Bochs x86-64, since Bochs is an emulator rather than virtualizator).