Virtueller sicherer Modus

Virtual Secure Mode (VSM) ist eine Reihe von Hypervisorfunktionen und Erleuchtungen, die hosten und Gastpartitionen angeboten werden, wodurch die Erstellung und Verwaltung neuer Sicherheitsgrenzen innerhalb der Betriebssystemsoftware ermöglicht wird. VSM ist die Hypervisor-Einrichtung, auf der Windows Sicherheitsfeatures wie Device Guard, Credential Guard, virtuelle TPMs und abgeschirmte VMs basieren. Diese Sicherheitsfeatures wurden in Windows 10 und Windows Server 2016 eingeführt.

VSM ermöglicht die Betriebssystemsoftware in den Stamm- und Gastpartitionen, isolierte Speicherbereiche für den Speicher und die Verarbeitung von Systemsicherheitsressourcen zu erstellen. Der Zugriff auf diese isolierten Regionen wird gesteuert und ausschließlich über den Hypervisor gewährt, der ein hochgradig privilegierter, hochgradig vertrauenswürdiger Teil der vertrauenswürdigen Compute Base (TCB) des Systems ist. Da der Hypervisor in einer höheren Berechtigungsstufe als Betriebssystemsoftware ausgeführt wird und exklusive Kontrolle über wichtige Systemhardwareressourcen wie Speicherzugriffsberechtigungssteuerelemente in der CPU-MMU und IOMMU zu Beginn der Systeminitialisierung hat, kann der Hypervisor diese isolierten Regionen vor nicht autorisiertem Zugriff schützen, auch vor Betriebssystemsoftware (z. B. Betriebssystem-Kernel- und Gerätetreibern) mit Aufsichtsmoduszugriff (z. B. CPL0, oder "Ring 0").

Bei dieser Architektur kann auch dann, wenn normale Software auf Systemebene, die im Aufsichtsmodus ausgeführt wird (z. B. Kernel, Treiber usw.) durch Schadsoftware beeinträchtigt wird, die Ressourcen in isolierten Regionen, die durch den Hypervisor geschützt sind, gesichert bleiben.

Virtuelle Vertrauensstufe (VTL)

VSM erreicht und verwaltet die Isolation über virtuelle Vertrauensstufen (VTLs). VTLs werden sowohl pro Partition als auch pro virtuellem Prozessor aktiviert und verwaltet.

Virtuelle Vertrauensstufen sind hierarchisch, wobei höhere Ebenen privilegierter als niedrigere Ebenen sind. VTL0 ist die am wenigsten privilegierte Ebene, wobei VTL1 privilegierter als VTL0 ist, VTL2 privilegierter als VTL1 usw. ist.

Architektonisch werden bis zu 16 Ebenen von VTLs unterstützt; Ein Hypervisor kann sich jedoch entscheiden, weniger als 16 VTL-Dateien zu implementieren. Derzeit werden nur zwei VTLs implementiert.

typedef UINT8 HV_VTL, *PHV_VTL;

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

Jede VTL verfügt über einen eigenen Satz von Speicherzugriffsschutzen. Diese Zugriffsschutze werden vom Hypervisor im physischen Adressraum einer Partition verwaltet und können daher nicht von Software auf Systemebene geändert werden, die in der Partition ausgeführt wird.

Da privilegiertere VTLs ihren eigenen Speicherschutz erzwingen können, können höhere VTLs effektiv Bereiche des Arbeitsspeichers vor niedrigeren VTLs schützen. In der Praxis ermöglicht dies eine niedrigere VTL, isolierte Speicherbereiche zu schützen, indem sie mit einer höheren VTL gesichert werden. Beispielsweise könnte VTL0 einen geheimen Schlüssel in VTL1 speichern, an dem nur VTL1 darauf zugreifen konnte. Selbst wenn VTL0 kompromittiert ist, wäre der Geheime sicher.

VTL-Schutz

Es gibt mehrere Facetten zum Erreichen der Isolation zwischen VTLs:

  • Speicherzugriffsschutz: Jede VTL verwaltet einen Satz von Schutz vor physischem Arbeitsspeicherzugriff für Gastspeicher. Software, die bei einer bestimmten VTL ausgeführt wird, kann nur auf den Arbeitsspeicher in Übereinstimmung mit diesen Schutzen zugreifen.
  • Virtueller Prozessorstatus: Virtuelle Prozessoren verwalten einen separaten VTL-Zustand. Beispielsweise definiert jede VTL einen Satz privater VP-Register. Software, die auf einer niedrigeren VTL ausgeführt wird, kann nicht auf den Registrierungsstatus des privaten virtuellen VTL-Prozessors zugreifen.
  • Interrupts: Zusammen mit einem separaten Prozessorstatus verfügt jede VTL auch über ein eigenes Interrupt-Subsystem (local APIC). Dadurch können höhere VTLs Unterbrechungen verarbeiten, ohne störungen von einer niedrigeren VTL zu riskieren.
  • Überlagerungsseiten: Bestimmte Überlagerungsseiten werden pro VTL beibehalten, sodass höhere VTLs zuverlässigen Zugriff haben. Es gibt z. B. eine separate Hypercall-Überlagerungsseite pro VTL.

VSM-Erkennung und Status

Die VSM-Funktion wird für Partitionen über das AccessVsm-Partitionsberechtigungskennzeichnung angekündigt. Nur Partitionen mit allen folgenden Berechtigungen können VSM verwenden: AccessVsm, AccessVpRegisters und AccessSynicRegs.

VSM-Funktionserkennung

Gäste sollten das folgende modellspezifische Register verwenden, um auf einen Bericht über VSM-Funktionen zuzugreifen:

MSR-Adresse Name registrieren Beschreibung
0x000D0006 HV_X64_REGISTER_VSM_CAPABILITIES Bericht über VSM-Funktionen.

Das Format der MsR-Funktionen für register VSM-Funktionen lautet wie folgt:

Bits Beschreibung Attribute
63 Dr6Shared Lesen
62:47 MbecVtlMask Lesen
46 DenyLowerVtlStartup Lesen
45:0 RsvdZ Lesen

Dr6Shared gibt an, ob Dr6 ein gemeinsames Register zwischen den VTLs ist.

MvecVtlMask gibt dem Gast die VTLs an, für die Mbec aktiviert werden kann.

"DenyLowerVtlStartup" gibt an, ob ein Vtl einen VP-Reset durch eine niedrigere VTL verweigern kann.

VSM-Statusregister

Zusätzlich zu einer Partitionsberechtigungskennzeichnung können zwei virtuelle Register verwendet werden, um weitere Informationen zum VSM-Status zu erhalten: HvRegisterVsmPartitionStatus und HvRegisterVsmVpStatus.

HvRegisterVsmPartitionStatus

HvRegisterVsmPartitionStatus ist ein schreibgeschütztes Register pro Partition, das über alle VTLs freigegeben wird. Dieses Register enthält Informationen darüber, welche VTLs für die Partition aktiviert wurden, welche VTLs über modusbasierte Ausführungssteuerelemente sowie die maximal zulässige VTL verfügen.

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

HvRegisterVsmVpStatus

HvRegisterVsmVpStatus ist ein schreibgeschütztes Register und wird für alle VTLs freigegeben. Es handelt sich um ein VP-Register, d. h. jeder virtuelle Prozessor verwaltet seine eigene Instanz. Dieses Register enthält Informationen darüber, welche VTLs aktiviert wurden, was aktiv ist, sowie den MBEC-Modus, der auf einem VP aktiv ist.

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 ist die ID des VTL-Kontexts, der derzeit auf dem virtuellen Prozessor aktiv ist.

ActiveMbecEnabled gibt an, dass MBEC derzeit auf dem virtuellen Prozessor aktiv ist.

EnabledVtlSet ist eine Bitmap der VTL-Dateien, die auf dem virtuellen Prozessor aktiviert sind.

VTL-Anfangszustand der Partition

Wenn eine Partition gestartet oder zurückgesetzt wird, beginnt sie mit der Ausführung in VTL0. Alle anderen VTLs werden bei der Partitionserstellung deaktiviert.

VTL-Aktivierung

Um mit der Verwendung einer VTL zu beginnen, muss eine niedrigere VTL Folgendes initiieren:

  1. Aktivieren Sie die Ziel-VTL für die Partition. Dadurch wird die VTL allgemein für die Partition verfügbar.
  2. Aktivieren Sie die Ziel-VTL auf einem oder mehreren virtuellen Prozessoren. Dadurch wird die VTL für einen VP verfügbar und legt den ursprünglichen Kontext fest. Es wird empfohlen, dass alle VPs dieselben aktivierten VTLs haben. Wenn eine VTL für einige VPs (aber nicht alle) aktiviert ist, kann zu unerwartetem Verhalten führen.
  3. Sobald die VTL für eine Partition und einen VP aktiviert ist, kann er mit dem Festlegen von Zugriffsschutzen beginnen, sobald das EnableVtlProtection-Flag festgelegt wurde.

Beachten Sie, dass VTLs nicht aufeinander folgen müssen.

Aktivieren einer Ziel-VTL für eine Partition

Das HypercallEnablePartitionVtl-Hypercall wird verwendet, um eine VTL für eine bestimmte Partition zu aktivieren. Beachten Sie, dass vor der Ausführung der Software in einem bestimmten VTL-Code die VTL auf virtuellen Prozessoren in der Partition aktiviert sein muss.

Aktivieren einer Ziel-VTL für virtuelle Prozessoren

Sobald eine VTL für eine Partition aktiviert ist, kann sie auf den virtuellen Prozessoren der Partition aktiviert werden. Der Hypercall HvCallEnableVpVtl kann verwendet werden, um VTLs für einen virtuellen Prozessor zu aktivieren, der den ursprünglichen Kontext festlegt.

Virtuelle Prozessoren verfügen über einen "Kontext" pro VTL. Wenn eine VTL gewechselt ist, wird auch der private Zustand des VTL gewechselt.

VTL-Konfiguration

Sobald eine VTL aktiviert wurde, kann die Konfiguration von einem VP geändert werden, der auf einer gleich oder höheren VTL ausgeführt wird.

Partitionskonfiguration

Partitionsweite Attribute können mithilfe des Registers "HvRegisterVsmPartitionConfig" konfiguriert werden. Es gibt eine Instanz dieses Registers für jede VTL (größer als 0) auf jeder Partition.

Jede VTL kann eine eigene Instanz von HV_REGISTER_VSM_PARTITION_CONFIG sowie Instanzen für niedrigere VTLs ändern. VTLs können dieses Register für höhere VTLs möglicherweise nicht ändern.

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;

Die Felder dieses Registers werden unten beschrieben.

Aktivieren von VTL-Schutzen

Nachdem ein VTL aktiviert wurde, muss das EnableVtlProtection-Flag festgelegt werden, bevor er mit der Anwendung von Speicherschutzen beginnen kann. Dieses Flag ist schreibgeschützt, d. h., sobald es festgelegt wurde, kann es nicht geändert werden.

Standardschutzmaske

Standardmäßig wendet das System RWX-Schutz auf alle aktuell zugeordneten Seiten und alle zukünftigen "Hot-add"-Seiten an. Hot-Added-Seiten beziehen sich auf jeden Speicher, der einer Partition hinzugefügt wird, während eines Resize-Vorgangs.

Eine höhere VTL kann eine andere Standardspeicherschutzrichtlinie festlegen, indem DefaultVtlProtectionMask in HV_REGISTER_VSM_PARTITION_CONFIG angegeben wird. Diese Maske muss zum Zeitpunkt der Aktivierung des VTL festgelegt werden. Es kann nicht geändert werden, sobald er festgelegt ist, und wird nur durch einen Partitionsrücksetzung gelöscht.

bit BESCHREIBUNG
0 Lesen
1 Schreiben
2 Kernelmodus ausführen (KMX)
3 Benutzermodus ausführen (UMX)

Null Arbeitsspeicher zum Zurücksetzen

ZeroMemOnReset ist ein Bit, das steuert, wenn der Arbeitsspeicher null ist, bevor eine Partition zurückgesetzt wird. Diese Konfiguration ist standardmäßig aktiviert. Wenn das Bit festgelegt ist, wird der Speicher der Partition beim Zurücksetzen null gesetzt, sodass der Arbeitsspeicher eines höheren VTL nicht von einem niedrigeren VTL kompromittiert werden kann. Wenn dieses Bit gelöscht wird, wird der Speicher der Partition nicht auf Zurücksetzen null gesetzt.

DenyLowerVtlStartup

Das Flag "DenyLowerVtlStartup" steuert, wenn ein virtueller Prozessor gestartet oder durch niedrigere VTLs zurückgesetzt werden kann. Dazu gehören architektonische Methoden zum Zurücksetzen eines virtuellen Prozessors (z. B. SIPI auf X64) sowie dem HypercallStartVirtualProcessor .

InterceptVpStartup

Wenn das Flag "InterceptVpStartup" festgelegt ist, generiert ein Abfangen oder Zurücksetzen eines virtuellen Prozessors auf den höheren VTL-Wert.

Konfigurieren von niedrigeren VTLs

Das folgende Register kann von höheren VTLs verwendet werden, um das Verhalten niedrigerer VTLs zu konfigurieren:

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

Jede VTL (höher als 0) verfügt über eine Instanz dieses Registers für jede VTL unter sich selbst. Beispielsweise hätte VTL2 zwei Instanzen dieses Registers – eine für VTL1 und eine zweite für VTL0.

Die Felder dieses Registers werden unten beschrieben.

MbecEnabled

Dieses Feld konfiguriert, ob MBEC für den unteren VTL aktiviert ist.

TlbLocked

Dieses Feld sperrt die TLB der unteren VTL. Diese Funktion kann verwendet werden, um zu verhindern, dass niedrigere VTLs zu TLB-Ungültigkeit führen, die möglicherweise eine höhere VTL beeinträchtigen. Wenn dieses Bit festgelegt ist, werden alle Adressraum-Leerzeichenanforderungen von der unteren VTL blockiert, bis die Sperre aufgehoben wird.

Um die TLB zu entsperren, kann die höhere VTL dieses Bit löschen. Wenn ein VP auch zu einem niedrigeren VTL zurückgibt, wird alle TLB-Sperrungen veröffentlicht, die er zum Zeitpunkt enthält.

VTL-Eintrag

Ein VTL wird "eingegeben", wenn ein VP von einer niedrigeren VTL zu einer höheren wechselt. Dies kann aus folgenden Gründen geschehen:

  1. VTL-Aufruf: Dies ist der Fall, wenn Software explizit Code in einer höheren VTL aufrufen möchte.
  2. Sichere Unterbrechung: Wenn eine Unterbrechung für eine höhere VTL empfangen wird, wird der VP die höhere VTL eingeben.
  3. Sicheres Abfangen: Bestimmte Aktionen auslösen eine sichere Unterbrechung (z. B. auf bestimmte MSRs).

Sobald ein VTL eingegeben wird, muss er freiwillig verlassen. Eine höhere VTL kann von einem niedrigeren VTL nicht vorgebestanden werden.

Identifizieren des VTL-Eintragsgrundes

Um angemessen auf einen Eintrag zu reagieren, muss ein höherer VTL möglicherweise den Grund kennen, aus dem er eingegeben wurde. Um zwischen Eintragsgründen zu unterscheiden, ist der VTL-Eintrag in der HV_VP_VTL_CONTROL Struktur enthalten.

VTL-Anruf

Ein "VTL-Aufruf" ist, wenn ein niedrigerer VTL-Eintrag einen Einstieg in eine höhere VTL (z. B. zum Schutz eines Speicherbereichs mit dem höheren VTL) durch den HvCallVtlCall-Hypercall initiiert.

VTL-Aufrufe behalten den Zustand der freigegebenen Register über VTL-Schalter hinweg auf. Private Register werden auf einer VTL-Ebene beibehalten. Die Ausnahme dieser Einschränkungen sind die Register, die von der VTL-Anrufsequenz erforderlich sind. Die folgenden Register sind für einen VTL-Aufruf erforderlich:

x64 x86 Beschreibung
RCX EDX:EAX Gibt eine VTL-Anrufsteuerungseingabe an den Hypervisor an.
RAX ECX Reserviert

Alle Bits in der VTL-Anrufsteuerungseingabe sind derzeit reserviert.

VTL-Anrufeinschränkungen

VTL-Aufrufe können nur im privilegiertesten Prozessormodus initiiert werden. Beispielsweise kann ein VTL-Anruf auf x64-Systemen nur von CPL0 stammen. Ein VTL-Aufruf, der aus einem Prozessormodus initiiert wurde, was jedoch der privilegierteste Auf dem System ist, führt dazu, dass der Hypervisor eine #UD Ausnahme in den virtuellen Prozessor eingibt.

Ein VTL-Anruf kann nur in den nächsten höchsten VTL wechseln. Mit anderen Worten, wenn mehrere VTLs aktiviert sind, kann ein Aufruf keine VTL überspringen. Die folgenden Aktionen führen zu einer #UD Ausnahme:

  • Ein VTL-Aufruf, der aus einem Prozessormodus initiiert wurde, der alles andere als die privilegiertesten auf dem System (Architekturspezifisch) ist.
  • Ein VTL-Anruf aus dem realen Modus (x86/x64)
  • Ein VTL-Aufruf auf einem virtuellen Prozessor, bei dem das Ziel-VTL deaktiviert ist (oder nicht bereits aktiviert wurde).
  • Ein VTL-Aufruf mit einem ungültigen Steuerelementeingabewert

VTL-Exit

Ein Wechsel zu einer niedrigeren VTL wird als "Rückgabe" bezeichnet. Nachdem eine VTL die Verarbeitung abgeschlossen hat, kann eine VTL-Rückgabe initiiert werden, um zu einer niedrigeren VTL zu wechseln. Die einzige Möglichkeit, wie eine VTL-Rückgabe auftreten kann, ist, wenn ein höherer VTL freiwillig eine initiiert. Ein niedrigerer VTL kann niemals höher sein.

VTL-Rückgabe

Eine "VTL-Rückgabe" ist, wenn ein höherer VTL einen Wechsel in eine niedrigere VTL durch den HypercallVtl-Hypercall initiiert. Ähnlich wie bei einem VTL-Anruf wird der Zustand des privaten Prozessors ausgeschaltet, und der freigegebene Zustand bleibt vorhanden. Wenn die untere VTL explizit in die höhere VTL aufgerufen wurde, erhöht der Hypervisor den Anweisungenzeiger der höheren VTL, bevor die Rückgabe abgeschlossen ist, damit er nach einem VTL-Aufruf fortgesetzt werden kann.

Eine VTL-Rückgabecodesequenz erfordert die Verwendung der folgenden Register:

x64 x86 Beschreibung
RCX EDX:EAX Gibt eine VTL-Rückgabesteuerungseingabe an den Hypervisor an.
RAX ECX Reserviert

Die Eingabe des VTL-Rückgabesteuerelements weist das folgende Format auf:

Bits Feld Beschreibung
63:1 RsvdZ
0 Schnelle Rückgabe Register werden nicht wiederhergestellt

Die folgenden Aktionen generieren eine #UD Ausnahme:

  • Versuchen einer VTL-Rückgabe, wenn die niedrigste VTL aktuell aktiv ist
  • Versuchen einer VTL-Rückgabe mit einem ungültigen Steuerelementeingabewert
  • Versuchen einer VTL-Rückgabe aus einem Prozessormodus, der alles andere als die privilegiertesten Elemente des Systems ist (architekturspezifisch)

Schnelle Rückgabe

Als Teil der Verarbeitung einer Rückgabe kann der Hypervisor den Registerstatus des unteren VTL aus der HV_VP_VTL_CONTROL Struktur wiederherstellen. Nach der Verarbeitung einer sicheren Unterbrechung kann beispielsweise ein höherer VTL-Wert zurückgegeben werden, ohne den Zustand der unteren VTL zu stören. Daher stellt der Hypervisor einen Mechanismus bereit, um einfach die Register der niedrigeren VTL auf ihren vorab in der VTL-Steuerelementstruktur gespeicherten Voraufrufwert wiederherzustellen.

Wenn dieses Verhalten nicht erforderlich ist, kann eine höhere VTL eine "schnelle Rückgabe" verwenden. Eine schnelle Rückgabe besteht darin, dass der Hypervisor den Registerstatus nicht aus der Steuerelementstruktur wiederherstellen kann. Dies sollte immer möglich verwendet werden, um unnötige Verarbeitung zu vermeiden.

Dieses Feld kann mit Bit 0 der VTL-Rückgabeeingabe festgelegt werden. Wenn er auf 0 festgelegt ist, werden die Register aus der HV_VP_VTL_CONTROL Struktur wiederhergestellt. Wenn dieses Bit auf 1 festgelegt ist, werden die Register nicht wiederhergestellt (eine schnelle Rückgabe).

Hypercall Page Assist

Der Hypervisor stellt Mechanismen bereit, die VTL-Aufrufe unterstützen und über die Hypercall-Seite zurückgibt. Diese Seite abstrahiert die spezifische Codesequenz, die zum Wechseln von VTLs erforderlich ist.

Auf die Codesequenzen zum Ausführen von VTL-Aufrufen und -Rückgaben kann zugegriffen werden, indem bestimmte Anweisungen auf der Hypercall-Seite ausgeführt werden. Die Aufruf-/Rückgabeblöcke befinden sich in einem Offset auf der hypercall-Seite, die durch das virtuelle Register HvRegisterVsmCodePageOffset bestimmt wird. Dies ist ein schreibgeschütztes und partitionsweites Register mit einer separaten Instanz pro VTL.

Eine VTL kann einen VTL-Aufruf/eine Rückgabe mithilfe der CALL-Anweisung ausführen. Ein ANRUF an die richtige Position auf der Hypercall-Seite initiiert einen VTL-Anruf/rückgabe.

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

Die Schritte zum Aufrufen einer Codesequenz mithilfe der Hypercall-Seite lauten wie folgt:

  1. Zuordnen der Hypercallseite in den GPA-Bereich eines VTL
  2. Bestimmen Sie den richtigen Offset für die Codesequenz (VTL-Aufruf oder Rückgabe).
  3. Führen Sie die Codesequenz mithilfe von CALL aus.

Speicherzugriffsschutz

Ein erforderlicher Schutz, der von VSM bereitgestellt wird, ist die Möglichkeit, Speicherzugriffe zu isolieren.

Höhere VTLs haben ein hohes Maß an Kontrolle über den Typ des Speicherzugriffs, der durch niedrigere VTLs zulässig ist. Es gibt drei grundlegende Schutztypen, die von einer höheren VTL für eine bestimmte GPA-Seite angegeben werden können: Lesen, Schreiben und eXecute. Diese werden in der folgenden Tabelle definiert:

Name BESCHREIBUNG
Lesen Steuert, ob lesezugriff auf eine Speicherseite zulässig ist
Schreiben Steuert, ob der Schreibzugriff auf eine Speicherseite zulässig ist
Execute Steuert, ob Anweisungsabrufe für eine Speicherseite zulässig sind.

Diese drei kombinieren für die folgenden Speicherschutztypen:

  1. Kein Zugriff
  2. Schreibgeschützt, keine Ausführung
  3. Schreibgeschützt, ausführen
  4. Lese-/Schreibzugriff, keine Ausführung
  5. Lese-/Schreibzugriff, Ausführen

Wenn "modusbasierte Ausführungskontrolle (MBEC)" aktiviert ist, können Benutzer- und Kernelmodusschutz separat festgelegt werden.

Höhere VTLs können den Speicherschutz für eine GPA über den Hypercall HvCallModifyVtlProtectionMask-Hypercall festlegen.

Speicherschutzhierarchie

Speicherzugriffsberechtigungen können durch eine Reihe von Quellen für eine bestimmte VTL festgelegt werden. Die Berechtigungen jeder VTL können möglicherweise durch eine Reihe anderer VTLs sowie durch die Hostpartition eingeschränkt werden. Die Reihenfolge, in der Schutz angewendet wird, ist folgendes:

  1. Vom Host festgelegte Speicherschutz
  2. Speicherschutz durch höhere VTLs festgelegt

Mit anderen Worten: Der VTL-Schutz ersetzt Hostschutz. VTLs auf höherer Ebene übergeordnete VTLs auf niedrigerer Ebene. Beachten Sie, dass eine VTL möglicherweise keine Speicherzugriffsberechtigungen für sich selbst festlegen kann.

Eine konforme Schnittstelle wird erwartet, dass kein NICHT-RAM-Typ über RAM überlagert wird.

Speicherzugriffsverletzungen

Wenn ein VP, der auf einer niedrigeren VTL ausgeführt wird, versucht, einen Speicherschutz durch eine höhere VTL zu verletzen, wird ein Abfangen generiert. Dieser Abfangen wird von der höheren VTL empfangen, die den Schutz festlegt. Dadurch können höhere VTLs die Verletzung auf Fall-nach-Fall-Basis behandeln. Beispielsweise kann die höhere VTL einen Fehler zurückgeben oder den Zugriff emulieren.

Modusbasiertes Execute-Steuerelement (MBEC)

Wenn eine VTL eine Speichereinschränkung auf einer niedrigeren VTL platziert, kann es sinnvoll sein, beim Erteilen einer "Execute"-Berechtigung einen Unterschied zwischen Benutzer- und Kernelmodus zu treffen. Wenn z. B. Codeintegritätsprüfungen in einer höheren VTL durchgeführt werden sollen, bedeutet die Möglichkeit, zwischen Benutzermodus und Kernelmodus zu unterscheiden, dass eine VTL die Codeintegrität nur für Kernelmodusanwendungen erzwingen könnte.

Abgesehen von den herkömmlichen drei Speicherschutzen (Lese-, Schreib-, Ausführungs-, Ausführungs-), führt MBEC einen Unterschied zwischen dem Benutzermodus und dem Kernelmodus für Ausführungsschutze ein. Wenn MBEC also aktiviert ist, hat eine VTL die Möglichkeit, vier Arten von Speicherschutz festzulegen:

Name BESCHREIBUNG
Lesen Steuert, ob lesezugriff auf eine Speicherseite zulässig ist
Schreiben Steuert, ob der Schreibzugriff auf eine Speicherseite zulässig ist
Benutzermodus ausführen (UMX) Steuert, ob anweisungsgesteuerte Abrufe, die im Benutzermodus generiert wurden, für eine Speicherseite zulässig sind. HINWEIS: Wenn MBEC deaktiviert ist, wird diese Einstellung ignoriert.
Kernelmodus Execute (UMX) Steuert, ob anweisungsgesteuerte Abrufe, die im Kernelmodus generiert wurden, für eine Speicherseite zulässig sind. HINWEIS: Wenn MBEC deaktiviert ist, steuert diese Einstellung sowohl den Benutzermodus als auch den Kernelmodus.

Arbeitsspeicher, der mit den Schutzen "Benutzermodus ausführen" gekennzeichnet ist, wäre nur ausführbare Datei, wenn der virtuelle Prozessor im Benutzermodus ausgeführt wird. Ebenso wäre der Speicher "Kernel-Mode Execute" nur ausführbarer, wenn der virtuelle Prozessor im Kernelmodus ausgeführt wird.

KMX und UMX können unabhängig festgelegt werden, sodass Ausführungsberechtigungen zwischen Benutzer und Kernelmodus unterschiedlich erzwungen werden. Alle Kombinationen von UMX und KMX werden unterstützt, mit Ausnahme von KMX=1, UMX=0. Das Verhalten dieser Kombination ist nicht definiert.

MBEC ist standardmäßig für alle VTLs und virtuellen Prozessoren deaktiviert. Wenn MBEC deaktiviert ist, bestimmt der Kernelmodus Bit, die Speicherzugriffseinschränkung. Wenn MBEC deaktiviert ist, ist KMX=1-Code daher sowohl im Kernel als auch im Benutzermodus ausführbarer Code.

Deskriptortabellen

Jeder Benutzermoduscode, der auf Deskriptortabellen zugreift, muss sich auf GPA-Seiten befinden, die als KMX=UMX=1 gekennzeichnet sind. Benutzermodus-Software, die auf Deskriptortabellen aus einer GPA-Seite mit der Markierung KMX=0 zugreifen, wird nicht unterstützt und führt zu einem allgemeinen Schutzfehler.

MBEC-Konfiguration

Um das Modusbasierte Ausführungssteuerelement zu verwenden, muss sie auf zwei Ebenen aktiviert werden:

  1. Wenn die VTL für eine Partition aktiviert ist, muss MBEC mithilfe von HvCallEnablePartitionVtl aktiviert werden.
  2. MBEC muss auf einer PRO-VP- und pro-VTL-Basis mit HvRegisterVsmVpSecureVtlConfig konfiguriert werden.

MBEC-Interaktion mit Der Aufsichtsmodus-Ausführungsverhütung (SMEP)

Supervisor-Mode Execution Prevention (SMEP) ist ein Prozessorfeature, das auf einigen Plattformen unterstützt wird. SMEP kann sich auf den Betrieb von MBEC aufgrund seiner Einschränkung des Aufsichtszugriffs auf Speicherseiten auswirken. Der Hypervisor entspricht den folgenden Richtlinien im Zusammenhang mit KMUP:

  • Wenn SMEP nicht für das Gastbetriebssystem verfügbar ist (unabhängig davon, ob es sich um Hardwarefunktionen oder Prozessorkompatibilitätsmodus handelt), funktioniert MBEC nicht betroffen.
  • Wenn SMEP verfügbar ist und aktiviert ist, funktioniert MBEC nicht betroffen.
  • Wenn SMEP verfügbar ist und deaktiviert ist, werden alle Ausführungseinschränkungen vom KMX-Steuerelement geregelt. Daher dürfen nur codemarkierten KMX=1 ausgeführt werden.

Isolation des virtuellen Prozessors

Virtuelle Prozessoren verwalten separate Zustände für jede aktive VTL. Einige dieser Staaten sind jedoch privat für eine bestimmte VTL, und der verbleibende Zustand wird unter allen VTLs geteilt.

Status, der per VTL (a.k.a. privater Zustand) beibehalten wird, wird vom Hypervisor über VTL-Übergänge gespeichert. Wenn ein VTL-Switch initiiert wird, speichert der Hypervisor den aktuellen privaten Zustand für die aktive VTL, und wechselt dann zu dem privaten Zustand des Ziel-VTL. Der freigegebene Zustand bleibt unabhängig von VTL-Schaltern aktiv.

Privater Zustand

Im Allgemeinen verfügt jede VTL über eigene Kontrollregister, RIP-Register, RSP-Register und MSRs. Nachfolgend finden Sie eine Liste spezifischer Register und MSRs, die für jede VTL privat sind.

Private MSRs:

  • 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
  • Lokale APIC-Register (einschließlich CR8/TPR)

Private Register:

  • RIP, RSP
  • RFLAGS
  • CR0, CR3, CR4
  • DR7
  • IDTR, GDTR
  • CS, DS, ES, FS, GS, SS, TR, LDTR
  • TSC
  • DR6 (*abhängig vom Prozessortyp. Virtuelles Register von HvRegisterVsmCapabilities lesen, um den freigegebenen/privaten Status zu ermitteln)

Freigegebener Zustand

VTLs teilen den Zustand, um den Aufwand für das Wechseln von Kontexten zu reduzieren. Der Freigabestatus ermöglicht auch einige erforderliche Kommunikation zwischen VTLs. Die meisten allgemeinen Und Gleitkommaregister werden gemeinsam genutzt, wie die meisten architektonischen MSRs. Nachfolgend finden Sie die Liste der spezifischen MSRs und Register, die unter allen VTLs freigegeben werden:

Freigegebene MSRs:

  • 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
  • MTRRs
  • MCG_CAP
  • MCG_STATUS

Freigegebene Register:

  • Rax, Rbx, Rcx, Rdx, Rsi, Rdi, Rbp
  • CR2
  • R8 – R15
  • DR0 – DR5
  • X87-Gleitkommastatus
  • XMM-Zustand
  • AVX-Status
  • XCR0 (XFEM)
  • DR6 (*abhängig vom Prozessortyp. Virtuelles Register von HvRegisterVsmCapabilities lesen, um den freigegebenen/privaten Status zu ermitteln)

Realmodus

Der reale Modus wird für jede VTL nicht unterstützt, die größer als 0 ist. VTLs größer als 0 können im 32-Bit- oder 64-Bit-Modus ausgeführt werden.

VTL-Unterbrechungsverwaltung

Um eine hohe Isolation zwischen virtuellen Vertrauensstufen zu erreichen, stellt virtual Secure Mode ein separates Unterbrechungs-Subsystem für jede auf einem virtuellen Prozessor aktivierte VTL bereit. Dadurch wird sichergestellt, dass ein VTL Sowohl Senden als auch Empfangen von Unterbrechungen ohne Störungen von einer weniger sicheren VTL ermöglicht.

Jede VTL verfügt über einen eigenen Unterbrechungscontroller, der nur aktiv ist, wenn der virtuelle Prozessor in diesem bestimmten VTL ausgeführt wird. Wenn ein virtueller Prozessor VTL-Zustände wechselt, wird der auf dem Prozessor aktive Unterbrechungscontroller ebenfalls gewechselt.

Eine Unterbrechung, die auf eine VTL ausgerichtet ist, die höher ist als die aktive VTL, führt zu einem sofortigen VTL-Schalter. Die höhere VTL kann dann die Unterbrechung empfangen. Wenn die höhere VTL aufgrund des TPR/CR8-Werts nicht den Unterbrechungsvorgang erhalten kann, wird die Unterbrechung als "ausstehend" gehalten und der VTL wechselt nicht. Wenn mehrere VTLs mit ausstehenden Unterbrechungen vorhanden sind, nimmt die höchste VTL Vorrang (ohne Hinweis auf die niedrigere VTL).

Wenn eine Unterbrechung auf eine niedrigere VTL ausgerichtet ist, wird die Unterbrechung erst nach dem nächsten Übergang des virtuellen Prozessors in die gezielte VTL übermittelt. INIT- und Start-IPIs, die auf eine niedrigere VTL ausgerichtet sind, werden auf einem virtuellen Prozessor mit einer höheren VTL aktiviert. Da INIT/SIPI blockiert wird, sollte der HypercallStartVirtualProcessor zum Starten von Prozessoren verwendet werden.

RFLAGS. WENN

Zur Umstellung von VTLs, RFLAGS. WENN nicht beeinflusst wird, ob eine sichere Unterbrechung einen VTL-Schalter auslöst. Wenn RFLAGS. WENN die Unterbrechungen in höherer VTLs gelöscht werden, führt ein VTL-Wechsel zu einer höheren VTL noch zu einer höheren VTL. Nur der höhere TPR/CR8-Wert des VTL wird berücksichtigt, wenn sie entscheiden, ob sofort unterbrochen werden soll.

Dieses Verhalten wirkt sich auch auf ausstehende Unterbrechungen bei einer VTL-Rückgabe aus. Wenn die RFLAGS. WENN Bit gelöscht wird, um Unterbrechungen in einem bestimmten VTL zu maskieren, und die VTL gibt zurück (zu einem niedrigeren VTL), wird der Hypervisor alle ausstehenden Unterbrechungen neu bewerten. Dadurch wird ein sofortiger Rückruf an die höhere VTL verursacht.

Unterstützung für virtuelle Unterbrechungsbenachrichtigungen

Höhere VTLs können registrieren, um eine Benachrichtigung zu erhalten, wenn sie die sofortige Übermittlung einer Unterbrechung an eine niedrigere VTL desselben virtuellen Prozessors blockieren. Höhere VTLs können virtual Interrupt Notification Assist (VINA) über ein virtuelles Register HvRegisterVsmVina aktivieren:

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

Jede VTL auf jedem VP verfügt über eine eigene VINA-Instanz sowie eine eigene Version von HvRegisterVsmVina. Die VINA-Einrichtung generiert eine edgeauslöste Unterbrechung für die derzeit aktive höhere VTL, wenn eine Unterbrechung für die niedrigere VTL bereit für die sofortige Übermittlung ist.

Um zu verhindern, dass eine Flut von Unterbrechungen auftritt, wenn diese Anlage aktiviert ist, umfasst die VINA-Einrichtung einen begrenzten Zustand. Wenn eine VINA-Unterbrechung generiert wird, wird der Zustand der VINA-Einrichtung in "Asserted" geändert. Das Senden eines End-of-Interrupts an das SINT, das mit der VINA-Einrichtung verknüpft ist, wird den Status "Asserted" nicht gelöscht. Der behauptete Zustand kann nur auf zwei Arten gelöscht werden:

  1. Der Zustand kann manuell gelöscht werden, indem er in das Feld VinaAsserted der HV_VP_VTL_CONTROL Struktur schreibt.
  2. Der Zustand wird automatisch auf dem nächsten Eintrag des VTL gelöscht, wenn die Option "Automatischer Zurücksetzen auf VTL-Eintrag" im HvRegisterVsmVina-Register aktiviert ist.

Dadurch kann code, der bei einem sicheren VTL ausgeführt wird, einfach über den ersten Unterbrechung benachrichtigt werden, der für einen niedrigeren VTL empfangen wird. Wenn ein sicherer VTL über zusätzliche Unterbrechungen benachrichtigt werden möchte, kann das Feld VinaAsserted der VP-Hilfsseite gelöscht werden, und er wird über den nächsten neuen Unterbrechung benachrichtigt.

Sichere Abfangen

Der Hypervisor ermöglicht es einem höheren VTL, Abfangen für Ereignisse zu installieren, die im Kontext einer niedrigeren VTL stattfinden. Dies bietet höhere VTLs eine erhöhte Kontrolle über untere VTL-Ressourcen. Sichere Abfangen können verwendet werden, um systemkritische Ressourcen zu schützen und Angriffe von niedrigeren VTLs zu verhindern.

Ein sicherer Abfangen wird an die höhere VTL Warteschlange gestellt, und die VTL wird auf dem VP ausgeführt.

Sichere Abfangentypen

Abfangentyp Abfangen gilt für
Speicherzugriff Versuchen Sie, auf GPA-Schutzmaßnahmen zuzugreifen, die von einer höheren VTL eingerichtet wurden.
Steuern des Registrierungszugriffs Versuchen Sie, auf eine Reihe von Steuerelementregistern zuzugreifen, die von einem höheren VTL angegeben sind.

Geschachtelte Abfangen

Mehrere VTLs können sichere Abfangen für dasselbe Ereignis in einer niedrigeren VTL installieren. Daher wird eine Hierarchie eingerichtet, um zu entscheiden, wo geschachtelte Abfangen benachrichtigt werden. Die folgende Liste ist die Reihenfolge, in der Abfangen benachrichtigt werden:

  1. Niedrigerer VTL
  2. Höherer VTL

Behandeln sicherer Abfangen

Sobald ein VTL über einen sicheren Abfangen benachrichtigt wurde, muss er Maßnahmen ergreifen, damit die niedrigere VTL fortgesetzt werden kann. Die höhere VTL kann den Abfangen auf mehrere Arten behandeln, einschließlich: Einfügen einer Ausnahme, Emulieren des Zugriffs oder Bereitstellen eines Proxys für den Zugriff. Falls der private Zustand des unteren VTL-VP geändert werden muss, sollte HvCallSetVpRegisters verwendet werden.

Sichere Registrierung abfangen

Eine höhere VTL kann zugriffe auf bestimmte Steuerelementregister abfangen. Dies wird durch Festlegen von HvX64RegisterCrInterceptControl mithilfe des HypercallSetVpRegisters von HvCallSetVpRegisters erreicht. Durch Festlegen des Steuerelementbits in HvX64RegisterCrInterceptControl wird ein Abfangen für jeden Zugriff des entsprechenden Steuerelementregisters ausgelöst.

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;

Maskenregister

Um eine feinere Kontrolle zu ermöglichen, verfügen auch eine Teilmenge von Steuerelementregistern über entsprechende Maskenregister. Maskenregister können verwendet werden, um Abfangen auf einer Teilmenge der entsprechenden Steuerelementregister zu installieren. Wenn ein Maskenregister nicht definiert ist, löst jeder Zugriff (wie durch HvX64RegisterCrInterceptControl definiert) einen Abfangen aus.

Der Hypervisor unterstützt die folgenden Maskenregister: HvX64RegisterCrInterceptCr0Mask, HvX64RegisterCrInterceptCr4Mask und HvX64RegisterCrInterceptIa32MiscEnableMask.

DMA und Geräte

Geräte verfügen effektiv über dieselbe Berechtigungsstufe wie VTL0. Wenn VSM aktiviert ist, wird der gesamte zugewiesene Speicher als VTL0 markiert. Alle DMA-Zugriffe verfügen über dieselben Berechtigungen wie VTL0.