Lecture et écriture dans un driver WDM

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

Moderator: Rick

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

Lecture et écriture dans un driver WDM

Post by Hydraxx »

Lecture et écriture dans un driver WDM

Introduction

Lorsqu'une application appelle ReadFile ou WriteFile sur un périphérique, la requête ne devient pas immédiatement un simple appel de fonction dans le driver. Windows transforme l'opération en une requête d'E/S représentée par un IRP (I/O Request Packet), puis l'envoie dans la pile de périphériques concernée.

Le but de ce cours est de comprendre le chemin complet d'une lecture ou d'une écriture dans un driver WDM, depuis l'arrivée de l'IRP jusqu'à sa complétion.

1. Les routines IRP_MJ_READ et IRP_MJ_WRITE

Pour recevoir les requêtes de lecture et d'écriture, le driver renseigne la table MajorFunction du DRIVER_OBJECT :

Code: Select all

DriverObject->MajorFunction[IRP_MJ_READ]  = DispatchRead;
DriverObject->MajorFunction[IRP_MJ_WRITE] = DispatchWrite;
Les prototypes classiques sont :

Code: Select all

NTSTATUS DispatchRead(
    PDEVICE_OBJECT DeviceObject,
    PIRP Irp
);

NTSTATUS DispatchWrite(
    PDEVICE_OBJECT DeviceObject,
    PIRP Irp
);
API importantes
  • IoGetCurrentIrpStackLocation
  • IoCompleteRequest
  • IoMarkIrpPending
  • IoSetCompletionRoutine
  • IoCallDriver
2. Récupérer les paramètres de la requête

Chaque IRP possède une pile d'emplacements IRP appelée IRP stack. Chaque driver de la pile travaille avec son propre IO_STACK_LOCATION.

Code: Select all

PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);
Pour une lecture :

Code: Select all

ULONG length = stack->Parameters.Read.Length;
LARGE_INTEGER offset = stack->Parameters.Read.ByteOffset;
Pour une écriture :

Code: Select all

ULONG length = stack->Parameters.Write.Length;
LARGE_INTEGER offset = stack->Parameters.Write.ByteOffset;
Le driver doit toujours vérifier les longueurs reçues avant d'accéder aux buffers.

3. Où se trouve le buffer ?

Le pointeur utilisé dépend du modèle d'E/S choisi par le driver.
  • Buffered I/O : Irp->AssociatedIrp.SystemBuffer
  • Direct I/O : Irp->MdlAddress, puis mapping du MDL
  • Neither I/O : pointeur utilisateur transmis directement, à manipuler avec beaucoup de précautions
Ces trois modèles seront détaillés dans le cours suivant.

4. Retourner le résultat d'une opération

Deux champs de l'IRP sont fondamentaux :

Code: Select all

Irp->IoStatus.Status
Irp->IoStatus.Information
Status indique si l'opération a réussi. Information contient généralement le nombre d'octets réellement lus ou écrits.

Exemple :

Code: Select all

Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = bytesTransferred;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
Si aucune donnée n'a été transférée, Information vaut normalement 0.

5. Exemple minimal de lecture

Code: Select all

NTSTATUS DispatchRead(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
    UNREFERENCED_PARAMETER(DeviceObject);

    PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);
    ULONG length = stack->Parameters.Read.Length;

    PUCHAR buffer = (PUCHAR)Irp->AssociatedIrp.SystemBuffer;

    ULONG toCopy = min(length, 4UL);
    if (buffer && toCopy == 4)
    {
        buffer[0] = 'T';
        buffer[1] = 'E';
        buffer[2] = 'S';
        buffer[3] = 'T';
    }

    Irp->IoStatus.Status = STATUS_SUCCESS;
    Irp->IoStatus.Information = toCopy;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);
    return STATUS_SUCCESS;
}
Cet exemple suppose que le périphérique utilise DO_BUFFERED_IO.

6. Opération synchrone et opération asynchrone

Une routine de dispatch n'est pas obligée de terminer l'IRP immédiatement.

Si le matériel doit attendre une interruption, un timer, un DPC ou une autre opération asynchrone, le driver peut conserver l'IRP en attente.

Code: Select all

IoMarkIrpPending(Irp);
return STATUS_PENDING;
L'IRP sera complété plus tard :

Code: Select all

Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = bytesTransferred;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
Point important

Un IRP retourné avec STATUS_PENDING doit réellement être complété plus tard. Un IRP ne doit jamais être complété deux fois.

7. File d'attente des IRP

Un périphérique matériel ne peut pas toujours traiter plusieurs opérations simultanément. Il faut alors mettre les IRP en file.

Le principe est généralement :

Code: Select all

IRP reçu
  -> vérifier si le périphérique est libre
  -> si libre : lancer l'opération
  -> sinon : mettre l'IRP en attente
  -> interruption/DPC de fin
  -> compléter l'IRP courant
  -> démarrer le suivant
Le framework KMDF automatise une grande partie de cette logique, mais en WDM elle doit souvent être gérée explicitement.

8. Propager un IRP dans une pile de drivers

Un filter driver ou un driver intermédiaire ne traite pas forcément lui-même la lecture ou l'écriture. Il peut transmettre l'IRP au driver inférieur.

Code: Select all

IoSkipCurrentIrpStackLocation(Irp);
return IoCallDriver(lowerDevice, Irp);
S'il faut observer la fin de la requête :

Code: Select all

IoCopyCurrentIrpStackLocationToNext(Irp);
IoSetCompletionRoutine(
    Irp,
    CompletionRoutine,
    context,
    TRUE,
    TRUE,
    TRUE
);
return IoCallDriver(lowerDevice, Irp);
9. Erreurs fréquentes
  • Compléter deux fois le même IRP.
  • Retourner STATUS_SUCCESS alors que l'IRP reste en attente.
  • Accéder au mauvais buffer selon le mode d'E/S.
  • Ne pas vérifier les longueurs fournies.
  • Écrire plus d'octets que la taille demandée.
  • Utiliser directement un pointeur User Mode à un IRQL inadapté.
  • Oublier de renseigner IoStatus.Information.
10. Résumé

Le chemin général d'une lecture ou écriture WDM est :

Code: Select all

ReadFile / WriteFile
        |
        v
IRP_MJ_READ / IRP_MJ_WRITE
        |
        v
IO_STACK_LOCATION
        |
        v
récupération du buffer
        |
        v
traitement ou mise en attente
        |
        v
IoStatus.Status / Information
        |
        v
IoCompleteRequest
La compréhension de ce chemin est indispensable avant d'étudier les MDL, les interruptions et le DMA.

Who is online

Users browsing this forum: No registered users and 1 guest