Condividi tramite


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

  1. Aprire Microsoft Visual Studio. Nel menu File scegliere Nuovo > Progetto.

  2. Nella finestra di dialogo Crea un nuovo progetto selezionare C++ nell'elenco a discesa a sinistra, scegliere Windows nell'elenco a discesa centrale e scegliere Driver nell'elenco a discesa a destra.

  3. Selezionare Kernel Mode Driver, Vuoto (KMDF) dall'elenco dei Tipi di Progetto. Selezionare Avanti.

    Screenshot della finestra di dialogo Nuovo progetto di Visual Studio con l'opzione driver in modalità kernel selezionata.

    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.

  4. 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.

  5. Nel campo Percorso, inserisci la directory in cui desideri creare il nuovo progetto.

  6. Controllare Posizionare la soluzione e il progetto nella stessa directory e selezionare Crea.

    Screenshot della finestra di dialogo Configura il nuovo progetto di Visual Studio con il pulsante Crea evidenziato.

    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.

    Screenshot della finestra Esplora soluzioni di Visual Studio che mostra la soluzione e il progetto driver vuoto denominato KmdfHelloWorld.

  7. 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.

  8. Nella finestra Esplora soluzioni, selezionare e tenere premuto (o fare clic con il pulsante destro del mouse) il progetto KmdfHelloWorld, scegliereAggiungi e quindi selezionareNuovo elemento .

  9. 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.

    Screenshot della finestra Esplora soluzioni di Visual Studio che visualizza il file driver.c aggiunto al progetto driver.

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.

  1. 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 aggiungere C:\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).

  2. Specificare quindi le dichiarazioni per i due callback che verranno utilizzati:

    DRIVER_INITIALIZE DriverEntry;
    EVT_WDF_DRIVER_DEVICE_ADD KmdfHelloWorldEvtDeviceAdd;
    
  3. 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.

  4. 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.

  5. 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;
    }
    
  6. 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

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  1. 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).

  2. Sul computer host, apri la tua soluzione in Visual Studio. È possibile fare doppio clic sul file della soluzione, KmdfHelloWorld.sln, nella cartella KmdfHelloWorld.

  3. Nella finestra esplora soluzioni, selezionare e tenere premuto (o fare clic con il pulsante destro del mouse) sul progetto KmdfHelloWorld e scegliere Proprietà.

  4. Nella finestra KmdfHelloWorld pagine delle proprietà, passare a Proprietà di configurazione > Installazione driver > Deployment, come illustrato di seguito.

  5. Controllare Rimuovere le versioni precedenti del driver prima della distribuzione.

  6. 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.

  7. Selezionare Hardware ID Driver Updatee immettere l'ID hardware per il driver. Per questo esercizio, l'ID hardware è Root\KmdfHelloWorld. Selezionare OK.

    Screenshot della finestra delle pagine delle proprietà kmdfhelloworld con l'opzione di installazione del driver di distribuzione selezionata.

    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
    
  8. 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.

  1. 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.

  2. Nel computer di destinazione installare il driver passando alla cartella contenente i file del driver, quindi eseguendo lo strumento DevCon.

    1. 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.

    2. 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.

      Screenshot dell'avviso di sicurezza visualizzato durante il processo di installazione del driver.

Eseguire il debug del driver

Dopo aver installato il driver KmdfHelloWorld sul computer di destinazione, collega in remoto un debugger dal computer host.

  1. 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

  2. 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

  3. 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>.

  4. A questo punto, è possibile sperimentare con il debugger immettendo i comandi al prompt kd>. Ad esempio, è possibile provare questi comandi:

  5. Per consentire l'esecuzione del computer di destinazione, scegliere Vai dal menu Debug o premere "g", quindi premere "invio".

  6. 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.

Strumenti di debug per Windows

Eseguire il debug di Driver Universali - Laboratorio Passo dopo Passo (Echo Kernel-Mode)

Scrivi il tuo primo driver