Flag: Tornado! Hurricane!

Blogs >> j00ru's Blog

Created: Wednesday, January 16 2008 20:11.36 CST Modified: Wednesday, January 16 2008 20:20.39 CST
Printer Friendly ...
Old new Virtual Machine detection method.
Author: j00ru # Views: 5889

Hello,

A long time ago I made some deeper analysis at Gynvael's Microsoft VirtualPC 2004 (build 528) Detection, and I found another way of finding out whether the code is running on some VM or not.

The method is based mostly on the previous one, however I was said it's also able to detect Bochs (haven't confirmed it).
It takes advantage of the fact that the real Intel CPU generates two different exceptions during the execution of 16 (EXCEPTION_ACCESS_VIOLATION) and 20 (EXCEPTION_ILLEGAL_INSTRUCTION) byte-long instructions, while VirtualPC and other VM's don't generate any or there is no difference beetwen how virtual machines react on these 2 looong prefixes+opcodes.

I've only tested it on the Microsoft VirtualPC 2007 now, but it worked on almost every VM, at the time of the research itself.
I haven't found this method described anywhere yet, but let me know, if You've seen it before.

PurplePill (some temp name, huh) binary download: http://j00ru.vexillium.org/PurplePill.exe

And here is the Proof of Concept code (masm32 style):


; Universal (?) Virtual Machine detector =^^=
; masm32
; research & code by j00ru//vx
;

.486
.model flat, stdcall
option casemap:none

include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\user32.inc

includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\user32.lib


.data
  ExpTitle  db 'VM PurplePill - (by j00ru//vx)',0
  ExpON     db 'Virtual Machine detected!',0
  ExpOFF    db 'Virtual Machine NOT detected!',0
  buf       dd 0DEADC0D3h

.code
start:

  ; SEH
  xor eax, eax
  push offset AccViolationSEH
  db 064h ; FS
  push dword ptr [eax]
  db 064h ; FS
  mov dword ptr [eax], esp

  ; 11 * Segment prefix DS: - on real CPU this code causes
  ; Access Violation #GP(0) exception
  db 3eh, 3eh, 3eh, 3eh, 3eh, 3eh, 3eh, 3eh, 3eh, 3eh, 3eh
  mov eax, dword ptr [buf]
  
  ; no exception - detected!
  jmp detected

AccViolationSEH:
  ; real CPU generates Access Violation exception
  ; other exception - vm detected!
  mov eax, [esp+4]
  cmp dword ptr [eax], EXCEPTION_ACCESS_VIOLATION
  jnz detected

  ; SEH
  xor eax, eax
  push offset IllInstructionSEH
  db 064h ; FS
  push dword ptr [eax]
  db 064h ; FS
  mov dword ptr [eax], esp

  ; 15 * Segment prefix DS: on real CPU this code causes
  ; Illegal Instruction #UD exception
  db 3eh, 3eh, 3eh, 3eh, 3eh, 3eh, 3eh, 3eh, 3eh, 3eh, 3eh, 3eh, 3eh, 3eh, 3eh
  mov eax, dword ptr [buf]

  ; no exception - vm detected!
  jmp detected

IllInstructionSEH:
  ; real CPU generates Illegal Instruction exception
  ; other exception - vm detected!
  mov eax, [esp+4]
  cmp dword ptr [eax], EXCEPTION_ILLEGAL_INSTRUCTION
  jnz detected
  
  ; no virtual machine detected
  invoke MessageBoxA, 0, offset ExpOFF, offset ExpTitle, MB_ICONINFORMATION
  jmp exit

detected:
  invoke MessageBoxA, 0, offset ExpON, offset ExpTitle, MB_ICONERROR
  jmp exit

exit:
  invoke ExitProcess, 0
    
end start



Blog Comments
RolfRolles Posted: Wednesday, January 16 2008 22:49.26 CST
Thanks to both of you for sharing and giving me mischievous ideas :-)  (BTW the name "Purple Pill" was by used Alex Ionescu already, albeit temporarily)

Sirmabus Posted: Thursday, January 17 2008 00:05.13 CST
Yet another simple way for VMWARE I found in use:

// Does a VMWARE command to test for it's presence (causes an exception if it's not incidentally)
.text:10009370                 mov     eax, 564D5868h
.text:10009375                 mov     ebx, 0
.text:1000937A                 mov     ecx, 0Ah
.text:1000937F                 mov     edx, 5658h
.text:10009384                 in      eax, dx
.text:10009385                 cmp     ebx, 564D5868h
.text:1000938B                 setz    [ebp+var_bResult]
..
..
return(var_bResult)


And one for finding Virtual PC, etc.
The huge dump here you can see is basically using WMI to compare CPU "manufacture" names.
"ConnectixCPU", "Virtual CPU ", "VPC6", and "VPC7":

IID *__usercall EnumCPUFunc00<eax>(int a1<edi>, int a2, int a3)
{
  WORD *ST20_4_0; // ST20_4@0
  int ST30_4_0; // ST30_4@0
  WORD *ST34_4_0; // ST34_4@0
  int ST38_4_0; // ST38_4@0
  int ST3C_4_0; // ST3C_4@0
  int ST40_4_0; // ST40_4@0
  LONG ST44_4_0; // ST44_4@0
  void *ST5C_4_0; // ST5C_4@0
  LONG ST60_4_0; // ST60_4@0
  SOLE_AUTHENTICATION_SERVICE *ST64_4_0; // ST64_4@0
  void *ST68_4_0; // ST68_4@0
  DWORD ST6C_4_0; // ST6C_4@0
  DWORD ST70_4_0; // ST70_4@0
  void *ST74_4_0; // ST74_4@0
  DWORD ST78_4_0; // ST78_4@0
  void *ST7C_4_0; // ST7C_4@0
  int v19; // ST5C_4@1
  const CLSID *v20; // ST6C_4@1
  IUnknown *v21; // ST70_4@1
  DWORD v22; // ST74_4@1
  const IID *v23; // ST78_4@1
  LPVOID *v24; // ST7C_4@1
  WORD *v25; // eax@10
  OLECHAR *v26; // eax@12
  OLECHAR *v27; // esi@12
  HRESULT v28; // eax@14
  int v29; // ST1C_4@16
  LONG v30; // esi@17
  HRESULT v31; // eax@20
  HRESULT v32; // eax@22
  void *v33; // eax@24
  void *v34; // esi@24
  int v35; // ebx@28
  void *v36; // eax@32
  void *v37; // esi@32
  int v38; // ecx@39
  LPVOID v40; // ST7C_4@1
  int v41; // edx@4
  int v42; // edx@6
  int v43; // edx@8
  int v44; // ecx@9
  void *v45; // eax@17
  int v46; // edx@20
  int v47; // eax@25
  int v48; // eax@33
  IID *v49; // esi@74
  int v50; // [sp+84h] [bp-50h]@1
  signed int v51; // [sp+D0h] [bp-4h]@1
  IID *v52; // [sp+5Ch] [bp-78h]@1
  VARIANTARG pvargSrc; // [sp+4Ch] [bp-88h]@1
  VARIANTARG pvarSrc; // [sp+3Ch] [bp-98h]@1
  LPVOID ppv; // [sp+7Ch] [bp-58h]@2
  int v56; // [sp+78h] [bp-5Ch]@4
  VARIANTARG pvarg; // [sp+2Ch] [bp-A8h]@6
  int v58; // [sp+24h] [bp-B0h]@8
  void *v59; // [sp+20h] [bp-B4h]@8
  WORD *v60; // [sp+1Ch] [bp-B8h]@9
  signed int v61; // [sp+14h] [bp-C0h]@9
  char v62; // [sp+64h] [bp-70h]@13
  int v63; // [sp+70h] [bp-64h]@43

  ST7C_4_0 = cNULL;
  v50 = 0;
  CStringConstructFunc01(ST7C_4_0);
  ST7C_4_0 = 0;
  v51 = 1;
  *(_BYTE *)a3 = 0;
  CoInitialize(v40);
  ST7C_4_0 = 0;
  v52 = 0;
  *(_QWORD *)&pvargSrc.lVal = 3i64;
  *(_QWORD *)&pvargSrc.vt = 4294967296i64;
  *(_QWORD *)&pvarSrc.lVal = 4294967295i64;
  *(_DWORD *)&pvarSrc.wReserved2 = 0;
  if ( CoInitializeSecurity(ST5C_4_0, ST60_4_0, ST64_4_0, ST68_4_0, ST6C_4_0, ST70_4_0, ST74_4_0, ST78_4_0, v24) >= 0 )
  {
    ppv = 0;
    ST7C_4_0 = &ppv;
    v52 = &IID_IUnknown;
    *(_QWORD *)&pvargSrc.lVal = 21474836480i64;
    *(_DWORD *)&pvargSrc.wReserved2 = &stru_10023784;
    if ( CoCreateInstance(v20, v21, v22, v23, v24) < 0 )
      goto LABEL_72;
    if ( !ppv )
      goto LABEL_74;
    v56 = 0;
    v41 = *(_DWORD *)ppv;
    ST7C_4_0 = &v56;
    v52 = 0;
    *(_QWORD *)&pvargSrc.lVal = 0i64;
    *(_QWORD *)&pvargSrc.vt = 0i64;
    *((_DWORD *)&pvarSrc.lVal + 1) = 0;
    pvarSrc.lVal = (LONG)L"root\\cimv2";
    *(_DWORD *)&pvarSrc.wReserved2 = ppv;
    if ( (*(int (__cdecl **)(int))(v41 + 12))(v19) < 0 || !pvargSrc.lVal )
      goto LABEL_72;
    *(_DWORD *)&pvargSrc.wReserved2 = 0;
    v42 = *(_DWORD *)pvargSrc.lVal;
    *(_DWORD *)&pvarSrc.vt = &pvargSrc.wReserved2;
    *(_QWORD *)&pvarg.lVal = 16i64;
    *(_DWORD *)&pvarg.wReserved2 = L"Select * from Win32_Processor";
    *(_DWORD *)&pvarg.vt = L"WQL";
    ST44_4_0 = pvargSrc.lVal;
    if ( (*(int (__cdecl **)(LONG))(v42 + 80))(ST44_4_0) < 0 || !*((_DWORD *)&pvarg.lVal + 1) )
    {
LABEL_70:
      if ( ST20_4_0 )
        (*(int (__cdecl **)(WORD *))(*(_DWORD *)ST20_4_0 + 8))(ST20_4_0);
LABEL_72:
      if ( ST20_4_0 )
        (*(int (__cdecl **)(WORD *))(*(_DWORD *)ST20_4_0 + 8))(ST20_4_0);
      goto LABEL_74;
    }
    v43 = **((_DWORD **)&pvarg.lVal + 1);
    v58 = a1;
    v59 = (void *)*((_DWORD *)&pvarg.lVal + 1);
    if ( (*(int (__cdecl **)(int, int))(v43 + 12))(ST3C_4_0, ST40_4_0) >= 0 )
    {
      v60 = &pvarSrc.wReserved2;
      ST34_4_0 = &pvarg.wReserved2;
      *(_DWORD *)&pvarg.wReserved2 = 0;
      v44 = *(_DWORD *)pvarg.lVal;
      v61 = 1;
      if ( (*(int (__cdecl **)(LONG, signed int, int, WORD *, int))(v44 + 16))(pvarg.lVal, -1, ST30_4_0, ST34_4_0, ST38_4_0) < 0 )
        goto LABEL_65;
      v25 = v60;
      if ( v60 )
      {
        if ( *(_DWORD *)&pvarg < 1u )
          goto LABEL_66;
        v26 = SysAllocString(L"Manufacturer");
        v27 = v26;
        if ( v26 )
        {
          SysFreeString(v26);
          ST20_4_0 = 0;
          if ( (*(int (__cdecl **)(WORD *, OLECHAR *, _DWORD, char *))(*(_DWORD *)v60 + 16))(v60, v27, 0, &v62) >= 0 )
          {
            VariantInit(&pvarg);
            v28 = VariantCopy(&pvarg, &pvargSrc);
            if ( v28 < 0 )
              sub_1001234F(v28);
            v29 = 0;
            v62 = 2;
            if ( (_WORD)pvarg.vt == 8 )
            {
              v30 = pvarg.lVal;
              sub_1000A080();
              v45 = operator new(0xCu);
              v60 = (WORD *)v45;
              v62 = 3;
              if ( v45 )
                v29 = sub_1000A1B0((OLECHAR *)v30);
              else
                v29 = 0;
            }
            else
            {
              VariantInit(&pvarSrc);
              v62 = 4;
              sub_1000A2D0(&pvarSrc, v46, 8u, (int)&pvarg);
              sub_1000A0D0((OLECHAR *)pvarSrc.lVal);
              v62 = 2;
              v31 = VariantClear(&pvarSrc);
              if ( v31 < 0 )
                sub_1001234F(v31);
            }
            v62 = 6;
            v32 = VariantClear(&pvarg);
            if ( v32 < 0 )
              sub_1001234F(v32);
            v33 = operator new(0xCu);
            v34 = v33;
            v60 = (WORD *)v33;
            v62 = 7;
            if ( v33 )
            {
              *((_DWORD *)v33 + 1) = 0;
              *((_DWORD *)v33 + 2) = 1;
              v47 = sub_1001227E("ConnectixCPU");    -- Anyone? What VM is this?
              *(_DWORD *)v34 = v47;
              if ( !v47 )
              {
                if ( "ConnectixCPU" )
                  sub_1001234F(-2147024882);
              }
              v35 = (int)v34;
            }
            else
            {
              v35 = 0;
            }
            v62 = 6;
            v60 = (WORD *)v35;
            if ( !v35 )
              sub_1001234F(-2147024882);
            v62 = 8;
            v36 = operator new(0xCu);
            v37 = v36;
            v59 = v36;
            v62 = 9;
            if ( v36 )
            {
              *((_DWORD *)v36 + 1) = 0;
              *((_DWORD *)v36 + 2) = 1;
              v48 = sub_1001227E("Virtual CPU ");
              *(_DWORD *)v37 = v48;
              if ( !v48 )
              {
                if ( "Virtual CPU " )
                  sub_1001234F(-2147024882);
              }
            }
            else
            {
              v37 = 0;
            }
            v62 = 8;
            v59 = v37;
            if ( !v37 )
              sub_1001234F(-2147024882);
            v38 = v29;
            v62 = 10;
            if ( v29 == v35 )
              goto LABEL_43;
            if ( v29 && v35 )
            {
              if ( !sub_1000A1F0(v35) )
              {
LABEL_43:
                *(_BYTE *)v63 = 1;
                ConstructorFunc02("VPC6");
LABEL_51:
                VariantClear(&pvargSrc);
                v62 = 8;
                if ( v37 )
                {
                  if ( !InterlockedDecrement((LPLONG)v37 + 2) )
                  {
                    sub_1000A2A0();
                    FreeDataFunc00(v37);
                  }
                }
                v62 = 6;
                if ( v35 )
                {
                  if ( !InterlockedDecrement((LPLONG)(v35 + 8)) )
                  {
                    sub_1000A2A0();
                    FreeDataFunc00(v35);
                  }
                }
                v62 = 1;
                if ( v29 )
                {
                  if ( !InterlockedDecrement((LPLONG)(v29 + 8)) )
                  {
                    if ( v29 )
                    {
                      if ( *(_DWORD *)v29 )
                        SysFreeString(*(BSTR *)v29);
                      if ( *(_DWORD *)(v29 + 4) )
                        FreeDataFunc00(*(_DWORD *)(v29 + 4));
                      FreeDataFunc00(v29);
                    }
                  }
                }
                goto LABEL_65;
              }
              v38 = v29;
            }
            if ( v38 == (_DWORD)v37 || v38 && v37 && !sub_1000A1F0(v37) )
            {
              *(_BYTE *)v63 = 1;
              ConstructorFunc02("VPC7");
            }
            else
            {
              *(_BYTE *)v63 = 0;
            }
            goto LABEL_51;
          }
        }
LABEL_65:
        v25 = ST20_4_0;
LABEL_66:
        if ( v25 )
          (*(int (__cdecl **)(WORD *))(*(_DWORD *)v25 + 8))(v25);
        goto LABEL_68;
      }
    }
LABEL_68:
    if ( ST20_4_0 )
      (*(int (__cdecl **)(_DWORD))(*(_DWORD *)ST20_4_0 + 8))(ST20_4_0);
    goto LABEL_70;
  }
LABEL_74:
  CoUninitialize();
  v49 = v52;
  CStringConstructFunc02(&v61);
  LOBYTE(pvargSrc.lVal) = 0;
  CStringDestructFunc00();
  return v49;
}










j00ru Posted: Thursday, January 17 2008 04:54.22 CST
RolfRolles: Good to know, I'll think about renaming it, then ;-)

Sirmabus: Cool, thanks for sharing ;>
I've found out that this ConnectixCPU string is likely to be a sign of VirtualPC:
"In VirtualPC, though, it is "ConnectixCPU", a reference to the company which developed the earlier versions of VirtualPC." - http://www.symantec.com/avcenter/reference/Virtual_Machine_Threats.pdf

ZuTLe Posted: Thursday, January 17 2008 05:11.56 CST
This is the first detection mechanism I've seen that works for Parallels on Mac. Thanks for sharing!

Soul12 Posted: Thursday, January 17 2008 07:57.26 CST
nice find.. :)

GynvaelColdwind Posted: Friday, January 18 2008 14:42.20 CST
@ZuTLe
Hmmm I didn't run Parallels on Mac, but I've run it on PC. Afair it was detectable using CPUID. It said that the processor is CeleronA, but showed features like SSE2 or SSE3 which CeleronA didn't posses. Additionally it showed 1mb cache (afair celeronA had 128kb or sth like that).
Could u check what it shows on Mac ? ;>

sovietskicpu Posted: Friday, January 18 2008 17:24.15 CST
Damn it !!! you begin to discover dangerous things hehehe...

RolfRolles : if you plan to study AV Emulators, keep in mind they are even weaker than boch and virtual pc, because they need to emulate everything programmatically ( by hand ) ==> no way to do dynamic compilation ==> more specific detections; here is some little openings you maybe already dont know :

- OS API Emulations attack :

Example : Use weired OS API params then checks OS Error code with GetLastError and believe me you won't believe your eyez.

- AV Disassemblers engines :

Example : crafted instructions ( crafted Prefixes ) may be usefuls to enlight your way :p




RolfRolles Posted: Saturday, January 19 2008 23:22.43 CST
Hey sovietskicpu,

You weren't kidding about the OS and API emulation:  this is ridiculously inaccurate in every respect that I have cared to investigate.  I was expecting much better than this, which is that you can detect this particular AV emulator with three lines of C code:


SetLastError(ERROR_SUCCESS);
/* Invalid parameter #0 */
Beep(0,0);
bBeingEmulated = GetLastError() != ERROR_INVALID_PARAMETER;


This is by no means the only way to accomplish this, and while I've only looked at one, I bet most or all of the other emulators are vulnerable to this as well.  Then there's also the wanton discrepancies between the chipset specification and the emulator implementation, the one presented in this blog entry (which we have hijacked; sorry) being just one example.  What a joke, and what a waste of four days statically analyzing this thing.  This project has taught me that AV is snake-oil of the worst kind.

j00ru Posted: Sunday, January 20 2008 03:55.46 CST
By the way, a few people have told me that VMWare Workstation 6.0.2 still remains undetected using my method, and so it does, huh ;o

sovietskicpu Posted: Sunday, January 20 2008 07:56.48 CST
RolfRolles,

Off course i wasn't kidding,I was even ashamed to give such dumb hints while I'm myself developping my own antivirus engine. However this is the real truth about the big lies of AV industries.

One day, i will discuss with you privately how to extract sandboxes from AV Engines like Norman Sandbox / BitDefender B-HAVE and stuffs.. :)

Waiting for that silly day to come, i wish you the best for your reversing quests.


sovietskicpu Posted: Sunday, January 20 2008 08:34.41 CST
j00ru, with all my respect :

1 - The MAX_INSTRUCTION_LENGTH prefixes exploit to detect VMWARE / VIRTUAL PC was already been fixed. However i heared it was specific to Microsoft Virtual PC and not VMWARE.

2 - The method of detection based on the value of the returned EXCEPTION_VALUE may be applied only to detect OS emulators like AV Emulators, wine or ReactOS and not to detect PURE CPU EMULATORS like VMWARE/Virtual PC. They are dynamic translators <==> Dynamic Compilers, what mean they don't emulate the Windows OS but they let the OS Execute by itself TRANSPARENTLY.

"EXCEPTION_ACCESS_VIOLATION" won't be handled by the CPU Translator but by the EMULATED OPERATING SYSTEM !!! the supervisor only handles privilieged instructions etc...

ZuTLe Posted: Sunday, January 20 2008 14:07.30 CST
@ GynvaelColdwind:

It's close, but no sigar :P

Intel Core 2 Duo, MMX/SSE/SSE2/SSE3, 4MB L2C, 32 kB L1C (x2), Cores: 1   :)



Add New Comment
Comment:









There are 28,212 total registered users.


Recently Created Topics
Reverse Engineering ...
Jan/23
Career: DoD Agency I...
Jan/22
"Disappearing&q...
Jan/17
Career: Software Sec...
Jan/11
Where is the call st...
Jan/07
IDA Pro 6.1 Breakpoi...
Jan/01
How to create data s...
Dec/30
can i search all mod...
Dec/23
IDA symbol table exp...
Dec/20
An anti-attach trick
Dec/17


Recent Forum Posts
Reverse Engineering ...
NirIzr
"Disappearing&q...
NirIzr
Reverse Engineering ...
charlie
"Disappearing&q...
charlie
An anti-attach trick
Bass
An anti-attach trick
waleeda...
An anti-attach trick
Bass
An anti-attach trick
waleeda...
An anti-attach trick
Bass
Looking for value in...
NirIzr


Recent Blog Entries
Ludwig
Feb/04
chi on sale

Ludwig
Feb/04
Monster In The Vicinity Of ...

Ludwig
Feb/04
Supra footwear Online

waleedassar
Jan/31
Yet Another Anti-Debug Trick

RolfRolles
Jan/22
Finding Bugs in VMs with a ...

More ...


Recent Blog Comments
waleedassar on:
Feb/01
Yet Another Anti-Debug Trick

NirIzr on:
Jan/31
Yet Another Anti-Debug Trick

jackchen on:
Jan/10
nike mercurial vapor iii

waleedassar on:
Dec/27
A new Anti-Olly trick.

PeterFerrie on:
Dec/27
A new Anti-Olly trick.

More ...


Imagery
SoySauce Blueprint
Jun 6, 2008

[+] expand

View Gallery (11) / Submit