Dieses Projekt verwendet MSVC C ++ STL in einem Windows -Kernel -Treiber. In dieser Lösung wird jxystl.lib als Kernel-abgestimmt, Pooltyp/Tag-AWARE, Vorlagenbibliothek und MSVC-Implementierung implementiert. Was unter der Motorhaube den MSVC C ++ Stl verwendet.
# include < wdm.h >
# include < jxy/string.hpp >
extern " C "
NTSTATUS DriverEntry (
PDRIVER_OBJECT DriverObject,
PUNICODE_STRING RegistryPath)
{
jxy::wstring<PagedPool, ' 0GAT ' > helloWorld;
try
{
helloWorld. assign ( L" Hello, World! " );
}
catch ( const std::bad_alloc&)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
return STATUS_SUCCESS;
} 1: kd> dv
DriverObject = 0xffffca83`5380d300 Driver "Driverstlkrn"
RegistryPath = 0xffffca83`5227f000 "REGISTRYMACHINESYSTEMControlSet001Servicesstlkrn"
helloWorld = "Hello, World!"
| Standard | Unterstützt | Notizen |
|---|---|---|
| CPP11 | NEIN | ungetestet, deine Kilomelme meine variieren |
| CPP14 | Ja | |
| CPP17 | Ja | |
| CPP20 | Ja |
Der Testfahrer in dieser Lösung, stdtest.sys , beherbergt die Unit -Tests für das Projekt. Unit -Tests werden im Kernel mit Treiberprüfer durchgeführt. Das Unit -Test -Framework ist bloße Knochen, aber für das Training jxystl.lib ausreichend.
Ein anderer Treiber, der in dieser Lösung implementiert ist, stdkrn.sys , setzt jxystl.lib in einem praktischen Szenario ein. Es verwendet verschiedene std -Namespace -Einrichtungen und -Container (verpackt unter dem jxy -Namespace). Dieser Treiber registriert sich für Prozess-, Thread- und Bildbenachrichtigungen; Verwendet dann moderne C ++, um Prozesskontexte, Threadkontexte und Modulkontexte zu verfolgen.
vcrtlDurch Ausnahmebedingung können C ++ - Objekte sich entspannen, wenn eine Ausnahme ausgelöst wird. Dies ist ein Kernmerkmal von C ++, das den Kernel -Treibern wenig Aufmerksamkeit erregt. Microsoft unterstützt keine C ++ - Ausnahmen für Kernel -Treiber.
Die C ++ - Ausnahmehandhabung wird durch Avakars VCRTL -Libraray ermöglicht. Dieses Projekt wäre ohne Avakars großartigen Beitrag viel mehr Arbeit gewesen. Informationen zum Ausnahmehandling in Windows -Treibern finden Sie in Avakars VCRTL GitHub. Außerdem enthält diese Seite hervorragende Details zur Ausnahmebehandlung auf AMD64.
jxystlWindows -Kernelzuweisungen sind einem Speicherpool zugeordnet. Darüber hinaus ist das Pool -Tagging in den Windows -Kernel eingebaut. Pool -Tagging erleichtert die Verfolgung von Zuteilungen, die durch Fahrer vorgenommen wurden. Diese Tagging -Einrichtung ermöglicht das Debuggen und Überwachung von Zuweisungen.
Der jxy -Namespace ermöglicht in dieser Lösung die Entwicklung von Windows -Treibern mithilfe der std -Namespace -Objekte mit Pool -Tipps und -markierung.
Die Bibliothek entscheidet sich dafür, "globale" new / delete Betreiber nicht zu implementieren. Es implementiert nur new / delete Operatoren mit Pool -Tipp- und Tagging -Funktionen. Dies erfordert die Angabe von Pooltypen und -Tags. Wenn einige Funktionen verwendet werden, würde dies einen "globalen Allocator" erfordern, wird er nicht verknüpft. Dies ist eine absichtliche Designentscheidung, so dass keine globalen Allokatoren verwendet werden. Alle Zuweisungen müssen einen Pooltyp und ein Tag -Tag angeben.
Der jxy -Namespace implementiert Allokatoren und Löscher, die dem Standard für die Verwendung in Vorlagenbehältern entsprechen. Diese Allokatoren und Löschgeräte sind Pooltypen/Tags bewusst. Sie müssen den Pooltyp und das Tag -Tag angeben und Conversions/Wiederherstellungen über Werkzeugtypen und Tags hinweg verhindern. Sie sollten anstelle der STL -Allocatoren verwendet werden.
jxy::allocator<T, PagedPool, ' 0GAT ' >;
jxy::default_delete<T, PagedPool, ' 0GAT ' >; jxystl.lib implementiert die notwendige "Füll" -Funktionalität für die Verwendung von MSVC -STL -Containern. Die Implementierungen (in msvcfill.cpp ) sind für den Kernel rücksichtsvoll. Mit dieser Funktionalität können die MSVC STL-Container mit kernelgerechter Funktionalität verknüpfen. Dies bedeutet auch, dass wenn eine std -Containerfunktionalität verwendet wird, die keine "Füllen" -Funktionalität hat, der Linker fällt aus. Dies ist eine absichtliche Designentscheidung, so dass Implementierungen zur Verwendung im Kernel durchdacht werden.
CRT -Initialisierung und ATEXIT -Funktionalität werden absichtlich nicht unterstützt. Die Reihenfolge der CRT-Initialisierung ist unklar und nicht offen. Wenn ein Kernel -Treiber die globalen Daten lädt, sollte eindeutig eingerichtet und während der Fahrerlast und des Entladens abgerissen werden. Die globale CRT-Initialisierung "versteckt" diese Initialisierung auf nicht offensichtliche Weise. Darüber hinaus wird die CRT -ATEXIT -Funktionalität nicht unterstützt. Emission der notwendigen Synchronisation, die die lokale statische Initialisierung von C ++ - Objekten ermöglicht, ermöglicht den Compiler nicht. Und würde eine nicht offene Synchronisation im Kernel einführen. Das Fehlen einer CRT -Initialisierung und die Unterstützung von ATEXIT ist eine absichtliche Entwurfsentscheidung. Ich empfehle dringend, es bei der Entwicklung von Kernelfahrern zu vermeiden.
Zum Beispiel der jxy -Namespace "Wraps" std::vector und erzwingt die Verwendung von Pooltypen und -Tags:
namespace jxy
{
template < typename T,
POOL_TYPE t_PoolType,
ULONG t_PoolTag,
typename TAllocator = jxy::allocator<T, t_PoolType, t_PoolTag>>
using vector = std::vector<T, TAllocator>;
}
jxy::vector< int , PagedPool, ' 0GAT ' > integers; stlkrn!DriverEntry+0xea:
0: kd> dx integers
integers : { size=10 } [Type: std::vector<int,jxy::details::allocator<int,1,809976148> >]
[<Raw View>] [Type: std::vector<int,jxy::details::allocator<int,1,809976148> >]
[capacity] : 10
[allocator] : {...} [Type: std::_Compressed_pair<jxy::details::allocator<int,1,809976148>,std::_Vector_val<std::_Simple_types<int> >,1>]
[0] : 1 [Type: int]
[1] : 2 [Type: int]
[2] : 3 [Type: int]
[3] : 4 [Type: int]
[4] : 5 [Type: int]
[5] : 6 [Type: int]
[6] : 7 [Type: int]
[7] : 8 [Type: int]
[8] : 9 [Type: int]
[9] : 10 [Type: int]
Unten finden Sie Funktionstabellen unter dem jxy -Namespace:
| Jxy | STL Äquivalent | Enthalten | Notizen |
|---|---|---|---|
jxy::allocator | std::allocator | <jxy/memory.hpp> | |
jxy::default_delete | std::default_delete | <jxy/memory.hpp> | |
jxy::unique_ptr | std::unique_ptr | <jxy/memory.hpp> | |
jxy::shared_ptr | std::shared_ptr | <jxy/memory.hpp> | |
jxy::basic_string | std::basic_string | <jxy/string.hpp> | |
jxy::string | std::string | <jxy/string.hpp> | |
jxy::wstring | std::wstring | <jxy/string.hpp> | |
jxy::vector | std::vector | <jxy/vector.hpp> | |
jxy::map | std::map | <jxy/map.hpp> | |
jxy::multimap | std::miltimap | <jxy/map.hpp> | |
jxy::mutex | std::mutex | <jxy/locks.hpp> | Verwendet KGUARDED_MUTEX |
jxy::shared_mutex | std::shared_mutex | <jxy/locks.hpp> | Verwendet EX_PUSH_LOCK |
jxy::unique_lock | std::unique_lock | <jxy/locks.hpp> | |
jxy::shared_lock | std::shared_lock | <jxy/locks.hpp> | |
jxy::scope_resource | Keiner | <jxy/scope.hpp> | Ähnlich wie std::experimental::unique_resource |
jxy::scope_exit | Keiner | <jxy/scope.hpp> | Ähnlich wie std::experimental::scope_exit |
jxy::thread | std::thread | <jxy/thread.hpp> | |
jxy::deque | std::deque | <jxy/deque.hpp> | |
jxy::queue | std:queue | <jxy/queue.hpp> | |
jxy::priority_queue | std::priority_queue | <jxy/queue.hpp> | |
jxy::set | std::set | <jxy/set.hpp> | |
jxy::multiset | std::multiset | <jxy/set.hpp> | |
jxy::stack | std::stack | <jxy/stack.hpp> |
stltest.sys Das stltest -Projekt implementiert einen Treiber, der einige Tests gegen Jxystl, die Verwendung von STL und Ausnahmen im Windows -Kernel durchführt.
stlkrn.sys Das stlkrn -Projekt ist ein Windows -Treiber, der jxystl.lib verwendet, um Prozess, Thread und Modulverfolgung im Windows -Kernel zu implementieren.
stlkrn.sys -Register für Prozess-, Thread- und Bildbenachrichtigungen unter Verwendung von von ntoskrnl exportierten Funktionen. Mit diesen Rückrufen verfolgt es Prozesse, Threads und Bildlasten in verschiedenen Objekten, die jxy::map , jxy::shared_mutex , jxy::wstring und mehr verwenden.
Der Fahrer hat zwei Singletons. jxy::ProcessMap und jxy::ThreadMap , diese werden konstruiert, wenn der Treiber lädt ( DriverEntry ) und abgerissen, wenn der Treiber entlädt ( DriverUnload ). Es ist erwähnenswert, dass jeder Prozess im jxy::ProcessMap (implementiert als jxy::ProcessContext ) auch eine jxy::ThreadMap verwaltet. Jeder "Kontext" ( jxy::ProcessContext , jxy::ThreadContext und jxy::ModuleContext ) ist ein freigegebenes (referenziertes) Objekt ( jxy::shared_ptr ). Daher ist der Thread -Kontext, der im Thread Map Singleton existiert, der gleiche Kontext, der dem Prozesskontext zugeordnet ist.
Schlüsselkomponenten von stlkrn.sys :
| Objekt | Zweck | Quelle | Notizen |
|---|---|---|---|
jxy::ProcessContext | Informationen für einen Prozess, der auf dem System ausgeführt wird. | process_context.hpp/cpp | Verwendet jxy::wstring . Hat Thread ( jxy::ThreadMap ) und Modul ( jxy::ModuleMap ) Map -Mitglieder. |
jxy::ThreadContext | Informationen für einen Thread, der auf dem System ausgeführt wird. | thread_context.hpp/cpp | Verwendet std::atomic . |
jxy::ModuleContext | Informationen für ein Bild, das in einem bestimmten Prozess geladen wurde. | module_context.hpp/cpp | Verwendet jxy::wstring und jxy::shared_mutex . |
jxy::ProcessMap | Singleton, Maps haben jxy::ProcessContext -Objekte an eine PID geteilt. | process_map.hpp/cpp | Singleton wird über jxy::GetProcessMap abgerufen. Verwendet jxy::shared_mutex und jxy::map . |
jxy::ThreadMap | Maps Shared jxy::ThreadContext -Objekte an einen TID. | thread_map.hpp/cpp | Auf die globale Thread -Tabelle (Singleton) wird über jxy::GetThreadMap zugegriffen. Jedes jxy::ProcessContext hat auch eine Thread -Karte, auf die über jxy::ProcessContext::GetThreads zugegriffen wird. Verwendet jxy::shared_mutex und jxy::map . |
jxy::GetModuleMap | Maps Shared jxy::ModuleContext in einen geladenen Bildumfang (Basis- und Endadresse). | module_map.hpp/cpp | Jeder Prozesskontext verfügt über ein Modul -Map -Mitglied. Auf diesem Objekt werden geladene Bilder für einen bestimmten Prozess verfolgt. Verwendet jxy::shared_mutex und jxy::map |
std::unordered_map wäre eine bessere Wahl über den geordneten Baum ( std::map ) für die Objektkarten gewesen. Es gibt einen Grund, warum dies nicht verwendet wird (siehe Abschnitt TODO ).
stlkrn!jxy::nt::CreateProcessNotifyRoutine+0xa6:
3: kd> dx proc
proc : {...} [Type: std::shared_ptr<jxy::ProcessContext>]
[<Raw View>] [Type: std::shared_ptr<jxy::ProcessContext>]
[ptr] : 0xffffaa020d73cf70 [Type: jxy::ProcessContext *]
[control block] : custom deleter, custom allocator [Type: std::_Ref_count_resource_alloc<jxy::ProcessContext *,jxy::details::default_delete<jxy::ProcessContext,1,1668307018>,jxy::details::allocator<jxy::ProcessContext,1,1668307018> > (derived from std::_Ref_count_base)]
[+0x000] m_ProcessId : 0x2760 [Type: unsigned int]
[+0x004] m_SessionId : 0x2 [Type: unsigned int]
[+0x008] m_ParentProcessId : 0xcc4 [Type: unsigned int]
[+0x010] m_FileName : "DeviceHarddiskVolume4WindowsSystem32cmd.exe" [Type: std::basic_string<unsigned short,std::char_traits<unsigned short>,jxy::details::allocator<unsigned short,1,1852856394> >]
[+0x030] m_FilePart : "cmd.exe" [Type: std::basic_string<unsigned short,std::char_traits<unsigned short>,jxy::details::allocator<unsigned short,1,1886410826> >]
[+0x050] m_CreatorProcessId : 0x1b08 [Type: unsigned int]
[+0x054] m_CreatorThreadId : 0x26a0 [Type: unsigned int]
[+0x058] m_Threads [Type: jxy::ThreadMap]
[+0x070] m_Modules [Type: jxy::ModuleMap]
Ich wollte anfangs std::unordered_map einbeziehen, aber es verwendet ceilf . Die schwimmende Punktarithmetik im Windows -Kernel hat einige Herausforderungen. Daher wird es vorerst weggelassen, bis eine geeignete Lösung entworfen wurde.
Diese Lösung ist ein Leidenschaftsprojekt. Zu diesem Zeitpunkt ist es nicht für den Produktionscode bestimmt. x64 ist gut getestet und stabil, stlkrn.sys überträgt die vollständigen Optionen für den Treiber -Verifizierer (einschließlich randomisierter Simulation mit niedriger Ressourcen). Die Ausnahmebehandlung am oder über dem Versand wurde getestet, jedoch nicht in praktischen Anwendungsfällen. x86 wurde nicht getestet. Es gibt Funktionen im jxy -Namespace, der unvollständig/nicht genutzt/ungetestet ist. Ihre Kilometerleistung kann variieren - ich möchte diese Arbeit im Laufe der Zeit fortsetzen, wenn Probleme/Fehler festgestellt werden, dass sie keine Probleme gegen dieses Repo eröffnen können.
Dieses Projekt bietet STL -Unterstützung im Windows -Kernel, indem so viel wie möglich von der STL -Einrichtung verwendet wird. Es gibt andere Lösungen für die Verwendung von STL in der Kernelentwicklung. In diesem Abschnitt wird Alternativen beschrieben, zuerst werde ich diese Arbeit zusammenfassen:
Dieses Projekt:
new oder delete implementiert.atexit -Funktionalität. Die CRT-Initialisierungsreihenfolge ist nicht offensichtlich, die Fahrerinitialisierung und der Abbau sollte offensichtlich sein . atexit -Funktionalität kann Datenrennen für den Kernelcode einführen, atexit wird nicht implementiert.Bareflank Hypervisor:
BareFlank implementiert Unterstützung für das Ausführen von C ++ in ihrem Hypervisor. Sie haben volle STL- und CRT -Unterstützung. Dies ist ein umfassendes Projekt, das eine Fülle des Standards im Kernel -Modus (einschließlich Ausnahmen) ermöglicht. Wie ich verstehe, erzwingt ihre Lösung nicht auf dem NonPagedPool globalen new / delete Zuweisungen. Ich muss Bareflank mit ihrer Implementierung loben, es ist gut durchdacht und überquert die Plattform. Die Windows -Implementierung baut jedoch über Cygwin und "SHIPS" auf, um den Windows -Kernel zu unterstützen. Im Vergleich dazu zielt dieses Projekt darauf ab, den Windows -Kernel rücksichtsvoll zu halten. Es ermöglicht die Angabe von Pool-Tags und -Typen (ausgelastet gegenüber nicht aus dem Pagen) und hofft, "scharfe Kanten" zu minimieren, die mit der Verwendung von C ++ und dem STL im Kernelmodus verbunden sind. Alles in allem ist Bareflank beeindruckend für das, was tut. Für eine hervorragende Präsentation über Bareflanks Unterstützung von C ++ empfehle ich dringend, Dr. Rian Quinns Präsentation bei CPPCON 2016 zu sehen.
Win32Kernelstl:
Mit dem Win32Kernelstl -Projekt können Sie STL -Funktionen direkt im Kernel verwenden. Das Projekt implementiert globale new / delete und zwingt NonPagedPool , es implementiert CRT -Initialisierungsunterstützung und Bugchecks, wenn eine CPP -Ausnahme ausgeworfen wird. Es macht keinen Versuch, eine CPP -Ausnahme durchzuführen. Aufgrund der Annahmen finde ich es für schwerwiegende Anwendungsfälle unkraktisch. Der Code ist einigermaßen klar und dokumentiert. Ich empfehle, diesem Projekt einen Durchsuchen zu geben, um die C ++ - Unterstützung im Kernel zu erziehen. Ein Hinweis: Der CRT -Code in Win32Kernelstl implementiert atexit , aber denken Sie daran, dass der Compiler hier keine Synchronisation gibt (im Gegensatz zum Benutzermodus). Eine lokale Statik, die einen Eintrag in die atexit -Liste einfügt, kann ein doppelter Init oder doppeltfreies Rennen verursachen.
Treiber Plus:
Dieses Projekt implementiert die erforderliche C ++ - Einrichtung zum Anziehen einer Reihe von C ++ - Lösungen in den Kernel -Modus ( EASTL , msgpack usw.). Driver Plus implementiert die CRT -Initialisierung und die globale Unterstützung new / delete (was NonPagedPool erzwingt). Auch dies widerspricht den Zielen dieses Projekts. Dieses Projekt ermöglicht jedoch eine Menge großartiger C ++ - Einrichtungen für den Einsatz im Kernel -Modus. Es führt Änderungen an den C ++ - Lösungen vor, die es zur Unterstützung der Anwendungsfälle setzt. Driver Plus wird auch die Annahme von atexit wie zuvor erwähnt.
KTL - DYMOK93
KTL (Windows -Kernel -Vorlagenbibliothek von Dymok93) erneut auf die moderne C ++ und entwickelt aktiv mehr Unterstützung für die Verwendung im Windows -Kernel. Es unterstützt auch RAII in vielen Kernel -Primitiven, bietet native ANSI_STRING und UNICODE_STRING -Unterstützung. Es bietet einige nützliche Verpackungen für die Registrierung von Kernel -Rückrufen und mehr Komfortfunktionen rund um den Windows -Kernel. Es implementiert Global new / delete und verfügt über eine Präprozessor-Definition ( KTL_USING_NON_PAGED_NEW_AS_DEFAULT ) zum Umschalten zwischen Standard- oder Nicht-Pagen-Wechsel, was gut ist. Es wird jedoch ein einzelnes Pool -Tag ( KTL_HEAP_TAG ) verwendet. Darüber hinaus ermöglichen die vorhandenen Allocator-Vorlagen einem Entwickler nicht, ein Pool-Tag anzugeben. Die Verwendung dieser Bibliothek ist daher so, dass alle Zuordnungen mit demselben Pool-Tag markiert werden. Trotzdem wäre es vernünftig, einen benutzerdefinierten Allocator zu implementieren, der das Taggen von Zuweisungen ermöglicht. Die Bibliothek hat eine Ausnahmeunterstützung, wenn auch nur x64. Die Ausnahmeunterstützung in KTL basiert auf Avakars mit Verbesserungen und Korrekturen. Ich empfehle die Arbeit hier und ich bin beeindruckt von der Menge an Einrichtungen, die einverstanden ist, und es gibt eine angemessene Art und Weise unter aktiver Entwicklung. Ich möchte es in Zukunft mehr nutzen und potenziell an einer besseren Ausnahmeunterstützung für stlkrn und KTL zusammenarbeiten. Neuauflagen der STL -Funktionalität, mangelnde Unterstützung für native Pool -Tagging und die globalen Allokatoren stehen gegen den Ideologien dieses Projekts.
KTL - Meesong:
KTL (Windows -Kernel -Vorlagenbibliothek von Meesong) rägt eine gute Menge moderner C ++ - Funktionalität für die Verwendung im Windows -Kernel. Es implementiert auch Global new / delete leistet jedoch eine anständige Aufgabe bei der Bereitstellung von Einrichtungen zum Angeben von Pool -Tags und -typen, sofern möglich. Dies bedeutet jedoch, dass der globale Allocator eine Allokation in einem nicht offenen Pool verbergen könnte. Darüber hinaus tragen die Vorlagen -Allocatoren in diesem Projekt die Kosten von zwei Punkten für einen Allocator- und Overtrocator -Objekt. Ich bin auch besorgt, dass die Konvertierung zwischen den Allocator -Typen Cross -Pool-/Tag -Allocs/Frees ermöglichen kann. Insgesamt bin ich beeindruckt von der Menge an Einrichtung, die hier implementiert wird. Neuauflagen der STL -Funktionalität und der globalen Allokatoren stimmen den Ideologien dieses Projekts wider.
Kernel-Bridge:
Kernel-Bridge implementiert eine großartige Einrichtung für die Windows-Kernel-Entwicklung. Die Bibliothek bietet Wrapper für die Registrierung für Windows -Rückrufe mit C ++ - Objekten. Ich möchte mehr Zeit finden, um diese Lösung zu verwenden und zu untersuchen. Es implementiert CRT -Unterstützung. Die implementierte atexit -Funktionalität ist nicht dynamisch - sie verwendet ein statisches Array, wenn es keine Slots mehr hat, schlägt es fehl. Die Standard new / delete erzwingt NonPagedPool . Es hat keine vollständige Ausnahmebutgung, es wird Bugcheck, wenn eine CPP -Ausnahme ausgelöst wird - sie werden keine Objekte auf dem Stapel entspannen.
Dieses Repository stammt aus einigen bereits bestehenden Arbeiten. Credits an ihre Autoren.