Mode sécurisé virtuel

Le mode sécurisé virtuel (VSM) est un ensemble de fonctionnalités d’hyperviseur et d’éclairage proposés aux partitions hôtes et invitées qui permettent la création et la gestion de nouvelles limites de sécurité au sein du logiciel du système d’exploitation. VSM est l’installation d’hyperviseur sur laquelle Windows fonctionnalités de sécurité, y compris Device Guard, Credential Guard, les machines virtuelles virtuelles et les machines virtuelles protégées sont basées. Ces fonctionnalités de sécurité ont été introduites dans Windows 10 et Windows Server 2016.

VSM permet aux logiciels du système d’exploitation dans les partitions racine et invité de créer des régions isolées de mémoire pour le stockage et le traitement des ressources de sécurité système. L’accès à ces régions isolées est contrôlé et accordé uniquement par le biais de l’hyperviseur, qui est une partie hautement privilégiée et hautement approuvée de la base de calcul approuvée (TCB) du système. Étant donné que l’hyperviseur s’exécute à un niveau de privilège supérieur au logiciel du système d’exploitation et qu’il dispose d’un contrôle exclusif des ressources matérielles du système d’exploitation, comme les contrôles d’autorisation d’accès en mémoire dans l’uc MMU et IOMMU au début de l’initialisation du système d’exploitation, l’hyperviseur peut protéger ces régions isolées contre l’accès non autorisé, même à partir de logiciels de système d’exploitation (par exemple, noyau du système d’exploitation et pilotes d’appareils) avec un accès en mode superviseur (cPL0, par exemple, CPL0, ou « Anneau 0 »).

Avec cette architecture, même si le logiciel de niveau système normal s’exécutant en mode superviseur (par exemple, noyau, pilotes, etc.) est compromis par des logiciels malveillants, les ressources dans les régions isolées protégées par l’hyperviseur peuvent rester sécurisées.

Niveau de confiance virtuelle (VTL)

VSM obtient et gère l’isolation par le biais de niveaux de confiance virtuel (VTL). Les VTL sont activés et gérés par partition et par processeur virtuel.

Les niveaux d’approbation virtuelle sont hiérarchiques, avec des niveaux plus élevés que des niveaux inférieurs. VTL0 est le niveau le moins privilégié, avec VTL1 plus privilégié que VTL0, VTL2 étant plus privilégié que VTL1, etc.

De façon architecturale, jusqu’à 16 niveaux de VTL sont pris en charge ; toutefois, un hyperviseur peut choisir d’implémenter moins de 16 VTL. Actuellement, seules deux VTL sont implémentées.

typedef UINT8 HV_VTL, *PHV_VTL;

#define HV_NUM_VTLS 2
#define HV_INVALID_VTL ((HV_VTL) -1)
#define HV_VTL_ALL 0xF

Chaque VTL possède son propre ensemble de protections d’accès à la mémoire. Ces protections d’accès sont gérées par l’hyperviseur dans l’espace d’adressage physique d’une partition, et ne peuvent donc pas être modifiées par les logiciels de niveau système exécutés dans la partition.

Étant donné que les VTL plus privilégiés peuvent appliquer leurs propres protections de mémoire, les VTL plus élevés peuvent protéger efficacement les zones de mémoire contre les VTL inférieures. Dans la pratique, cela permet à une durée de vie VTL inférieure de protéger les régions de mémoire isolées en les sécurisant avec une durée de vie VTL plus élevée. Par exemple, VTL0 peut stocker un secret dans VTL1, auquel point seul VTL1 peut y accéder. Même si VTL0 est compromis, le secret serait sûr.

Protections de durée de vie virtuelle

Il existe plusieurs facettes pour atteindre l’isolation entre les VTL :

  • Protections d’accès à la mémoire : chaque durée de vie virtuelle gère un ensemble de protections d’accès à la mémoire physique invité. Les logiciels exécutés sur une durée de vie virtuelle particulière peuvent uniquement accéder à la mémoire conformément à ces protections.
  • État du processeur virtuel : les processeurs virtuels conservent un état distinct par VTL. Par exemple, chaque VTL définit un ensemble d’enregistrements VP privés. Les logiciels s’exécutant à une durée de vie VTL inférieure ne peuvent pas accéder à l’état d’inscription du processeur virtuel privé supérieur.
  • Interruptions : Avec un état de processeur distinct, chaque VTL possède également son propre sous-système d’interruption (APIC local). Cela permet aux VTL plus élevés de traiter les interruptions sans risquer d’interférences à partir d’une durée de vie VTL inférieure.
  • Pages de superposition : certaines pages de superposition sont conservées par VTL afin que les VTL plus élevés aient un accès fiable. Par exemple, il existe une page de superposition hypercall distincte par VTL.

Détection et état VSM

La fonctionnalité VSM est annoncée aux partitions via l’indicateur de privilège de partition AccessVsm. Seules les partitions avec tous les privilèges suivants peuvent utiliser VSM : AccessVsm, AccessVpRegisters et AccessSynicRegs.

Détection des fonctionnalités VSM

Les invités doivent utiliser le registre spécifique au modèle suivant pour accéder à un rapport sur les fonctionnalités VSM :

Adresse MSR Nom de l’inscription Description
0x000D0006 HV_X64_REGISTER_VSM_CAPABILITIES Rapport sur les fonctionnalités VSM.

Le format du MSR Des fonctionnalités VSM d’inscription est le suivant :

Bits Description Attributs
63 Dr6Shared Lire
62:47 MbecVtlMask Lire
46 DenyLowerVtlStartup Lire
45:0 RsvdZ Lire

Dr6Shared indique à l’invité si Dr6 est un registre partagé entre les VTL.

MvecVtlMask indique à l’invité les VTL pour lesquels Mbec peut être activé.

DenyLowerVtlStartup indique à l’invité si un Vtl peut refuser une réinitialisation VP par une durée de vie VTL inférieure.

Registre d’état VSM

Outre un indicateur de privilège de partition, deux registres virtuels peuvent être utilisés pour en savoir plus sur l’état VSM : HvRegisterVsmPartitionStatus et HvRegisterVsmVpStatus.

HvRegisterVsmPartitionStatus

HvRegisterVsmPartitionStatus est un registre en lecture seule par partition partagé sur toutes les VTL. Ce registre fournit des informations sur les VTL qui ont été activés pour la partition, dont les VTL ont activé les contrôles d’exécution basés sur le mode, ainsi que la durée de vie virtuelle maximale autorisée.

typedef union
{
    UINT64 AsUINT64;
    struct
    {
        UINT64 EnabledVtlSet : 16;
        UINT64 MaximumVtl : 4;
        UINT64 MbecEnabledVtlSet: 16;
        UINT64 ReservedZ : 28;
    };
} HV_REGISTER_VSM_PARTITION_STATUS;

HvRegisterVsmVpStatus

HvRegisterVsmVpStatus est un registre en lecture seule et est partagé entre toutes les VTL. Il s’agit d’un registre par VP, ce qui signifie que chaque processeur virtuel gère sa propre instance. Ce registre fournit des informations sur les VTL qui ont été activés, qui sont actifs, ainsi que le mode MBEC actif sur un VP.

typedef union
{
    UINT64 AsUINT64;
    struct
    {
        UINT64 ActiveVtl : 4;
        UINT64 ActiveMbecEnabled : 1;
        UINT64 ReservedZ0 : 11;
        UINT64 EnabledVtlSet : 16;
        UINT64 ReservedZ1 : 32;
    };
} HV_REGISTER_VSM_VP_STATUS;

ActiveVtl est l’ID du contexte VTL actuellement actif sur le processeur virtuel.

ActiveMbecEnabled spécifie que MBEC est actuellement actif sur le processeur virtuel.

EnabledVtlSet est une bitmap des VTL activées sur le processeur virtuel.

État initial de la partition VTL

Lorsqu’une partition démarre ou réinitialise, elle commence à s’exécuter dans VTL0. Toutes les autres VTL sont désactivées lors de la création de la partition.

Activation de la durée de vie virtuelle

Pour commencer à utiliser une durée de vie virtuelle, une durée de vie virtuelle inférieure doit lancer ce qui suit :

  1. Activez la durée de vie virtuelle cible pour la partition. Cela rend la durée de vie virtuelle généralement disponible pour la partition.
  2. Activez la durée de vie VTL cible sur un ou plusieurs processeurs virtuels. Cela rend la durée de vie virtuelle disponible pour un VP et définit son contexte initial. Il est recommandé que toutes les adresses virtuelles aient les mêmes VTL activées. L’activation d’une durée de vie virtuelle sur certaines adresses virtuelles (mais pas toutes) peut entraîner un comportement inattendu.
  3. Une fois la durée de vie virtuelle activée pour une partition et un VP, elle peut commencer à définir des protections d’accès une fois que l’indicateur EnableVtlProtection a été défini.

Notez que les VTL n’ont pas besoin d’être consécutifs.

Activation d’une durée de vie virtuelle cible pour une partition

L’hypercall HvCallEnablePartitionVtl est utilisé pour activer une durée de vie virtuelle pour une certaine partition. Notez que avant que le logiciel ne puisse réellement s’exécuter dans une durée de vie VTL particulière, cette durée de vie virtuelle doit être activée sur les processeurs virtuels de la partition.

Activation d’une durée de vie virtuelle cible pour les processeurs virtuels

Une fois qu’une durée de vie virtuelle est activée pour une partition, elle peut être activée sur les processeurs virtuels de la partition. L’hypercall HvCallEnableVpVtl peut être utilisé pour activer les VTL pour un processeur virtuel, qui définit son contexte initial.

Les processeurs virtuels ont un « contexte » par VTL. Si une durée de vie virtuelle est activée, l’état privé du VTL est également changé.

Configuration de la durée de vie virtuelle

Une fois qu’une durée de vie virtuelle a été activée, sa configuration peut être modifiée par un VP s’exécutant à une durée de vie VTL égale ou supérieure.

Configuration de partition

Les attributs à l’échelle de la partition peuvent être configurés à l’aide du registre HvRegisterVsmPartitionConfig. Il existe une instance de ce registre pour chaque durée de vie virtuelle (supérieure à 0) sur chaque partition.

Chaque VTL peut modifier sa propre instance de HV_REGISTER_VSM_PARTITION_CONFIG, ainsi que les instances pour les VTL inférieures. Les VTL peuvent ne pas modifier ce registre pour les VTL plus élevés.

typedef union
{
    UINT64 AsUINT64;
    struct
    {
        UINT64 EnableVtlProtection : 1;
        UINT64 DefaultVtlProtectionMask : 4;
        UINT64 ZeroMemoryOnReset : 1;
        UINT64 DenyLowerVtlStartup : 1;
        UINT64 ReservedZ : 2;
        UINT64 InterceptVpStartup : 1;
        UINT64 ReservedZ : 54; };
} HV_REGISTER_VSM_PARTITION_CONFIG;

Les champs de ce registre sont décrits ci-dessous.

Activer les protections VTL

Une fois qu’une durée de vie virtuelle a été activée, l’indicateur EnableVtlProtection doit être défini pour pouvoir commencer à appliquer des protections de mémoire. Cet indicateur est en écriture seule, ce qui signifie qu’une fois qu’il a été défini, il ne peut pas être modifié.

Masque de protection par défaut

Par défaut, le système applique des protections RWX à toutes les pages actuellement mappées et aux futures pages « ajoutées à chaud ». Les pages ajoutées à chaud font référence à toute mémoire ajoutée à une partition pendant une opération de redimensionnement.

Une durée de vie VTL supérieure peut définir une autre stratégie de protection de la mémoire par défaut en spécifiant DefaultVtlProtectionMask dans HV_REGISTER_VSM_PARTITION_CONFIG. Ce masque doit être défini au moment de l’activation de la durée de vie virtuelle. Il ne peut pas être modifié une fois qu’il est défini et n’est effacé que par une réinitialisation de partition.

bit Description
0 Lire
1 Write
2 Exécution en mode noyau (KMX)
3 Exécution en mode utilisateur (UMX)

Mémoire zéro lors de la réinitialisation

ZeroMemOnReset est un bit qui contrôle si la mémoire est zéro avant la réinitialisation d’une partition. Cette configuration est activée par défaut. Si le bit est défini, la mémoire de la partition est mise à zéro lors de la réinitialisation afin qu’une mémoire de VTL supérieure ne puisse pas être compromise par une valeur VTL inférieure. Si ce bit est effacé, la mémoire de la partition n’est pas mise à zéro lors de la réinitialisation.

DenyLowerVtlStartup

L’indicateur DenyLowerVtlStartup contrôle si un processeur virtuel peut être démarré ou réinitialisé par des VTL inférieurs. Cela inclut des méthodes architecturales de réinitialisation d’un processeur virtuel (par exemple, SIPI sur X64) ainsi que l’hypercall HvCallStartVirtualProcessor .

InterceptVpStartup

Si l’indicateur InterceptVpStartup est défini, le démarrage ou la réinitialisation d’un processeur virtuel génère une interception à la durée de vie VTL supérieure.

Configuration de VTLs inférieurs

Le registre suivant peut être utilisé par des VTL plus élevés pour configurer le comportement des VTL inférieurs :

typedef union
{
    UINT64 AsUINT64;
    struct
    {
        UINT64 MbecEnabled : 1;
        UINT64 TlbLocked : 1;
        UINT64 ReservedZ : 62;
    };
} HV_REGISTER_VSM_VP_SECURE_VTL_CONFIG;

Chaque VTL (supérieur à 0) a une instance de ce registre pour chaque VTL inférieure à elle-même. Par exemple, VTL2 aurait deux instances de ce registre : une pour VTL1 et une seconde pour VTL0.

Les champs de ce registre sont décrits ci-dessous.

MbecEnabled

Ce champ configure si MBEC est activé pour la durée de vie virtuelle inférieure.

TlbLocked

Ce champ verrouille le TLB inférieur de la durée de vie virtuelle. Cette fonctionnalité peut être utilisée pour empêcher les VTL inférieurs d’entraîner des invalidations TLB qui peuvent interférer avec une durée de vie VTL plus élevée. Lorsque ce bit est défini, toutes les demandes de vidage de l’espace d’adressage de la durée de vie inférieure sont bloquées jusqu’à ce que le verrou soit levé.

Pour déverrouiller le TLB, la durée de vie VTL supérieure peut effacer ce bit. En outre, une fois qu’un VP revient à une durée de vie VTL inférieure, il libère tous les verrous TLB qu’il contient à l’heure.

Entrée VTL

Une durée de vie virtuelle est « entrée » lorsqu’un VP passe d’une durée de vie VTL inférieure à une valeur supérieure. Ceci peut se produire pour les raisons suivantes :

  1. Appel VTL : il s’agit du moment où le logiciel souhaite appeler explicitement du code dans une durée de vie VTL supérieure.
  2. Interruption sécurisée : si une interruption est reçue pour une durée de vie VTL supérieure, le VP entrera dans la durée de vie VTL supérieure.
  3. Interception sécurisée : certaines actions déclenchent une interruption sécurisée (accès à certains MSR par exemple).

Une fois qu’une VTL est entrée, elle doit se quitter volontairement. Une durée de vie VTL supérieure ne peut pas être préemptée par une valeur VTL inférieure.

Identification de la raison de l’entrée VTL

Pour réagir de manière appropriée à une entrée, une durée de vie VTL plus élevée peut avoir besoin de connaître la raison pour laquelle elle a été entrée. Pour distinguer les raisons d’entrée, l’entrée VTL est incluse dans la structure HV_VP_VTL_CONTROL .

Appel VTL

Un « appel VTL » est lorsqu’une VTL inférieure initie une entrée dans une VTL supérieure (par exemple, pour protéger une région de mémoire avec la valeur VTL supérieure) via l’hypercall HvCallVtlCall .

Les appels VTL conservent l’état des registres partagés entre les commutateurs VTL. Les registres privés sont conservés au niveau de la durée de vie par VTL. L’exception à ces restrictions est les registres requis par la séquence d’appels VTL. Les registres suivants sont requis pour un appel de durée de vie virtuelle :

x64 x86 Description
RCX EDX:EAX Spécifie une entrée de contrôle d’appel VTL sur l’hyperviseur
RAX ECX Réservé

Tous les bits de l’entrée de contrôle d’appel VTL sont actuellement réservés.

Restrictions d’appel VTL

Les appels VTL ne peuvent être lancés qu’à partir du mode processeur le plus privilégié. Par exemple, sur les systèmes x64, un appel VTL ne peut provenir que de CPL0. Un appel VTL lancé à partir d’un mode processeur, mais le plus privilégié sur le système entraîne l’injection d’une exception de #UD dans le processeur virtuel.

Un appel VTL ne peut basculer qu’en VTL le plus élevé suivant. En d’autres termes, s’il existe plusieurs VTL activés, un appel ne peut pas « ignorer » une durée de vie virtuelle. Les actions suivantes entraînent une exception #UD :

  • Un appel VTL lancé à partir d’un mode processeur, qui est tout sauf le plus privilégié sur le système (propre à l’architecture).
  • Un appel VTL à partir du mode réel (x86/x64)
  • Un appel de durée de vie virtuelle sur un processeur virtuel sur lequel la VTL cible est désactivée (ou n’a pas déjà été activée).
  • Un appel VTL avec une valeur d’entrée de contrôle non valide

Sortie VTL

Un commutateur vers une durée de vie virtuelle inférieure est appelé « retour ». Une fois qu’une durée de vie virtuelle a terminé le traitement, elle peut lancer un retour VTL afin de basculer vers une durée de vie VTL inférieure. La seule façon dont un retour VTL peut se produire est si une durée de vie VTL plus élevée en lance volontairement une. Une durée de vie VTL inférieure ne peut jamais préempter une valeur supérieure.

Retour VTL

Un « retour VTL » est lorsqu’une valeur VTL plus élevée lance un commutateur dans une VTL inférieure via l’hypercall HvCallVtlReturn . Comme pour un appel VTL, l’état du processeur privé est désactivé et l’état partagé reste en place. Si la durée de vie VTL inférieure a explicitement été appelée dans la durée de vie VTL supérieure, l’hyperviseur incrémente le pointeur d’instruction VTL supérieur avant la fin du retour afin qu’il puisse continuer après un appel VTL.

Une séquence de code de retour VTL nécessite l’utilisation des registres suivants :

x64 x86 Description
RCX EDX:EAX Spécifie une entrée de contrôle de retour VTL sur l’hyperviseur
RAX ECX Réservé

L’entrée de contrôle de retour VTL a le format suivant :

Bits Champ Description
63:1 RsvdZ
0 Retour rapide Les registres ne sont pas restaurés

Les actions suivantes génèrent une exception #UD :

  • Tentative d’un retour de durée de vie virtuelle lorsque la durée de vie la plus basse est actuellement active
  • Tentative d’un retour de durée de vie virtuelle avec une valeur d’entrée de contrôle non valide
  • Tentative d’un retour de durée de vie virtuelle à partir d’un mode processeur qui est tout sauf le plus privilégié sur le système (spécifique à l’architecture)

Retour rapide

Dans le cadre du traitement d’un retour, l’hyperviseur peut restaurer l’état d’enregistrement de la valeur VTL inférieure à partir de la structure HV_VP_VTL_CONTROL . Par exemple, après le traitement d’une interruption sécurisée, une durée de vie VTL plus élevée peut souhaiter retourner sans interrompre l’état inférieur de la durée de vie virtuelle. Par conséquent, l’hyperviseur fournit un mécanisme pour restaurer simplement les registres de la VTL inférieure dans leur valeur de pré-appel stockée dans la structure de contrôle VTL.

Si ce comportement n’est pas nécessaire, une durée de vie VTL supérieure peut utiliser un « retour rapide ». Un retour rapide est lorsque l’hyperviseur ne restaure pas l’état d’enregistrement à partir de la structure de contrôle. Cette opération doit être utilisée chaque fois que cela est possible pour éviter un traitement inutile.

Ce champ peut être défini avec le bit 0 de l’entrée de retour VTL. S’il est défini sur 0, les registres sont restaurés à partir de la structure HV_VP_VTL_CONTROL. Si ce bit est défini sur 1, les registres ne sont pas restaurés (un retour rapide).

Assistance à la page Hypercall

L’hyperviseur fournit des mécanismes permettant d’aider les appels VTL et de retourner via la page hypercall. Cette page extrait la séquence de code spécifique requise pour changer de VTL.

Les séquences de code permettant d’exécuter des appels VTL et des retours sont accessibles en exécutant des instructions spécifiques dans la page Hypercall. Les blocs d’appel/retour se trouvent à un décalage dans la page hypercall déterminée par le registre virtuel HvRegisterVsmCodePageOffset. Il s’agit d’un registre en lecture seule et à l’échelle de la partition, avec une instance distincte par VTL.

Une durée de vie virtuelle peut exécuter un appel/retour VTL à l’aide de l’instruction CALL. Un APPEL à l’emplacement correct dans la page hypercall lance un appel/retour VTL.

typedef union
{
    UINT64 AsUINT64;
    struct
    {
        UINT64 VtlCallOffset : 12;
        UINT64 VtlReturnOffset : 12;
        UINT64 ReservedZ : 40;
    };
} HV_REGISTER_VSM_CODE_PAGE_OFFSETS;

Pour résumer, les étapes d’appel d’une séquence de code à l’aide de la page hypercall sont les suivantes :

  1. Mapper la page hypercall dans l’espace GPA d’une durée de vie virtuelle
  2. Déterminez le décalage correct pour la séquence de code (appel VTL ou retour).
  3. Exécutez la séquence de code à l’aide de CALL.

Protections de l’accès à la mémoire

Une protection nécessaire fournie par VSM est la possibilité d’isoler les accès à la mémoire.

Les VTL plus élevés ont un haut degré de contrôle sur le type d’accès à la mémoire autorisé par des VTL inférieurs. Il existe trois types de protections de base qui peuvent être spécifiés par une valeur VTL plus élevée pour une page GPA particulière : lecture, écriture et eXecute. Ceux-ci sont définis dans le tableau suivant :

Nom Description
Lire Contrôle si l’accès en lecture est autorisé à une page mémoire
Write Contrôle si l’accès en écriture autorisé à une page mémoire est autorisé
Execute Détermine si les extractions d’instructions sont autorisées pour une page mémoire.

Ces trois combinaisons pour les types de protection de mémoire suivants :

  1. Aucun accès
  2. En lecture seule, aucune exécution
  3. En lecture seule, exécuter
  4. Lecture/écriture, aucune exécution
  5. Lecture/écriture, exécution

Si le « contrôle d’exécution basé sur le mode (MBEC) » est activé, les protections d’exécution en mode utilisateur et noyau peuvent être définies séparément.

Les VTL plus élevés peuvent définir la protection de la mémoire pour un GPA via l’hypercall HvCallModifyVtlProtectionMask .

Hiérarchie de protection de la mémoire

Les autorisations d’accès à la mémoire peuvent être définies par un certain nombre de sources pour une durée de vie virtuelle particulière. Les autorisations de chaque VTL peuvent potentiellement être restreintes par un certain nombre d’autres VTL, ainsi que par la partition hôte. L’ordre dans lequel les protections sont appliquées est le suivant :

  1. Protections de la mémoire définies par l’hôte
  2. Protections de la mémoire définies par des VTL plus élevées

En d’autres termes, les protections VTL remplacent les protections des hôtes. Les VTL de niveau supérieur remplacent les VTL de niveau inférieur. Notez qu’une durée de vie virtuelle peut ne pas définir les autorisations d’accès à la mémoire pour elle-même.

Une interface conforme est censée ne pas superposer un type non RAM sur LA RAM.

Violations de l’accès à la mémoire

Si un VP s’exécutant à une durée de vie virtuelle inférieure tente de violer un jeu de protection de mémoire défini par une durée de vie VTL supérieure, un intercept est généré. Cette interception est reçue par la durée de vie VTL supérieure qui définit la protection. Cela permet aux VTL plus élevés de traiter la violation au cas par cas. Par exemple, la durée de vie VTL supérieure peut choisir de retourner une erreur ou d’émuler l’accès.

Mode Based Execute Control (MBEC)

Lorsqu’une durée de vie virtuelle place une restriction de mémoire sur une durée de vie VTL inférieure, elle peut faire une distinction entre le mode utilisateur et le mode noyau lors de l’octroi d’un privilège d’exécution. Par exemple, si des vérifications d’intégrité du code étaient effectuées dans une durée de vie VTL supérieure, la possibilité de faire la distinction entre le mode utilisateur et le mode noyau signifierait qu’une VTL pouvait appliquer l’intégrité du code uniquement pour les applications en mode noyau.

Outre les trois protections de mémoire traditionnelles (lecture, écriture, exécution), MBEC introduit une distinction entre le mode utilisateur et le mode noyau pour les protections d’exécution. Par conséquent, si MBEC est activé, une durée de vie virtuelle a la possibilité de définir quatre types de protections de mémoire :

Nom Description
Lire Contrôle si l’accès en lecture est autorisé à une page mémoire
Write Contrôle si l’accès en écriture autorisé à une page mémoire est autorisé
Exécution en mode utilisateur (UMX) Détermine si les extractions d’instructions générées en mode utilisateur sont autorisées pour une page mémoire. REMARQUE : Si MBEC est désactivé, ce paramètre est ignoré.
Exécution en mode noyau (UMX) Détermine si les extractions d’instructions générées en mode noyau sont autorisées pour une page mémoire. REMARQUE : Si MBEC est désactivé, ce paramètre contrôle les accès en mode utilisateur et en mode noyau.

La mémoire marquée avec les protections « Exécution en mode utilisateur » ne serait exécutable que lorsque le processeur virtuel s’exécute en mode utilisateur. De même, la mémoire « Exécution en mode noyau » ne serait exécutable que lorsque le processeur virtuel s’exécute en mode noyau.

KMX et UMX peuvent être définis indépendamment afin que les autorisations d’exécution soient appliquées différemment entre le mode utilisateur et noyau. Toutes les combinaisons de UMX et KMX sont prises en charge, à l’exception de KMX=1, UMX=0. Le comportement de cette combinaison n’est pas défini.

MBEC est désactivé par défaut pour toutes les VTL et processeurs virtuels. Lorsque MBEC est désactivé, le bit d’exécution en mode noyau détermine la restriction d’accès à la mémoire. Par conséquent, si MBEC est désactivé, le code KMX=1 est exécutable à la fois en mode noyau et utilisateur.

Tables de descripteurs

Tout code en mode utilisateur qui accède aux tables de descripteur doit se trouver dans des pages GPA marquées comme KMX=UMX=1. Le logiciel en mode utilisateur accédant aux tables de descripteur à partir d’une page GPA marquée KMX=0 n’est pas pris en charge et entraîne une erreur de protection générale.

Configuration de MBEC

Pour utiliser le contrôle d’exécution en mode, il doit être activé à deux niveaux :

  1. Lorsque la durée de vie virtuelle est activée pour une partition, MBEC doit être activé à l’aide de HvCallEnablePartitionVtl
  2. MBEC doit être configuré sur une base par VP et par VTL, à l’aide de HvRegisterVsmVpSecureVtlConfig.

Interaction MBEC avec la prévention de l’exécution du mode superviseur (SMEP)

Supervisor-Mode Prévention de l’exécution (SMEP) est une fonctionnalité de processeur prise en charge sur certaines plateformes. SMEP peut avoir un impact sur le fonctionnement de MBEC en raison de sa restriction de l’accès des superviseurs aux pages mémoire. L’hyperviseur adhère aux stratégies suivantes relatives à SMEP :

  • Si SMEP n’est pas disponible pour le système d’exploitation invité (qu’il s’agisse de fonctionnalités matérielles ou du mode de compatibilité du processeur), MBEC fonctionne non affecté.
  • Si SMEP est disponible et activé, MBEC fonctionne sans impact.
  • Si SMEP est disponible et est désactivé, toutes les restrictions d’exécution sont régies par le contrôle KMX. Ainsi, seul le code marqué KMX=1 est autorisé à s’exécuter.

Isolation de l’état du processeur virtuel

Les processeurs virtuels conservent des états distincts pour chaque durée de vie virtuelle active. Toutefois, certains de ces états sont privés à une durée de vie virtuelle particulière, et l’état restant est partagé entre toutes les VTL.

L’état qui est conservé par durée de vie virtuelle (état privé) est enregistré par l’hyperviseur entre les transitions VTL. Si un commutateur VTL est lancé, l’hyperviseur enregistre l’état privé actuel de la VTL active, puis bascule vers l’état privé de la VTL cible. L’état partagé reste actif, quel que soit le commutateur VTL.

État privé

En règle générale, chaque VTL possède ses propres registres de contrôle, registre RIP, registre RSP et RSP. Vous trouverez ci-dessous une liste de registres spécifiques et de MSR privés pour chaque durée de vie virtuelle.

MsR privés :

  • SYSENTER_CS, SYSENTER_ESP, SYSENTER_EIP, STAR, LSTAR, CSTAR, SFMASK, EFER, PAT, KERNEL_GSBASE, FS. BASE, GS. BASE, TSC_AUX
  • HV_X64_MSR_HYPERCALL
  • HV_X64_MSR_GUEST_OS_ID
  • HV_X64_MSR_REFERENCE_TSC
  • HV_X64_MSR_APIC_FREQUENCY
  • HV_X64_MSR_EOI
  • HV_X64_MSR_ICR
  • HV_X64_MSR_TPR
  • HV_X64_MSR_APIC_ASSIST_PAGE
  • HV_X64_MSR_NPIEP_CONFIG
  • HV_X64_MSR_SIRBP
  • HV_X64_MSR_SCONTROL
  • HV_X64_MSR_SVERSION
  • HV_X64_MSR_SIEFP
  • HV_X64_MSR_SIMP
  • HV_X64_MSR_EOM
  • HV_X64_MSR_SINT0 – HV_X64_MSR_SINT15
  • HV_X64_MSR_STIMER0_CONFIG – HV_X64_MSR_STIMER3_CONFIG
  • HV_X64_MSR_STIMER0_COUNT – HV_X64_MSR_STIMER3_COUNT
  • Registres APIC locaux (y compris CR8/TPR)

Registres privés :

  • RIP, RSP
  • RFLAGS
  • CR0, CR3, CR4
  • DR7
  • IDTR, GDTR
  • CS, DS, ES, FS, GS, SS, TR, LDTR
  • TSC
  • DR6 (*dépendant du type de processeur. Lire le registre virtuel HvRegisterVsmCapabilities pour déterminer l’état partagé/privé)

État partagé

Les réseaux virtuels partagent l’état afin de réduire la surcharge des contextes de basculement. L’état de partage permet également une communication nécessaire entre les VTL. La plupart des registres à usage général et à virgule flottante sont partagés, comme la plupart des MSR architecturaux. Vous trouverez ci-dessous la liste des MSR et registres spécifiques qui sont partagés entre toutes les dll virtuelles :

MSR partagés :

  • HV_X64_MSR_TSC_FREQUENCY
  • HV_X64_MSR_VP_INDEX
  • HV_X64_MSR_VP_RUNTIME
  • HV_X64_MSR_RESET
  • HV_X64_MSR_TIME_REF_COUNT
  • HV_X64_MSR_GUEST_IDLE
  • HV_X64_MSR_DEBUG_DEVICE_OPTIONS
  • MTRR
  • MCG_CAP
  • MCG_STATUS

Registres partagés :

  • Rax, Rbx, Rcx, Rdx, Rsi, Rdi, Rbp
  • CR2
  • R8 – R15
  • DR0 – DR5
  • État à virgule flottante X87
  • État XMM
  • État AVX
  • XCR0 (XFEM)
  • DR6 (*dépendant du type de processeur. Lire le registre virtuel HvRegisterVsmCapabilities pour déterminer l’état partagé/privé)

Mode réel

Le mode réel n’est pas pris en charge pour une durée de vie virtuelle supérieure à 0. Les VTL supérieures à 0 peuvent s’exécuter en mode 32 bits ou 64 bits.

Gestion des interruptions VTL

Pour atteindre un niveau élevé d’isolation entre les niveaux de confiance virtuelle, le mode sécurisé virtuel fournit un sous-système d’interruption distinct pour chaque VTL activé sur un processeur virtuel. Cela garantit qu’une durée de vie virtuelle est en mesure d’envoyer et de recevoir des interruptions sans interférence d’une durée de vie virtuelle moins sécurisée.

Chaque VTL a son propre contrôleur d’interruption, qui est actif uniquement si le processeur virtuel est en cours d’exécution dans cette VTL particulière. Si un processeur virtuel bascule les états VTL, le contrôleur d’interruption actif sur le processeur est également basculé.

Une interruption ciblée sur une durée de vie virtuelle supérieure à la durée de vie active entraîne un commutateur de durée de vie virtuelle immédiate. La durée de vie VTL supérieure peut ensuite recevoir l’interruption. Si la durée de vie VTL supérieure ne peut pas recevoir l’interruption en raison de sa valeur TPR/CR8, l’interruption est conservée comme « en attente » et la durée de vie virtuelle ne change pas. S’il existe plusieurs VTL avec des interruptions en attente, la durée de vie VTL la plus élevée est prioritaire (sans préavis à la valeur VTL inférieure).

Lorsqu’une interruption est ciblée sur une durée de vie VTL inférieure, l’interruption n’est remise qu’à la prochaine transition du processeur virtuel vers la VTL ciblée. LES ADRESSES IP de démarrage et INIT ciblant une durée de vie virtuelle inférieure sont supprimées sur un processeur virtuel avec une durée de vie VTL plus élevée activée. Comme INIT/SIPI est bloqué, l’hypercall HvCallStartVirtualProcessor doit être utilisé pour démarrer des processeurs.

RFLAGS. SI

Dans le but de changer de VTL, RFLAGS. SI n’affecte pas si une interruption sécurisée déclenche un commutateur VTL. Si RFLAGS. SI est effacé pour masquer les interruptions, les interruptions dans des VTL plus élevées entraînent toujours un commutateur VTL vers une durée de vie VTL supérieure. Seule la valeur TPR/CR8 de la durée de vie supérieure est prise en compte lors de la décision d’interrompre immédiatement.

Ce comportement affecte également les interruptions en attente lors d’un retour de durée de vie virtuelle. Si rfLAGS. Si le bit est effacé pour masquer les interruptions dans une durée de vie donnée, et la durée de vie virtuelle retourne (à une valeur VTL inférieure), l’hyperviseur réévalue toutes les interruptions en attente. Cela entraîne un rappel immédiat à la durée de vie VTL supérieure.

Assistance aux notifications d’interruption virtuelle

Les VTL plus élevés peuvent s’inscrire pour recevoir une notification s’ils bloquent la remise immédiate d’une interruption à une VTL inférieure du même processeur virtuel. Les VTL plus élevés peuvent activer l’assistance aux notifications d’interruption virtuelle (VINA) via un registre virtuel HvRegisterVsmVina :

typedef union
{
    UINT64 AsUINT64;
    struct
    {
        UINT64 Vector : 8;
        UINT64 Enabled : 1;
        UINT64 AutoReset : 1;
        UINT64 AutoEoi : 1;
        UINT64 ReservedP : 53;
    };
} HV_REGISTER_VSM_VINA;

Chaque VTL sur chaque VP a sa propre instance VINA, ainsi que sa propre version de HvRegisterVsmVina. L’installation VINA génère une interruption déclenchée par la périphérie vers la durée de vie VTL actuellement active lorsqu’une interruption pour la durée de vie inférieure est prête pour une livraison immédiate.

Afin d’éviter une inondation d’interruptions lorsque cette installation est activée, l’installation VINA comprend un état limité. Lorsqu’une interruption VINA est générée, l’état de l’installation VINA est remplacé par « Asserted ». L’envoi d’une interruption de fin au SINT associé à l’installation VINA ne efface pas l’état « Asserted ». L’état déclaré ne peut être effacé que de l’une des deux manières suivantes :

  1. L’état peut être effacé manuellement en écrivant dans le champ VinaAsserted de la structure HV_VP_VTL_CONTROL .
  2. L’état est automatiquement effacé lors de l’entrée suivante de la VTL si l’option « Réinitialisation automatique sur l’entrée VTL » est activée dans le registre HvRegisterVsmVina.

Cela permet au code s’exécutant sur une durée de vie virtuelle sécurisée d’être averti de la première interruption reçue pour une durée de vie VTL inférieure. Si une durée de vie virtuelle sécurisée souhaite être avertie d’interruptions supplémentaires, elle peut effacer le champ VinaAsserted de la page d’assistance VP, et il sera averti de la nouvelle interruption suivante.

Interceptions sécurisées

L’hyperviseur permet à une durée de vie VTL plus élevée d’installer des intercepts pour les événements qui se produisent dans le contexte d’une VTL inférieure. Cela donne aux réseaux virtuels plus élevés un niveau élevé de contrôle sur les ressources de durée de vie inférieure. Les interceptions sécurisées peuvent être utilisées pour protéger les ressources critiques du système et empêcher les attaques à partir de réseaux virtuels inférieurs.

Un intercept sécurisé est mis en file d’attente vers la durée de vie VTL la plus élevée, et cette durée de vie est exécutée sur le VP.

Types d’intercept sécurisés

Type d’interception L’interception s’applique à
Accès à la mémoire Tentative d’accès aux protections GPA établies par une durée de vie VTL supérieure.
Contrôler l’accès au registre Tentative d’accès à un ensemble de registres de contrôle spécifiés par une durée de vie VTL supérieure.

Intercepts imbriqués

Plusieurs VTL peuvent installer des interceptions sécurisées pour le même événement dans une durée de vie VTL inférieure. Par conséquent, une hiérarchie est établie pour déterminer où les intercepts imbriqués sont avertis. La liste suivante correspond à l’ordre d’où l’interception est avertie :

  1. VTL inférieur
  2. Durée de vie VTL supérieure

Gestion des interceptions sécurisées

Une fois qu’une durée de vie virtuelle a été avertie d’un intercept sécurisé, elle doit prendre des mesures afin que la durée de vie inférieure puisse continuer. La durée de vie virtuelle supérieure peut gérer l’interception de plusieurs façons, notamment : injecter une exception, simuler l’accès ou fournir un proxy à l’accès. Dans tous les cas, si l’état privé du VTL VP inférieur doit être modifié, HvCallSetVpRegisters doit être utilisé.

Intercepts de registre sécurisé

Une durée de vie VTL supérieure peut intercepter les accès à certains registres de contrôle. Pour ce faire, définissez HvX64RegisterCrInterceptControl à l’aide de l’hypercall HvCallSetVpRegisters . La définition du bit de contrôle dans HvX64RegisterCrInterceptControl déclenche une interception pour chaque accès au registre de contrôle correspondant.

typedef union
{
    UINT64 AsUINT64;
    struct
    {
        UINT64 Cr0Write : 1;
        UINT64 Cr4Write : 1;
        UINT64 XCr0Write : 1;
        UINT64 IA32MiscEnableRead : 1;
        UINT64 IA32MiscEnableWrite : 1;
        UINT64 MsrLstarRead : 1;
        UINT64 MsrLstarWrite : 1;
        UINT64 MsrStarRead : 1;
        UINT64 MsrStarWrite : 1;
        UINT64 MsrCstarRead : 1;
        UINT64 MsrCstarWrite : 1;
        UINT64 ApicBaseMsrRead : 1;
        UINT64 ApicBaseMsrWrite : 1;
        UINT64 MsrEferRead : 1;
        UINT64 MsrEferWrite : 1;
        UINT64 GdtrWrite : 1;
        UINT64 IdtrWrite : 1;
        UINT64 LdtrWrite : 1;
        UINT64 TrWrite : 1;
        UINT64 MsrSysenterCsWrite : 1;
        UINT64 MsrSysenterEipWrite : 1;
        UINT64 MsrSysenterEspWrite : 1;
        UINT64 MsrSfmaskWrite : 1;
        UINT64 MsrTscAuxWrite : 1;
        UINT64 MsrSgxLaunchControlWrite : 1;
        UINT64 RsvdZ : 39;
    };
} HV_REGISTER_CR_INTERCEPT_CONTROL;

Registres de masque

Pour permettre un contrôle plus fin, un sous-ensemble de registres de contrôle a également des registres de masque correspondants. Les registres de masque peuvent être utilisés pour installer des interceptions sur un sous-ensemble des registres de contrôle correspondants. Lorsqu’un registre de masque n’est pas défini, tout accès (tel que défini par HvX64RegisterCrInterceptControl) déclenche un intercept.

L’hyperviseur prend en charge les registres de masque suivants : HvX64RegisterCrInterceptCr0Mask, HvX64RegisterCrInterceptCr4Mask et HvX64RegisterCrInterceptIa32MiscEnableMask.

DMA et appareils

Les appareils ont effectivement le même niveau de privilège que VTL0. Lorsque VSM est activé, toutes les mémoires allouées à l’appareil sont marquées comme VTL0. Tous les accès DMA ont les mêmes privilèges que VTL0.