Ce projet utilise MSVC C ++ STL dans un pilote de noyau Windows. Dans cette solution, jxystl.lib est implémenté en tant que noyau, type de pool / étiquette, bibliothèque de modèles et implémentation MSVC. Qui, sous le capot, utilise le MSVC C ++ STL.
# 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 | Soutenu | Notes |
|---|---|---|
| cpp11 | Non | non testé, votre milage ma variation |
| CPP14 | Oui | |
| CPP17 | Oui | |
| cpp20 | Oui |
Le pilote de test dans cette solution, stdtest.sys , abrite les tests unitaires du projet. Les tests unitaires sont exécutés dans le noyau avec le vérificateur du pilote. Le cadre de test unitaire est des os nus mais suffit pour faire de l'exercice jxystl.lib .
Un autre pilote implémenté dans cette solution, stdkrn.sys , met jxystl.lib à utiliser dans un scénario pratique. Il utilise diverses installations et conteneurs d'espace de noms std (enveloppés sous l'espace de noms jxy ). Ce pilote enregistre pour les notifications de processus, de thread et d'image; Utilise ensuite C ++ moderne pour suivre les contextes de processus, les contextes de threads et les contextes de modules.
vcrtlLa gestion des exceptions permet aux objets C ++ de se détendre lorsqu'une exception est lancée. Il s'agit d'une caractéristique centrale de C ++ qui attire peu d'attention pour les pilotes du noyau. Microsoft ne prend pas en charge nativement les exceptions C ++ pour les pilotes de noyau.
La gestion des exceptions C ++ est rendue possible par le VCRTL Libraray d'Avakar. Ce projet aurait été beaucoup plus de travail sans la contribution impressionnante d'Avakar. Pour plus d'informations sur la gestion des exceptions dans les pilotes Windows, rendez-vous vers le GitHub VCRTL d'Avakar. De plus, cette page donne d'excellents détails sur la gestion des exceptions sur AMD64.
jxystlLes allocations du noyau Windows sont associées à un pool de mémoire. De plus, le balisage de la piscine est intégré dans le noyau Windows. Le marquage de piscine facilite le suivi des allocations faites par les conducteurs. Cette installation de marquage permet le débogage et la surveillance des allocations.
L'espace de noms jxy , dans cette solution, permet le développement de pilotes Windows à l'aide des objets d'espace de noms std avec tapisse et étiquetage de pool.
La bibliothèque choisit de ne pas implémenter les opérateurs "globaux" new / delete . Il implémente uniquement les opérateurs new / delete avec la capacité de typage et de marquage de pool. Cela nécessite de spécifier les types de pools et les balises. Si certaines fonctionnalités sont utilisées, cela nécessiterait un "allocateur global", il ne liera pas. Il s'agit d'une décision de conception intentionnelle telle qu'aucun allocateur global n'est utilisé, toutes les allocations doivent spécifier un type de pool et une balise.
L'espace de noms jxy implémente les allocateurs et les délèves qui se conforment à la norme à utiliser dans les conteneurs de modèle. Ces allocateurs et les délecteurs sont conscients de type de piscine / étiquettes. Ils nécessitent de spécifier le type de piscine et la balise et empêcher les conversions / la reliure entre les types d'outils et les balises - ils doivent être utilisés à la place des allocateurs STL.
jxy::allocator<T, PagedPool, ' 0GAT ' >;
jxy::default_delete<T, PagedPool, ' 0GAT ' >; jxystl.lib met en œuvre la fonctionnalité "Rempliez" nécessaire pour l'utilisation des conteneurs MSVC STL. Les implémentations (dans msvcfill.cpp ) sont prévenantes pour le noyau. Cette fonctionnalité permet aux conteneurs MSVC STL de relier à la fonctionnalité adaptée au noyau. Cela signifie également que si certaines fonctionnalités de conteneur std sont utilisées qui n'ont pas de fonctionnalité "remplir" derrière lui - le linker échouera. Il s'agit d'une décision de conception intentionnelle de telle sorte que toutes les implémentations soient pensées pour être utilisées dans le noyau.
L'initialisation du CRT et la fonctionnalité ATexit ne sont pas intentionnellement prises en charge. L'ordre d'initialisation du CRT n'est pas clair et non évident. Lorsqu'un pilote de noyau charge les données globales doit être clairement configurée et démolie pendant la charge et le déchargement du pilote. L'initialisation mondiale du CRT "cache" cette initialisation d'une manière non évidente. De plus, la fonctionnalité CRT ATexit n'est pas prise en charge. L'émission de la synchronisation nécessaire permettant une initialisation statique locale des objets C ++ n'est pas effectuée par le compilateur. Et introduirait une synchronisation non évidente dans le noyau. L'absence d'initialisation du CRT et le support ATexit est une décision de conception intentionnelle. Je recommande fortement de l'éviter lors du développement de conducteurs de noyau.
À titre d'exemple, l'espace jxy "Wraps" std::vector et forces Utilisation des types et balises de pool:
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]
Vous trouverez ci-dessous le tableau des fonctionnalités sous l'espace de noms jxy :
| jxy | STL équivalent | Inclure | Notes |
|---|---|---|---|
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> | Utilise KGUARDED_MUTEX |
jxy::shared_mutex | std::shared_mutex | <jxy/locks.hpp> | Utilise EX_PUSH_LOCK |
jxy::unique_lock | std::unique_lock | <jxy/locks.hpp> | |
jxy::shared_lock | std::shared_lock | <jxy/locks.hpp> | |
jxy::scope_resource | Aucun | <jxy/scope.hpp> | Similar to std::experimental::unique_resource |
jxy::scope_exit | Aucun | <jxy/scope.hpp> | Similaire à 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 Le projet stltest implémente un pilote qui exécute certains tests contre JXYSTL, l'utilisation de STL et les exceptions dans le noyau Windows.
stlkrn.sys Le projet stlkrn est un pilote Windows qui utilise jxystl.lib pour implémenter le suivi du processus, du thread et du module dans le noyau Windows.
stlkrn.sys enregistre pour les notifications de processus, de thread et d'image à l'aide de la fonctionnalité exportée par ntoskrnl . En utilisant ces rappels, il suit les processus, les threads et les charges d'image dans divers objets qui utilisent jxy::map , jxy::shared_mutex , jxy::wstring , et plus encore.
Le conducteur a deux singletons. jxy::ProcessMap et jxy::ThreadMap , ceux-ci sont construits lorsque le pilote se charge ( DriverEntry ) et démolir lorsque le pilote décharge ( DriverUnload ). Il convient de noter ici chaque processus suivi dans le jxy::ProcessMap (implémenté en jxy::ProcessContext ) gère également un jxy::ThreadMap . Chaque "contexte" ( jxy::ProcessContext , jxy::ThreadContext et jxy::ModuleContext ) est un objet partagé (référencé) ( jxy::shared_ptr ). Par conséquent, le contexte du thread qui existe dans le Singleton de la carte du thread est le même contexte associé au contexte de processus.
Composants clés de stlkrn.sys :
| Objet | But | Source | Notes |
|---|---|---|---|
jxy::ProcessContext | Informations pour un processus exécuté sur le système. | process_context.hpp/cpp | Utilise jxy::wstring . A du thread ( jxy::ThreadMap ) et du module ( jxy::ModuleMap ) membres de la carte. |
jxy::ThreadContext | Informations pour un thread fonctionnant sur le système. | thread_context.hpp/cpp | Utilise std::atomic . |
jxy::ModuleContext | Informations pour une image chargée dans un processus donné. | module_context.hpp/cpp | Utilise jxy::wstring et jxy::shared_mutex . |
jxy::ProcessMap | Singleton, Maps a partagé jxy::ProcessContext objets à un PID. | process_map.hpp/cpp | Singleton est accessible via jxy::GetProcessMap . Utilise jxy::shared_mutex et jxy::map . |
jxy::ThreadMap | Les cartes ont partagé jxy::ThreadContext objets à un TID. | thread_map.hpp/cpp | La table de threads globale (singleton) est accessible via jxy::GetThreadMap . Chaque jxy::ProcessContext a également une carte de thread qui est accessible via jxy::ProcessContext::GetThreads . Utilise jxy::shared_mutex et jxy::map . |
jxy::GetModuleMap | Maps a partagé jxy::ModuleContext à une image chargée des étendues (adresse de base et fin). | module_map.hpp/cpp | Chaque contexte de processus a un membre de la carte du module. Les images chargées pour un processus donné sont suivies à l'aide de cet objet. Utilise jxy::shared_mutex et jxy::map |
std::unordered_map aurait été un meilleur choix par rapport à l'arbre ordonné ( std::map ) pour les cartes d'objets. Il y a une raison pour laquelle il n'est pas utilisé (voir la section 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]
J'avais voulu inclure std::unordered_map initialement, mais il utilise ceilf . L'arithmétique du point flottant dans le noyau Windows est livrée avec certains défis. Ainsi, pour l'instant, il est omis jusqu'à ce qu'une solution appropriée soit conçue.
Cette solution est un projet passionné. À l'heure actuelle, il n'est pas destiné au code de production. x64 est bien testé et stable, stlkrn.sys transmet les options complètes du vérificateur du pilote (y compris la simulation randomisée de ressources faibles). La gestion des exceptions à out ou au-dessus de l'envoi a été testée, mais pas dans les cas d'utilisation pratiques. x86 n'a pas été testé. Il y a des fonctionnalités sous l'espace de noms jxy qui est incomplet / inutilisé / non testé. Votre kilométrage peut varier - je voudrais continuer ce travail au fil du temps, si des problèmes / bugs sont trouvés, n'hésitez pas à ouvrir des problèmes contre ce repo.
Ce projet fournit une prise en charge STL dans le noyau Windows en utilisant autant de fonctionnalités STL que possible. Il existe d'autres solutions pour l'utilisation de STL dans le développement du noyau. Cette section décrira des alternatives, je vais d'abord résumer ce travail:
Ce projet:
new ou delete mondiale n'est mise en œuvre.atexit . L'ordre d'initialisation du CRT n'est pas évident, l'initialisation du pilote et le démontage devraient être évidents . La fonctionnalité atexit peut introduire des races de données pour le code du noyau, atexit n'est pas implémenté.Hyperviseur Bareflank:
BareFlank met en œuvre la prise en charge de l'exécution de C ++ dans leur hyperviseur. Ils ont un soutien complet STL et CRT. Il s'agit d'un projet complet qui permet une pléthore de fonctionnalités de la norme en mode noyau (y compris des exceptions). Si je comprends bien, leur solution force NonPagedPool sur les allocations globales new / delete . Je dois féliciter Bareflank avec leur implémentation, c'est bien pensé et cross-plateforme. Cependant, l'implémentation Windows se construit via Cygwin et "cales" en prise en charge du noyau Windows. En comparaison, ce projet vise à être prévenant pour le noyau Windows. Il permet de spécifier les balises et les types de pool (paginés vs non pagis) et espère minimiser les "arêtes tranchantes" associées à l'utilisation de C ++ et du STL en mode noyau. Cela dit, BareFlank est impressionnant pour ce qui fait. Pour une excellente présentation sur le soutien par Bareflank en C ++, je recommande fortement de regarder la présentation du Dr Rian Quinn au CPPCON 2016.
Win32kernelstl:
Le projet Win32Kernelstl vous permet d'utiliser les fonctionnalités STL directement dans le noyau. Le projet implémente Global new / delete and Forces NonPagedPool , il met en œuvre le support d'initialisation CRT et BugChecks lorsqu'une exception CPP est lancée. Il ne tente pas de faire l'exception CPP se déroulant. En raison des hypothèses, je trouve que cela ne trouve pas pratique pour tout cas d'utilisation sérieux. Le code est raisonnablement clair et documenté, je recommande de donner à ce projet un parcours pour éduquer le support C ++ dans le noyau. Une note, le code CRT dans Win32Kernelstl implémente atexit mais gardez à l'esprit qu'il n'y a pas de synchronisation émise par le compilateur ici (par opposition au mode utilisateur). Ainsi, une insertion statique locale nécessitant une entrée dans la liste atexit peut courir, provoquant un double-init ou un double libre.
Driver Plus Plus:
Ce projet implémente la fonction C ++ nécessaire pour tirer un certain nombre de solutions C ++ en mode noyau ( EASTL , msgpack , etc.). Driver Plus Plus implémente l'initialisation CRT et le support global new / delete (qui force NonPagedPool ). Encore une fois, cela est contraire aux objectifs de ce projet. Cependant, ce projet permet à une grande grande installation C ++ d'utiliser en mode noyau. Il apporte des modifications aux solutions C ++ qu'il s'accumule à la cale de prise en charge de ses cas d'utilisation. Driver Plus Plus fait également l'hypothèse autour atexit comme mentionné précédemment.
Ktl - dymok93
KTL (bibliothèque de modèles de noyau Windows par Dymok93) réimplique une bonne quantité de C ++ moderne et développe activement plus de prise en charge pour une utilisation dans le noyau Windows. Il prend également en charge le RAII autour de nombreuses primitives du noyau, fournit une prise en charge native ANSI_STRING et UNICODE_STRING , il fournit des emballages utiles pour enregistrer les rappels du noyau et plus de fonctionnalités de commodité autour du noyau Windows. Il implémente Global new / delete et a une définition de préprocesseur ( KTL_USING_NON_PAGED_NEW_AS_DEFAULT ) pour basculer entre Page par défaut ou non paginé, ce qui est bien. Cependant, il utilise une seule balise de pool ( KTL_HEAP_TAG ). En outre, les modèles d'allocateur existants ne permettent pas à un développeur de spécifier une balise de pool, donc l'utilisation de cette bibliothèque tel quel fait que toutes les allocations sont marquées avec la même balise de pool. Cela dit, il serait raisonnable de mettre en œuvre un allocateur personnalisé qui permet le marquage des allocations. La bibliothèque a une prise en charge des exceptions, mais seulement x64. Le support d'exception dans KTL est basé sur Avakar avec des améliorations et des correctifs. Je félicite le travail ici et je suis impressionné par la quantité d'installation qui existe, elle est raisonnablement emballée et sous développement actif. Je voudrais explorer l'utiliser davantage à l'avenir et potentiellement collaborer sur un meilleur support d'exception pour stlkrn et KTL . Remplémentation de la fonctionnalité STL, manque de support de marquage de pool natifs et les allocateurs mondiaux sont contraires aux idéologies de ce projet.
KTL - Meesong:
KTL (Windows Kernel Template Library by Meesong) réimplique une bonne quantité de fonctionnalités C ++ modernes à utiliser dans le noyau Windows. Il implémente également Global new / delete mais fait un travail décent dans la fourniture de l'installation pour spécifier les étiquettes et les types de pool lorsque cela est possible. Cependant, cela signifie que l'allocateur global pourrait masquer une allocation dans une piscine non évidente. En outre, les allocateurs de modèle dans ce projet portent le coût de deux points pour un objet d'allocateur et de transacteur, je crains également que la conversion entre les types d'allocateurs puisse permettre des allocs / libellés de croisement / balises. Dans l'ensemble, je suis impressionné par la quantité d'installation mise en œuvre ici. La réimplémentation de la fonctionnalité STL et des allocateurs mondiaux est contraire aux idéologies de ce projet.
Bridge noyau:
Kernel-Bridge met en œuvre une excellente installation pour le développement du noyau Windows. La bibliothèque fournit des emballages pour s'inscrire aux rappels Windows à l'aide d'objets C ++. Je voudrais trouver plus de temps à utiliser et enquêter sur cette solution. Il implémente le support CRT. La fonctionnalité atexit implémentée n'est pas dynamique - elle utilise un tableau statique, s'il manque de créneaux, il échoue. La new / delete par défaut Forces NonPagedPool . Il n'a pas de support d'exception complet, il sera bugcheck si une exception CPP est lancée - elle ne se déroulera pas d'objets sur la pile.
Ce référentiel s'inspire de certains travaux préexistants. Crédits à leurs auteurs.