Freigeben über


Checkliste zur Treibersicherheit

Dieser Artikel enthält eine Prüfliste zur Treibersicherheit, die für Treiberentwickler konzipiert wurde, um das Risiko der Kompromittierung von Treibern zu verringern.

Übersicht über Treibersicherheit

Ein Sicherheitsfehler ist jeder Fehler, der es einem Angreifer ermöglicht, einen Treiber so kompromittiert, dass dies dazu führt, 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, wodurch die Möglichkeit entsteht, das gesamte Betriebssystem zu gefährden. Die meisten Entwickler konzentrieren sich bei der Arbeit an ihrem Treiber darauf, dass der Treiber ordnungsgemäß funktioniert, und nicht darauf, ob ein böswilliger Angreifer versucht, Sicherheitsrisiken innerhalb ihres Codes auszunutzen.

Nachdem ein Treiber veröffentlicht wurde, können Angreifer jedoch versuchen, ihn auf Sicherheitsfehler zu testen und diese zu identifizieren. Entwickler müssen diese Probleme während der Entwicklungs- und Implementierungsphase berücksichtigen, um die Wahrscheinlichkeit solcher Sicherheitsrisiken zu minimieren. Ziel ist es, alle bekannten Sicherheitsfehler zu beseitigen, bevor der Treiber veröffentlicht wird.

Die Entwicklung von Treibern mit erhöhter Sicherheit erfordert die Zusammenarbeit des Systemarchitekten (welcher bewusst potenzielle Bedrohungen für den Treiber abwägt), den Entwickler, der den Code implementiert (welcher defensiv übliche Vorgänge codiert, die die Quelle von Schwachstellen sein können), und das Testteam (das proaktiv versucht, Schwachstellen und Sicherheitsrisiken zu finden). Durch eine ordnungsgemäße Koordinierung all dieser Aktivitäten wird die Sicherheit des Treibers erheblich verbessert.

Neben der Vermeidung der Probleme, die mit einem Angriff auf einen Treiber verbunden sind, erhöhen viele der beschriebenen Schritte, wie eine genauere 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 nachstehenden Checkliste trägt dazu bei, all diese Ziele zu erreichen.

Sicherheitsprüfliste: Führen Sie die in den einzelnen Themen beschriebene Sicherheitsaufgabe aus.

Nichtmarkiertes Kontrollkästchen, das ein Element in der Sicherheitsprüfliste darstellt.Bestätigen, dass ein Kerneltreiber erforderlich ist

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

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

Nichtmarkiertes Kontrollkästchen, das ein Element in der Sicherheitsprüfliste darstellt.Testtreibercode nicht in der Produktion signieren

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

Nichtmarkiertes Kontrollkästchen, das ein Element in der Sicherheitsprüfliste darstellt.Befolgen von Richtlinien für sicheres Codieren von Treibern

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

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

Nichtmarkiertes Kontrollkästchen, das ein Element in der Sicherheitsprüfliste darstellt.Durchführen einer Peer-Codeüberprüfung

Nichtmarkiertes Kontrollkästchen, das ein Element in der Sicherheitsprüfliste darstellt.Verwalten der Steuerung des Treiberzugriffs

Nichtmarkiertes Kontrollkästchen, das ein Element in der Sicherheitsprüfliste darstellt.Verbessern der Geräteinstallationssicherheit

Nichtmarkiertes Kontrollkästchen, das ein Element in der Sicherheitsprüfliste darstellt.Ausführen der korrekten Signatur der Treiberversion

Nichtmarkiertes Kontrollkästchen, das ein Element in der Sicherheitsprüfliste darstellt.Verwenden von CodeQL zum Überprüfen des Treibercodes

Nichtmarkiertes Kontrollkästchen, das ein Element in der Sicherheitsprüfliste darstellt.Hinzufügen von SAL-Anmerkungen zum Treibercode

Nichtmarkiertes Kontrollkästchen, das ein Element in der Sicherheitsprüfliste darstellt.Verwenden der Treiberüberprüfung zum Überprüfen auf Sicherheitsrisiken

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

Nichtmarkiertes Kontrollkästchen, das ein Element in der Sicherheitsprüfliste darstellt.Überprüfen von Code mit den Tests des Hardwarekompatibilitätsprogramms

Nichtmarkiertes Kontrollkästchen, das ein Element in der Sicherheitsprüfliste darstellt.Überprüfen von Debuggertechniken und Erweiterungen

Nichtmarkiertes Kontrollkästchen, das ein Element in der Sicherheitsprüfliste darstellt.Verstehen, wie Treiber mithilfe des Microsoft Vulnerable and Malicious Driver Reporting Centers gemeldet werden

Nichtmarkiertes Kontrollkästchen, das ein Element in der Sicherheitsprüfliste darstellt.Überprüfen von Ressourcen für sicheres Codieren

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

Bestätigen, dass ein Kerneltreiber erforderlich ist

Sicherheitsprüflistenelement Nr. 1: Vergewissern Sie sich, dass ein Kerneltreiber erforderlich ist und dass ein weniger risikoreicher Ansatz, wie Windows-Dienste oder -Apps, nicht die bessere Option ist.

Treiber befinden sich im Windows-Kernel, und Probleme beim Ausführen im Kernel machen das gesamte Betriebssystem verwundbar. Wenn eine andere Option verfügbar ist, ist dies wahrscheinlich günstiger und birgt weniger Risiken als das Erstellen eines neuen Kerneltreibers. Weitere Informationen zur Verwendung der integrierten Windows-Treiber finden Sie unter Müssen Sie einen Treiber schreiben?.

Informationen zu Hintergrundaufgaben finden Sie unter Unterstützen Ihrer App mit Hintergrundaufgaben.

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

Verwenden der Treiberframeworks

Sicherheitsprüflistenelement Nr. 2: Verwenden Sie Treiberframeworks, um die Größe Ihres Codes zu reduzieren und die Zuverlässigkeit und Sicherheit zu erhöhen.

Verwenden Sie Windows-Treiberframeworks , um die Größe Ihres Codes zu verringern und die Zuverlässigkeit und Sicherheit zu erhöhen. Erste Schritte finden Sie unter Verwendung von WDF zum Entwickeln eines Treibers. Informationen zur Verwendung des Frameworktreibers für den Benutzermodus mit geringerem Risiko (lower risk user mode framework driver, UMDF) finden Sie unter Auswählen eines Treibermodells.

Das Schreiben eines altmodischen Treibers via einem Windows-Treibermodell (WDM) ist zeitaufwändiger, kostspieliger und erfordert fast immer das Nachbauen von Code, der in den Treiberframeworks verfügbar ist.

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

Steuern des Zugriffs auf reine Softwaretreiber

Sicherheitsprüflistenelement Nr. 3: Wenn ein reiner Softwaretreiber erstellt werden soll, muss eine zusätzliche Zugriffssteuerung implementiert werden.

Nur-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 die ursprünglich beabsichtigten verwendet werden, wodurch ein Angriffsvektor erstellt wird.

Da Nur-Software-Kerneltreiber zusätzliche Risiken enthalten, müssen sie auf bestimmte Hardware beschränkt sein (z. B. mithilfe 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 eine Übertaktungshilfsprogramm für ihre Systeme ermöglicht. Wenn dieser reine Softwaretreiber auf einem System von einem anderen OEM ausgeführt werden sollte, kann dies zu Systeminstabilität oder Beschädigung führen. 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, muss dieser Treiber eine weitere Methode finden, um zu verifizieren, dass er auf einem Fabrikam-System ausgeführt wird (z. B. durch Untersuchung der SMBIOS-Tabelle vor dem Aktivieren von Funktionen).

Testcode nicht in der Produktion signieren

Sicherheitsprüflistenelement Nr. 4: Entwicklung, Testen und Herstellung des Kerneltreibercodes nicht als Produktionscode signieren.

Kerneltreibercode, der für 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 als vertrauenswürdig eingestuft wird. Der richtige Vorgang zum Ausführen von gefährlichem Treibercode besteht darin, UEFI Secure Boot zu deaktivieren, BCD „TESTSIGNING“ zu aktivieren und den Entwicklungs-, Test- und Herstellungscode mithilfe eines nicht vertrauenswürdigen Zertifikats zu signieren (z. B. eines von makecert.exe).

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

Beispiele für gefährliches Verhalten sind die folgenden:

  • Bietet die Möglichkeit, beliebigen Kernel-, physischen oder Gerätespeicher dem Benutzermodus zuzuordnen.
  • Bietet die Möglichkeit, beliebigen Kernel-, physischen oder Gerätespeicher zu lesen oder zu schreiben, einschließlich Porteingabe/-Ausgabe (E/A).
  • Bereitstellen des Zugriffs auf Speicher, der die Windows-Zugriffssteuerung umgeht.
  • Bereitstellen der Möglichkeit, Hardware oder Firmware zu ändern, für deren Verwaltung der Treiber nicht entwickelt wurde.

Durchführen einer Bedrohungsanalyse

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

Unter Berücksichtigung der Sicherheit besteht eine übliche Vorgehensweise darin, spezifische Bedrohungsmodelle zu erstellen, die versuchen, die Arten von möglichen Angriffen zu beschreiben. Diese Technik ist beim Entwerfen eines Treibers nützlich, da er den Entwickler zwingt, potenzielle Angriffsvektoren für einen Treiber im Voraus zu berücksichtigen. Nachdem der Identifizierung potenzieller Bedrohungen, kann ein Treiberentwickler dann Mittel zur Verteidigung gegen diese Bedrohungen in Betracht ziehen, um die Gesamtsicherheit der Treiberkomponente zu stärken.

Dieser Artikel enthält treiberspezifische Anleitungen zum Erstellen eines einfachen Bedrohungsmodells: Bedrohungsmodellierung für Treiber. Der Artikel enthält ein Beispiel für ein Bedrohungsmodelldiagramm für Treiber, 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 von Richtlinien für sicheres Codieren von Treibern

Sicherheitsprüflistenelement Nr. 6: Überprüfen Sie Ihren Code, und entfernen Sie alle bekannten Coderisiken.

Die Kernaktivität beim Erstellen sicherer Treiber besteht darin, Bereiche im Code zu identifizieren, die geändert werden müssen, um bekannte Softwarerisiken zu vermeiden. Viele dieser bekannten Softwarerisiken erfordern, die Verwendung von Arbeitsspeicher streng nachzuverfolgen, um Probleme mit anderen Quellen zu vermeiden, die überschreiben oder anderweitig die von Ihrem Treiber verwendeten Speicherspeicherorten kompromittieren.

Codescantools wie CodeQL und treiberspezifische Tests können verwendet werden, um einige, aber nicht alle dieser Sicherheitsrisiken zu finden. Diese Tools und Tests werden weiter unten in diesem Thema beschrieben.

Arbeitsspeicherpuffer

Verwenden der geeigneten Methode für den Zugriff auf Datenpuffer mit IOCTLs

Eine der Hauptaufgaben eines Windows-Treibers ist das Übertragen von Daten zwischen Benutzermodusanwendungen 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
METHOD_BUFFERED Empfohlen für die meisten Situationen 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 der direkten E/A-Vorgänge
METHOD_NEITHER Nach Möglichkeit vermeiden Verwenden weder gepufferter noch direkter E/A

Im Allgemeinen wird gepufferte E/A empfohlen, da sie die sichersten Puffermethoden bereitstellt. Aber auch bei der Verwendung von gepufferten E/A-Vorgängen bestehen Risiken, z. B. eingebettete Zeiger, die entschärft werden müssen.

Weitere Informationen zum Arbeiten mit Puffern in IOCTLs finden Sie unter Methoden für den Zugriff auf Datenpuffer.

Fehler bei Verwendung von IOCTL gepufferten E/A

  • Ü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 von Puffern mit variabler Länge.

  • Achten Sie bei Der Verwendung von gepufferten E/A-Vorgängen darauf, die richtige Länge für den OutputBuffer im Strukturinformationsfeld IO_STATUS_BLOCK zurückzugeben. Geben Sie die Länge nicht nur direkt aus einer READ-Anforderung zurück. Nehmen wir beispielsweise eine Situation, in der die zurückgegebenen Daten aus dem Benutzerbereich angeben, dass ein 4K-Puffer vorhanden ist. Wenn der Treiber tatsächlich nur 200 Byte zurückgeben soll, aber stattdessen nur 4K im Informationsfeld zurückgibt, ist eine Sicherheitslücke zur Offenlegung von Informationen aufgetreten. Dieses Problem tritt auf, da in früheren Versionen von Windows der Puffer, den der E/A-Manager für gepufferte E/A verwendet, nicht null ist. Daher erhält die Benutzer-App die ursprünglichen 200 Byte von Daten plus 4K-200 Bytes aller Elemente im Puffer (nicht ausgelagerte Poolinhalte). Dieses Szenario kann bei allen Verwendungen von gepufferten E/A und nicht nur bei IOCTLs auftreten.

Fehler in direkten IOCTL E/A-Vorgängen

Korrekte Behandlung von Puffern mit Null-Länge. Weitere Informationen finden Sie unter Fehler in direkten E/A-Vorgängen.

Fehler beim Verweisen auf Benutzerraumadressen

MSR-modellspezifische Registerlese- und Schreibvorgänge

Intrinsische Compiler, wie __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 Bewährte Methoden der Entwicklungssicherheit für Windows-Treiberentwickler.

TOCTOU-Sicherheitsrisiken

Es gibt eine potenziellen Sicherheitsanfälligkeit beim Zeitpunkt der Überprüfung gegenüber der Verwendungszeit (time of check to time of use, TOCTOU) bei Verwendung direkter E/A-Vorgänge (für IOCTLs oder für Lese-/Schreibzugriff). Beachten Sie, dass wenn der Treiber auf den Benutzerdatenpuffer zugreift, der Benutzer gleichzeitig darauf zugreifen kann.

Um dieses Risiko zu verwalten, kopieren Sie alle Parameter, die vom Benutzerdatenpuffer im Speicher validiert werden müssen, auf welchen ausschließlich über den Kernelmodus zugegriffen werden kann (wie der Stapel oder Pool). Sobald auf die Daten von der Benutzeranwendung nicht zugegriffen werden kann, validieren Sie die Daten, und bearbeiten Sie dann die Daten, die übergeben wurden.

Der Treibercode muss den Arbeitsspeicher korrekt verwenden.

  • Alle Treiberpoolzuweisungen müssen sich im nicht-ausführbaren Pool (NX) befinden. Die Verwendung von NX-Speicherpools ist inhärent sicherer als die Verwendung von ausführbaren nicht ausgelagerten Pools (NP) und bietet einen besseren Schutz vor Überlaufangriffen.

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

Um Treibern die Unterstützung der HVCI-Virtualisierung zu ermöglichen, gibt es zusätzliche Speicheranforderungen. Weitere Informationen finden Sie unter Implementieren von HVCI-kompatiblem Code weiter unten in diesem Artikel.

Ziehpunkte

Geräteobjekte

IRPs

WDF und IRPs

Ein Vorteil der Verwendung von WDF besteht darin, 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 Framework-Anforderungsobjekte, 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 direkten E/A-Vorgängen

Fehler in direkten E/A-Vorgängen

Sicherheitsprobleme für E/A-Kontrollcodes

Erwägen Sie die Validierung von Werten, die einem IRP zugeordnet sind, z. B. Pufferadressen und Längen.

Wenn Sie sich für die Verwendung von „Keine E/A“ entschieden haben, sollten Sie beachten, dass im Gegensatz zu Lesen und Schreiben und im Gegensatz zu Gepufferte E/A und Direkte E/A bei der Verwendung von Keine E/A-IOCTL die Pufferzeiger und -längen nicht vom E/A-Manager validiert werden.

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

Ein Treiber darf niemals ein IRP mit einem Statuswert von STATUS_SUCCESS abschließen, es sei denn, er unterstützt und verarbeitet das IRP. Informationen zu den richtigen Methoden zum Behandeln von IRP-Vervollständigungsvorgängen finden Sie unter Abschließen von IRPs.

Verwalten des ausstehenden Zustands von Treiber-IRP

Der Treiber sollte das ausstehende IRP markieren, bevor das IRP gespeichert wird, und er sollte sowohl den Aufruf von IoMarkIrpPending als auch die Zuordnung in einer Interlocked-Sequenz einschließen. Weitere Informationen finden Sie unter Fehler beim Überprüfen des Zustands eines Treibers und anhalten 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 behandelt, können lange unbemerkt bleiben, da dieser Code in der Regel nicht häufig in einem ausgeführten System ausgeführt wird. Lesen und verstehen Sie alle Informationen, die unter IRPs abbrechen bereitgestellt werden. Achten Sie besonders auf die Synchronisierung von IRP-Abbrüchen und Punkte, die beim Abbrechen von IRPs berücksichtigt werden sollten.

Eine empfohlene Möglichkeit, um die Synchronisierungsprobleme zu minimieren, die mit Abbruchvorgängen verbunden sind, besteht darin, eine abbruchsichere IRP-Warteschlange zu implementieren.

Ordnungsgemäße Behandlung von IRP-Bereinigungs- und Schließvorgängen

Achten Sie darauf, dass Sie den Unterschied zwischen den Anforderungen IRP_MJ_CLEANUP und IRP_MJ_CLOSE verstehen. Bereinigungsanforderungen treffen ein, nachdem eine Anwendung alle Handles für ein Dateiobjekt schließt, aber manchmal bevor alle E/A-Anforderungen abgeschlossen wurden. Schließanforderungen treffen ein, nachdem alle E/A-Anforderungen für das Dateiobjekt abgeschlossen oder abgebrochen wurden. Weitere Informationen finden Sie in den folgenden Artikeln:

DispatchCreate-, DispatchClose- und DispatchCreateClose-Routinen

DispatchCleanup-Routinen

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

Weitere Informationen zum ordnungsgemäßen Behandeln von IRPs finden Sie unter Zusätzliche Fehler beim Behandeln von IRPs.

Andere Sicherheitsprobleme

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

  • Treiber müssen verschiedene Benutzermodus- und Kernel-zu-Kernel-E/A-Anforderungen ordnungsgemäß verarbeiten.

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

Sichere Funktionen nutzen

Zusätzliche Coderisiken

Zusätzlich zu den möglichen Sicherheitsrisiken, die hier behandelt werden, enthält dieser Artikel zusätzliche Informationen zur Verbesserung der Sicherheit des Kernelmodustreibercodes: Erstellen von zuverlässigen Kernelmodustreibern.

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

Verwalten der Steuerung des Treiberzugriffs

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

Verwalten der Steuerung des Treiberzugriffs – WDF

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

  • Geräteobjekte nur bei Bedarf benennen. Benannte Geräteobjekte sind in der Regel nur aus Legacy-Gründen erforderlich, z. B. wenn Sie über eine Anwendung verfügen, die erwartet, dass das Gerät mit einem bestimmten Namen geöffnet wird, oder wenn Sie ein nicht-PNP-Gerät/Steuerungsgerät verwenden. Beachten Sie, dass WDF-Treiber ihre PnP-Geräte-FDO nicht benennen müssen, um eine symbolische Verknüpfung mit WdfDeviceCreateSymbolicLink zu erstellen.

  • Sicherer Zugriff auf Geräteobjekte und Schnittstellen.

Damit Anwendungen oder andere WDF-Treiber auf PnP-Geräte-PDO zugreifen können, sollten Sie Geräteschnittstellen verwenden. Weitere Informationen finden Sie unter Geräteschnittstellen verwenden. Eine Geräteschnittstelle dient als symbolische Verknüpfung mit dem PDO Ihres Gerätestapels.

Eine der besseren Möglichkeiten zum Steuern des Zugriffs auf die PDO besteht darin, eine SDDL-Zeichenfolge in Ihrem INF anzugeben. Wenn sich die SDDL-Zeichenfolge nicht in der INF-Datei befindet, wendet Windows einen Standardsicherheitsdeskriptor an. Weitere Informationen finden Sie unter Sichern von Geräteobjekten und SDDL für Geräteobjekte.

Weitere Informationen zur Zugriffssteuerung finden Sie in den folgenden Artikeln:

Steuern des Gerätezugriffs in KMDF-Treibern

Namen, Sicherheitsdeskriptoren und Geräteklassen – Erstellen barrierefreier Geräteobjekte... and SAFE aus dem Januar 2017 NT Insider Newsletter veröffentlicht von OSR.

Verwalten der Steuerung des Treiberzugriffs – WDM

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

Weitere Informationen finden Sie in den folgenden Artikeln:

Steuern des Gerätezugriffs

Steuern des Geräte-Namespace-Zugriffs

Windows-Sicherheitsmodell für Treiberentwickler

Sicherheits-IDs (SIDs)-Risikohierarchie

Im folgenden Abschnitt wird die Risikohierarchie der allgemeinen 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, wenn Aufrufer mit geringeren Privilegien auf den Kernel zugreifen dürfen, das Coderisiko erhöht wird. In diesem zusammenfassenden Diagramm sieht man eine Erhöhung des Risikos, da Sie SIDs mit niedrigeren Privilegien Zugriff auf Ihre Treiberfunktionen gewähren.

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

Konfigurieren Sie nach dem allgemeinen Sicherheitsprinzip der geringsten Rechte nur die Mindestzugriffsebene, die für die Funktion des Treibers erforderlich ist.

WDM Granular IOCTL-Sicherheitskontrolle

Um die Sicherheit weiter zu verwalten, wenn IOCTLs von Benutzermodus-Aufrufern gesendet werden, kann der Treibercode die IoValidateDeviceIoControlAccess-Funktion enthalten. Mit dieser Funktion kann ein Treiber Zugriffsrechte überprüfen. Wenn ein IOCTL empfangen wird, kann ein Treiber IoValidateDeviceIoControlAccess aufrufen und FILE_READ_ACCESS, FILE_WRITE_ACCESS oder beides angeben.

Die Implementierung präziser IOCTL-Sicherheitskontrollen 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-Steuercodes

Implementieren von HVCI-kompatiblem Code

Sicherheitsprüflistenelement Nr. 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 Entscheidungsfindungsfunktion der Codeintegrität (Code Integrity, CI) vom Rest des Betriebssystems zu isolieren. Wenn virtualisierungsbasierte Sicherheit zum Isolieren von CI verwendet wird, kann der Kernelspeicher nur durch 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 folgendes ausführt:

  • Aktiviert NX standardmäßig
  • Verwendet NX-APIs/Flags für die Speicherzuweisung (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 Datendateien nicht als ausführbare Datei
  • Die Abschnittsausrichtung ist ein Vielfaches von 0x1000 (PAGE_SIZE). Zum Beispiel, DRIVER_ALIGNMENT=0x1000

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

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

Befolgen von bewährten Methoden für technologiespezifischen Code

Sicherheitsprüflistenelement Nr. 9: Überprüfen Sie die folgenden technologiespezifischen Anleitungen für Ihren Treiber.

Dateisysteme

Weitere Informationen zur Dateisystemtreiber-Sicherheit finden Sie in den folgenden Artikeln:

Einführung in die Sicherheit von Dateisystemen

Dateisystemsicherheitsprobleme

Sicherheitsfeatures für Dateisysteme

Koexistenz mit anderen Dateisystemfiltertreibern

NDIS – Netzwerk

Informationen zur NDIS-Treibersicherheit finden Sie unter Sicherheitsprobleme für Netzwerktreiber.

Anzeige

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

Drucker

Informationen zur Sicherheit des Druckertreibers finden Sie unter Sicherheitsaspekte des V4-Druckertreibers.

Sicherheitsprobleme bei Windows Image Acquisition (WIA)-Treibern

Informationen zur WIA-Sicherheit finden Sie unter Sicherheitsprobleme bei Windows Image Acquisition (WIA)-Treibern.

Verbessern der Geräteinstallationssicherheit

Sicherheitsprüflistenelement Nr. 10: Überprüfen Sie die Anleitungen zum Erstellen und Installieren von Treibern, um sicherzustellen, dass Sie bewährte Methoden verwenden.

Wenn Sie den Code erstellen, der den Treiber installiert, müssen Sie sicherstellen, dass die Installation Ihres Geräts immer sicher ausgeführt wird. Eine sichere Geräteinstallation beinhaltet folgende Aktionen:

  • Schränkt den Zugriff auf das Gerät und die zugehörigen Geräteschnittstellenklassen ein
  • Schränkt den Zugriff auf die Treiberdienste, die für das Gerät erstellt wurden, ein
  • Schützt Treiberdateien vor Änderung oder Löschung
  • Beschränkt den Zugriff auf die Registrierungseinträge des Geräts
  • Beschränkt den Zugriff auf die WMI-Klassen des Geräts
  • 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 Peer-Codeüberprüfung

Sicherheitsprüflistenelement Nr. 11: Durchführen einer Peer-Codeüberprüfung, um nach Problemen zu suchen, die von den anderen Tools und Prozessen nicht angezeigt werden

Suchen Sie wissenskundige Codeprüfer, um nach Problemen zu suchen, die Sie möglicherweise verpasst haben. Eine zweites Paar Augen wird häufig Probleme sehen, die Sie möglicherweise übersehen haben.

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

Ausführen der korrekten Signatur der Treiberversion

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

Bevor Sie ein Treiberpaket für die Öffentlichkeit freigeben, empfehlen wir, das Paket zur Zertifizierung zu übermitteln. Weitere Informationen finden Sie unter Testen der Leistung und Kompatibilität, Erste Schritte mit dem Hardwareprogramm, den Hardware-Dashboarddiensten und der Attestierung der Signierung eines Kerneltreibers für die öffentliche Freigabe.

Verwenden von CodeQL zum Überprüfen des Treibercodes

Sicherheitsprüflistenelement Nr. 13: Verwenden Sie CodeQL, um Sicherheitsrisiken im Treibercode zu überprüfen.

CodeQL von GitHub ist eine semantische Code-Analyse-Engine. Die Kombination aus einer umfangreichen Suite von Sicherheitsabfragen und einer robusten Plattform macht CodeQL zu einem unschätzbaren Tool zur Sicherung von Treibercode. Weitere Informationen finden Sie unter CodeQL und dem Logotest für statische Tools.

Hinzufügen von SAL-Anmerkungen zum Treibercode

Sicherheitsprüflistenelement Nr. 14: Fügen Sie SAL-Anmerkungen in Ihrem Treibercode hinzu.

Die Quellcodeanmerkungssprache (Source-Code Annotation Language, SAL) bietet eine Reihe von Anmerkungen, mit denen Sie beschreiben können, wie eine Funktion ihre Parameter verwendet, welche Annahmen sie darüber macht und welche Garantien sie bei ihrer Beendigung gibt. Die Anmerkungen sind in der Headerdatei sal.h definiert. Visual Studio-Codeanalyse für C++ verwendet SAL-Anmerkungen, um die Analyse von Funktionen zu ändern. Weitere Informationen zur Entwicklung von SAL 2.0 für Windows-Treiber finden Sie unter SAL 2.0 Anmerkungen für Windows-Treiber und Verwenden von SAL-Anmerkungen zur Reduzierung von C/C++-Codefehlern.

Allgemeine Informationen zu SAL finden Sie in diesem Artikel, der über OSR zur Verfügung steht. https://www.osr.com/blog/2015/02/23/sal-annotations-dont-hate-im-beautiful/

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

Sicherheitsprüflistenelement Nr. 15: Verwenden Sie Treiberüberprüfung, um Sicherheitsrisiken im Treibercode zu überprüfen.

Treiberüberprüfung (Driver Verifier, DV) verwendet eine Reihe von Schnittstellenregeln und ein Modell des Betriebssystems, um zu ermitteln, ob der Treiber ordnungsgemäß mit dem Windows-Betriebssystem interagiert. DV findet Fehler im Treibercode, die auf potenzielle Fehler in Treibern verweisen könnten.

DV ermöglicht live-Tests 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. Die Treiberüberprüfung kann die Windows-Treiber einer Vielzahl von Belastungstests und Überprüfungen unterziehen, um falsches Verhalten zu ermitteln. Weitere Informationen finden Sie unter Treiberüberprüfung.

Beachten Sie, dass nur bestimmte Arten von Treibern von DV unterstützt werden. Weitere Informationen zu den Treibern, die DV überprüfen kann, finden Sie unter Unterstützte Treiber. Auf den folgenden Seiten finden Sie Informationen zu den DV-Tests, die für den Treibertyp verfügbar sind, mit dem Sie arbeiten.

Um sich mit DV 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).

Überprüfen von Code mit BinSkim Binary Analyzer

Sicherheitsprüflistenelement Nr. 16: Führen Sie die folgenden Schritte aus, um mit BinSkim zu überprüfen, ob Kompilierungs- und Buildoptionen so konfiguriert sind, dass sie bekannte Sicherheitsprobleme minimieren.

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

BinSkim sucht nach:

  • Verwendung veralteter Compilertoolsätze – Binärdateien sollten nach Möglichkeit anhand der neuesten Compilertoolsätze 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 unter anderem vom Betriebssystem bereitgestellte Sicherheitsminderungen zu ermöglichen und Berichte über Compilerfehler und umsetzbare Warnungen zu maximieren.
  • Signaturprobleme – Signierte Binärdateien sollten mit kryptografisch starken Algorithmen signiert werden.

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

Weitere Informationen zu BinSkim finden Sie im BinSkim-Benutzerhandbuch.

Führen Sie die folgenden Schritte aus, um zu validieren, dass die Sicherheitskompilierungsoptionen im Code, den Sie versenden, ordnungsgemäß konfiguriert sind.

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

  2. Bestätigen Sie, dass Visual Studio installiert ist. Informationen über das Herunterladen und Installieren von Visual Studio finden Sie unter Installieren von Visual Studio.

  3. Es gibt eine Reihe von Optionen zum Herunterladen von BinSkim, wie ein NuGet-Paket. In diesem Beispiel verwenden wir die Git-Klonoption, um sie von hier herunterzuladen: 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 zu diesem soeben erstellten Verzeichnis.

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

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

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

    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 zum Ausgabeverzeichnis des Win-x64-Builds.

    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.

...

Den Symbolpfad festlegen

Wenn Sie den gesamten Code, den Sie analysieren, auf demselben Computer erstellen, auf dem BinSkim ausgeführt wird, müssen Sie den Symbolpfad normalerweise nicht festlegen. Dies liegt daran, dass Ihre Symboldateien lokal verfügbar sind, wo Sie kompilieren. 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 zur Symboldateisuche hinzuzufügen. Wenn Ihr Code auf eine kompilierte Binärdatei verweist, die nicht Teil des Codes ist, kann der Window-Debugger-Sympath verwendet werden, um die Sicherheit dieser Codeabhängigkeiten zu überprüfen. Wenn Sie ein Problem in diesen Abhängigkeiten feststellen, können Sie sie möglicherweise nicht beheben. Es kann jedoch hilfreich sein, jedes mögliche Sicherheitsrisiko zu kennen, das Sie akzeptieren, indem Sie diese Abhängigkeiten annehmen.

Tipp

Fügen Sie beim Hinzufügen eines Symbolpfads (der auf einen Netzwerksymbolserver verweist) einen lokalen Cachespeicherort hinzu, um einen lokalen Pfad zum Zwischenspeichern der Symbole anzugeben. Ansonsten könnte die Leistung von BinSkim erheblich beeinträchtigt werden. 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 Treiber-Binärdatei zu analysieren. Aktualisieren Sie den Zielpfad, um auf ihre kompilierte Treiber-.sys-Datei zu verweisen.

    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 die ausführliche Option wie folgt hinzu, um weitere Informationen zu erfahren.

    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 --ausführlich erzeugt für jede Prüfung explizite Pass-/Fail-Ergebnisse. Wenn Sie „ausführlich“ nicht bereitstellen, sehen Sie nur die Fehler, die BinSkim erkennt. Die Option „--ausführlich“ wird in der Regel nicht für tatsächliche Automatisierungssysteme empfohlen, da die Größe der Protokolldateien zunimmt und es schwieriger wird, einzelne Fehler zu erkennen, wenn sie auftreten, sie inmitten einer großen Anzahl von „pass“-Ergebnissen eingebettet sind.

  3. Überprüfen Sie die Befehlsausgabe, um nach möglichen Problemen zu suchen. Diese Beispielausgabe zeigt drei bestandene Tests. Weitere Informationen zu den Regeln, wie 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 der 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 den 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 nicht unter Bestimmte Warnungen deaktivieren ausschließen möchten.

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

Die Standardkompilierungsoptionen 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 weicht nach der Verwendung des vorkompilierten Headers ab

C4627 - 'description': wird übersprungen, wenn nach Verwendung des vorkompilierten Headers gesucht wird

C4986 - 'declaration': Ausnahmespezifikation stimmt mit der vorherigen Deklaration nicht überein

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

Überprüfen von Code mit den Tests des Hardwarekompatibilitätsprogramms

Sicherheitsprüflistenelement Nr. 17: Verwenden Sie die sicherheitsrelevanten Hardwarekompatibilitätsprogrammtests, um nach Sicherheitsproblemen zu suchen.

Das Hardwarekompatibilitätsprogramm enthält Sicherheitstests, die verwendet werden können, um nach Coderisiken 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 auszuführen und nach Schwächen zu suchen. Allgemeine Informationen zu den Device Fundamentals Tests und zum Hardwarekompatibilitätsprogramm finden Sie unter Windows Hardware Lab Kit.

Die folgenden Tests sind Beispieltests, die hilfreich sein können, um Treibercode auf einige Verhaltensweisen zu überprüfen, die mit codebedingten Sicherheitsrisiken zusammenhängen:

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 die Kernelsynchronisierungsverzögerung verwenden, die in der Treiberüberprüfung enthalten ist.

Bei CHAOS-Tests (Concurrent Hardware and Operating System, Hardware und Betriebssystem gleichzeitig) werden verschiedene PnP-Treibertests, Gerätetreiber-Fuzz-Tests und Tests des Stromversorgungssystems gleichzeitig ausgeführt. Weitere Informationen finden Sie unter CHAOS Tests (Device Fundamentals).

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

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

Benutzerdefinierte und domänenspezifische Testtools

Erwägen Sie die Entwicklung benutzerdefinierter domänenspezifischer Sicherheitstests. Um zusätzliche Tests zu entwickeln, sammeln Sie Eingaben von den ursprünglichen Designern der Software sowie nichtverwandte Entwicklungsressourcen, die mit dem spezifischen Typ des zu entwickelnden Treibers vertraut sind, und einen oder mehrere Personen, die mit der Analyse und Verhinderung von Sicherheitsangriffen vertraut sind.

Überprüfen von Debuggertechniken und Erweiterungen

Sicherheitsprüflistenelement Nr. 18: Überprüfen Sie diese Debuggertools, und ziehen Sie deren Verwendung in Ihrem Entwicklungsdebuggingworkflow in Betracht.

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

Die Erweiterung !token zeigt eine formatierte Ansicht eines Sicherheits-Token-Objekts an. Weitere Informationen finden Sie unter !token.

Die Erweiterung !tokenfields zeigt die Namen und Offsets der Felder innerhalb des Zugriffstokenobjekts an (die TOKEN-Struktur). 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 Vulnerable and Malicious Driver Reporting Center

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

Das Reporting Center kann Windows-Treiber scannen und analysieren, die für x86- und x64-Architekturen erstellt wurden. Anfällige und böswillige gescannte Treiber durch das Vulnerable Driver-Team von Microsoft zur Analyse und Untersuchung gekennzeichnet. Nachdem anfällige Treiber bestätigt wurden, erscheint eine entsprechende Benachrichtigung, sie werden der Blockliste für anfällige Treiber hinzugefügt. Weitere Informationen hierzu finden Sie unter Von Microsoft empfohlene Treiberblockregeln. Diese Regeln werden standardmäßig auf Geräte mit hypervisorgeschützter Codeintegrität (HVCI) und Windows 10 im S-Modus angewendet.

Überprüfen von Ressourcen für sicheres Codieren

Sicherheitsprüflistenelement Nr. 19: Überprüfen Sie diese Ressourcen, um Ihr Verständnis der bewährten Methoden für sicheres Codieren zu erweitern, die für Treiberentwickler gelten.

Überprüfen Sie die folgenden Ressourcen, um mehr über Treibersicherheit zu erfahren:

Richtlinien für das Codieren sicherer Kernelmodustreiber

Erstellen von zuverlässigen Kernelmodustreibern

Organisationen für sicheres Codieren

Carnegie Mellon University SEI CERT

Carnegie Mellon University SEI CERT C Coding Standard: Regeln für die Entwicklung sicherer, zuverlässiger und gesicherter Systeme (2016 Edition).

MITRE – Schwachstellen, die vom CERT C Secure Coding Standard behandelt werden

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

SAFECode – https://safecode.org/

CISA-Ressourcen

OSR

OSR bietet Training für die Treiberentwicklung und Beratungsleistungen an. In diesen Artikeln aus dem OSR-Newsletter werden Treibersicherheitsprobleme hervorgehoben.

Namen, Sicherheitsdeskriptoren und Geräteklassen – Erstellen barrierefreier Geräteobjekte... and SAFE

You've Gotta Use Protection -- Inside Driver & Device Security

Locking Down Drivers - A Survey of Techniques

Meltdown and Spectre: What about drivers?

Fallstudie

Von Warnung zu Treiberrisiken: Microsoft Defender ATP-Untersuchung deckt Berechtigungseskalationsfehler auf

Bücher

24 tödliche Sünden der Softwaresicherheit : Programmierfehler und wie sie behoben werden von Michael Howard, David LeBlanc und John Viega

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

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

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

Sicheres Codieren in C und C++ (SEI Series in Software Engineering) 2. Edition, Robert C. Seacord

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

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

Training

Windows-Treiberschulungen stehen von Anbietern wie den folgenden zur Verfügung:

Onlineschulungen für sicheres Codieren stehen von einer Vielzahl von Quellen zur Verfügung. Dieser Kurs ist zum Beispiel bei coursera verfügbar unter:

Identifizieren von Sicherheitsrisiken in 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 der wichtigsten Punkte:

  • Treiber befinden sich im Windows-Kernel, und Probleme beim Ausführen im Kernel machen das gesamte Betriebssystem verwundbar. Achten Sie daher besonders auf die Sicherheit der Treiber und entwickeln Sie ein sicheres Design.

  • Anwenden des Prinzips der geringsten Rechte:

    a. Verwenden Sie eine strenge SDDL-Zeichenfolge zum Einschränken des Zugriffs auf den Treiber

    b. Weitere Einschränkung einzelner IOCTL-Elemente

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

  • Seien Sie vorsichtig im Hinblick auf eingebettete Zeiger, die vom Benutzermodus aus übergeben werden. Sie müssen überprüft werden, auf sie muss von innerhalb des Try-Except-Vorgangs zugegriffen werden, und sie sind anfällig für ToCToU-Probleme, es sei denn, der Wert des Puffers wird erfasst und verglichen.

  • Wenn Sie unsicher sind, verwenden Sie METHOD_BUFFERED als IOCTL-Puffermethode.

  • Verwenden Sie Hilfsprogramme zum Scannen, um nach bekannten Coderisiken zu suchen und identifizierte Probleme zu beheben.

  • Suchen Sie wissenskundige Codeprüfer, um nach Problemen zu suchen, die Sie möglicherweise verpasst haben.

  • Verwenden Sie Treiberprüfer, und testen Sie Ihren Treiber mit mehreren Eingaben, einschließlich Ausnahmefällen.