2010-09-08 3 views
1

J'essaie de détecter si je cours sur un environnement virtuel (vmware, virtualbox, etc)
Sur Windows j'ai utilisé plusieurs ASM mais je ne peux pas les utiliser sous Linux, principalement parce que le code peut être compilé et exécuté sur Linux 32 ou 64 bits.Détecter VMM sur Linux

Le code suivant fonctionne sur Windows 32 et 64 ans et a été testé sur VMWare, VirtualBox et d'autres machines virtuelles:

#include <stdio.h> 

int idtCheck() 
{ 
    unsigned char m[2]; 
    __asm sidt m; 
    printf("IDTR: %2.2x %2.2x\n", m[0], m[1]); 
    return (m[1]>0xd0) ? 1 : 0; 
} 

int gdtCheck() 
{ 
    unsigned char m[2]; 
    __asm sgdt m; 
    printf("GDTR: %2.2x %2.2x\n", m[0], m[1]); 
    return (m[1]>0xd0) ? 1 : 0; 
} 

int ldtCheck() 
{ 
    unsigned char m[2]; 
    __asm sldt m; 
    printf("LDTR: %2.2x %2.2x\n", m[0], m[1]); 
    return (m[0] != 0x00 && m[1] != 0x00) ? 1 : 0; 
} 

int main(int argc, char * argv[]) 
{ 
    idtCheck(); 
    gdtCheck(); 

    if (ldtCheck()) 
     printf("Virtual Machine detected.\n"); 
    else 
     printf("Native machine detected.\n"); 

    return 0; 
} 

maintenant, GCC se plaint du __asm ​​sur toutes les fonctions. J'ai essayé avec asm(), asm et d'autres formes que j'ai utilisées dans le passé mais aucun ne fonctionne. Des idées?

+0

Je vous suggère de lire l'ensemble du document, en particulier le texte après ** NOTE: **. Ensuite, vous seriez en mesure de poser une question pertinente ou de le comprendre vous-même. – jbcreix

+0

merci. voir la question éditée. –

+3

SIDT génère un résultat de 6 octets (32 bits) ou de 10 octets (64 bits). Ne le placez pas dans un tableau de caractères de 2 octets, sauf si vous préférez écraser des bits aléatoires de votre pile. – bdonlan

Répondre

4

Eh bien, je n'ai pas désassemblé le code de la machine là-dedans, mais voici une version en utilisant l'assembleur en ligne de GCC:

int redpill() 
{ 
     unsigned char idt_addr[(sizeof(long)==8)?10:6]; 
     __asm__("SIDT (%[ptr])" 
         : "=m" (idt_addr) 
         : [ptr] "r" (&idt_addr)); 
     // examine high order byte 
     return idt_addr[(sizeof(long)==8)?9:5] > 0xd0; 
} 

Ce devrait « travail », même pour 64 bits, mais j'ai pas testé là-bas.

CEPENDANT! Ce n'est pas garanti pour donner le résultat que vous voulez, du tout. Tout d'abord, cela ne fonctionnera pas avec la virtualisation matérielle, car vous ne pouvez pas voir le véritable IDT. Deuxièmement, il s'appuie sur un détail de mise en œuvre de VMWare et Virtual PC, qui pourrait probablement être changé assez facilement. Il pourrait même déclencher de fausses alarmes si votre système d'exploitation décide de mettre son IDT à une adresse élevée. Donc, je ne recommande pas cette approche du tout.

Pour les machines virtuelles utilisant le support matériel VMX, il est probablement préférable de faire quelque chose qui devrait être rapide dans le matériel mais nécessiter un piège dans une machine virtuelle, et vérifier le timing. Quelque chose comme CPUID serait un bon pari; comparez-le dans une VM et sur du matériel réel (comparé à une boucle fictive qui fait un ADD ou quelque chose pour gérer des fréquences d'horloge différentes), et voyez quel profil correspond le plus à la machine de test. Comme chaque CPUID devra quitter la VM pour demander au noyau hôte quelles capacités il veut présenter, il faudra BEAUCOUP plus long que sur le matériel réel. Pour d'autres types de machines virtuelles, vous pouvez effectuer une vérification de synchronisation similaire simplement en chargeant un registre de contrôle ou un registre de débogage - la VM devra le piéger, ou le remplacer par du code émulé. Si vous êtes sur quelque chose comme VMware, il pourrait remplacer le piège par du code émulé - dans ce cas, vous pourriez l'identifier en chronométrant en modifiant le code contenant l'opération de registre de contrôle ou de débogage. Cela forcera la machine virtuelle à invalider son code caché, ce qui nécessitera un piège coûteux pour la machine virtuelle, qui apparaîtra sur votre banc d'essai. Notez que ces deux approches nécessiteront l'aide du noyau OS - il sera difficile de déterminer si vous êtes dans une VM si vous n'avez pas le contrôle du noyau émulé au moins. Et si la machine virtuelle est vraiment sophistiquée, elle peut aussi fausser le timing, ce qui rend les choses vraiment difficiles - mais cela a tendance à tuer les performances et à dériver dans l'horloge (facile à identifier si vous pouvez vous connecter à Internet et interroger serveur quelque part!), donc la plupart des machines virtuelles commerciales ne le font pas.

+0

Êtes-vous sûr de CPUID? Je rappelle que CPUID est mis en cache. Ceci est nécessaire pour déplacer des machines virtuelles sur des architectures matérielles suffisamment similaires. Google "VMotion CPUID" semble être d'accord. – MSalters

+0

Le processeur Intel traite les documents CPUID, INVD, GETSEC et XSETBV comme provoquant une sortie de machine virtuelle. Le système d'exploitation peut mettre en cache CPUID dans le logiciel, mais cela nécessitera toujours la sortie de la machine virtuelle, ce qui est assez lent pour être suffisant. Les autres instructions ne sont pas largement supportées, ou provoquent des effets secondaires indésirables. – bdonlan