Udostępnij za pośrednictwem


Samouczek: pisanie sterownika Hello World dla systemu Windows (Kernel-Mode Driver Framework)

W tym artykule opisano sposób pisania małego sterowników uniwersalnych systemu Windows przy użyciu programu Kernel-Mode Driver Framework (KMDF), a następnie wdrażania i instalowania sterownika na osobnym komputerze.

Warunki wstępne

  • Postępuj zgodnie z instrukcjami, aby zainstalować zestaw Windows Driver Kit (WDK). Narzędzia debugowania dla systemu Windows są uwzględniane podczas instalowania zestawu WDK.

  • Zainstaluj program Visual Studio 2022. Po zainstalowaniu programu Visual Studio 2022 wybierz pakiet roboczy Rozwój aplikacji desktopowych z C++, a następnie w sekcji Poszczególne składniki dodaj:

    • MSVC v143 — VS 2022 C++ ARM64/ARM64EC Biblioteki ze wsparciem dla ograniczenia Spectre (najnowsza wersja)
    • MSVC w wersji 143 — biblioteki zabezpieczone przed Spectre dla programu VS 2022 C++ x64/x86 (najnowsze)
    • C++ ATL dla najnowszych narzędzi kompilacji w wersji 143 z zabezpieczeniami Spectre (ARM64/ARM64EC)
    • C++ ATL dla najnowszych narzędzi kompilacyjnych w wersji 143 ze wsparciem zabezpieczeń Spectre (x86 & x64)
    • C++ MFC dla najnowszych narzędzi kompilacji wersji v143 z zabezpieczeniami Spectre (ARM64/ARM64EC)
    • C++ MFC dla najnowszej wersji 143 narzędzi kompilacyjnych ze środkami zaradczymi Spectre (x86 & x64)
    • Zestaw sterowników systemu Windows

Tworzenie i budowanie sterownika

  1. Otwórz program Microsoft Visual Studio. W menu Plik wybierz pozycję Nowy > projekt.

  2. W oknie dialogowym tworzenie nowego projektu wybierz pozycję C++ na liście rozwijanej po lewej stronie, wybierz pozycję Windows na środkowej liście rozwijanej, a następnie wybierz pozycję Driver na prawej liście rozwijanej.

  3. Wybierz sterownik trybu jądra (Kernel Mode Driver), pusty (KMDF) z listy typów projektów. Wybierz pozycję Dalej.

    Zrzut ekranu przedstawiający okno dialogowe nowego projektu programu Visual Studio z wybraną opcją sterownika trybu jądra.

    Napiwek

    Jeśli nie możesz znaleźć szablonów projektów sterowników w programie Visual Studio, rozszerzenie programu Visual Studio WDK nie zostało poprawnie zainstalowane. Aby rozwiązać ten problem, uruchom Instalator programu Visual Studio, wybierz pozycję Modyfikuj, dodaj Zestawy sterowników systemu Windows na karcie Składniki indywidualne, a następnie wybierz pozycję Modyfikuj.

  4. W oknie dialogowym Konfigurowanie nowego projektu wpisz "KmdfHelloWorld" w polu nazwa projektu.

    Notatka

    Podczas tworzenia nowego sterownika KMDF lub UMDF należy wybrać nazwę sterownika, który ma 32 znaki lub mniej. Ten limit długości jest zdefiniowany w pliku wdfglobals.h.

  5. W polu Lokalizacja wprowadź katalog, w którym chcesz utworzyć nowy projekt.

  6. Sprawdź Umieść rozwiązanie i projekt w tym samym katalogu i wybierz pozycję Utwórz.

    Zrzut ekranu przedstawiający okno dialogowe Konfigurowanie nowego projektu w programie Visual Studio z wyróżnionym przyciskiem Utwórz.

    Program Visual Studio tworzy jeden projekt i rozwiązanie. Można je wyświetlić w oknie eksploratora rozwiązań . (Jeśli okno Eksplorator rozwiązań nie jest widoczne, wybierz pozycję Eksplorator rozwiązań z menu Widok). Rozwiązanie ma projekt sterownika o nazwie KmdfHelloWorld.

    Zrzut ekranu przedstawiający okno Eksploratora rozwiązań programu Visual Studio z wyświetlonym rozwiązaniem i pustym projektem sterownika o nazwie KmdfHelloWorld.

  7. W oknie Eksploratora rozwiązań kliknij prawym przyciskiem myszy na rozwiązanie "KmdfHelloWorld" (1 z 1 projektu) i wybierz pozycję Configuration Manager. Wybierz konfigurację i platformę dla projektu sterownika. Na przykład wybierz pozycję Debug i x64.

  8. W oknie Eksploratora rozwiązań kliknij prawym przyciskiem myszy projekt KmdfHelloWorld, wybierz pozycję Dodaj, a następnie wybierz Nowy Element.

  9. W oknie dialogowym Dodaj nowy element wprowadź "Driver.c".

    Notatka

    Rozszerzenie nazwy pliku to .c, a nie .cpp.

    Wybierz pozycję Dodaj. Plik Driver.c jest dodawany w obszarze Pliki źródłowe, jak tutaj pokazano.

    Zrzut ekranu przedstawiający okno Eksploratora rozwiązań programu Visual Studio z wyświetlonym plikiem driver.c dodanym do projektu sterownika.

Pisanie pierwszego kodu sterownika

Po utworzeniu pustego projektu Hello World i dodaniu pliku źródłowego Driver.c napiszesz najbardziej podstawowy kod niezbędny do uruchomienia sterownika przez zaimplementowanie dwóch podstawowych funkcji wywołania zwrotnego zdarzeń.

  1. W pliku Driver.c zacznij od włączenia następujących nagłówków:

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

    Napiwek

    Jeśli nie możesz dodać Ntddk.h, otwórz Konfiguracja —> C/C++ —> Ogólne —> Dodatkowe Katalogi Dołączania i dodaj C:\Program Files (x86)\Windows Kits\10\Include\<build#>\km, zastępując <build#> odpowiednim katalogiem w instalacji zestawu WDK.

    Ntddk.h zawiera podstawowe definicje jądra systemu Windows dla wszystkich sterowników, a Wdf.h zawiera definicje sterowników opartych na strukturze sterowników systemu Windows (WDF).

  2. Następnie podaj deklaracje dla dwóch wywołań zwrotnych:

    DRIVER_INITIALIZE DriverEntry;
    EVT_WDF_DRIVER_DEVICE_ADD KmdfHelloWorldEvtDeviceAdd;
    
  3. Użyj następującego kodu, aby napisać 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 to punkt wejścia dla wszystkich sterowników, na przykład Main() jest przeznaczony dla wielu aplikacji w trybie użytkownika. Zadaniem DriverEntry jest zainicjowanie struktur i zasobów obejmujących cały sterownik. W tym przykładzie wydrukowano frazę "Hello World" dla DriverEntry, skonfigurowano obiekt sterownika do zarejestrowania punktu wejścia wywołania zwrotnego EvtDeviceAdd, a następnie utworzono obiekt sterownika i zwrócono go.

    Obiekt sterownika działa jako obiekt nadrzędny dla wszystkich innych obiektów struktury, które można utworzyć w sterowniku, w tym obiektów urządzeń, kolejek we/wy, czasomierzy, spinlocks i nie tylko. Aby uzyskać więcej informacji na temat obiektów struktury, zobacz Introduction to Framework Objects.

    Napiwek

    W przypadku DriverEntryzdecydowanie zalecamy zachowanie nazwy jako "DriverEntry", aby ułatwić analizę kodu i debugowanie.

  4. Następnie użyj następującego kodu, aby napisać 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 jest wywoływany przez system po wykryciu, że urządzenie dotarło. Jego zadaniem jest inicjowanie struktur i zasobów dla tego urządzenia. W tym przykładzie wydrukowałeś komunikat "Hello World" dla EvtDeviceAdd, utworzyłeś obiekt urządzenia i zwróciłeś. W innych pisanych przez ciebie sterownikach możesz utworzyć kolejki I/O dla sprzętu, skonfigurować kontekst urządzenia przestrzeni przechowywania dla informacji specyficznych dla urządzenia lub wykonać inne zadania potrzebne do przygotowania urządzenia.

    Napiwek

    Dodając wywołanie zwrotne dla urządzenia, zwróć uwagę na to, jak nazwano je przy użyciu nazwy sterownika jako prefiksu (KmdfHelloWorldEvtDeviceAdd). Ogólnie rzecz biorąc, zalecamy nazewnictwo funkcji kierowcy w ten sposób, aby odróżnić je od funkcji innych sterowników. DriverEntry to jedyna nazwa, którą powinieneś użyć dokładnie w tej formie.

  5. Kompletny plik Driver.c wygląda teraz następująco:

    #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. Zapisz driver.c.

W tym przykładzie przedstawiono podstawową koncepcję sterowników: są one "kolekcją wywołań zwrotnych", które po zainicjowaniu siedzą i czekają, aż system je wywoła, gdy coś potrzebuje. Wywołanie systemowe może być zdarzeniem pojawienia się nowego urządzenia, operacją wejścia/wyjścia z aplikacji trybu użytkownika, zdarzeniem wyłączenia zasilania systemu, żądaniem od innego sterownika lub zdarzeniem niespodziewanego usunięcia, kiedy użytkownik nieoczekiwanie odłączy urządzenie. Na szczęście, aby napisać "Hello World", musiałeś się martwić tylko o tworzenie sterowników i urządzeń.

Następnie skompilujesz sterownik.

Kompilowanie sterownika

  1. W oknie Solution Explorer kliknij prawym przyciskiem na Solution 'KmdfHelloWorld' (1 z 1 projektu) i wybierz Configuration Manager. Wybierz konfigurację i platformę dla projektu sterownika. W tym ćwiczeniu wybierz Debug i x64.

  2. W oknie Eksploratora rozwiązań wybierz prawym przyciskiem KmdfHelloWorld i wybierz Właściwości. W śledzenie Wpp > wszystkie opcjeustaw Uruchom śledzenie WppNie. Wybierz pozycję Zastosuj, następnie OK.

  3. Aby skompilować sterownik, wybierz pozycję Kompiluj rozwiązanie z menu kompilacji. Program Visual Studio pokazuje postęp kompilacji w oknie Wynik. (Jeśli okno danych wyjściowych nie jest widoczne, wybierz Dane wyjściowe z menu Widok.) Po upewnieniu się, że rozwiązanie zostało pomyślnie stworzone, możesz zamknąć program Visual Studio.

  4. Aby wyświetlić wbudowany sterownik, w Eksploratorze plików przejdź do folderu KmdfHelloWorld, a następnie przejdź do folderu x64\Debug\KmdfHelloWorld. Folder zawiera następujące elementy:

    • KmdfHelloWorld.sys — plik sterownika trybu jądra
    • KmdfHelloWorld.inf — plik informacyjny używany przez system Windows podczas instalowania sterownika
    • KmdfHelloWorld.cat — plik wykazu używany przez instalatora do weryfikowania podpisu testowego sterownika

Napiwek

Jeśli podczas kompilowania sterownika zobaczysz DriverVer set to a date in the future, zmień ustawienia projektu sterownika, aby program Inf2Cat ustawił /uselocaltime. W tym celu użyj właściwości konfiguracji —>Inf2Cat->Ogólne —>użyjczasu lokalnego. Teraz zarówno stampinf, jak i Inf2Cat używają czasu lokalnego.

Wdrażaj sterownik

Zazwyczaj podczas testowania i debugowania sterownika debuger i sterownik są uruchamiane na oddzielnych komputerach. Komputer z uruchomionym debugerem jest nazywany komputerem hosta , a komputer z uruchomionym sterownikiem jest nazywany komputerem docelowym . Komputer docelowy jest również nazywany komputerem testowym .

Do tej pory użyto programu Visual Studio do skompilowania sterownika na komputerze hosta. Teraz należy skonfigurować komputer docelowy.

  1. Postępuj zgodnie z instrukcjami w Konfiguruj komputer na potrzeby wdrażania i testowania sterowników (WDK 10).

    Napiwek

    Podczas wykonywania kroków w celu automatycznego aprowizowania komputera docelowego przy użyciu kabla sieciowego, zanotuj port i klucz. Będą one używane w dalszej części kroku debugowania. W tym przykładzie jako klucza użyjesz 50000 jako portu i 1.2.3.4.

    W rzeczywistych scenariuszach debugowania sterowników zalecamy użycie klucza wygenerowanego przez KDNET. Aby uzyskać więcej informacji na temat używania KDNET do generowania klucza losowego, zobacz temat Debug Drivers - Step by Step Lab (Sysvad Kernel Mode).

  2. Na komputerze-hoście otwórz rozwiązanie w programie Visual Studio. Możesz kliknąć dwukrotnie plik rozwiązania, KmdfHelloWorld.sln, w folderze KmdfHelloWorld.

  3. W oknie eksploratora rozwiązań kliknij prawym przyciskiem myszy projekt KmdfHelloWorld, a następnie wybierz polecenie Właściwości .

  4. Przejdź do Instalacja Sterownika > Wdrożenie.

  5. Dla nazwa urządzenia docelowego, wybierz nazwę komputera, który skonfigurowałeś do testowania i debugowania. W tym ćwiczeniu użyjemy komputera o nazwie MyTestComputer.

  6. Aby upewnić się, że testujesz najnowszą wersję sterownika, sprawdź Usuń poprzednie wersje sterowników przed wdrożeniem.

  7. Wybierz aktualizację sterownika identyfikatora sprzętui wprowadź identyfikator sprzętu dla swojego sterownika. W tym ćwiczeniu identyfikator sprzętu to Root\KmdfHelloWorld. Wybierz pozycję OK.

    Notatka

    W tym ćwiczeniu identyfikator sprzętu nie identyfikuje rzeczywistego sprzętu. Identyfikuje wyimaginowane urządzenie, które jest umieszczone w drzewie urządzeń jako dziecko węzła głównego. Dla rzeczywistego sprzętu nie wybieraj opcji aktualizacji sterowników identyfikatora sprzętu; zamiast tego wybierz opcję Zainstaluj i zweryfikuj. Identyfikator sprzętu jest widoczny w pliku informacji o sterowniku (INF). W oknie Eksplorator rozwiązań przejdź do KmdfHelloWorld > Driver Files, a następnie kliknij dwukrotnie plik KmdfHelloWorld.inf. Identyfikator sprzętu znajduje się w obszarze [Standard.NT$ARCH$].

    [Standard.NT$ARCH$]
    %KmdfHelloWorld.DeviceDesc%=KmdfHelloWorld_Device, Root\KmdfHelloWorld
    
  8. W menu kompilacji wybierz pozycję Wdróż rozwiązanie. Program Visual Studio automatycznie kopiuje pliki wymagane do zainstalowania i uruchomienia sterownika na komputerze docelowym. Wdrożenie może potrwać minutę lub dwie.

    Podczas wdrażania sterownika pliki sterowników są kopiowane do folderu %Systemdrive%\drivertest\drivers na komputerze testowym. Jeśli coś pójdzie nie tak podczas wdrażania, możesz sprawdzić, czy pliki są kopiowane na komputer testowy. Sprawdź, czy pliki .inf, cat, test cert i .sys oraz inne niezbędne pliki znajdują się w folderze %systemdrive%\drivertest\drivers.

    Aby uzyskać więcej informacji na temat wdrażania sterowników, zobacz Wdrażanie sterownika na komputerze testowym.

Instalowanie sterownika

Po wdrożeniu sterownika Hello World na komputerze docelowym należy zainstalować sterownik. Po przygotowaniu komputera docelowego przy użyciu opcji automatycznego w programie Visual Studio, program Visual Studio skonfigurował komputer docelowy do uruchamiania testowo podpisanych sterowników w ramach procesu przygotowywania. Teraz wystarczy zainstalować sterownik przy użyciu narzędzia DevCon.

  1. Na komputerze-hoście przejdź do folderu Narzędzia w instalacji zestawu WDK i znajdź narzędzie DevCon. Na przykład poszukaj w następującym folderze:

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

    Skopiuj narzędzie DevCon do komputera zdalnego.

  2. Na komputerze docelowym zainstaluj sterownik, przechodząc do folderu zawierającego pliki sterowników, a następnie uruchamiając narzędzie DevCon.

    1. Poniżej przedstawiono ogólną składnię narzędzia deweloperskiego, którego użyjesz do zainstalowania sterownika:

      devcon install <INF file><identyfikator sprzętu>

      Plik INF wymagany do zainstalowania tego sterownika to KmdfHelloWorld.inf. Plik INF zawiera identyfikator sprzętu do instalowania pliku binarnego sterownika KmdfHelloWorld.sys. Pamiętaj, że identyfikator sprzętu znajdujący się w pliku INF jest Root\KmdfHelloWorld.

    2. Otwórz okno wiersza polecenia jako administrator. Przejdź do folderu zawierającego ten skompilowany plik sterownika .sys i wprowadź polecenie.

      devcon install kmdfhelloworld.inf root\kmdfhelloworld

      Jeśli otrzymasz komunikat o błędzie informujący, że devcon nie został rozpoznany, spróbuj dodać ścieżkę do narzędzia devcon. Jeśli na przykład skopiowano go do folderu na komputerze docelowym o nazwie C:\Tools, spróbuj użyć następującego polecenia:

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

      Zostanie wyświetlone okno dialogowe wskazujące, że sterownik testowy jest niepodpisanym sterownikiem. Wybierz pozycję Zainstaluj ten sterownik mimo wszystko, aby kontynuować.

      Zrzut ekranu przedstawiający ostrzeżenie o zabezpieczeniach wyświetlane podczas procesu instalacji sterownika.

Debugowanie sterownika

Po zainstalowaniu sterownika KmdfHelloWorld na komputerze docelowym należy dołączyć debuger zdalnie z komputera hosta.

  1. Na komputerze hosta otwórz okno wiersza polecenia jako administrator. Przejdź do katalogu WinDbg.exe. Używasz x64version WinDbg.exe z Windows Driver Kit (WDK), który został zainstalowany jako część instalacji zestawu Windows. Oto domyślna ścieżka do WinDbg.exe:

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

  2. Uruchom narzędzie WinDbg, aby nawiązać połączenie z sesją debugowania jądra na komputerze docelowym, używając następującego polecenia. Wartość portu i klucza powinna być taka sama jak wartość użyta do aprowizacji komputera docelowego. Należy użyć 50000 dla portu i 1.2.3.4 dla klucza, wartości użyte podczas kroku wdrażania. Flaga k wskazuje, że jest to sesja debugowania jądra.

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

  3. Na menu Debug wybierz Break. Debuger na komputerze hosta przerywa działanie na komputerze docelowym. W oknie polecenia debugera można zobaczyć wiersz polecenia debugowania jądra: kd>.

  4. W tym momencie możesz eksperymentować z debugerem, wprowadzając polecenia w wierszu polecenia kd>. Możesz na przykład wypróbować następujące polecenia:

  5. Aby umożliwić ponowne uruchomienie komputera docelowego, wybierz pozycję Przejdź z menu Debug lub naciśnij "g", a następnie naciśnij "Enter".

  6. Aby zatrzymać sesję debugowania, wybierz pozycję Odłącz debuger z menu Debug.

    Ważny

    Upewnij się, że używasz polecenia "go", aby umożliwić ponowne uruchomienie komputera docelowego przed wyjściem z debugera lub komputer docelowy pozostanie nie odpowiadać na dane wejściowe myszy i klawiatury, ponieważ nadal rozmawia z debugerem.

Aby uzyskać szczegółowy przewodnik krok po kroku procesu debugowania sterowników, zobacz Debugowanie uniwersalnych sterowników — Step by Step Lab (Echo Kernel-Mode).

Aby uzyskać więcej informacji na temat zdalnego debugowania, zobacz Zdalne debugowanie przy użyciu WinDbg.

narzędzia debugowania dla systemu Windows

Debugowanie uniwersalnych sterowników — laboratorium krok po kroku (Echo Kernel-Mode)

Napisz swój pierwszy sterownik