Compartir a través de


Tutorial: Escribir un controlador de Windows Hello World (Kernel-Mode Driver Framework)

En este artículo se describe cómo escribir un pequeño controlador universal de Windows utilizando el Kernel-Mode Marco de Controladores (KMDF) y, a continuación, implementar e instalar su controlador en un equipo independiente.

Prerrequisitos

  • Siga los pasos para instalar Windows Driver Kit (WDK). Las Herramientas de depuración para Windows se incluyen al instalar el WDK.

  • Instale Visual Studio 2022. Al instalar Visual Studio 2022, seleccione la carga de trabajo Desarrollo de escritorio con C++ y, a continuación, en Componentes individuales, agregue lo siguiente:

    • Bibliotecas con mitigaciones de Spectre de ARM64/ARM64EC de MSVC v143 de VS 2022 C++ (más recientes)
    • Bibliotecas con mitigaciones de Spectre de MSVC v143 - VS 2022 C++ x64/x86 (más recientes)
    • Últimas herramientas de compilación de ATL de C++ para v143 con mitigaciones de Spectre (ARM64/ARM64EC)
    • Últimas herramientas de compilación de ATL de C++ para v143 con mitigaciones de Spectre (x86 y x64)
    • Últimas herramientas de compilación de MFC de C++ para v143 con mitigaciones de Spectre (ARM64/ARM64EC)
    • Últimas herramientas de compilación de MFC de C++ para v143 con mitigaciones de Spectre (x86 y x64)
    • Kit de controladores de Windows

Creación y compilación de un controlador

  1. Abra Microsoft Visual Studio. En el menú Archivo, elija Nuevo > Proyecto.

  2. En el cuadro de diálogo Crear un nuevo proyecto, seleccione de C++ en la lista desplegable de la izquierda, elija Windows en el menú desplegable central y elija Controlador en la lista desplegable derecha.

  3. Seleccione Controlador en modo kernel, vacío (KMDF) en la lista de tipos de proyecto. Seleccione Siguiente.

    Captura de pantalla del cuadro de diálogo nuevo proyecto de Visual Studio con la opción controlador en modo kernel seleccionada.

    Sugerencia

    Si no encuentra plantillas de proyecto de controlador en Visual Studio, la extensión de Visual Studio de WDK no se instaló correctamente. Para resolver este problema, inicie el Instalador de Visual Studio, haga clic en Modificar, agregue Kit para controladores de Windows en la pestaña Componente individual y seleccione Modificar.

  4. En el cuadro de diálogo Configurar nuevo proyecto, introduzca "KmdfHelloWorld" en el campo Nombre de proyecto.

    Nota

    Al crear un nuevo controlador KMDF o UMDF, debe seleccionar un nombre de controlador que tenga 32 caracteres o menos. Este límite de longitud se define en wdfglobals.h.

  5. En el campo Ubicación , escriba el directorio donde desea crear el nuevo proyecto.

  6. Compruebe Colocar solución y proyecto en el mismo directorio y seleccione Crear.

    Captura de pantalla del cuadro de diálogo Configurar nuevo proyecto de Visual Studio con el botón Crear resaltado.

    Visual Studio crea un proyecto y una solución. Puede verlos en la ventana Explorador de soluciones. (Si la ventana del Explorador de Soluciones no está visible, elija Explorador de Soluciones en el menú Ver). La solución tiene un proyecto de controlador denominado KmdfHelloWorld.

    Captura de pantalla de la ventana del Explorador de soluciones de Visual Studio que muestra la solución y el proyecto de controlador vacío denominado KmdfHelloWorld.

  7. En la ventana Explorador de soluciones de, seleccione Solución "KmdfHelloWorld" (proyecto 1 de 1) y elija Administrador de configuración. Elija una configuración y una plataforma para el proyecto de controlador. Por ejemplo, elija Depuración y x64.

  8. En la ventana Explorador de Soluciones, haga clic con el botón derecho en el proyecto KmdfHelloWorld, elija Agregary, a continuación, seleccione Nuevo elemento.

  9. En el cuadro de diálogo Agregar nuevo elemento , escriba "Driver.c".

    Nota

    La extensión de nombre de archivo es .c, no .cpp.

    Seleccione Agregar. El archivo Driver.c se agrega en Archivos de origen, como se muestra aquí.

    Captura de pantalla de la ventana del Explorador de soluciones de Visual Studio que muestra el archivo driver.c agregado al proyecto de controlador.

Escribir el primer código de controlador

Ahora que ha creado el proyecto Hello World vacío y ha agregado el archivo de código fuente Driver.c, escriba el código más básico necesario para que el controlador se ejecute mediante la implementación de dos funciones básicas de devolución de llamada de eventos.

  1. En Driver.c, empiece por incluir estos encabezados:

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

    Sugerencia

    Si no puede agregar Ntddk.h, abra Configuration -> C/C++ -> General -> Directorios de inclusión adicionales y agregue C:\Program Files (x86)\Windows Kits\10\Include\<build#>\km, reemplazando <build#> por el directorio adecuado en la instalación de WDK.

    Ntddk.h contiene definiciones principales del kernel de Windows para todos los controladores, mientras que Wdf.h contiene definiciones de controladores basados en Windows Driver Framework (WDF).

  2. A continuación, proporcione declaraciones para las dos devoluciones de llamada:

    DRIVER_INITIALIZE DriverEntry;
    EVT_WDF_DRIVER_DEVICE_ADD KmdfHelloWorldEvtDeviceAdd;
    
  3. Usa el siguiente código para escribir tu 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 es el punto de entrada de todos los controladores, como Main() es para muchas aplicaciones en modo de usuario. El trabajo de DriverEntry consiste en inicializar estructuras y recursos de todo el controlador. En este ejemplo, imprime "Hello World" para DriverEntry, configura el objeto del controlador para registrar el punto de entrada de la devolución de llamada EvtDeviceAdd y luego crea el objeto del controlador y se devuelve.

    El objeto driver actúa como el objeto primario de todos los demás objetos de marco que puede crear en el controlador, que incluyen objetos de dispositivo, colas de E/S, temporizadores, interbloqueos, etc. Para obtener más información sobre los objetos de marco, vea Introducción a objetos de marco.

    Sugerencia

    Para DriverEntry, recomendamos encarecidamente mantener el nombre como "DriverEntry" para ayudar con el análisis y la depuración de código.

  4. A continuación, utilice el siguiente código para escribir su 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;
    }
    

    el sistema invoca EvtDeviceAdd cuando detecta que el dispositivo llegó. Su trabajo consiste en inicializar estructuras y recursos para ese dispositivo. En este ejemplo, imprimió un mensaje "Hola mundo" para EvtDeviceAdd, creó el objeto de dispositivo y devolvió. En otros controladores que escriba, puede crear colas de E/S para su hardware, configurar un espacio de almacenamiento de contexto de dispositivo para información específica del dispositivo, o realizar otras tareas necesarias para preparar su dispositivo.

    Sugerencia

    En la devolución de llamada de adición de dispositivo, observe cómo lo nombró usando el nombre de su controlador como prefijo (KmdfHelloWorldEvtDeviceAdd). Por lo general, se recomienda asignar nombres a las funciones del controlador de esta manera para diferenciarlas de otras funciones de los controladores. DriverEntry es el único que debe nombrar exactamente así.

  5. Su driver.c completo ahora tiene el siguiente aspecto:

    #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. Guarde Driver.c.

En este ejemplo se muestra un concepto fundamental de controladores: son una "colección de devoluciones de llamada" que, una vez inicializadas, esperan a que el sistema las llame cuando necesita algo. Una llamada al sistema podría ser un nuevo evento de llegada de dispositivos, una solicitud de E/S de una aplicación en modo de usuario, un evento de apagado de energía del sistema, una solicitud de otro controlador o un evento de eliminación sorpresa cuando un usuario desconecta el dispositivo inesperadamente. Afortunadamente, para decir "Hola Mundo", solo necesita preocuparse por la creación de controladores y dispositivos.

A continuación, compile el controlador.

Compilación del controlador

  1. En la ventana Explorador de soluciones de, seleccione Solución "KmdfHelloWorld" (proyecto 1 de 1) y elija Administrador de configuración. Elija una configuración y una plataforma para el proyecto de controlador. Para este ejercicio, elija Debug y x64.

  2. En la ventana Explorador de Soluciones, seleccione KmdfHelloWorld y elija Propiedades. En Seguimiento de Wpp > Todas las opciones, establezca Ejecutar seguimiento de Wpp en No. Seleccione Apply (Aplicar) y, después, OK (Aceptar).

  3. Para compilar el controlador, seleccione Compilar solución en el menú Compilar. Visual Studio muestra el progreso de la compilación en la ventana Salida. (Si la ventana Salida no está visible, elija Salida en el menú Vista). Al comprobar que la solución se ha construido correctamente, puede cerrar Visual Studio.

  4. Para ver el controlador integrado, en el Explorador de archivos, vaya a la carpeta KmdfHelloWorld y, a continuación, a x64\Debug\KmdfHelloWorld. La carpeta incluye:

    • KmdfHelloWorld.sys: el archivo de controlador en modo kernel
    • KmdfHelloWorld.inf: un archivo de información que Windows usa al instalar el controlador
    • KmdfHelloWorld.cat: un archivo de catálogo que usa el instalador para comprobar la firma de prueba del controlador

Sugerencia

Si ve DriverVer set to a date in the future al compilar el controlador, cambie la configuración del proyecto de controlador para que Inf2Cat establezca /uselocaltime. Para ello, use Propiedades de configuración->Inf2Cat->General->Usar hora local. Ahora, tanto Stampinf como Inf2Cat usan la hora local.

Despliegue del controlador

Normalmente, al probar y depurar un controlador, el depurador y el controlador se ejecutan en equipos independientes. El equipo que ejecuta el depurador se denomina equipo hosty el equipo que ejecuta el controlador se denomina equipo de destino . El equipo de destino también se denomina equipo de prueba.

Hasta ahora has usado Visual Studio para compilar un controlador en el equipo host. Ahora debe configurar un equipo de destino.

  1. Siga las instrucciones de Configurar un equipo para la implementación y pruebas de controladores (WDK 10).

    Sugerencia

    Cuando siga los pasos para aprovisionar el equipo de destino automáticamente mediante un cable de red, tome nota del puerto y la clave. Los usará más adelante en el paso de depuración. En este ejemplo, se usa 50000 como puerto y 1.2.3.4 como clave.

    En escenarios de depuración de controladores reales, se recomienda usar una clave generada por KDNET. Para obtener más información sobre cómo usar KDNET para generar una clave aleatoria, consulte el tema Controladores de depuración - Laboratorio paso a paso (modo kernel Sysvad).

  2. En el equipo host, abra la solución en Visual Studio. Puede hacer doble clic en el archivo de solución, KmdfHelloWorld.sln, en la carpeta KmdfHelloWorld.

  3. En la ventana Explorador de soluciones, haga clic con el botón derecho en el proyecto KmdfHelloWorld_ y elija Propiedades.

  4. Vaya a Instalación del controlador > Despliegue.

  5. En Nombre de dispositivo de destino, seleccione el nombre del equipo que configuró para probar y depurar. En este ejercicio, usamos un equipo denominado MyTestComputer.

  6. Para asegurarse de que está probando la versión más reciente del controlador, compruebe Quitar versiones anteriores del controlador antes de la implementación.

  7. Seleccione Actualización del controlador de ID de hardware y escriba el identificador de hardware del controlador. Para este ejercicio, el identificador de hardware es Root\KmdfHelloWorld. Seleccione Aceptar.

    Nota

    En este ejercicio, el identificador de hardware no identifica un elemento real de hardware. Identifica un dispositivo imaginario que se coloca en el árbol de dispositivos como hijo del nodo raíz. Para hardware real, no seleccione Actualización del controlador por ID de hardware; en su lugar, seleccione Instalar y verificar. Verá el identificador de hardware en el archivo de información del controlador (INF). En la ventana del Explorador de soluciones de, vaya a KmdfHelloWorld > Driver Filesy haga doble clic en KmdfHelloWorld.inf. El identificador de hardware se encuentra en [Standard.NT$ARCH$].

    [Standard.NT$ARCH$]
    %KmdfHelloWorld.DeviceDesc%=KmdfHelloWorld_Device, Root\KmdfHelloWorld
    
  8. En el menú Compilar , elija Implementar solución. Visual Studio copia automáticamente los archivos necesarios para instalar y ejecutar el controlador en el equipo de destino. La implementación puede tardar un minuto o dos.

    Al implementar un controlador, los archivos del controlador se copian en la carpeta \drivertest\drivers del equipo de prueba %Systemdrive%. Si algo va mal durante la implementación, puede comprobar si los archivos se copian en el equipo de prueba. Compruebe que los archivos .inf, .cat, cert de prueba y .sys, y cualquier otro archivo necesario, estén presentes en la carpeta %systemdrive%\drivertest\drivers.

    Para obtener más información sobre la implementación de controladores, consulte Implementación de un controlador en un equipo de prueba.

Instalación del controlador

Con el controlador Hello World implementado en el equipo de destino, ahora instala el controlador. Cuando aprovisionó previamente el equipo de destino con Visual Studio mediante la opción automática, Visual Studio configuró el equipo de destino para ejecutar controladores firmados de prueba como parte del proceso de aprovisionamiento. Ahora solo tiene que instalar el controlador mediante la herramienta DevCon.

  1. En el equipo host, vaya a la carpeta Herramientas de la instalación de WDK y busque la herramienta DevCon. Por ejemplo, busque en la carpeta siguiente:

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

    Copia la herramienta DevCon en el equipo remoto.

  2. En el equipo de destino, instale el controlador; para ello, vaya a la carpeta que contiene los archivos del controlador y ejecute la herramienta DevCon.

    1. Esta es la sintaxis general de la herramienta devcon que usará para instalar el controlador:

      devcon install <archivo INF><ID de hardware>

      El archivo INF necesario para instalar este controlador es KmdfHelloWorld.inf. El archivo INF contiene el identificador de hardware para instalar el archivo binario del controlador, KmdfHelloWorld.sys. Recuerde que el identificador de hardware, ubicado en el archivo INF, es Root\KmdfHelloWorld.

    2. Abra una ventana de comandos como administrador. Vaya a la carpeta que contiene el archivo .sys del controlador compilado y escriba este comando:

      devcon install kmdfhelloworld.inf root\kmdfhelloworld

      Si recibe un mensaje de error sobre devcon que no se reconoce, intente agregar la ruta de acceso a la herramienta devcon. Por ejemplo, si lo copió en una carpeta del equipo de destino llamada C:\Tools, intente usar el siguiente comando:

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

      Aparecerá un cuadro de diálogo que indica que el controlador de prueba es un controlador sin firmar. Seleccione Instalar este controlador de todos modos para continuar.

      Captura de pantalla de la advertencia de seguridad que se muestra durante el proceso de instalación del controlador.

Depuración del controlador

Ahora que ha instalado el controlador KmdfHelloWorld en el equipo de destino, conecte un depurador de forma remota desde el equipo host.

  1. En el ordenador anfitrión, abra una ventana de comandos como administrador. Cambie al directorio WinDbg.exe. Usas la versión x64 de WinDbg.exe del Kit de controladores de Windows (WDK) que se instaló como parte de la instalación del kit de Windows. Esta es la ruta de acceso predeterminada a WinDbg.exe:

    C:\Archivos de programa (x86)\Windows Kits\10\Debuggers\x64

  2. Inicie WinDbg para conectarse a una sesión de depuración del kernel en el equipo de destino mediante el comando siguiente. El valor del puerto y la clave deben ser los mismos que usó para configurar el equipo de destino. Use 50000 para el puerto y 1.2.3.4 para la clave, los valores que usaste durante el paso de implementación. La marca k indica que se trata de una sesión de depuración del kernel.

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

  3. En el menú Depurar, elija Interrumpir. El depurador del equipo host interrumpe en el equipo de destino. En la ventana Comando del depurador de, puede ver el símbolo del sistema de depuración del kernel: kd>.

  4. En este momento, puede experimentar con el depurador escribiendo comandos en el símbolo del sistema kd>. Por ejemplo, puede probar estos comandos:

  5. Para permitir que el equipo de destino vuelva a ejecutarse, elija Ir a en el menú Depurar o pulse "g" y luego "Intro".

  6. Para detener la sesión de depuración, elija Desasociar depurador en el menú Depurar.

    Importante

    Asegúrese de usar el comando "go" para permitir que el equipo de destino vuelva a ejecutarse antes de salir del depurador, o el equipo de destino permanecerá sin responder a la entrada del mouse y el teclado porque sigue hablando con el depurador.

Para obtener una guía detallada paso a paso del proceso de depuración de controladores, consulte Depuración de controladores universales - Laboratorio paso a paso (modo kernel Echo).

Para obtener más información sobre la depuración remota, consulte Depuración remota con WinDbg.

Herramientas de depuración para Windows

Depuración de controladores universales - Laboratorio paso a paso (modo kernel Eco)

Escritura de su primer controlador