Share via


Sicherheitscheckliste für Treiber

Dieser Artikel enthält eine Checkliste für Treibersicherheit für Treiberentwickler, um das Risiko einer Kompromittierung von Treibern zu verringern.

Übersicht über die Treibersicherheit

Ein Sicherheitsfehler ist jeder Fehler, der es einem Angreifer ermöglicht, eine Fehlfunktion eines Treibers so zu verursachen, dass das System abstürzt oder unbrauchbar wird. Darüber hinaus können Sicherheitsrisiken im Treibercode es einem Angreifer ermöglichen, Zugriff auf den Kernel zu erhalten, was eine Möglichkeit schafft, das gesamte Betriebssystem zu kompromittieren. Wenn die meisten Entwickler an ihrem Treiber arbeiten, liegt der Fokus darauf, dass der Treiber ordnungsgemäß funktioniert, und nicht darauf, ob ein böswilliger Angreifer versucht, Sicherheitsrisiken in ihrem Code auszunutzen.

Nach der Freigabe eines Treibers können Angreifer jedoch versuchen, Sicherheitslücken zu untersuchen und zu identifizieren. Entwickler müssen diese Probleme während der Entwurfs- und Implementierungsphase berücksichtigen, um die Wahrscheinlichkeit solcher Sicherheitsrisiken zu minimieren. Das Ziel besteht darin, alle bekannten Sicherheitslücken zu beseitigen, bevor der Treiber freigegeben wird.

Das Erstellen sichererer Treiber erfordert die Zusammenarbeit des Systemarchitekten (der bewusst an potenzielle Bedrohungen für den Treiber denkt), des Entwicklers, der den Code implementiert (defensiv codiert gängige Vorgänge, die die Quelle von Exploits sein können) und des Testteams (proaktiv versucht, Schwächen und Sicherheitsrisiken zu finden). Durch die ordnungsgemäße Koordinierung all dieser Aktivitäten wird die Sicherheit des Fahrers erheblich verbessert.

Neben der Vermeidung der Probleme im Zusammenhang mit einem Angriff auf einen Treiber erhöhen viele der beschriebenen Schritte, z. B. eine präzisere Verwendung des Kernelspeichers, die Zuverlässigkeit Ihres Treibers. Dies reduziert die Supportkosten und erhöht die Kundenzufriedenheit mit Ihrem Produkt. Das Ausführen der Aufgaben in der folgenden Checkliste hilft, all diese Ziele zu erreichen.

Sicherheitscheckliste:Führen Sie die in jedem dieser Themen beschriebene Sicherheitsaufgabe aus.

Nicht markiertes Kontrollkästchen, das ein Element in der Sicherheitsprüfliste darstellt.Vergewissern Sie sich, dass ein Kerneltreiber erforderlich ist.

Nicht markiertes Kontrollkästchen, das ein Element in der Sicherheitsprüfliste darstellt.Verwenden der Treiberframeworks

Nicht markiertes Kontrollkästchen, das ein Element in der Sicherheitsprüfliste darstellt.Steuern des Zugriffs auf Softwaretreiber

Nicht markiertes Kontrollkästchen, das ein Element in der Sicherheitsprüfliste darstellt.Code des Produktionszeichentesttreibers nicht verwenden

Nicht markiertes Kontrollkästchen, das ein Element in der Sicherheitsprüfliste darstellt.Durchführen einer Bedrohungsanalyse

Nicht markiertes Kontrollkästchen, das ein Element in der Sicherheitsprüfliste darstellt.Befolgen der Richtlinien für sichere Treibercodierung

Nicht markiertes Kontrollkästchen, das ein Element in der Sicherheitsprüfliste darstellt.Implementieren von HVCI-kompatiblem Code

Nicht markiertes Kontrollkästchen, das ein Element in der Sicherheitsprüfliste darstellt.Befolgen von bewährten Methoden für technologiespezifischen Code

Nicht markiertes Kontrollkästchen, das ein Element in der Sicherheitsprüfliste darstellt.Durchführen der Peercodeüberprüfung

Nicht markiertes Kontrollkästchen, das ein Element in der Sicherheitsprüfliste darstellt.Verwalten der Treiberzugriffssteuerung

Nicht markiertes Kontrollkästchen, das ein Element in der Sicherheitsprüfliste darstellt.Erhöhen der Geräteinstallationssicherheit

Nicht markiertes Kontrollkästchen, das ein Element in der Sicherheitsprüfliste darstellt.Ausführen der richtigen Signatur des Releasetreibers

Nicht markiertes Kontrollkästchen, das ein Element in der Sicherheitsprüfliste darstellt.Verwenden der Codeanalyse in Visual Studio, um die Treibersicherheit zu untersuchen

Nicht markiertes Kontrollkästchen, das ein Element in der Sicherheitsprüfliste darstellt.Verwenden der statischen Treiberüberprüfung, um auf Sicherheitsrisiken zu überprüfen

Nicht markiertes Kontrollkästchen, das ein Element in der Sicherheitsprüfliste darstellt.Code mit BinSkim Binary Analyzer überprüfen

Nicht markiertes Kontrollkästchen, das ein Element in der Sicherheitsprüfliste darstellt.Verwenden von Codevalidierungstools

Nicht markiertes Kontrollkästchen, das ein Element in der Sicherheitsprüfliste darstellt.Überprüfen von Debuggertechniken und -erweiterungen

Nicht markiertes Kontrollkästchen, das ein Element in der Sicherheitsprüfliste darstellt.Informationen zur Meldung von Treibern im Microsoft Reporting Center für anfällige und schädliche Treiber

Nicht markiertes Kontrollkästchen, das ein Element in der Sicherheitsprüfliste darstellt.Überprüfen von Ressourcen für sichere Codierung

Nicht markiertes Kontrollkästchen, das ein Element in der Sicherheitsprüfliste darstellt. Überprüfen der Zusammenfassung der wichtigsten Erkenntnisse

Vergewissern Sie sich, dass ein Kerneltreiber erforderlich ist.

Sicherheitsprüflisteelement #1:Vergewissern Sie sich, dass ein Kerneltreiber erforderlich ist und dass ein Ansatz mit geringerem Risiko, z. B. Windows-Dienst oder -App, keine bessere Option ist.

Treiber befinden sich im Windows-Kernel, und ein Problem bei der Ausführung im Kernel macht das gesamte Betriebssystem verfügbar. Wenn eine andere Option verfügbar ist, sind wahrscheinlich niedrigere Kosten und ein geringeres Risiko verbunden als das Erstellen eines neuen Kerneltreibers. Weitere Informationen zur Verwendung der integrierten Windows-Treiber finden Sie unter Müssen Sie einen Treiber schreiben?.

Informationen zur Verwendung von Hintergrundaufgaben finden Sie unter Unterstützen Ihrer App mit Hintergrundaufgaben.

Informationen zur Verwendung von Windows-Diensten finden Sie unter Dienste.

Verwenden der Treiberframeworks

Sicherheitschecklistenelement #2:Verwenden Sie die Treiberframeworks, um die Größe Ihres Codes zu reduzieren und dessen Zuverlässigkeit und Sicherheit zu erhöhen.

Verwenden Sie die Windows-Treiberframeworks , um die Größe Ihres Codes zu reduzieren und die Zuverlässigkeit und Sicherheit zu erhöhen. Informationen zu den ersten Schritten finden Sie unter Verwenden von WDF zum Entwickeln eines Treibers. Informationen zur Verwendung des Frameworktreibers mit geringerem Risiko (User Mode Framework Driver, UMDF) finden Sie unter Auswählen eines Treibermodells.

Das Schreiben eines old fashion Windows Driver Model (WDM) -Treibers ist zeitaufwändiger, kostspieliger und beinhaltet fast immer die Neuerstellung von Code, der in den Treiberframeworks verfügbar ist.

Der Windows Driver Framework-Quellcode ist Open Source und auf GitHub verfügbar. Dies ist der gleiche Quellcode, aus dem die WDF-Laufzeitbibliothek erstellt wird, die in Windows 10 ausgeliefert wird. Sie können Ihren Treiber effektiver debuggen, wenn Sie die Interaktionen zwischen dem Treiber und WDF verfolgen können. Laden Sie es von herunter https://github.com/Microsoft/Windows-Driver-Frameworks.

Steuern des Zugriffs auf Softwaretreiber

Sicherheitschecklistenelement #3:Wenn ein rein softwarebasierter Treiber erstellt wird, muss eine zusätzliche Zugriffssteuerung implementiert werden.

Reine Software-Kerneltreiber verwenden kein Plug-and-Play (PnP), um bestimmten Hardware-IDs zugeordnet zu werden, und können auf jedem PC ausgeführt werden. Ein solcher Treiber könnte für andere Zwecke als den ursprünglich vorgesehenen verwendet werden, um einen Angriffsvektor zu erstellen.

Da reine Software-Kerneltreiber ein zusätzliches Risiko enthalten, müssen sie auf bestimmte Hardware ausgeführt werden (z. B. durch Verwendung einer eindeutigen PnP-ID zum Aktivieren der Erstellung eines PnP-Treibers oder durch Überprüfen der SMBIOS-Tabelle auf das Vorhandensein bestimmter Hardware).

Stellen Sie sich beispielsweise vor, OEM Fabrikam möchte einen Treiber verteilen, der ein Übertaktungshilfsprogramm für ihre Systeme ermöglicht. Wenn dieser reine Softwaretreiber auf einem System von einem anderen OEM ausgeführt werden soll, kann es zu Systeminstabilität oder -schäden kommen. Fabrikam-Systeme sollten eine eindeutige PnP-ID enthalten, um die Erstellung eines PnP-Treibers zu ermöglichen, der auch über Windows Update aktualisierbar ist. Wenn dies nicht möglich ist und Fabrikam einen Legacy-Treiber erstellt, sollte dieser Treiber eine andere Methode finden, um zu überprüfen, ob er auf einem Fabrikam-System ausgeführt wird (z. B. durch Überprüfung der SMBIOS-Tabelle vor dem Aktivieren von Funktionen).

Testcode für Produktionszeichen nicht verwenden

Sicherheitsprüflisteelement #4:Erstellen Sie keine Produktionscodezeichen für Entwicklung, Testen und Herstellen von Kerneltreibercode.

Kerneltreibercode, der für Die Entwicklung, Tests oder Herstellung verwendet wird, kann gefährliche Funktionen enthalten, die ein Sicherheitsrisiko darstellen. Dieser gefährliche Code sollte niemals mit einem Zertifikat signiert werden, das von Windows vertrauenswürdig ist. Der richtige Mechanismus zum Ausführen von gefährlichem Treibercode besteht darin, den sicheren UEFI-Start zu deaktivieren, das BCD "TESTSIGNING" zu aktivieren und den Entwicklungs-, Test- und Herstellungscode mithilfe eines nicht vertrauenswürdigen Zertifikats zu signieren (z. B. eines, das von makecert.exe generiert wird).

Code, der von einem vertrauenswürdigen Software Publishers Certificate (SPC) oder einer WHQL-Signatur (Windows Hardware Quality Labs) signiert ist, darf die Umgehung der Windows-Codeintegrität und -Sicherheitstechnologien nicht erleichtern. Bevor Code von einer vertrauenswürdigen SPC- oder WHQL-Signatur signiert wird, stellen Sie zunächst sicher, dass er den Anweisungen unter Erstellen von zuverlässigen Kernel-Mode Treibern entspricht. Darüber hinaus darf der Code keine gefährlichen Verhaltensweisen enthalten, die unten beschrieben werden. Weitere Informationen zur Treibersignatur finden Sie weiter unten in diesem Artikel unter Releasetreibersignatur .

Beispiele für gefährliches Verhalten sind:

  • Bietet die Möglichkeit, beliebigen Kernel-, physischen oder Gerätespeicher dem Benutzermodus zuzuordnen.
  • Ermöglicht das Lesen oder Schreiben beliebiger Kernel-, physischen oder Gerätespeichers, einschließlich Porteingabe/-ausgabe (E/A).
  • Bereitstellen des Zugriffs auf Speicher, der die Windows-Zugriffssteuerung umgeht.
  • Bietet die Möglichkeit, Hardware oder Firmware zu ändern, für die der Treiber nicht vorgesehen war.

Durchführen einer Bedrohungsanalyse

Sicherheitsprüflisteelement #5:Ändern Sie entweder ein vorhandenes Treiber-Bedrohungsmodell oder erstellen Sie ein benutzerdefiniertes Bedrohungsmodell für Ihren Treiber.

Bei der Berücksichtigung der Sicherheit besteht eine gängige Methodik darin, spezifische Bedrohungsmodelle zu erstellen, die versuchen, die möglichen Angriffstypen zu beschreiben. Diese Technik ist beim Entwerfen eines Treibers nützlich, da sie den Entwickler zwingt, die potenziellen Angriffsvektoren für einen Treiber im Voraus zu berücksichtigen. Nachdem er potenzielle Bedrohungen identifiziert hat, kann ein Treiberentwickler mittel in Erwägung ziehen, sich gegen diese Bedrohungen zu schützen, um die Allgemeine Sicherheit der Treiberkomponente zu erhöhen.

Dieser Artikel enthält treiberspezifische Anleitungen zum Erstellen eines einfachen Bedrohungsmodells: Bedrohungsmodellierung für Treiber. Der Artikel enthält ein Beispieldiagramm für ein Treiber-Bedrohungsmodell, das als Ausgangspunkt für Ihren Treiber verwendet werden kann.

Beispieldatenflussdiagramm, das einen hypothetischen Kernelmodustreiber veranschaulicht.

Bewährte Methoden für den Security Development Lifecycle (SDL) und zugehörige Tools können von IHVs und OEMs verwendet werden, um die Sicherheit ihrer Produkte zu verbessern. Weitere Informationen finden Sie unter SDL-Empfehlungen für OEMs.

Befolgen der Richtlinien für sichere Treibercodierung

Sicherheitschecklistenelement #6:Überprüfen Sie Ihren Code, und entfernen Sie alle bekannten Coderisiken.

Die Kernaktivität des Erstellens sicherer Treiber besteht darin, Bereiche im Code zu identifizieren, die geändert werden müssen, um bekannte Softwarerisiken zu vermeiden. Viele dieser bekannten Softwarerisiken betreffen die strikte Überwachung der Verwendung von Arbeitsspeicher, um Probleme zu vermeiden, bei denen andere überschreiben oder anderweitig die Speicherspeicherorte umfassen, die Ihr Treiber verwendet.

Im Abschnitt Codeüberprüfungstools dieses Artikels werden Softwaretools beschrieben, mit denen bekannte Softwarerisiken gefunden werden können.

Speicherpuffer

Verwenden Sie die geeignete Methode für den Zugriff auf Datenpuffer mit IOCTLs.

Eine der Hauptaufgaben eines Windows-Treibers ist die Übertragung von Daten zwischen Anwendungen im Benutzermodus und den Geräten eines Systems. Die drei Methoden für den Zugriff auf Datenpuffer sind in der folgenden Tabelle dargestellt.

IOCTL-Puffertyp Zusammenfassung Weitere Informationen finden Sie unter
METHOD_BUFFERED Empfohlen für die meisten Situtationen Verwenden von gepufferten E/A-Vorgängen
METHOD_IN_DIRECT oder METHOD_OUT_DIRECT Wird in einigen Hochgeschwindigkeits-HW-E/A-Vorgängen verwendet Verwenden direkter E/A-Vorgänge
METHOD_NEITHER Vermeiden Sie nach Möglichkeit Verwenden von weder gepufferten noch direkten E/A-Vorgängen

Im Allgemeinen wird gepufferte E/A empfohlen, da sie die sichersten Puffermethoden bietet. Aber auch bei verwendung von gepufferten E/A-Vorgängen gibt es Risiken, z. B. eingebettete Zeiger, die gemindert werden müssen.

Weitere Informationen zum Arbeiten mit Puffern in IOCTLs finden Sie unter Methoden zum Zugreifen auf Datenpuffer.

Fehler bei der Verwendung von gepufferten IOCTL-E/A-Vorgängen

  • Überprüfen Sie die Größe der IOCTL-bezogenen Puffer. Weitere Informationen finden Sie unter Fehler beim Überprüfen der Größe von Puffern.

  • Initialisieren Sie Ausgabepuffer ordnungsgemäß. Weitere Informationen finden Sie unter Fehler beim Initialisieren von Ausgabepuffern.

  • Überprüfen Sie Puffer mit variabler Länge ordnungsgemäß. Weitere Informationen finden Sie unter Fehler beim Überprüfen Variable-Length Puffer.

  • Wenn Sie gepufferte E/A verwenden, stellen Sie sicher, dass sie die richtige Länge für den OutputBuffer im Feld IO_STATUS_BLOCK Strukturinformationen zurückgeben. Geben Sie die Länge nicht direkt aus einer READ-Anforderung zurück. Betrachten Sie beispielsweise eine Situation, in der die zurückgegebenen Daten aus dem Benutzerbereich darauf hinweisen, dass ein 4K-Puffer vorhanden ist. Wenn der Treiber eigentlich nur 200 Bytes zurückgeben soll, aber stattdessen nur 4K im Feld Information zurückgibt, ist eine Sicherheitslücke zur Offenlegung von Informationen aufgetreten. Dieses Problem tritt auf, weil in früheren Versionen von Windows der Puffer, den der E/A-Manager für gepufferte E/A verwendet, nicht auf Null gesetzt ist. Daher erhält die Benutzer-App die ursprünglichen 200 Bytes Daten plus 4K-200 Bytes von dem, was sich im Puffer befand (nicht ausgelagerter Poolinhalt). Dieses Szenario kann bei allen Verwendungen von gepufferten E/A-Vorgängen und nicht nur mit IOCTLs auftreten.

Fehler in direkten IOCTL-E/A-Vorgängen

Behandeln Sie Puffer der Länge null ordnungsgemäß. Weitere Informationen finden Sie unter Fehler in direkten E/A-Vorgängen.

Fehler beim Verweisen auf Benutzerraumadressen

MSR-modellspezifische Register lese- und schreibvorgänge

Compilertrins, z. B. __readmsr und __writemsr , können für den Zugriff auf die modellspezifischen Register verwendet werden. Wenn dieser Zugriff erforderlich ist, muss der Treiber immer überprüfen, ob das Register zum Lesen oder Schreiben auf den erwarteten Index oder Bereich beschränkt ist.

Weitere Informationen und Codebeispiele finden Sie unter Bereitstellen der Möglichkeit zum Lesen/Schreiben von MSRs in der Entwicklungssicherheit bewährte Methoden für Windows-Treiberentwickler.

TOCTOU-Sicherheitsrisiken

Bei verwendung direkter E/A -Vorgänge (für IOCTLs oder für Lese-/Schreibzugriff) besteht eine potenzielle ToCTOU-Sicherheitslücke ( Check to Time of Use , TOCTOU). Beachten Sie, dass der Treiber auf den Benutzerdatenpuffer zugreift und der Benutzer gleichzeitig darauf zugreifen kann.

Um dieses Risiko zu verwalten, kopieren Sie alle Parameter, die überprüft werden müssen, aus dem Benutzerdatenpuffer in den Arbeitsspeicher, der ausschließlich über den Kernelmodus zugänglich ist (z. B. stapel oder pool). Sobald die Benutzeranwendung nicht auf die Daten zugreift, überprüfen Sie die übergebenen Daten, und arbeiten Sie dann mit den übergebenen Daten.

Treibercode muss den Arbeitsspeicher richtig nutzen

  • Alle Treiberpoolzuordnungen müssen sich im nicht ausführbaren Pool (NX) befinden. Die Verwendung von NX-Speicherpools ist von Natur aus sicherer als die Verwendung ausführbarer, nicht ausgelagerter Pools (NP) und bietet einen besseren Schutz vor Überlaufangriffen.

  • Gerätetreiber müssen verschiedene Benutzermodus- sowie Kernel-zu-Kernel-E/A-Anforderungen ordnungsgemäß verarbeiten.

Damit Treiber die HVCI-Virtualisierung unterstützen können, sind zusätzliche Arbeitsspeicheranforderungen erforderlich. Weitere Informationen finden Sie weiter unten in diesem Artikel unter Implementieren von HVCI-kompatiblem Code .

Ziehpunkte

Geräteobjekte

Irps

WDF und IRPs

Ein Vorteil der Verwendung von WDF ist, dass WDF-Treiber in der Regel nicht direkt auf IRPs zugreifen. Beispielsweise konvertiert das Framework die WDM-IRPs, die Lese-, Schreib- und Geräte-E/A-Steuerungsvorgänge darstellen, in Frameworkanforderungsobjekte, die KMDF/UMDF in E/A-Warteschlangen empfangen.

Wenn Sie einen WDM-Treiber schreiben, lesen Sie die folgenden Anleitungen.

Ordnungsgemäße Verwaltung von IRP-E/A-Puffern

Die folgenden Artikel enthalten Informationen zum Überprüfen von IRP-Eingabewerten:

DispatchReadWrite mit gepufferten E/A-Vorgängen

Fehler in gepufferten E/A-Vorgängen

DispatchReadWrite mit direkter E/A

Fehler in der direkten E/A

Sicherheitsprobleme für E/A-Steuerungscodes

Erwägen Sie, Werte zu überprüfen, die einem IRP zugeordnet sind, z. B. Pufferadressen und -längen.

Wenn Sie keine E/A-E/A verwenden möchten, sollten Sie beachten, dass die Pufferzeiger und -längen im Gegensatz zum Lesen und Schreiben sowie im Gegensatz zu gepufferten E/A- und Direkten E/A-Adressen nicht vom E/A-Manager überprüft werden.

Ordnungsgemäße Verarbeitung von IRP-Vervollständigungsvorgängen

Ein Treiber darf niemals einen IRP mit dem status Wert STATUS_SUCCESS abschließen, es sei denn, er unterstützt und verarbeitet die IRP tatsächlich. Informationen zu den richtigen Methoden zum Verarbeiten von IRP-Vervollständigungsvorgängen finden Sie unter Abschließen von IRPs.

Verwalten des IRP-Treiberstatus ausstehend

Der Treiber sollte das IRP als ausstehend markieren, bevor er das IRP speichert, und erwäge, sowohl den Aufruf von IoMarkIrpPending als auch die Zuweisung in eine ineinander verzahnte Sequenz einzureihen. Weitere Informationen finden Sie unter Fehler beim Überprüfen des Zustands eines Treibers und Halten eingehender IRPs, wenn ein Gerät angehalten wird.

Ordnungsgemäßes Behandeln von IRP-Abbruchvorgängen

Abbruchvorgänge können schwierig sein, ordnungsgemäß zu codieren, da sie in der Regel asynchron ausgeführt werden. Probleme im Code, der Abbruchvorgänge verarbeitet, können lange Zeit unbemerkt bleiben, da dieser Code in einem ausgeführten System normalerweise nicht häufig ausgeführt wird. Achten Sie darauf, alle Informationen unter Abbrechen von IRPs zu lesen und zu verstehen. Achten Sie besonders auf die Synchronisierung von IRP-Abbruch und Punkte, die beim Abbrechen von IRPs berücksichtigt werden sollten.

Eine empfohlene Möglichkeit zum Minimieren der Synchronisierungsprobleme, die mit Abbruchvorgängen verbunden sind, ist die Implementierung einer abbruchsicheren IRP-Warteschlange.

Behandeln von IRP-Bereinigungs- und -Schließvorgängen ordnungsgemäß

Stellen Sie sicher, dass Sie den Unterschied zwischen IRP_MJ_CLEANUP und IRP_MJ_CLOSE Anforderungen verstehen. Bereinigungsanforderungen kommen ein, nachdem eine Anwendung alle Handles für ein Dateiobjekt geschlossen hat, manchmal jedoch, bevor alle E/A-Anforderungen abgeschlossen sind. Schließen sie, nachdem alle E/A-Anforderungen für das Dateiobjekt abgeschlossen oder abgebrochen wurden. Weitere Informationen finden Sie in den folgenden Artikeln:

DispatchCreate, DispatchClose und DispatchCreateCloseClose Routinen

DispatchCleanup-Routinen

Fehler beim Behandeln von Bereinigungs- und Schließenvorgängen

Weitere Informationen zur ordnungsgemäßen Behandlung von IRPs finden Sie unter Zusätzliche Fehler bei der Behandlung von IRPs.

Weitere Sicherheitsprobleme

  • Verwenden Sie eine Sperre oder eine ineinander verriegelte Sequenz, um Racebedingungen zu verhindern. Weitere Informationen finden Sie unter Fehler in einer Multiprozessorumgebung.

  • Stellen Sie sicher, dass Gerätetreiber verschiedene Benutzermodus- sowie Kernel- und Kernel-E/A-Anforderungen ordnungsgemäß verarbeiten.

  • Stellen Sie sicher, dass während der Installation oder Nutzung keine TDI-Filter oder LSPs vom Treiber oder den zugehörigen Softwarepaketen installiert werden.

Verwenden von sicheren Funktionen

Zusätzliche Coderisiken

Zusätzlich zu den hier behandelten möglichen Sicherheitsrisiken enthält dieser Artikel zusätzliche Informationen zur Verbesserung der Sicherheit von Kernelmodustreibercode: Creating Reliable Kernel-Mode Drivers.

Weitere Informationen zur sicheren C- und C++-Codierung finden Sie unter Sichere Codierungsressourcen am Ende dieses Artikels.

Verwalten der Treiberzugriffssteuerung

Sicherheitsprüflisteelement #7:Überprüfen Sie Ihren Treiber, um sicherzustellen, dass Sie den Zugriff ordnungsgemäß steuern.

Verwalten der Treiberzugriffssteuerung – WDF

Treiber müssen funktionieren, um zu verhindern, dass Benutzer unangemessen auf die Geräte und Dateien eines Computers zugreifen. Um nicht autorisierten Zugriff auf Geräte und Dateien zu verhindern, müssen Sie:

  • Benennen Sie Geräteobjekte nur bei Bedarf. Benannte Geräteobjekte sind in der Regel nur aus älteren Gründen erforderlich, z. B. wenn Sie über eine Anwendung verfügen, die erwartet, das Gerät mit einem bestimmten Namen zu öffnen, oder wenn Sie ein Nicht-PNP-Gerät/Steuerungsgerät verwenden. Beachten Sie, dass WDF-Treiber ihr PnP-Gerät nicht FDO nennen müssen, um mithilfe von WdfDeviceCreateSymbolicLink einen symbolischen Link zu erstellen.

  • Sicherer Zugriff auf Geräteobjekte und Schnittstellen.

Damit Anwendungen oder andere WDF-Treiber auf Ihr PnP-Geräte-PDO zugreifen können, sollten Sie Geräteschnittstellen verwenden. Weitere Informationen finden Sie unter Verwenden von Geräteschnittstellen. Eine Geräteschnittstelle dient als symbolische Verbindung zum PDO Ihres Gerätestapels.

Eine der besseren Möglichkeit, den Zugriff auf die PDO zu steuern, besteht darin, eine SDDL-Zeichenfolge in Ihrem INF anzugeben. Wenn sich die SDDL-Zeichenfolge nicht in der INF-Datei befindet, wendet Windows eine Standardsicherheitsbeschreibung an. Weitere Informationen finden Sie unter Sichern von Geräteobjekten und SDDL für Geräteobjekte.

Weitere Informationen zum Steuern des Zugriffs finden Sie in den folgenden Artikeln:

Steuern des Gerätezugriffs in KMDF-Treibern

Namen, Sicherheitsdeskriptoren und Geräteklassen : Barrierefreie Geräteobjekte... und SAFE vom Januar Februar 2017 Der NT Insider Newsletter veröffentlicht von OSR.

Verwalten der Treiberzugriffssteuerung – WDM

Wenn Sie mit einem WDM-Treiber arbeiten und ein benanntes Geräteobjekt verwendet haben, können Sie IoCreateDeviceSecure verwenden und eine SDDL angeben, um es zu schützen. Wenn Sie IoCreateDeviceSecure implementieren, geben Sie immer eine benutzerdefinierte Klassen-GUID für DeviceClassGuid an. Sie sollten hier keine vorhandene Klassen-GUID angeben. Dies hat das Potenzial, Sicherheitseinstellungen oder Die Kompatibilität für andere Geräte, die zu dieser Klasse gehören, zu unterbrechen. Weitere Informationen finden Sie unter WdmlibIoCreateDeviceSecure.

Weitere Informationen finden Sie in den folgenden Artikeln:

Steuern des Gerätezugriffs

Steuern des Gerätenamespacezugriffs

Windows-Sicherheitsmodell für Treiberentwickler

Sicherheitsbezeichner (SIDs) Risikohierarchie

Im folgenden Abschnitt wird die Risikohierarchie der gängigen SIDs beschrieben, die im Treibercode verwendet werden. Allgemeine Informationen zu SDDL finden Sie unter SDDL für Geräteobjekte, SID-Zeichenfolgen und SDDL-Zeichenfolgensyntax.

Es ist wichtig zu verstehen, dass das Coderisiko erhöht wird, wenn Aufrufer mit niedrigeren Berechtigungen auf den Kernel zugreifen dürfen. In diesem Zusammenfassungsdiagramm erhöht sich das Risiko, wenn Sie SIDs mit geringeren Berechtigungen Zugriff auf Ihre Treiberfunktionalität gewähren.

SY (System)
\/
BA (Built-in Administrators)
\/
LS (Local Service)
\/
BU (Built-in User)
\/
AC (Application Container)

Konfigurieren Sie nach dem allgemeinen Prinzip der geringsten Rechte nur die Minimale Zugriffsebene, die für die Funktion Ihres Treibers erforderlich ist.

WDM Granular IOCTL-Sicherheitssteuerung

Um die Sicherheit beim Senden von IOCTLs durch Aufrufer im Benutzermodus weiter zu verwalten, kann der Treibercode die IoValidateDeviceIoControlAccess-Funktion enthalten. Diese Funktion ermöglicht es einem Treiber, Zugriffsrechte zu überprüfen. Beim Empfang einer IOCTL kann ein Treiber IoValidateDeviceIoControlAccess aufrufen und FILE_READ_ACCESS, FILE_WRITE_ACCESS oder beides angeben.

Die Implementierung einer differenzierten IOCTL-Sicherheitssteuerung ersetzt nicht die Notwendigkeit, den Treiberzugriff mithilfe der oben beschriebenen Techniken zu verwalten.

Weitere Informationen finden Sie in den folgenden Artikeln:

Definieren von E/A-Steuerungscodes

Implementieren von HVCI-kompatiblem Code

Sicherheitsprüflisteelement #8:Überprüfen Sie, ob Ihr Treiber Arbeitsspeicher verwendet, damit er HVCI-kompatibel ist.

Speicherauslastung und HVCI-Kompatibilität

HVCI verwendet Hardwaretechnologie und Virtualisierung, um die Entscheidungsfunktion code integrity (CI) vom rest des Betriebssystems zu isolieren. Wenn Sie virtualisierungsbasierte Sicherheit zum Isolieren von CI verwenden, kann der Kernelspeicher nur über eine CI-Überprüfung ausführbar werden. Das bedeutet, dass Kernelspeicherseiten nie beschreibbar und ausführbar sein können, und ausführbarer Code kann nicht direkt geändert werden.

Um HVCI-kompatiblen Code zu implementieren, stellen Sie sicher, dass Ihr Treibercode die folgenden Schritte ausführt:

  • Aktiviert sich standardmäßig für NX.
  • Verwendet NX-APIs/Flags für die Speicherzuordnung (NonPagedPoolNx)
  • Verwendet keine Abschnitte, die sowohl beschreibbar als auch ausführbar sind.
  • Versucht nicht, den ausführbaren Systemspeicher direkt zu ändern.
  • Verwendet keinen dynamischen Code im Kernel
  • Lädt keine Datendateien als ausführbare Datei
  • Die Abschnittsausrichtung ist ein Vielfaches von 0x1000 (PAGE_SIZE). Z.B. DRIVER_ALIGNMENT=0x1000

Weitere Informationen zur Verwendung des Tools und eine Liste mit inkompatiblen Speicheraufrufen finden Sie unter Implementieren von HVCI-kompatiblem Code.

Weitere Informationen zu den zugehörigen Systemgrundlagen-Sicherheitstests finden Sie unter HyperVisor Code Integrity Readiness Test und Hypervisor-Protected Code Integrity (HVCI).

Befolgen von bewährten Methoden für technologiespezifischen Code

Sicherheitschecklistenelement #9:Lesen Sie die folgenden technologiespezifischen Anleitungen für Ihren Treiber.

Dateisysteme

Weitere Informationen zur Sicherheit des Dateisystemtreibers finden Sie in den folgenden Artikeln:

Einführung in die Dateisystemsicherheit

Dateisystemsicherheitsprobleme

Sicherheitsfeatures für Dateisysteme

Koexistenz mit anderen Dateisystemfiltertreibern

NDIS – Netzwerk

Informationen zur Sicherheit des NDIS-Treibers finden Sie unter Sicherheitsprobleme für Netzwerktreiber.

Anzeige

Informationen zur Sicherheit des Anzeigetreibers finden Sie unter <Ausstehender> Inhalt.

Drucker

Informationen zur Druckertreibersicherheit finden Sie unter Überlegungen zur V4-Druckertreibersicherheit.

Sicherheitsprobleme für WIA-Treiber (Windows Image Acquisition)

Informationen zur WIA-Sicherheit finden Sie unter Sicherheitsprobleme für Windows-Imageerfassungstreiber (WIA- Treiber).

Erhöhen der Geräteinstallationssicherheit

Sicherheitsprüflisteelement #10:Überprüfen Sie die Anleitung zur Erstellung und Installation des Treibers, um sicherzustellen, dass Sie bewährte Methoden befolgen.

Wenn Sie den Code erstellen, mit dem Der Treiber installiert wird, müssen Sie sicherstellen, dass die Installation Ihres Geräts immer sicher ausgeführt wird. Bei einer sicheren Geräteinstallation handelt es sich um folgendes:

  • Schränkt den Zugriff auf das Gerät und seine Geräteschnittstellenklassen ein.
  • Beschränkt den Zugriff auf die Treiberdienste, die für das Gerät erstellt wurden
  • Schützt Treiberdateien vor Änderungen oder Löschungen
  • Schränkt den Zugriff auf die Registrierungseinträge des Geräts ein
  • Schränkt den Zugriff auf die WMI-Klassen des Geräts ein
  • Verwendet SetupAPI-Funktionen ordnungsgemäß

Weitere Informationen finden Sie in den folgenden Artikeln:

Erstellen sicherer Geräteinstallationen

Richtlinien für die Verwendung von SetupAPI

Verwenden von Geräteinstallationsfunktionen

Erweiterte Themen zur Geräte- und Treiberinstallation

Durchführen einer Peercodeüberprüfung

Sicherheitsprüfliste -Element #11:Durchführen einer Peercodeüberprüfung, um nach Problemen zu suchen, die von den anderen Tools und Prozessen nicht angezeigt werden

Suchen Sie sachkundige Codeprüfer, um nach Problemen zu suchen, die Sie möglicherweise übersehen haben. Eine zweite Gruppe von Augen wird oft Probleme sehen, die Sie möglicherweise übersehen haben.

Wenn Sie nicht über geeignete Mitarbeiter verfügen, um Ihren Code intern zu überprüfen, ziehen Sie in Betracht, externe Hilfe zu diesem Zweck in Betracht zu ziehen.

Ausführen der richtigen Signatur des Releasetreibers

Sicherheitsprüfliste #12:Verwenden Sie das Windows-Partnerportal, um Ihren Treiber ordnungsgemäß für die Verteilung zu signieren.

Bevor Sie ein Treiberpaket für die Öffentlichkeit freigeben, wird empfohlen, das Paket zur Zertifizierung zu übermitteln. Weitere Informationen finden Sie unter Testen auf Leistung und Kompatibilität, Erste Schritte mit dem Hardwareprogramm, Hardwaredashboarddienste und Nachweis zur Signatur eines Kerneltreibers für die öffentliche Version.

Verwenden der Codeanalyse in Visual Studio zum Untersuchen der Treibersicherheit

Sicherheitsprüflisteelement #13:Führen Sie die folgenden Schritte aus, um die Codeanalysefunktion in Visual Studio zu verwenden, um auf Sicherheitsrisiken im Treibercode zu überprüfen.

Verwenden Sie das Codeanalysefeature in Visual Studio, um nach Sicherheitsrisiken in Ihrem Code zu suchen. Das Windows Driver Kit (WDK) installiert Regelsätze, die darauf ausgelegt sind, probleme im systemeigenen Treibercode zu überprüfen.

Weitere Informationen finden Sie unter Ausführen der Codeanalyse für Treiber.

Weitere Informationen finden Sie unter Übersicht über die Codeanalyse für Treiber. Weitere Informationen zur Codeanalyse finden Sie unter Visual Studio 2013 Statische Codeanalyse im Detail.

Um sich mit der Codeanalyse vertraut zu machen, können Sie einen der Beispieltreiber verwenden, z. B. das ausgewählte Toasterbeispiel https://github.com/Microsoft/Windows-driver-samples/tree/main/general/toaster/toastDrv/kmdf/func/featured oder das ELAM Early Launch Anti-Malware-Beispiel https://github.com/Microsoft/Windows-driver-samples/tree/main/security/elam.

  1. Öffnen Sie die Treiberprojektmappe in Visual Studio.

  2. Ändern Sie in Visual Studio für jedes Projekt in der Projektmappe die Projekteigenschaften, um den gewünschten Regelsatz zu verwenden. Beispiel: Projekteigenschaften >>>> Codeanalyse >> Allgemein, wählen Sie Empfohlene Treiberregeln aus. Verwenden Sie nicht nur die wieder empfohlenen Treiberregeln, sie können auch den Regelsatz Empfohlene native Regeln verwenden.

  3. Wählen Sie Erstellen >> Codeanalyse für Projektmappe ausführen aus.

  4. Zeigen Sie Warnungen auf der Registerkarte Fehlerliste des Buildausgabefensters in Visual Studio an.

Wählen Sie die Beschreibung für jede Warnung aus, um den problematischen Bereich in Ihrem Code anzuzeigen.

Wählen Sie den verknüpften Warnungscode aus, um weitere Informationen anzuzeigen.

Bestimmen Sie, ob Ihr Code geändert werden muss oder ob eine Anmerkung hinzugefügt werden muss, damit die Codeanalyse-Engine der Absicht ihres Codes ordnungsgemäß folgen kann. Weitere Informationen zur Codeanmerkung finden Sie unter Verwenden von SAL-Anmerkungen zum Reduzieren von C/C++-Codedefekten und SAL 2.0-Anmerkungen für Windows-Treiber.

Allgemeine Informationen zu SAL finden Sie in diesem Artikel, der von OSR verfügbar ist. https://www.osr.com/blog/2015/02/23/sal-annotations-dont-hate-im-beautiful/

Verwenden der statischen Treiberüberprüfung zum Überprüfen auf Sicherheitsrisiken

Sicherheitsprüfliste-Element #14:Führen Sie die folgenden Schritte aus, um die statische Treiberüberprüfung (Static Driver Verifier, SDV) in Visual Studio zu verwenden, um auf Sicherheitsrisiken in Ihrem Treibercode zu überprüfen.

Static Driver Verifier (SDV) verwendet eine Reihe von Schnittstellenregeln und ein Modell des Betriebssystems, um zu bestimmen, ob der Treiber ordnungsgemäß mit dem Windows-Betriebssystem interagiert. SDV findet Fehler im Treibercode, die auf potenzielle Fehler in Treibern hinweisen können.

Weitere Informationen finden Sie unter Introducing Static Driver Verifier and Static Driver Verifier.For more information, see Introducing Static Driver Verifier and Static Driver Verifier.

Beachten Sie, dass von SDV nur bestimmte Treibertypen unterstützt werden. Weitere Informationen zu den Treibern, die von SDV überprüft werden können, finden Sie unter Unterstützte Treiber. Auf den folgenden Seiten finden Sie Informationen zu den SDV-Tests, die für den Treibertyp verfügbar sind, mit dem Sie arbeiten.

Um sich mit SDV vertraut zu machen, können Sie einen der Beispieltreiber verwenden (z. B. das empfohlene Toasterbeispiel: https://github.com/Microsoft/Windows-driver-samples/tree/main/general/toaster/toastDrv/kmdf/func/featured).

  1. Öffnen Sie die Zieltreiberlösung in Visual Studio.

  2. Ändern Sie in Visual Studio den Buildtyp in Release. Die statische Treiberüberprüfung erfordert, dass der Buildtyp release und nicht debug ist.

  3. Wählen Sie in Visual Studio Build Solution (Projektmappe erstellen) >> aus.

  4. Wählen Sie in Visual Studio treiberstart >> static driver verifier aus.

  5. Wählen Sie in SDV auf der Registerkarte Regeln unter Regelsätze die Option Standard aus.

    Obwohl bei den Standardregeln viele häufige Probleme auftreten, sollten Sie auch den umfangreicheren Regelsatz Alle Treiberregeln ausführen.

  6. Wählen Sie auf der Registerkarte Haupt von SDV die Option Start aus.

  7. Wenn die SDV abgeschlossen ist, überprüfen Sie alle Warnungen in der Ausgabe. Auf der Registerkarte Haupt wird die Gesamtzahl der gefundenen Fehler angezeigt.

  8. Wählen Sie jede Warnung aus, um die SDV-Berichtsseite zu laden und die Informationen zu untersuchen, die mit der möglichen Codesicherheitsanfälligkeit verbunden sind. Verwenden Sie den Bericht, um das Überprüfungsergebnis zu untersuchen und Pfade in Ihrem Treiber zu identifizieren, die bei einer SDV-Überprüfung fehlschlagen. Weitere Informationen finden Sie unter Static Driver Verifier Report( Static Driver Verifier Report).

Überprüfen des Codes mit dem BinSkim Binary Analyzer

Sicherheitsprüfliste -Element #15:Führen Sie die folgenden Schritte aus, um binSkim zu verwenden, um zu überprüfen, ob Kompilierungs- und Buildoptionen konfiguriert sind, um bekannte Sicherheitsprobleme zu minimieren.

Verwenden Sie BinSkim, um Binärdateien zu untersuchen, um Codierungs- und Erstellungsmethoden zu identifizieren, die die Binärdatei potenziell anfällig machen können.

BinSkim prüft auf:

  • Verwendung veralteter Compilertoolsätze: Binärdateien sollten nach Möglichkeit mit den neuesten Compilertoolsätzen kompiliert werden, um die Verwendung der aktuellen Compilerebene und der vom Betriebssystem bereitgestellten Sicherheitsminderungen zu maximieren.
  • Unsichere Kompilierungseinstellungen: Binärdateien sollten mit den sichersten Einstellungen kompiliert werden, um vom Betriebssystem bereitgestellte Sicherheitsminderungen zu ermöglichen, Compilerfehler zu maximieren und umsetzbare Warnungen zu melden.
  • Signaturprobleme: Signierte Binärdateien sollten mit kryptografisch starken Algorithmen signiert werden.

BinSkim ist ein Open Source Tool und generiert Ausgabedateien, die das SARIF-Format (Static Analysis Results Interchange Format) verwenden. BinSkim ersetzt das frühere BinScope-Tool .

Weitere Informationen zu BinSkim finden Sie im BinSkim-Benutzerhandbuch.

Führen Sie die folgenden Schritte aus, um zu überprüfen, ob die Optionen für die Sicherheitskompilierung im Code, den Sie senden, ordnungsgemäß konfiguriert sind.

  1. Laden Sie das plattformübergreifende .NET Core SDK herunter, und installieren Sie es.

  2. Vergewissern Sie sich, dass Visual Studio installiert ist. Informationen zum Herunterladen und Installieren von Visual Studio finden Sie unter Installieren von Visual Studio.

  3. Es gibt eine Reihe von Optionen zum Herunterladen von BinSkim, z. B. ein NuGet-Paket. In diesem Beispiel verwenden wir die Git-Klonoption zum Herunterladen von hier: https://github.com/microsoft/binskim und installieren sie auf einem 64-Bit-Windows-PC.

  4. Öffnen Sie ein Visual Studio Developer-Eingabeaufforderungsfenster, und erstellen Sie ein Verzeichnis, z. B C:\binskim-master. .

    C:\> Md \binskim-master
    
  5. Wechseln Sie in das soeben erstellte Verzeichnis.

    C:\> Cd \binskim-master
    
  6. Verwenden Sie den Git-Befehl clone, um alle erforderlichen Dateien herunterzuladen.

    C:\binskim-master> git clone --recurse-submodules https://github.com/microsoft/binskim.git
    
  7. Wechseln Sie zu der neuen binskim Dirktory, die der Klonbefehl erstellt hat.

    C:\> Cd \binskim-master\binskim
    
  8. Führen Sie BuildAndTest.cmd aus, um sicherzustellen, dass der Releasebuild erfolgreich ist und dass alle Tests erfolgreich sind.

    C:\binskim-master\binskim> BuildAndTest.cmd
    
    Welcome to .NET Core 3.1!
    ---------------------
    SDK Version: 3.1.101
    
    ...
    
    C:\binskim-master\binskim\bld\bin\AnyCPU_Release\Publish\netcoreapp2.0\win-x64\BinSkim.Sdk.dll
    1 File(s) copied
    C:\binskim-master\binskim\bld\bin\AnyCPU_Release\Publish\netcoreapp2.0\linux-x64\BinSkim.Sdk.dll
    1 File(s) copied
    
    ...
    
    
  9. Der Buildprozess erstellt eine Reihe von Verzeichnissen mit den ausführbaren BinSkim-Dateien. Wechseln Sie zum Win-x64-Buildausgabeverzeichnis.

    C:\binskim-master\binskim> Cd \binskim-master\bld\bin\AnyCPU_Release\Publish\netcoreapp2.0\win-x64>
    
  10. Hilfe für die Analyseoption anzeigen.

C:\binskim-master\binskim\bld\bin\AnyCPU_Release\Publish\netcoreapp2.0\win-x64> BinSkim help analyze

BinSkim PE/MSIL Analysis Driver 1.6.0.0

--sympath                      Symbols path value, e.g., SRV*http://msdl.microsoft.com/download/symbols or Cache*d:\symbols;Srv*http://symweb. See
                              https://learn.microsoft.com/windows-hardware/drivers/debugger/advanced-symsrv-use for syntax information. Note that BinSkim will clear the
                              _NT_SYMBOL_PATH environment variable at runtime. Use this argument for symbol information instead.

--local-symbol-directories     A set of semicolon-delimited local directory paths that will be examined when attempting to locate PDBs.

-o, --output                   File path to which analysis output will be written.

--verbose                      Emit verbose output. The resulting comprehensive report is designed to provide appropriate evidence for compliance scenarios.

...

Festlegen des Symbolpfads

Wenn Sie den gesamten Code, den Sie analysieren, auf demselben Computer erstellen, auf dem Sie BinSkim ausführen, müssen Sie in der Regel nicht den Symbolpfad festlegen. Dies liegt daran, dass Ihre Symboldateien im lokalen Feld verfügbar sind, in dem Sie kompiliert haben. Wenn Sie ein komplexeres Buildsystem verwenden oder Ihre Symbole an einen anderen Speicherort umleiten (nicht zusammen mit der kompilierten Binärdatei), verwenden Sie --local-symbol-directories , um diese Speicherorte der Symboldateisuche hinzuzufügen. Wenn Ihr Code auf eine kompilierte Binärdatei verweist, die nicht Teil Ihres Codes ist, kann die Windows-Debugger-Sympath zum Abrufen von Symbolen verwendet werden, um die Sicherheit dieser Codeabhängigkeiten zu überprüfen. Wenn Sie ein Problem in diesen Abhängigkeiten finden, können Sie diese möglicherweise nicht beheben. Es kann jedoch hilfreich sein, sich über mögliche Sicherheitsrisiken zu informieren, die Sie akzeptieren, indem Sie diese Abhängigkeiten übernehmen.

Tipp

Wenn Sie einen Symbolpfad hinzufügen (der auf einen Netzwerksymbolserver verweist), fügen Sie einen lokalen Cachespeicherort hinzu, um einen lokalen Pfad zum Zwischenspeichern der Symbole anzugeben. Dies kann die Leistung von BinSkim erheblich beeinträchtigen. Im folgenden Beispiel wird ein lokaler Cache unter d:\symbols angegeben. --sympath Cache*d:\symbols;Srv*http://symweb Weitere Informationen zu sympath finden Sie unter Symbolpfad für Windows-Debugger.

  1. Führen Sie den folgenden Befehl aus, um eine kompilierte Treiberbinärdatei zu analysieren. Aktualisieren Sie den Zielpfad so, dass er auf Den erfüllten Treiber .sys-Datei verweist.

    C:\binskim-master\binskim\bld\bin\AnyCPU_Release\Publish\netcoreapp2.0\win-x64> BinSkim analyze "C:\Samples\KMDF_Echo_Driver\echo.sys"
    
  2. Fügen Sie für weitere Informationen die ausführliche Option wie folgt hinzu.

    C:\binskim-master\binskim\bld\bin\AnyCPU_Release\Publish\netcoreapp2.0\win-x64> BinSkim analyze "C:\Samples\KMDF_Echo_Driver\osrusbfx2.sys" --verbose
    

    Hinweis

    Die Option --verbose erzeugt explizite Pass/Fail-Ergebnisse für jede Überprüfung. Wenn Sie nicht ausführlich angeben, werden nur die Fehler angezeigt, die Von BinSkim erkannt werden. Die Option --verbose wird in der Regel nicht für tatsächliche Automatisierungssysteme empfohlen, da sie die Größe der Protokolldateien erhöht und es schwieriger macht, einzelne Fehler zu erfassen, wenn sie auftreten, da sie inmitten einer großen Anzahl von "Pass"-Ergebnissen eingebettet werden.

  3. Überprüfen Sie die Befehlsausgabe, um nach möglichen Problemen zu suchen. Diese Beispielausgabe zeigt drei bestandene Tests. Weitere Informationen zu den Regeln, z. B. BA2002, finden Sie im BinSkim-Benutzerhandbuch.

    Analyzing...
    Analyzing 'osrusbfx2.sys'...
    ...
    
    C:\Samples\KMDF_Echo_Driver\osrusbfx2.sys\Debug\osrusbfx2.sys: pass BA2002: 'osrusbfx2.sys' does not incorporate any known vulnerable dependencies, as configured by current policy.
    C:\Samples\KMDF_Echo_Driver\Debug\osrusbfx2.sys: pass BA2005: 'osrusbfx2.sys' is not known to be an obsolete binary that is vulnerable to one or more security problems.
    C:\Samples\KMDF_Echo_Driver\osrusbfx2.sys: pass BA2006: All linked modules of 'osrusbfx2.sys' generated by the Microsoft front-end satisfy configured policy (compiler minimum version 17.0.65501.17013).
    
  4. Diese Ausgabe zeigt, dass test BA3001 nicht ausgeführt wird, da das Tool angibt, dass der Treiber keine ELF-Binärdatei ist.

    ...
    C:\Samples\KMDF_Echo_Driver\Debug\osrusbfx2.sys: notapplicable BA3001: 'osrusbfx2.sys' was not evaluated for check 'EnablePositionIndependentExecutable' as the analysis is not relevant based on observed metadata: image is not an ELF binary.
    
  5. Diese Ausgabe zeigt einen Fehler für Test BA2007 an.

    ...
    
    C:\Samples\KMDF_Echo_Driver\Debug\osrusbfx2.sys: error BA2007: 'osrusbfx2.sys' disables compiler warning(s) which are required by policy.
    A compiler warning is typically required if it has a high likelihood of flagging memory corruption, information disclosure, or double-free vulnerabilities.
    To resolve this issue, enable the indicated warning(s) by removing /Wxxxx switches (where xxxx is a warning id indicated here) from your command line, and resolve any warnings subsequently raised during compilation.
    

Um diese Warnungen in Visual Studio zu aktivieren, entfernen Sie unter C/C++ auf den Eigenschaftenseiten für das Projekt die Werte, die Sie unter Deaktivieren bestimmter Warnungen nicht ausschließen möchten.

Screenshot des Dialogfelds zum Deaktivieren bestimmter Warnungen in Visual Studio 2019.

Die standardmäßigen Kompilierungsoptionen in Visual Studio für Treiberprojekte können Warnungen wie die folgenden deaktivieren. Diese Warnungen werden von BinSkim gemeldet.

C4603 – "Name": Das Makro ist nicht definiert, oder die Definition unterscheidet sich nach der verwendung von vorkompilierten Headern.

C4627 – "description": bei der Suche nach vorkompilierten Headerverwendung übersprungen

C4986 – "Deklaration": Ausnahmespezifikation stimmt nicht mit vorheriger Deklaration überein

Weitere Informationen zu den Compilerwarnungen finden Sie unter Compilerwarnungen nach Compilerversion.

Verwenden zusätzlicher Codevalidierungstools

Sicherheitsprüfliste #16:Verwenden Sie diese zusätzlichen Tools, um zu überprüfen, ob Ihr Code Sicherheitsempfehlungen befolgt, und um lückenhaft zu prüfen, die in Ihrem Entwicklungsprozess übersehen wurden.

Zusätzlich zur Visual Studio Code-Analyse, statischen Treiberüberprüfung und Binskim , die oben beschrieben wurden, verwenden Sie die folgenden Tools, um lückenhaft zu prüfen, die in Ihrem Entwicklungsprozess übersehen wurden.

Treiberüberprüfung

Driver Verifier ermöglicht Livetests des Treibers. Driver Verifier überwacht Treiber und Grafiktreiber im Windows-Kernelmodus und Grafiktreiber, um unzulässige Funktionsaufrufe oder Aktionen zu erkennen, die das System beschädigen könnten. Driver Verifier kann die Windows-Treiber einer Vielzahl von Belastungen und Tests unterziehen, um falsches Verhalten zu finden. Weitere Informationen finden Sie unter Treiberüberprüfung.

Tests des Hardwarekompatibilitätsprogramms

Das Hardwarekompatibilitätsprogramm enthält sicherheitsbezogene Tests, die verwendet werden können, um nach Sicherheitsrisiken im Code zu suchen. Das Windows-Hardwarekompatibilitätsprogramm nutzt die Tests im Windows Hardware Lab Kit (HLK). Die HLK Device Fundamentals-Tests können in der Befehlszeile verwendet werden, um Treibercode zu trainieren und auf Schwächen zu testen. Allgemeine Informationen zu den Tests der Gerätegrundlagen und zum Hardwarekompatibilitätsprogramm finden Sie unter Windows Hardware Lab Kit.

Die folgenden Tests sind Beispiele für Tests, die hilfreich sein können, um Treibercode auf einige Verhaltensweisen zu überprüfen, die mit Codesicherheitsrisiken verbunden sind:

DF – Fuzz – Zufälliger IOCTL-Test (Zuverlässigkeit)

DF – Fuzz – Test zum Öffnen von Sub (Zuverlässigkeit)

DF – Fuzz – FSCTL-Test auf Puffer mit Länge null (Zuverlässigkeit)

DF – Fuzz – Zufälliger FSCTL-Test (Zuverlässigkeit)

DF – Fuzz – Sonstiger API-Test (Zuverlässigkeit)

Sie können auch das Kernelsynchronisierungsverzögerungsfuzzing verwenden, das in der Treiberüberprüfung enthalten ist.

Die CHAOS-Tests (Concurrent Hardware and Operating System) führen verschiedene PnP-Treibertests, Gerätetreiber-Fuzz-Tests und Stromversorgungssystemtests gleichzeitig aus. Weitere Informationen finden Sie unter CHAOS-Tests (Gerätegrundlagen).

Die Gerätegrundlagen-Penetrationstests führen verschiedene Arten von Eingabeangriffen durch, die eine wichtige Komponente von Sicherheitstests sind. Angriffs- und Penetrationstests können dabei helfen, Sicherheitsrisiken in Softwareschnittstellen zu identifizieren. Weitere Informationen finden Sie unter Penetrationstests (Gerätegrundlagen).

Verwenden Sie den Device Guard – Compliance-Test zusammen mit den anderen in diesem Artikel beschriebenen Tools, um zu bestätigen, dass Ihr Treiber mit HVCI kompatibel ist.

Benutzerdefinierte und domänenspezifische Testtools

Berücksichtigen Sie die Entwicklung benutzerdefinierter domänenspezifischer Sicherheitstests. Um zusätzliche Tests zu entwickeln, sammeln Sie Beiträge von den ursprünglichen Designern der Software sowie nicht verwandte Entwicklungsressourcen, die mit dem spezifischen Typ des treibers vertraut sind, der entwickelt wird, und einer oder mehreren Personen, die mit der Analyse und Verhinderung von Sicherheitsangriffen vertraut sind.

Überprüfen von Debuggertechniken und -erweiterungen

Sicherheitsprüfliste #17:Überprüfen Sie diese Debuggertools, und berücksichtigen Sie deren Verwendung in Ihrem Entwicklungsdebuggingworkflow.

Die Erweiterung !acl formatiert und zeigt den Inhalt einer Zugriffssteuerungsliste (Access Control List, ACL) an. Weitere Informationen finden Sie unter Bestimmen der ACL eines Objekts und !acl.

Die Erweiterung !token zeigt eine formatierte Ansicht eines Sicherheitstokenobjekts an. Weitere Informationen finden Sie unter !token.

Die Erweiterung !tokenfields zeigt die Namen und Offsets der Felder innerhalb des Zugriffstokenobjekts (die TOKEN-Struktur) an. Weitere Informationen finden Sie unter !tokenfields.

Die Erweiterung !sid zeigt die Sicherheits-ID (SID) an der angegebenen Adresse an. Weitere Informationen finden Sie unter !sid.

Die Erweiterung !sd zeigt den Sicherheitsdeskriptor an der angegebenen Adresse an. Weitere Informationen finden Sie unter !sd.

Microsoft Reporting Center für anfällige und schädliche Treiber

Jeder kann einen fragwürdigen Treiber über das Microsoft Vulnerable and Malicious Driver Reporting Center übermitteln. Informationen dazu, wie Treiber zur Analyse übermittelt werden, finden Sie in diesem Blogeintrag: Verbessern der Kernelsicherheit mit dem neuen Microsoft Vulnerable and Malicious Driver Reporting Center

Das Reporting Center kann Windows-Treiber überprüfen und analysieren, die für x86- und x64-Architekturen erstellt wurden. Anfällige und böswillige gescannte Treiber werden zur Analyse und Untersuchung durch das Anfällige Treiberteam von Microsoft gekennzeichnet. Nachdem vulerierbare Treiber bestätigt wurden, wird eine entsprechende Benachrichtigung angezeigt, und sie werden der Sperrliste für anfällige Treiber hinzugefügt. Weitere Informationen hierzu finden Sie unter Von Microsoft empfohlene Treiberblockregeln. Diese Regeln werden standardmäßig auf HVCI-fähige Geräte (Hypervisor-Protected Code Integrity) und Windows 10 im S-Modus angewendet.

Überprüfen der sicheren Codierungsressourcen

Sicherheitsprüfliste #18:Überprüfen Sie diese Ressourcen, um Ihr Verständnis der bewährten Methoden für die sichere Codierung zu erweitern, die für Treiberentwickler gelten.

Lesen Sie diese Ressourcen, um mehr über die Treibersicherheit zu erfahren.

Richtlinien für die Sichere Codierung von Kernelmodustreibern

Erstellen zuverlässiger Kernel-Mode-Treiber

Sicheres Codieren von Organisationen

Carnegie Mellon University SEI CERT

Carnegie Mellon University SEI CERT C Coding Standard: Rules for Developing Safe, Reliable, and Secure Systems (Edition 2016).

MITRE : Schwachstellen, die vom CERT C Secure Coding Standard behoben werden

Building Security In Maturity Model (BSIMM) – https://www.bsimm.com/

SAFECode : https://safecode.org/

CISA-Ressourcen

OSR

OSR bietet Trainings- und Beratungsdienste für die Treiberentwicklung. In diesen Artikeln aus dem OSR-Newsletter werden Probleme mit der Treibersicherheit hervorgehoben.

Namen, Sicherheitsbeschreibungen und Geräteklassen : Barrierefreie Geräteobjekte... und SAFE

Sie haben Gotta Use Protection –- In Driver & Device Security

Sperren von Treibern – Eine Umfrage zu Techniken

Meltdown and Spectre: Was ist mit Treibern?

Fallstudie

Von der Warnung zum Treiberrisiko: Microsoft Defender ATP-Untersuchung entdeckt Fehler bei der Rechteausweitung

Bücher

24 Todsünden der Softwaresicherheit: Programmierfehler und wie man sie beheben kann von Michael Howard, David LeBlanc und John Viega

Die Kunst der Softwaresicherheitsbewertung: Identifizieren und Verhindern von Software-Schwachstellen, Mark Dowd, John McDonald und Justin Schuh

Schreiben von Secure Software Second Edition, Michael Howard und David LeBlanc

The Art of Software Security Assessment: Identifizieren und Verhindern von Software-Sicherheitsrisiken, Mark Dowd und John McDonald

Secure Coding in C and C++ (SEI Series in Software Engineering) 2nd Edition, Robert C. Seacord

Programmieren des Microsoft Windows-Treibermodells (2. Edition), Walter Oney

Entwickeln von Treibern mit der Windows Driver Foundation (Entwicklerreferenz), Penny Orwick und Guy Smith

Training

Das Kurstraining für Windows-Treiber ist z. B. bei folgenden Anbietern verfügbar:

Onlineschulungen zum sicheren Programmieren sind aus einer Vielzahl von Quellen verfügbar. Dieser Kurs ist beispielsweise ab coursera verfügbar:

Identifizieren von Sicherheitsrisiken bei der C/C++-Programmierung.

SAFECode bietet auch kostenlose Schulungen an:

SAFECode.org/training

Professionelle Zertifizierung

CERT bietet eine Secure Coding Professional-Zertifizierung an.

Zusammenfassung der wichtigsten Erkenntnisse

Treibersicherheit ist ein komplexes Unterfangen, das viele Elemente enthält, aber hier sind einige wichtige Punkte zu berücksichtigen:

  • Treiber befinden sich im Windows-Kernel, und ein Problem bei der Ausführung im Kernel macht das gesamte Betriebssystem verfügbar. Achten Sie daher genau auf die Sicherheit und das Design des Fahrers, um die Sicherheit im Auge zu behalten.

  • Wenden Sie das Prinzip der geringsten Rechte an:

    a. Verwenden Einer strengen SDDL-Zeichenfolge, um den Zugriff auf den Treiber einzuschränken

    b. Weitere Einschränkung einzelner IOCTL's

  • Erstellen Sie ein Bedrohungsmodell, um Angriffsvektoren zu identifizieren und zu überlegen, ob etwas weiter eingeschränkt werden kann.

  • Achten Sie darauf, dass eingebettete Zeiger aus dem Benutzermodus übergeben werden. Sie müssen überprüft, innerhalb von try außer auf sie zugegriffen werden, und sie sind anfällig für Probleme mit der Überprüfungszeit (Check Time of Use, ToCToU), es sei denn, der Wert des Puffers wird erfasst und verglichen.

  • Wenn Sie sich nicht sicher sind, verwenden Sie METHOD_BUFFERED als IOCTL-Puffermethode.

  • Verwenden Sie Hilfsprogramme für die Codeüberprüfung, um nach bekannten Coderisiken zu suchen und alle identifizierten Probleme zu beheben.

  • Suchen Sie nach sachkundigen Codeprüfern, um nach Problemen zu suchen, die Sie möglicherweise übersehen haben.

  • Verwenden Sie Treiberüberprüfungen, und testen Sie Ihren Treiber mit mehreren Eingaben, einschließlich Eckfällen.