Débuter le kernel Windows

Ce cours dédiée au développement de drivers en kernel mode sans Framework

Moderator: Rick

Post Reply
Hydraxx
Site Admin
Posts: 46
Joined: Mon Jan 12, 2026 4:04 pm
Location: France
Contact:

Débuter le kernel Windows

Post by Hydraxx »

Introduction au kernel Windows

Salut l'équipe 8-)

Aujourd’hui on attaque un sujet central du développement système Windows : le kernel Windows.

C’est une étape importante, parce qu’à partir de maintenant on ne parle plus seulement d’API user-mode, de processus, de threads ou de synchronisation classique. On parle de la partie du système qui contrôle la machine entière, coordonne les requêtes, arbitre l’accès aux ressources critiques et exécute le code le plus privilégié.

Et surtout, ici, les erreurs n’ont pas du tout les mêmes conséquences.

En user mode :
  • une erreur peut faire planter ton programme
  • une mauvaise lecture mémoire peut provoquer une exception
  • Windows peut souvent contenir les dégâts
En kernel mode :
  • un mauvais pointeur peut corrompre le système
  • une erreur logique peut bloquer une pile de drivers
  • une faute de synchronisation peut provoquer un BSOD
  • une mauvaise gestion d’IRP peut rendre un device inutilisable
Donc ce cours est volontairement plus détaillé et plus rigoureux.
Le but n’est pas juste de “survoler le kernel”, mais de construire une vraie base mentale solide.

1) C’est quoi le kernel

Le kernel, ou noyau, est la partie de Windows qui exécute les opérations les plus sensibles et les plus privilégiées du système.

C’est lui qui :
  • gère la mémoire virtuelle
  • ordonnance les threads
  • arbitre l’accès au CPU
  • pilote les entrées / sorties
  • fait le lien entre les applications et le matériel
  • applique une partie importante des règles de sécurité
  • coordonne les drivers
  • gère les interruptions matérielles
Autrement dit, le kernel maintient l’ordre dans le système.

On peut le voir comme un chef d’orchestre :
  • les applications demandent des services
  • le matériel génère des événements
  • les drivers traitent les requêtes
  • le kernel arbitre le tout
Il ne sert donc pas seulement à “parler au hardware”. Il organise, limite, contrôle et synchronise toute l’activité du système.

2) Pourquoi le kernel existe

Sur une machine moderne, plusieurs programmes tournent en même temps. Ils veulent tous :
  • utiliser de la mémoire
  • créer des threads
  • accéder au disque
  • envoyer des données réseau
  • ouvrir des handles
  • lire des fichiers
  • dialoguer avec des devices
Sans noyau, ce serait le chaos.

Chaque programme pourrait :
  • écraser la mémoire d’un autre
  • parler directement au matériel sans contrôle
  • monopoliser le CPU
  • faire planter toute la machine au moindre bug
Le rôle du kernel est donc d’imposer une discipline. Il fournit un cadre où :
  • les ressources sont partagées
  • les accès sont contrôlés
  • les transitions critiques sont centralisées
  • le matériel est exposé via des abstractions cohérentes
3) User mode vs kernel mode

C’est la séparation la plus importante à comprendre.

Windows distingue principalement deux grands niveaux d’exécution :
  • user mode
  • kernel mode
3.1) User mode

Le user mode est l’espace dans lequel tournent les programmes classiques :
  • applications console
  • applications GUI
  • services user-mode
  • outils d’administration
  • la majorité des logiciels du système
En user mode :
  • l’accès mémoire est limité à l’espace du processus
  • certaines instructions CPU privilégiées sont interdites
  • l’accès direct au matériel n’est pas autorisé
  • le programme doit passer par les API du système
Le user mode est donc un environnement restreint mais sûr.
Si une application plante, Windows peut généralement :
  • arrêter le processus
  • lever une exception
  • préserver le reste du système
3.2) Kernel mode

Le kernel mode est l’espace privilégié.

Le code qui s’exécute ici a un niveau d’autorité beaucoup plus élevé. Il peut :
  • accéder à la mémoire du noyau
  • interagir avec des structures système critiques
  • gérer les devices
  • recevoir des interruptions matérielles
  • exécuter du code avec très peu de garde-fous
Le problème, c’est que cette puissance a un prix.

En kernel mode, une erreur ne menace pas seulement ton programme. Elle menace l’intégrité du système entier.

3.3) Conséquence directe

La différence pratique est énorme :
  • en user mode, tu développes une application
  • en kernel mode, tu participes au fonctionnement du système
C’est pour ça que le style de code, la rigueur, le debugging et la compréhension du modèle d’exécution changent complètement.

4) Comment une application atteint le kernel

Quand tu codes en Win32, tu utilises souvent des API comme :
Mais il faut bien comprendre une chose :

Les API Win32 ne sont pas “le kernel”. Elles sont une abstraction user-mode.

4.1) Win32 = couche d’abstraction

L’API Win32 est la couche que les développeurs utilisent le plus souvent.
Elle est pratique, documentée, relativement stable, et masque beaucoup de détails internes.

Par exemple :

Code: Select all

CreateFile(...)
semble être “l’appel système” qui ouvre un fichier.
En réalité, ce n’est qu’une façade de plus haut niveau.

4.2) Native API = interface plus proche du noyau

Sous Win32, on trouve la Native API, exposée notamment via

Code: Select all

ntdll.dll
.

Par exemple, derrière un appel Win32 comme

Code: Select all

CreateFile
, on trouve généralement un appel vers une fonction native comme :

Code: Select all

NtCreateFile(...)
Puis :
  • passage par une transition système
  • entrée dans le noyau
  • traitement par les composants internes de Windows
Le schéma mental correct est donc :

Code: Select all

Application
↓
Win32 API
↓
Native API (ntdll)
↓
System call
↓
Kernel
4.3) Pourquoi c’est important

Parce que beaucoup de développeurs s’arrêtent à Win32.

En développement kernel, tu dois comprendre que Win32 n’est qu’une surcouche pratique. Le vrai cœur du traitement se trouve plus bas.

Donc oui, on peut dire que :
  • Win32 = abstraction user-mode
  • Native API = interface plus proche du noyau
Mais il faut éviter une simplification excessive.
La Native API n’est pas “le kernel lui-même” : elle est surtout le pont entre le monde user-mode et les services du noyau.

5) L’idée centrale : toute I/O devient une requête système

L’un des points les plus importants du kernel Windows est la gestion des entrées / sorties.

Quand une application lit un fichier, envoie une commande à un device ou communique avec un driver, elle ne parle pas directement au matériel.
Le système transforme cette demande en un objet de travail standardisé.

Cet objet, dans le modèle Windows, c’est l’IRP.

6) IRP — I/O Request Packet

L’IRP est une structure centrale dans l’architecture d’I/O de Windows.

Tu peux voir un IRP comme la représentation d’une requête d’entrée / sortie.

Exemples de requêtes :
  • ouvrir un device
  • lire des données
  • écrire
  • envoyer un IOCTL
  • fermer un handle
  • exécuter certaines opérations PnP ou power
6.1) Pourquoi l’IRP est si important

Parce qu’il sert de support commun entre plusieurs couches du système.

Le kernel peut ainsi :
  • créer une requête de façon standardisée
  • la faire passer à une pile de drivers
  • laisser chaque driver ajouter son traitement
  • centraliser la complétion et le retour d’état
6.2) Ce qu’il faut retenir sans entrer tout de suite dans tout le détail structurel

Un IRP contient en substance :
  • l’état de la requête
  • des informations de statut
  • les paramètres de l’opération
  • éventuellement des buffers
  • des informations pour la pile de drivers
Il est conçu pour circuler.
Et c’est ça qu’il faut déjà comprendre.

Le kernel ne dit pas juste “lis ce fichier”. Il fabrique une requête structurée qui traverse un chemin précis.

6.3) Règle mentale fondamentale

Toute I/O sérieuse dans Windows se matérialise sous forme de requête. Dans le modèle driver classique, cette requête est souvent portée par un IRP.

7) De l’application au hardware : le vrai pipeline

Prenons un exemple simple : une application appelle

Code: Select all

ReadFile
.

Le déroulement mental correct ressemble à ça :

Code: Select all

Application
↓
Win32 API
↓
Native API
↓
System call
↓
I/O Manager
↓
Création / préparation d’une requête
↓
IRP
↓
Driver stack
↓
Hardware
Et au retour :

Code: Select all

Hardware
↓
Interrupt / notification
↓
Driver
↓
Complétion de la requête
↓
Retour vers le noyau
↓
Retour vers user mode
Ce schéma paraît simple, mais il contient déjà l’essentiel de la philosophie kernel Windows :
  • les applications demandent
  • le noyau orchestre
  • les drivers participent au traitement
  • le matériel exécute
  • le résultat remonte ensuite dans l’autre sens
8) Les drivers

Un driver est un composant logiciel qui s’exécute en kernel mode et qui participe au traitement des requêtes système, souvent en lien avec un device ou une pile d’I/O.

Mais il faut éviter une définition trop simpliste.

Un driver ne sert pas uniquement à “piloter une carte”.
Dans Windows, un driver peut :
  • contrôler un périphérique physique
  • filtrer des requêtes
  • représenter un device logique
  • participer à une pile logicielle complexe
  • gérer des opérations PnP, power et I/O
8.1) Le piège de la vision “driver = code actif qui tourne tout seul”

C’est faux dans la majorité des cas.

Le bon modèle mental est celui-ci :

Code: Select all

Un driver est surtout un ensemble de callbacks enregistrés dans le système.
Cela veut dire :
  • il n’a pas de

    Code: Select all

    main
    classique
  • il n’impose pas son propre flow général
  • il est appelé par le kernel quand un événement pertinent survient
Exemples d’événements :
  • une requête I/O arrive
  • un device est détecté
  • un device est retiré
  • une demande d’alimentation change
  • une interruption matérielle se produit
8.2) Driver passif : ce que ça veut vraiment dire

Quand on dit qu’un driver est passif, on ne veut pas dire qu’il ne fait rien.

On veut dire que :
  • le système décide quand il est appelé
  • le contexte d’appel ne lui appartient pas
  • son exécution est déclenchée par des événements externes
Donc :

Le driver ne contrôle pas le rythme du système. Il répond au rythme imposé par le système.

C’est une différence énorme avec une application classique.

9) “The system is in charge”

C’est probablement l’idée la plus importante de l’introduction au kernel.

En user mode, tu écris souvent du code qui suit un flux relativement clair :
  • initialisation
  • boucle
  • appels
  • traitement
  • sortie
En kernel mode, surtout dans un driver, le schéma est différent :
  • le système charge le driver
  • le système appelle tes routines
  • le système t’envoie des requêtes
  • le système peut t’appeler depuis différents contextes
  • le système attend que tu respectes ses règles
Autrement dit :

Le système est toujours maître du timing et du cadre d’exécution.

Si tu développes en oubliant ça, tu vas écrire du code dangereux.

10) La pile de drivers

Dans Windows, un driver n’est généralement pas seul.

Les requêtes passent souvent à travers plusieurs drivers superposés. On appelle ça une driver stack.

10.1) Les trois grandes catégories à connaître au début

Même si la réalité peut être plus riche, il faut déjà connaître ces trois rôles majeurs.

Function driver

C’est le driver principal qui implémente la logique essentielle pour un device.

Filter driver

Il s’insère dans la pile pour observer, modifier, surveiller, bloquer ou ajuster certaines requêtes.

Bus driver

Il gère un bus matériel ou logique, comme USB ou PCI, et participe à la découverte des devices.

10.2) Représentation mentale simple

Code: Select all

Filter driver
↓
Function driver
↓
Bus driver
↓
Hardware
10.3) Pourquoi c’est capital

Parce qu’un débutant imagine souvent :

Code: Select all

app → mon driver → matériel
Alors qu’en pratique, c’est souvent plus proche de :

Code: Select all

app → I/O Manager → plusieurs couches de drivers → matériel
Et chaque couche doit respecter le contrat global.

11) Le rôle du I/O Manager

Le I/O Manager est un composant central du noyau dans la gestion des requêtes d’entrée / sortie.

C’est lui qui participe à :
  • la création et la préparation des IRP
  • le routage vers les bons objets de device
  • l’orchestration du passage dans la pile
  • la coordination de la complétion
Le I/O Manager fait le lien entre la demande système et la pile de drivers.

12) Comment une application parle à un driver

Le mécanisme classique passe par :
  • un device object exposé par le driver
  • souvent un symbolic link accessible depuis user mode
  • un handle ouvert avec

    Code: Select all

    CreateFile
  • une commande envoyée via

    Code: Select all

    DeviceIoControl
12.1) Vue d’ensemble

Code: Select all

Application (.exe)
↓
CreateFile("\\.\MonDriver")
↓
DeviceIoControl(...)
↓
I/O Manager
↓
IRP_MJ_DEVICE_CONTROL
↓
Driver
12.2) Pourquoi c’est important

Ça montre que :
  • le driver n’est pas une DLL classique
  • on ne l’appelle pas comme une fonction normale
  • on dialogue avec lui via le mécanisme d’I/O du système
13) Le cycle de vie général d’un driver

Code: Select all

Chargement du driver
↓
Initialisation (DriverEntry)
↓
Éventuelle association à des devices
↓
Réception de requêtes
↓
Traitement I/O / PnP / Power / Interruptions
↓
Nettoyage / déchargement
14) Le contexte d’exécution : une difficulté majeure

Ton code peut être appelé :
  • depuis des threads différents
  • dans des contextes variés
  • parfois à des niveaux d’exécution différents
  • parfois en concurrence avec d’autres chemins d’exécution
C’est pour ça qu’on parle souvent de thread context arbitraire.

Ce que ça veut dire :
  • tu ne dois pas présumer que c’est “ton thread”
  • tu ne peux pas bloquer n’importe comment
  • tu ne peux pas toucher n’importe quelle structure sans synchronisation
  • tu dois connaître les contraintes du contexte d’appel
15) Interruptions, ISR et DPC

Quand un périphérique veut signaler un événement important, il peut déclencher une interruption.

ISR — Interrupt Service Routine

L’ISR est appelée très rapidement en réponse à une interruption.
Elle doit rester courte et faire le minimum.

DPC — Deferred Procedure Call

Le DPC permet de repousser une partie du travail à un moment plus approprié.

Schéma mental :

Code: Select all

Hardware
↓
Interrupt
↓
ISR
↓
DPC
↓
Traitement ultérieur / complétion
Le kernel sépare les traitements ultra urgents des traitements plus lourds.

16) IRQL — notion critique

Sans encore entrer dans tout le détail, retiens ceci :
  • toutes les portions de code kernel ne s’exécutent pas dans les mêmes conditions
  • selon le niveau courant, certaines opérations sont autorisées ou interdites
  • plus le niveau est élevé, plus les contraintes sont fortes
Donc dès l’introduction, il faut intégrer cette idée :

En kernel mode, le “où” et le “quand” d’exécution comptent autant que le “quoi”.

17) Synchronisation en kernel

Les bugs de synchronisation en kernel sont particulièrement dangereux.

Exemples de conséquences :
  • corruption mémoire
  • état incohérent du driver
  • IRP bloqués
  • deadlocks
  • BSOD
18) Pourquoi les erreurs sont si critiques en kernel

Une erreur classique en kernel peut être bien plus grave qu’en user mode.

Exemples :
  • déréférencer un pointeur invalide
  • oublier de compléter une requête
  • renvoyer un mauvais statut
  • mal gérer un buffer
  • oublier une synchronisation
  • faire un travail lourd dans une routine sensible
  • casser la cohérence de la pile de drivers
Le système peut alors :
  • se figer
  • produire un bugcheck
  • perdre l’accès à un device
  • devenir instable de façon intermittente
19) Ce qu’un débutant doit absolument retenir
  • Win32 n’est pas le fond du système
  • la Native API est plus proche du noyau
  • le noyau orchestre les requêtes
  • l’IRP est une unité centrale de travail
  • les drivers sont événementiels et passifs
  • les drivers travaillent en pile
  • le contexte d’exécution est une contrainte majeure
  • les erreurs de logique sont structurellement plus graves
20) Modèle mental global à retenir

Code: Select all

Application
↓
Win32 API
↓
Native API
↓
System call
↓
Kernel
↓
I/O Manager
↓
IRP
↓
Driver stack
↓
Hardware
↓
Interrupt / traitement de retour
↓
Complétion
↓
Retour vers l’application
Et autour de ce schéma :
  • le système est en contrôle
  • le driver est passif
  • les requêtes traversent une pile
  • le contexte d’exécution est critique
  • une erreur kernel peut compromettre toute la machine
21) Conclusion

Ce qu’il faut retenir de cette introduction, c’est surtout ceci :
  • Win32 est une abstraction, pas le fond réel du système
  • la Native API sert de pont vers le noyau
  • les requêtes I/O sont structurées et routées par le système
  • l’IRP est un élément central du modèle driver
  • un driver est un ensemble de routines appelées par le kernel
  • les drivers vivent dans une pile, pas en isolation
  • le contexte d’exécution et les contraintes système sont fondamentaux
  • la moindre erreur peut avoir des conséquences critiques
Si tu comprends vraiment ça, tu ne regarderas plus Windows comme une simple collection d’API.

Résumé ultra condensé

Le kernel contrôle les ressources critiques du système.
Win32 est une abstraction user-mode.
La Native API sert de pont vers le noyau.
Les I/O deviennent des requêtes structurées, souvent portées par des IRP.
Ces requêtes traversent une pile de drivers.
Les drivers sont passifs, événementiels, et appelés par le système.
Le contexte d’exécution, l’IRQL, la synchronisation et la rigueur sont essentiels.
En kernel mode, une erreur peut compromettre toute la machine.

Who is online

Users browsing this forum: No registered users and 1 guest