2012-04-10 8 views
9

Sto cercando di sviluppare un'applicazione che rileva se il programma è in esecuzione all'interno di una macchina virtuale.Windows 64 bit Rilevamento VMware

Per Windows a 32 bit, ci sono già i metodi spiegati nel seguente link: http://www.codeproject.com/Articles/9823/Detect-if-your-program-is-running-inside-a-Virtual

Sto cercando di adattare il codice in materia di Virtual PC e VMware rilevamento in un sistema operativo Windows a 64 bit. Per VMware, il codice può rilevare correttamente in un sistema operativo Windows XP a 64 bit. Ma il programma si blocca quando lo eseguo in un sistema nativo (sistema operativo Windows 7 a 64 bit).

Inserisco il codice in un file .asm e definisco il passo di generazione personalizzato con il file ml64.exe. Il codice asm a 64 bit di Windows è:

IsInsideVM proc 

     push rdx 
     push rcx 
     push rbx 

     mov rax, 'VMXh' 
     mov rbx, 0  ; any value but not the MAGIC VALUE 
     mov rcx, 10 ; get VMWare version 
     mov rdx, 'VX' ; port number 

     in  rax, dx ; read port 
         ; on return EAX returns the VERSION 
     cmp rbx, 'VMXh'; is it a reply from VMWare? 
     setz al   ; set return value 
     movzx rax,al 

     pop rbx 
     pop rcx 
     pop rdx 

     ret 
IsInsideVM endp 

Io chiamo questa parte in un file cpp come:

__try 
{ 
returnValue = IsInsideVM(); 
} 
__except(1) 
{ 
    returnValue = false; 
} 

Grazie in anticipo.

+1

Ebbene sì, il tentativo di accedere alle porte hardware, che è un'operazione privilegiata, verrà intrappolato nel codice utente. È necessario rilevare l'eccezione utilizzando SEH. Sembra che tu stia cercando, ma non hai mostrato (1) opzioni del compilatore o (2) una traccia del debugger. –

risposta

4

Il vecchio pillola rossa da Joanna può funzionare: random backup page of invisiblethings.org blog:

deglutire la pillola rossa è più o meno equivalente al seguente codice (restituisce non zero quando in Matrix):

int swallow_redpill() { 
    unsigned char m[2+4], rpill[] = "\x0f\x01\x0d\x00\x00\x00\x00\xc3"; 
    *((unsigned*)&rpill[3]) = (unsigned)m; 
    ((void(*)())&rpill)(); 
    return (m[5]>0xd0) ? 1 : 0; 
} 

Il cuore di questo codice è in realtà l'istruzione SIDT (codificata come 0F010D [addr]), che memorizza il contenuto del registro della tabella descrittore di interrupt (IDTR) nell'operando di destinazione, che in realtà è una posizione di memoria. Ciò che è speciale e interessante nell'istruzione SIDT è che, può essere eseguito in modalità non privilegiata (ring3) ma restituisce il contenuto del registro sensibile, utilizzato internamente dal sistema operativo.

Poiché esiste un solo registro IDTR, ma sono in esecuzione almeno due sistemi operativi (ovvero l'host e il sistema operativo guest), VMM deve riposizionare l'IDTR del guest in un luogo sicuro, in modo che non sia in conflitto con quello di un ospite. Sfortunatamente, VMM non può sapere se (e quando) il processo in esecuzione nel SO guest esegue l'istruzione SIDT, poiché non è privilegiato (e non genera eccezioni). In questo modo il processo ottiene l'indirizzo trasferito della tabella IDT. È stato osservato che su VMWare, l'indirizzo trasferito di IDT è all'indirizzo 0xffXXXXXX, mentre su Virtual PC è 0xe8XXXXXX. Questo è stato testato su VMWare Workstation 4 e Virtual PC 2004, entrambi in esecuzione su SO Windows XP.

Nota: non l'ho provato da solo ma osservo che utilizza un approccio non privilegiato. Se inizialmente non funziona per x64, potrebbe essere utile qualche ritocco.

Inoltre, appena scoperto che una domanda di contenuti che possono aiutare: Detecting VMM on linux

0

La mia ipotesi è che la funzione corrups registri.

L'esecuzione su hardware reale (non VM) dovrebbe probabilmente generare un'eccezione su "in rax, dx". Se ciò accade, il controllo viene passato al gestore delle eccezioni, che imposta il risultato, ma non ripristina i registri. Questo comportamento sarà completamente inaspettato dal chiamante. Ad esempio, può salvare qualcosa nel registro EBX/RBX, quindi chiamare il tuo codice asm, il tuo codice asm fa "mov RBX, 0", esegue, ricava eccezione, imposta risultato, restituisce - e quindi il chiamante si rende conto che i suoi dati salvati non è più in EBX/RBX!Se ci fosse qualche puntatore memorizzato in EBX/RBX - stai andando a crash duro. Tutto può succedere.

Sicuramente, il tuo codice asm salva/ripristina i registri, ma questo accade solo quando non viene sollevata alcuna eccezione. Cioè se il tuo codice è in esecuzione su VM. Quindi il tuo codice esegue il suo normale percorso di esecuzione, senza eccezioni, i registri verranno ripristinati normalmente. Ma se c'è un'eccezione, i tuoi POP verranno saltati, perché l'esecuzione verrà passata al gestore delle eccezioni.

Il codice corretto dovrebbe probabilmente eseguire PUSH/POP al di fuori del try/except block, non all'interno.

+0

Forse 'setjmp' e' longjmp' potrebbero essere usati per preservare forzatamente registri e stack pointer (una variabile globale può essere usata per permettere al risultato di sopravvivere longjmp) –

+0

Forse potresti usare SetUnhandledExceptionFilter per catturare l'errore, armare con PEXCEPTION_POINTERS per regola l'indirizzo "continua", quindi restituisci EXCEPTION_CONTINUE_EXECUTION. –