Derek Soeder, uber researcher and code ninja extraordinaire posted this over at Rootkit.Com without the code so we thought we would add it here.
We stumbled on another way of detecting VMware in emulation mode while writing that PiXiE network boot virus, not very elegant but it works...
Basically, VMware emulates near execution transfers outside the CS: limit incorrectly, in that it completes the transfer (changes EIP and modifies the stack when applicable) before raising the General Protection Fault. Physical processors raise the fault before actually doing anything.
To test this in Windows user-mode, create a < 4GB code segment (ZwSetLdtEntries), then transfer into it (change CS), and with an exception handler in place, try doing e.g. a near RET out of it (like PUSH -1 / RET). If you're running natively (or in accelerated mode in VMware), then EIP will still be within the CS: limits and ESP will remain unchanged; if you're running in emulation mode (ring-0 or with acceleration disabled), EIP will be above the CS: limit and the return address will have been popped.
(Far execution transfers are emulated faithfully, so that's why you want to do the near transfer from within the short code segment. Trying to do a CALL FAR seg:ofs to outside the short segment, from within the default flat ring-3 code segment, would raise a GPF with CS:EIP and ESP unmodified, the same between native execution and emulation.)
If you use a technique like this in combination with Joanna's SIDT trick, that should give you comprehensive detection. BTW haven't tested this on anything except VMware.
We ran into this quirk because we were trying to make OSLOADER raise a GPF, so yeah, it's not just a feature, it's a bug =] I wonder if it would interfere with any of the execution protection software that relies upon a short CS and a high-addressed stack?
This trick only works if VMware's running in emulation mode, which is the case if the code is running privileged, or if acceleration is disabled (VM -> Settings -> Options, Advanced: Disable acceleration). Joanna's SIDT technique only works if VMware is not running in emulation mode, so if you combine these two, you should get "comprehensive" VMware detection.
I figure that VMware would consider any form of unfaithful emulation as a "bug", so this might be fixed in some current or future version of VMware.
The code:
================================
// vmgpftest.cpp
// Derek Soeder - eEye Digital Security - 09/05/2006
#include <windows.h>
#include <stdio.h>
typedef LONG (NTAPI *NTSETLDTENTRIES)(DWORD,DWORD,DWORD,DWORD,DWORD,DWORD);
#define EndUserModeAddress (*(UINT_PTR*)0x7FFE02B4)
int detect(LPEXCEPTION_POINTERS lpep)
{
if ((UINT_PTR)(lpep->ExceptionRecord->ExceptionAddress) > EndUserModeAddress)
printf("VMware emulation mode detected!\n");
return EXCEPTION_EXECUTE_HANDLER;
}
void __declspec(naked) switchcs()
{
__asm
{
pop eax
push 0x000F
push eax
retf
}
}
int main(int argc, char* argv[])
{
NTSETLDTENTRIES ZwSetLdtEntries;
LDT_ENTRY csdesc;
ZwSetLdtEntries = (NTSETLDTENTRIES)GetProcAddress(GetModuleHandle("ntdll.dll"), "ZwSetLdtEntries");
memset(&csdesc, 0, sizeof(csdesc));
csdesc.LimitLow = (WORD)(EndUserModeAddress >> 12);
csdesc.HighWord.Bytes.Flags1 = 0xFA; // P=1, DPL=3, Code32, RX
csdesc.HighWord.Bytes.Flags2 = 0xC0 | ((EndUserModeAddress >> 28) & 0x0F); // G=1, B=1, Limit[19..16]
ZwSetLdtEntries(0x000F, ((DWORD*)&csdesc)[0], ((DWORD*)&csdesc)[1], 0, 0, 0);
__try
{
switchcs();
__asm
{
or eax, -1
jmp eax
}
}
__except(detect(GetExceptionInformation())) { }
return 0;
}
================================