Partager via


Tutoriel : Écrire un pilote Windows Hello World (infrastructure de pilotesKernel-Mode)

Cet article explique comment écrire un petit pilote Windows universel à l’aide de Kernel-Mode Driver Framework (KMDF), puis déployer et installer votre pilote sur un ordinateur distinct.

Conditions préalables

  • Suivez les étapes pour installer le kit de pilotes Windows (WDK). Les outils de débogage pour Windows sont inclus à l'installation du WDK.

  • Installez Visual Studio 2022. Lorsque vous installez Visual Studio 2022, sélectionnez la charge de travail Développement Desktop avec C++, puis, sous Composants individuels, ajoutez :

    • Bibliothèques avec atténuations Spectre MSVC v143 - VS 2022 C++ ARM64/ARM64EC (dernière version)
    • Bibliothèques avec atténuations Spectre MSVC v143 – VS 2022 C++ x64/x86 (dernière version)
    • C++ ATL pour la dernière version de Build Tools v143 avec atténuations Spectre (ARM64/ARM64EC)
    • C++ ATL pour la dernière version de Build Tools v143 avec atténuations Spectre (x86 & x64)
    • C++ MFC pour la dernière version de Build Tools v143 avec atténuations Spectre (ARM64/ARM64EC)
    • C++ MFC pour la dernière version de Build Tools v143 avec atténuations Spectre (x86 & x64)
    • Kit de pilotes Windows

Créez et construisez un pilote

  1. Ouvrez Microsoft Visual Studio. Dans le menu Fichier, choisissez Nouveau> projet.

  2. Dans la boîte de dialogue Créer un projet, sélectionnez C++ dans la liste déroulante gauche, choisissez Windows au milieu de la liste déroulante, puis choisissez pilote dans la liste déroulante de droite.

  3. Sélectionnez Kernel Mode Driver, Empty (KMDF) dans la liste des types de projets. Sélectionnez Suivant.

    Capture d’écran de la boîte de dialogue Nouveau projet Visual Studio avec l’option de pilote en mode noyau sélectionnée.

    Conseil

    Si vous ne trouvez pas de modèles de projet de pilote dans Visual Studio, l’extension WDK Visual Studio n’a pas été correctement installée. Pour résoudre ce problème, lancez visual Studio Installer, sélectionnez Modifier, ajoutez Kits de pilotes Windows sous l’onglet composant individuel, puis sélectionnez Modifier.

  4. Dans la boîte de dialogue Configurer votre nouveau projet, entrez « KmdfHelloWorld » dans le champ Nom du projet.

    Remarque

    Lorsque vous créez un pilote KMDF ou UMDF, vous devez sélectionner un nom de pilote qui comporte 32 caractères ou moins. Cette limite de longueur est définie dans wdfglobals.h.

  5. Dans le champ Emplacement, entrez le répertoire dans lequel vous souhaitez créer le projet.

  6. Vérifiez Placez la solution et le projet dans le même répertoire, puis sélectionnez Créer.

    Capture d’écran de la boîte de dialogue Configurer votre nouveau projet dans Visual Studio avec le bouton Créer mis en surbrillance.

    Visual Studio crée un projet et une solution. Vous pouvez les voir dans la fenêtre de l’Explorateur de solutions. (Si la fenêtre Explorateur de solutions n’est pas visible, choisissez Explorateur de solutions dans le menu Affichage.) La solution a un projet de pilote nommé KmdfHelloWorld.

    Capture d’écran de la fenêtre de l’Explorateur de solutions Visual Studio affichant la solution et le projet de pilote vide nommé KmdfHelloWorld.

  7. Dans la fenêtre de l’Explorateur de solutions, sélectionnez avec le bouton droit solution « KmdfHelloWorld » (1 sur 1 projet) et choisissez Configuration Manager. Choisissez une configuration et une plateforme pour le projet de pilote. Par exemple, choisissez Debug et x64.

  8. Dans la fenêtre de l’Explorateur de solutions, sélectionnez avec le bouton droit le projet KmdfHelloWorld, choisissez Ajouter, puis sélectionnez nouvel élément.

  9. Dans la boîte de dialogue Ajouter un nouvel élément, entrez « Driver.c ».

    Remarque

    L’extension de nom de fichier est .c, pas .cpp.

    Sélectionnez Ajouter. Le fichier Driver.c est ajouté sous Fichiers source, comme illustré ici.

    Capture d’écran de la fenêtre de l’Explorateur de solutions Visual Studio affichant le fichier driver.c ajouté au projet de pilote.

Écrivez votre premier code de pilote

Maintenant que vous avez créé votre projet Hello World vide et ajouté le fichier source Driver.c, vous écrivez le code le plus simple nécessaire pour que le pilote s’exécute en implémentant deux fonctions de rappel d’événement de base.

  1. Dans Driver.c, commencez par inclure ces en-têtes :

    #include <ntddk.h>
    #include <wdf.h>
    

    Conseil

    Si vous ne pouvez pas ajouter Ntddk.h, ouvrez Configuration,> C/C++,> Général,> Répertoires d'inclusion supplémentaires et ajoutez C:\Program Files (x86)\Windows Kits\10\Include\<build#>\km, en remplaçant <build#> par le répertoire approprié dans votre installation WDK.

    Ntddk.h contient des définitions de noyau Windows principales pour tous les pilotes, tandis que Wdf.h contient des définitions pour les pilotes basés sur WDF (Windows Driver Framework).

  2. Ensuite, fournissez des déclarations pour les deux rappels :

    DRIVER_INITIALIZE DriverEntry;
    EVT_WDF_DRIVER_DEVICE_ADD KmdfHelloWorldEvtDeviceAdd;
    
  3. Utilisez le code suivant pour écrire votre DriverEntry :

    NTSTATUS 
    DriverEntry(
        _In_ PDRIVER_OBJECT     DriverObject, 
        _In_ PUNICODE_STRING    RegistryPath
    )
    {
        // NTSTATUS variable to record success or failure
        NTSTATUS status = STATUS_SUCCESS;
    
        // Allocate the driver configuration object
        WDF_DRIVER_CONFIG config;
    
        // Print "Hello World" for DriverEntry
        KdPrintEx(( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "KmdfHelloWorld: DriverEntry\n" ));
    
        // Initialize the driver configuration object to register the
        // entry point for the EvtDeviceAdd callback, KmdfHelloWorldEvtDeviceAdd
        WDF_DRIVER_CONFIG_INIT(&config, 
                               KmdfHelloWorldEvtDeviceAdd
                               );
    
        // Finally, create the driver object
        status = WdfDriverCreate(DriverObject, 
                                 RegistryPath, 
                                 WDF_NO_OBJECT_ATTRIBUTES, 
                                 &config, 
                                 WDF_NO_HANDLE
                                 );
        return status;
    }
    

    DriverEntry est le point d’entrée de tous les pilotes, comme Main() concerne de nombreuses applications en mode utilisateur. Le rôle de DriverEntry est d'initialiser les structures et les ressources du pilote. Dans cet exemple, vous avez imprimé « Hello World » pour DriverEntry, configuré l'objet pilote pour enregistrer le point d'entrée de votre callback EvtDeviceAdd, puis créé l'objet pilote et renvoyé.

    L’objet de pilote fait office d’objet parent pour tous les autres objets de framework que vous pouvez créer dans votre pilote, notamment les objets d’appareil, les files d’attente d’E/S, les minuteurs, les verrous de spinlocks, etc. Pour plus d’informations sur les objets framework, consultez Présentation des objets framework.

    Conseil

    Pour DriverEntry, nous vous recommandons vivement de conserver le nom « DriverEntry » pour faciliter l’analyse du code et le débogage.

  4. Utilisez ensuite le code suivant pour écrire votre KmdfHelloWorldEvtDeviceAdd :

    NTSTATUS 
    KmdfHelloWorldEvtDeviceAdd(
        _In_    WDFDRIVER       Driver, 
        _Inout_ PWDFDEVICE_INIT DeviceInit
    )
    {
        // We're not using the driver object,
        // so we need to mark it as unreferenced
        UNREFERENCED_PARAMETER(Driver);
    
        NTSTATUS status;
    
        // Allocate the device object
        WDFDEVICE hDevice;    
    
        // Print "Hello World"
        KdPrintEx(( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "KmdfHelloWorld: KmdfHelloWorldEvtDeviceAdd\n" ));
    
        // Create the device object
        status = WdfDeviceCreate(&DeviceInit, 
                                 WDF_NO_OBJECT_ATTRIBUTES,
                                 &hDevice
                                 );
        return status;
    }
    

    EvtDeviceAdd est appelé par le système lorsqu’il détecte que votre appareil est arrivé. Son travail consiste à initialiser des structures et des ressources pour cet appareil. Dans cet exemple, vous avez imprimé un message « Hello World » pour EvtDeviceAdd, créé l’objet de l’appareil et retourné. Dans d'autres pilotes que vous pourriez écrire, vous pouvez créer des files d’attente d’E/S pour votre matériel, configurer un espace de stockage de contexte d’appareil pour des informations spécifiques au dispositif, ou effectuer d'autres tâches nécessaires pour préparer votre dispositif.

    Conseil

    Pour le callback d'ajout d'appareil, remarquez que vous l'avez nommé avec le nom de votre pilote comme préfixe (KmdfHelloWorldEvtDeviceAdd). En règle générale, nous vous recommandons de nommer les fonctions de votre pilote de cette façon pour les différencier des autres fonctions des pilotes. DriverEntry est le seul élément que vous devez nommer exactement de cette manière.

  5. Votre driver.c complet ressemble maintenant à ceci :

    #include <ntddk.h>
    #include <wdf.h>
    DRIVER_INITIALIZE DriverEntry;
    EVT_WDF_DRIVER_DEVICE_ADD KmdfHelloWorldEvtDeviceAdd;
    
    NTSTATUS 
    DriverEntry(
        _In_ PDRIVER_OBJECT     DriverObject, 
        _In_ PUNICODE_STRING    RegistryPath
    )
    {
        // NTSTATUS variable to record success or failure
        NTSTATUS status = STATUS_SUCCESS;
    
        // Allocate the driver configuration object
        WDF_DRIVER_CONFIG config;
    
        // Print "Hello World" for DriverEntry
        KdPrintEx(( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "KmdfHelloWorld: DriverEntry\n" ));
    
        // Initialize the driver configuration object to register the
        // entry point for the EvtDeviceAdd callback, KmdfHelloWorldEvtDeviceAdd
        WDF_DRIVER_CONFIG_INIT(&config, 
                               KmdfHelloWorldEvtDeviceAdd
                               );
    
        // Finally, create the driver object
        status = WdfDriverCreate(DriverObject, 
                                 RegistryPath, 
                                 WDF_NO_OBJECT_ATTRIBUTES, 
                                 &config, 
                                 WDF_NO_HANDLE
                                 );
        return status;
    }
    
    NTSTATUS 
    KmdfHelloWorldEvtDeviceAdd(
        _In_    WDFDRIVER       Driver, 
        _Inout_ PWDFDEVICE_INIT DeviceInit
    )
    {
        // We're not using the driver object,
        // so we need to mark it as unreferenced
        UNREFERENCED_PARAMETER(Driver);
    
        NTSTATUS status;
    
        // Allocate the device object
        WDFDEVICE hDevice;    
    
        // Print "Hello World"
        KdPrintEx(( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "KmdfHelloWorld: KmdfHelloWorldEvtDeviceAdd\n" ));
    
        // Create the device object
        status = WdfDeviceCreate(&DeviceInit, 
                                 WDF_NO_OBJECT_ATTRIBUTES,
                                 &hDevice
                                 );
        return status;
    }
    
  6. Enregistrer Driver.c.

Cet exemple illustre un concept fondamental des pilotes : il s’agit d’une « collection de rappels » qui, une fois initialisée, reste inactive et attend que le système les sollicite lorsque nécessaire. Un appel système peut être un nouvel événement d’arrivée d’appareil, une demande d’E/S à partir d’une application en mode utilisateur, un événement d’arrêt de l’alimentation du système, une demande d’un autre pilote ou un événement de suppression surprise lorsqu’un utilisateur déconnecte l’appareil de manière inattendue. Heureusement, pour dire « Hello World », vous n'avez besoin de vous soucier que de la création de drivers et d'appareils.

Ensuite, vous construisez votre pilote.

Générer le pilote

  1. Dans la fenêtre de l’Explorateur de solutions, sélectionnez avec le bouton droit solution « KmdfHelloWorld » (1 sur 1 projet) et choisissez Configuration Manager. Choisissez une configuration et une plateforme pour le projet de pilote. Pour cet exercice, choisissez Debug et x64.

  2. Dans la fenêtre de l’Explorateur de solutions, cliquez avec le bouton droit sur KmdfHelloWorld et choisissez Propriétés. Dans Traçage Wpp> Toutes les options, définissez Lancer le traçage Wpp sur Non Sélectionnez Appliquer, puis OK.

  3. Pour construire votre pilote, choisissez Build Solution dans le menu Build. Visual Studio affiche la progression de la construction dans la fenêtre Sortie. (Si la fenêtre Sortie n’est pas visible, choisissez Sortie dans le menu Affichage .) Lorsque vous vérifiez que la solution a été créée avec succès, vous pouvez fermer Visual Studio.

  4. Pour afficher le pilote généré, dans l’Explorateur de fichiers, accédez à votre dossier KmdfHelloWorld, puis à x64\Debug\KmdfHelloWorld. Le dossier comprend les éléments suivants :

    • KmdfHelloWorld.sys :- le fichier de pilote en mode noyau
    • KmdfHelloWorld.inf : fichier d’informations que Windows utilise lorsque vous installez le pilote
    • KmdfHelloWorld.cat -- fichier catalogue que le programme d’installation utilise pour vérifier la signature de test du pilote

Conseil

Si vous voyez DriverVer set to a date in the future lors de la génération de votre pilote, modifiez les paramètres de votre projet de pilote afin que Inf2Cat définit /uselocaltime. Pour ce faire, utilisez les propriétés de configuration >Inf2Cat->Général->Utiliser l’heure locale. À présent, Stampinf et Inf2Cat utilisent l’heure locale.

Déployer le pilote

En règle générale, lorsque vous testez et déboguez un pilote, le débogueur et le pilote s’exécutent sur des ordinateurs distincts. L’ordinateur qui exécute le débogueur est appelé 'ordinateur hôte, et l’ordinateur qui exécute le pilote est appelé ordinateur cible. L’ordinateur cible est également appelé ordinateur de test.

Jusqu’à présent, vous avez utilisé Visual Studio pour générer un pilote sur l’ordinateur hôte. Vous devez maintenant configurer un ordinateur cible.

  1. Suivez les instructions dans Configurer un ordinateur pour déployer et tester des pilotes (WDK 10).

    Conseil

    Lorsque vous suivez les étapes permettant de provisionner automatiquement l’ordinateur cible à l’aide d’un câble réseau, notez le port et la clé. Vous les utiliserez ultérieurement à l’étape de débogage. Dans cet exemple, vous utilisez 50000 comme port et 1.2.3.4 comme clé.

    Dans les scénarios de débogage de pilotes réels, nous vous recommandons d’utiliser une clé générée par KDNET. Pour plus d’informations sur l’utilisation de KDNET pour générer une clé aléatoire, consultez la rubrique Debug Drivers - Step by Step Lab (Mode noyau Sysvad).

  2. Sur l’ordinateur hôte, ouvrez votre solution dans Visual Studio. Vous pouvez double-cliquer sur le fichier de solution, KmdfHelloWorld.sln, dans votre dossier KmdfHelloWorld.

  3. Dans la fenêtre de l’Explorateur de solutions, cliquez avec le bouton droit sur le projet KmdfHelloWorld, puis choisissez Propriétés.

  4. Allez à Installation du pilote > Déploiement.

  5. Pour Nom de l’appareil cible, sélectionnez le nom de l’ordinateur que vous avez configuré pour les tests et le débogage. Dans cet exercice, nous utilisons un ordinateur nommé MyTestComputer.

  6. Pour vous assurer que vous testez la dernière version du pilote, vérifiez Supprimer les versions précédentes du pilote avant le déploiement.

  7. Sélectionnez Mise à jour du pilote via l’ID matériel, et entrez l’ID matériel pour votre pilote. Pour cet exercice, l’ID matériel est Root\KmdfHelloWorld. Sélectionnez OK.

    Remarque

    Dans cet exercice, l’ID matériel n’identifie pas un élément matériel réel. Il identifie un appareil imaginaire qui est donné un emplacement dans l’arborescence d’appareils en tant qu’enfant du nœud racine. Pour le matériel réel, ne sélectionnez pas Mise à jour du pilote ID matériel ; sélectionnez plutôt Installation et vérification. L'ID du matériel apparaît dans le fichier d'information du pilote (INF). Dans la fenêtre de l’Explorateur de solutions, accédez à KmdfHelloWorld > Driver Files, puis double-cliquez sur KmdfHelloWorld.inf. L’ID matériel se trouve sous [Standard.NT$ARCH$].

    [Standard.NT$ARCH$]
    %KmdfHelloWorld.DeviceDesc%=KmdfHelloWorld_Device, Root\KmdfHelloWorld
    
  8. Dans le menu Build, choisissez Déployer la solution. Visual Studio copie automatiquement les fichiers requis pour installer et exécuter le pilote sur l’ordinateur cible. Le déploiement peut prendre une minute ou deux.

    Lorsque vous déployez un pilote, les fichiers du pilote sont copiés dans le dossier %Systemdrive%\drivertest\drivers sur l’ordinateur de test. Si un problème se produit pendant le déploiement, vous pouvez vérifier si les fichiers sont copiés sur l’ordinateur de test. Vérifiez que les fichiers .inf, .cat, test cert et .sys, et tous les autres fichiers nécessaires, sont présents dans le dossier %systemdrive%\drivertest\drivers.

    Pour plus d’informations sur le déploiement de pilotes, consultez Déploiement d’un pilote sur un ordinateur de test.

Installer le pilote

Avec votre pilote Hello World déployé sur l’ordinateur cible, vous installez maintenant le pilote. Lorsque vous avez précédemment approvisionné l’ordinateur cible avec Visual Studio à l’aide de l’option automatique , Visual Studio a configuré l’ordinateur cible pour exécuter des pilotes signés de test dans le cadre du processus d’approvisionnement. Vous devez maintenant installer le pilote à l’aide de l’outil DevCon.

  1. Sur l’ordinateur hôte, accédez au dossier Outils de votre installation WDK et recherchez l’outil DevCon. Par exemple, recherchez dans le dossier suivant :

    C:\Program Files (x86)\Windows Kits\10\Tools\x64\devcon.exe

    Copiez l’outil DevCon sur votre ordinateur distant.

  2. Sur l’ordinateur cible, installez le pilote en accédant au dossier contenant les fichiers du pilote, puis en exécutant l’outil DevCon.

    1. Voici la syntaxe générale de l’outil devcon que vous allez utiliser pour installer le pilote :

      Fichier INF< de ><devcon installI>dentification du matériel

      Le fichier INF requis pour l’installation de ce pilote est KmdfHelloWorld.inf. Le fichier INF contient l'ID du matériel pour l'installation du pilote binaire, KmdfHelloWorld.sys. Rappelez-vous que l’ID matériel, situé dans le fichier INF, est Root\KmdfHelloWorld.

    2. Ouvrez une fenêtre d’invite de commande en tant qu’Administrateur. Accédez à votre dossier contenant le fichier de pilote construit .sys et saisissez cette commande :

      devcon install kmdfhelloworld.inf root\kmdfhelloworld

      Si vous recevez un message d'erreur indiquant que devcon n'est pas reconnu, essayez d'ajouter le chemin d'accès à l'outil devcon. Par exemple, si vous l’avez copiée dans un dossier sur l’ordinateur cible appelé C :\Tools, essayez d’utiliser la commande suivante :

      c :\tools\devcon install kmdfhelloworld.inf root\kmdfhelloworld

      Une boîte de dialogue s’affiche indiquant que le pilote de test est un pilote non signé. Sélectionnez Installer ce pilote quand même pour continuer.

      Capture d’écran de l’avertissement de sécurité affiché pendant le processus d’installation du pilote.

Déboguez le pilote

Maintenant que vous avez installé votre pilote KmdfHelloWorld sur l’ordinateur cible, vous attachez un débogueur à distance à partir de l’ordinateur hôte.

  1. Sur l’ordinateur hôte, ouvrez une fenêtre d’invite de commandes en tant qu’administrateur. Accédez au répertoire WinDbg.exe. Vous utilisez la version x64 de WinDbg.exe à partir du Kit de pilotes Windows (WDK) installé dans le cadre de l’installation du kit Windows. Voici le chemin d’accès par défaut à WinDbg.exe:

    C :\Program Files (x86)\Windows Kits\10\Debuggers\x64

  2. Lancez WinDbg pour vous connecter à une session de débogage du noyau sur l’ordinateur cible à l’aide de la commande suivante. La valeur du port et de la clé doit être identique à celle que vous avez utilisée pour approvisionner l’ordinateur cible. Vous utilisez 50000 pour le port et 1.2.3.4 pour la clé, les valeurs que vous avez utilisées pendant l’étape de déploiement. L’indicateur k indique qu’il s’agit d’une session de débogage du noyau.

    WinDbg -k net:port=50000,key=1.2.3.4

  3. Dans le menu Debug, choisissez Break. Le débogueur de l'ordinateur hôte se connecte à l'ordinateur cible. Dans la fenêtre de commande du débogueur, vous pouvez voir la requête de commande de débogage du noyau : kd>.

  4. À ce stade, vous pouvez expérimenter le débogueur en entrant des commandes à la requête kd>. Par exemple, vous pouvez essayer ces commandes :

  5. Pour laisser réexécuter l’ordinateur cible, choisissez Go dans le menu Déboguer ou appuyez sur « g », puis appuyez sur « Entrée ».

  6. Pour arrêter la session de débogage, choisissez Detach Debugger dans le menu Debug

    Important

    Veillez à utiliser la commande « go » pour laisser l’ordinateur cible s’exécuter à nouveau avant de quitter le débogueur, ou l’ordinateur cible ne répond pas à votre souris et à l’entrée du clavier, car il parle toujours au débogueur.

Pour une procédure pas-à-pas détaillée du processus de débogage des pilotes, voir Déboguer les pilotes universels - Labo étape par étape (Mode noyau Echo).

Pour plus d’informations sur le débogage à distance, consultez Débogage à distance avec WinDbg.

Outils de débogage pour Windows

Débogage des pilotes universels - laboratoire étape par étape (mode noyau écho)

Écrire votre premier pilote