Esercitazione: Scrivere un driver Windows Hello World (Kernel-Mode Driver Framework)
Questo articolo descrive come scrivere un piccolo driver di Windows Universale usando il Kernel-Mode Driver Framework (KMDF) e quindi distribuire e installare il driver su un altro computer.
Prerequisiti
Seguire la procedura per installare Windows Driver Kit (WDK). Strumenti di debug per Windows viene incluso quando si installa WDK.
Installare Visual Studio 2022. Quando si installa Visual Studio 2022, selezionare il carico di lavoro sviluppo di
desktop con C++ e quindi in singoli componenti aggiungere:- MSVC v143 - VS 2022 C++ ARM64/ARM64EC librerie con mitigazione Spectre (più recente)
- MSVC v143 - VS 2022 C++ x64/x86 Librerie con mitigazione Spectre (versione più recente)
- ATL C++ per gli strumenti di compilazione v143 più recenti con mitigazioni Spectre (ARM64/ARM64EC)
- ATL C++ per gli strumenti di compilazione v143 più recenti con mitigazioni Spectre (x86 & x64)
- MFC C++ per gli strumenti di compilazione v143 più recenti con mitigazioni Spectre (ARM64/ARM64EC)
- MFC C++ per gli strumenti di compilazione v143 più recenti con mitigazioni Spectre (x86 & x64)
- Windows Driver Kit
Creare e compilare un driver
Aprire Microsoft Visual Studio. Nel menu File scegliere Nuovo > Progetto.
Nella finestra di dialogo
Crea un nuovo progetto selezionareC++ nell'elenco a discesa a sinistra, scegliere Windows nell'elenco a discesa centrale e scegliere Driver nell'elenco a discesa a destra.Selezionare Kernel Mode Driver, Vuoto (KMDF) dall'elenco dei Tipi di Progetto. Selezionare Avanti.
Suggerimento
Se non è possibile trovare modelli di progetto driver in Visual Studio, l'estensione WDK di Visual Studio non è stata installata correttamente. Per risolvere questo problema, avviare il programma di installazione di Visual Studio, selezionare Modifica, aggiungere Windows Driver Kits nella scheda Componente individuale e selezionare Modifica.
Nella finestra di dialogo Configura il nuovo progetto immettere "KmdfHelloWorld" nel campo Nome progetto.
Nota
Quando si crea un nuovo driver KMDF o UMDF, è necessario selezionare un nome di driver con un massimo di 32 caratteri. Questo limite di lunghezza è definito in wdfglobals.h.
Nel campo Percorso, inserisci la directory in cui desideri creare il nuovo progetto.
Controllare Posizionare la soluzione e il progetto nella stessa directory e selezionare Crea.
Visual Studio crea un progetto e una soluzione. È possibile visualizzarli nella finestra Esplora soluzioni. Se la finestra Esplora soluzioni non è visibile, scegliere Esplora soluzioni dal menu Visualizza. La soluzione ha un progetto driver denominato KmdfHelloWorld.
Nella finestra esplora soluzioni, tenere premuto (o fare clic con il tasto destro) sulla soluzione KmdfHelloWorld e scegliere Configuration Manager. Scegli una configurazione e una piattaforma per il progetto del driver. Ad esempio, scegliere Debug e x64.
Nella finestra
Esplora soluzioni , selezionare e tenere premuto (o fare clic con il pulsante destro del mouse) il progettoKmdfHelloWorld , scegliereAggiungie quindi selezionareNuovo elemento . Nella finestra di dialogo Aggiungi nuovo elemento selezionare File C++. Per Nome, immettere "Driver.c".
Nota
L'estensione del nome file è .c, non .cpp.
Selezionare Aggiungi. Il file Driver.c viene aggiunto sotto File di origine, come mostrato qui.
Scrivere il primo codice driver
Dopo aver creato il progetto Hello World vuoto e aggiunto il file di origine Driver.c, si scriverà il codice più semplice necessario per l'esecuzione del driver implementando due funzioni di callback degli eventi di base.
In Driver.c iniziare includendo queste intestazioni:
#include <ntddk.h> #include <wdf.h>
Consiglio
Se non è possibile aggiungere
Ntddk.h
, aprire Configurazione -> C/C++ -> Generale -> Directory di inclusione aggiuntiva e aggiungereC:\Program Files (x86)\Windows Kits\10\Include\<build#>\km
, sostituendo<build#>
con la directory appropriata nell'installazione di WDK.ntddk.h contiene definizioni del kernel Windows di base per tutti i driver, mentre Wdf.h contiene definizioni per i driver basati su Windows Driver Framework (WDF).
Specificare quindi le dichiarazioni per i due callback che verranno utilizzati:
DRIVER_INITIALIZE DriverEntry; EVT_WDF_DRIVER_DEVICE_ADD KmdfHelloWorldEvtDeviceAdd;
Usare il codice seguente per scrivere 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 è il punto di ingresso per tutti i driver, ad esempio
Main()
è per molte applicazioni in modalità utente. Il compito di DriverEntry è inizializzare le strutture e le risorse a livello del driver. In questo esempio, hai stampato "Hello World" per DriverEntry, hai configurato l'oggetto driver per registrare il punto di ingresso del callback EvtDeviceAdd, quindi hai creato l'oggetto driver e lo hai restituito.L'oggetto driver funge da oggetto padre per tutti gli altri oggetti framework che è possibile creare nel driver, che includono oggetti dispositivo, code di I/O, timer, spinlock e altro ancora. Per altre informazioni sugli oggetti framework, vedere Introduction to Framework Objects.
Consiglio
Per DriverEntry, è consigliabile mantenere il nome "DriverEntry" per facilitare l'analisi del codice e il debug.
Usare quindi il codice seguente per scrivere il 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 viene richiamato dal sistema quando rileva che il dispositivo è arrivato. Il processo consiste nell'inizializzare strutture e risorse per tale dispositivo. In questo esempio, hai semplicemente stampato un messaggio "Hello World" per EvtDeviceAdd, hai creato l'oggetto dispositivo e sei tornato. In altri driver che scrivi, potresti creare code di I/O per il tuo hardware, configurare uno spazio di archiviazione per il contesto del dispositivo per informazioni specifiche del dispositivo, o eseguire altre attività necessarie per preparare il dispositivo.
Suggerimento
Per aggiungere il callback del dispositivo, nota come l'hai chiamato con il nome del driver come prefisso (KmdfHelloWorldEvtDeviceAdd). In genere, è consigliabile denominare le funzioni del driver in questo modo per differenziarle dalle funzioni di altri driver. DriverEntry è l'unico che dovrebbe avere esattamente quel nome.
Il file Driver.c completo è ora simile al seguente:
#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; }
Salva Driver.c.
In questo esempio viene illustrato un concetto fondamentale dei driver: si tratta di una "raccolta di callback" che, una volta inizializzata, si siedono e attendono che il sistema li chiami quando ha bisogno di qualcosa. Una chiamata di sistema può essere un nuovo evento di arrivo del dispositivo, una richiesta di I/O da un'applicazione in modalità utente, un evento di arresto dell'alimentazione del sistema, una richiesta da un altro driver o un evento di rimozione a sorpresa quando un utente scollega il dispositivo in modo imprevisto. Fortunatamente, per dire "Hello World", è sufficiente preoccuparsi della creazione di driver e dispositivi.
Successivamente, creerai il tuo driver.
Compilare il driver
Nella finestra Esplora soluzioni, selezionare e tenere premuto (o fare clic con il pulsante destro del mouse su) la Soluzione 'KmdfHelloWorld' (1 progetto) e scegliere Configuration Manager. Scegliere una configurazione e una piattaforma per il progetto del driver. Per questo esercizio scegliere Debug e x64.
Nella finestra Esplora Soluzioni
selezionare e tenere premuto (o selezionare con il pulsante destro del mouse) KmdfHelloWorld e scegliere Proprietà. In Wpp Tracing > All Options, impostare il Run Wpp tracing su No. Selezionare Applica e quindi OK. Per compilare il driver, scegliere Compila soluzione dal menu Compila. Visual Studio mostra lo stato di avanzamento della compilazione nella finestra Output. Se la finestra Output non è visibile, scegliere Output dal menu Visualizza. Dopo aver verificato che la soluzione è stata compilata con successo, è possibile chiudere Visual Studio.
Per visualizzare il driver compilato, in Esplora File vai alla cartella KmdfHelloWorld e quindi a x64\Debug\KmdfHelloWorld. La cartella include:
- KmdfHelloWorld.sys - file del driver in modalità kernel
- KmdfHelloWorld.inf - un file di informazioni usato da Windows durante l'installazione del driver
- KmdfHelloWorld.cat - un file di catalogo usato dal programma di installazione per verificare la firma di test del driver
Mancia
Se viene visualizzato DriverVer set to a date in the future
durante la compilazione del driver, modificare le impostazioni del progetto driver in modo che Inf2Cat imposti /uselocaltime
. A tale scopo, usare Proprietà di configurazione ->Inf2Cat->Generale->Usa Ora Locale. Ora sia Stampinf che Inf2Cat usano l'ora locale.
Distribuire il driver
In genere, quando si esegue il test e il debug di un driver, il debugger e il driver vengono eseguiti in computer separati. Il computer che esegue il debugger viene chiamato computer host e il computer che esegue il driver viene chiamato il computer di destinazione . Il computer di destinazione viene chiamato anche il computer di test .
Finora è stato usato Visual Studio per compilare un driver nel computer host. È ora necessario configurare un computer di destinazione.
Seguire le istruzioni in Provvedere al provisioning di un computer per la distribuzione e il test dei driver (WDK 10).
Consiglio
Quando segui i passaggi per provisionare automaticamente il computer di destinazione usando un cavo di rete, prendi nota della porta e della chiave. Verranno usati più avanti nel passaggio di debug. In questo esempio si userà 50000 come porta e 1.2.3.4 come chiave.
Negli scenari di debug dei driver reali, è consigliabile usare una chiave generata da KDNET. Per ulteriori informazioni su come utilizzare KDNET per generare una chiave casuale, vedere l'argomento Debug Drivers - Step by Step Lab (Sysvad Kernel Mode).
Sul computer host, apri la tua soluzione in Visual Studio. È possibile fare doppio clic sul file della soluzione, KmdfHelloWorld.sln, nella cartella KmdfHelloWorld.
Nella finestra esplora soluzioni, selezionare e tenere premuto (o fare clic con il pulsante destro del mouse) sul progetto KmdfHelloWorld e scegliere Proprietà.
Nella finestra KmdfHelloWorld pagine delle proprietà, passare a Proprietà di configurazione > Installazione driver > Deployment, come illustrato di seguito.
Controllare Rimuovere le versioni precedenti del driver prima della distribuzione.
Per Nome dispositivo di destinazione, selezionare il nome del computer configurato per il test e il debug. In questo esercizio si usa un computer denominato MyTestComputer.
Selezionare Hardware ID Driver Updatee immettere l'ID hardware per il driver. Per questo esercizio, l'ID hardware è Root\KmdfHelloWorld. Selezionare OK.
Nota
In questo esercizio l'ID hardware non identifica un componente hardware reale. Identifica un dispositivo immaginario a cui verrà assegnato un posto nell'albero dei dispositivi come figlio del nodo principale. Per l'hardware reale, non selezionare Aggiornamento del driver per l'ID hardware; invece, selezionare Installa e verifica. L'ID hardware verrà visualizzato nel file inF (Information) del driver. Nella finestra Esplora soluzioni, passare a KmdfHelloWorld > Driver Files, e fare doppio clic su KmdfHelloWorld.inf. L'ID hardware si trova in [Standard.NT$ARCH$].
[Standard.NT$ARCH$] %KmdfHelloWorld.DeviceDesc%=KmdfHelloWorld_Device, Root\KmdfHelloWorld
Nel menu Compila, scegliere Distribuire la soluzione. Visual Studio copia automaticamente i file necessari per installare ed eseguire il driver nel computer di destinazione. La distribuzione può richiedere un minuto o due.
Quando si distribuisce un driver, i file del driver vengono copiati nella cartella %Systemdrive%\drivertest\drivers nel computer di test. Se si verifica un errore durante la distribuzione, è possibile verificare se i file vengono copiati nel computer di test. Verificare che i file con estensione inf, cat, certificato di test e .sys e tutti gli altri file necessari siano presenti nella cartella %systemdrive%\drivertest\drivers.
Per altre informazioni sulla distribuzione dei driver, vedere Distribuzione di un driver in un computer di test.
Installare il driver
Con il tuo driver Hello World distribuito sul computer di destinazione, ora installerai il driver. Quando in precedenza è stato effettuato il provisioning del computer di destinazione con Visual Studio usando l'opzione automatica , Visual Studio ha configurato il computer di destinazione per eseguire driver con firma di test come parte del processo di provisioning. Ora è sufficiente installare il driver usando lo strumento DevCon.
Sul computer host, accedi alla cartella Strumenti nell'installazione del WDK e individua lo strumento DevCon. Ad esempio, cercare nella cartella seguente:
C:\Program Files (x86)\Windows Kits\10\Tools\x64\devcon.exe
Copia lo strumento DevCon nel computer remoto.
Nel computer di destinazione installare il driver passando alla cartella contenente i file del driver, quindi eseguendo lo strumento DevCon.
Ecco la sintassi generale per lo strumento devcon che verrà usato per installare il driver:
devcon installare <file INF><ID hardware>
Il file INF necessario per l'installazione di questo driver è KmdfHelloWorld.inf. Il file INF contiene l'ID hardware per l'installazione del file binario del driver, KmdfHelloWorld.sys. Tenere presente che l'ID hardware, che si trova nel file INF, è Root\KmdfHelloWorld.
Apri una finestra del prompt dei comandi come amministratore. Vai alla cartella contenente il file del driver compilato .sys e inserisci questo comando:
devcon install kmdfhelloworld.inf root\kmdfhelloworld
Se viene visualizzato un messaggio di errore relativo a devcon che non viene riconosciuto, provare ad aggiungere il percorso allo strumento devcon . Ad esempio, se è stato copiato in una cartella nel computer di destinazione denominato C:\Tools, provare a usare il comando seguente:
c:\tools\devcon install kmdfhelloworld.inf root\kmdfhelloworld
Verrà visualizzata una finestra di dialogo che indica che il driver di test è un driver non firmato. Selezionare Installare questo driver comunque per continuare.
Eseguire il debug del driver
Dopo aver installato il driver KmdfHelloWorld sul computer di destinazione, collega in remoto un debugger dal computer host.
Nel computer host aprire una finestra del prompt dei comandi come amministratore. Passare alla directory WinDbg.exe. Verrà usata la versione x64 di WinDbg.exe da Windows Driver Kit (WDK) installata come parte dell'installazione del kit di Windows. Ecco il percorso predefinito per WinDbg.exe:
C:\Programmi (x86)\Windows Kits\10\Debuggers\x64
Avviare WinDbg per connettersi a una sessione di debug del kernel nel computer di destinazione usando il comando seguente. Il valore della porta e della chiave deve essere lo stesso di quello usato per configurare il computer di destinazione. Useremo 50000 per la porta e 1.2.3.4 per la chiave, i valori usati durante la fase di distribuzione. Il flag
k indica che si tratta di una sessione di debug del kernel. WinDbg -k net:port=50000,key=1.2.3.4
Sul menu Debug, scegliere Interrompi. Il debugger nel computer host accederà al computer di destinazione. Nella finestra di comando del debugger , è possibile visualizzare il prompt dei comandi di debug del kernel: kd>.
A questo punto, è possibile sperimentare con il debugger immettendo i comandi al prompt kd>. Ad esempio, è possibile provare questi comandi:
Per consentire l'esecuzione del computer di destinazione, scegliere Vai dal menu Debug o premere "g", quindi premere "invio".
Per arrestare la sessione di debug, scegliere Disconnetti Debugger dal menu Debug.
Importante
Assicurati di usare il comando "go" per permettere al computer di destinazione di riprendere l'esecuzione prima di uscire dal debugger, altrimenti il computer di destinazione non risponderà all'input del mouse e della tastiera perché è ancora in comunicazione con il debugger.
Per un procedimento dettagliato, passo dopo passo, del processo di debug dei driver, vedere Debug dei driver universali - Step by Step Lab (Echo Kernel-Mode).
Per ulteriori informazioni sul debug remoto, vedere il debug remoto tramite WinDbg.
Articoli correlati
Strumenti di debug per Windows
Eseguire il debug di Driver Universali - Laboratorio Passo dopo Passo (Echo Kernel-Mode)