Partilhar via


Gravar um KMDF (Driver do Windows Olá, Mundo)

Este artigo descreve como escrever um pequeno driver Universal do Windows usando o KMDF (Kernel-Mode Driver Framework) e, em seguida, implantar e instalar o driver em um computador separado.

Antes de prosseguir, conclua as etapas de instalação listadas em Baixar o WDK (Kit de Driver do Windows).

As Ferramentas de Depuração para Windows são incluídas quando você instala o WDK.

Criar e criar um driver

  1. Abra o Microsoft Visual Studio. No menu Arquivo , escolha Novo > Projeto.

  2. Na caixa de diálogo Criar um novo projeto , selecione C++ na lista suspensa à esquerda, escolha Windows na lista suspensa intermediária e escolha Driver na lista suspensa à direita.

  3. Selecione Driver do Modo Kernel, Vazio (KMDF) na lista de tipos de projeto. Selecione Avançar.

    Captura de tela da caixa de diálogo novo projeto do Visual Studio com a opção de driver do modo kernel selecionada.

  4. Na caixa de diálogo Configurar seu novo projeto , insira "KmdfHelloWorld" no campo Nome do projeto.

    Observação

    Ao criar um novo driver KMDF ou UMDF, você deve selecionar um nome de driver que tenha 32 caracteres ou menos. Esse limite de comprimento é definido em wdfglobals.h.

  5. No campo Local , insira o diretório onde você deseja criar o novo projeto.

  6. Marque Colocar solução e projeto no mesmo diretório e selecione Criar.

    Captura de tela da caixa de diálogo Configurar seu novo projeto do Visual Studio com o botão Criar realçado.

    O Visual Studio cria um projeto e uma solução. Veja-os na janela Gerenciador de Soluções. (Se a janela Gerenciador de Soluções não estiver visível, escolha Gerenciador de Soluções no menu Exibir.) A solução tem um projeto de driver chamado KmdfHelloWorld.

    Captura de tela da janela do gerenciador de soluções do Visual Studio exibindo a solução e o projeto de driver vazio chamado KmdfHelloWorld.

  7. Na janela Gerenciador de Soluções, selecione e segure (ou selecione à direita) a solução KmdfHelloWorld e escolha Configuration Manager. Escolha uma configuração e uma plataforma para o projeto de driver. Por exemplo, escolha Depurar e x64.

  8. Na janela Gerenciador de Soluções, selecione e segure novamente (ou selecione à direita) o projeto KmdfHelloWorld, escolha Adicionar e, em seguida, selecione Novo Item.

  9. Na caixa de diálogo Adicionar Novo Item , selecione Arquivo C++. Em Nome, insira "Driver.c".

    Observação

    A extensão de nome de arquivo é .c, não .cpp.

    Selecione Adicionar. O arquivo Driver.c é adicionado em Arquivos de Origem, conforme mostrado aqui.

    Captura de tela da janela gerenciador de soluções do Visual Studio exibindo o arquivo driver.c adicionado ao projeto do driver.

Escrever seu primeiro código de driver

Agora que você criou seu projeto de Olá, Mundo vazio e adicionou o arquivo de origem Driver.c, escreverá o código mais básico necessário para que o driver seja executado implementando duas funções básicas de retorno de chamada de evento.

  1. Em Driver.c, comece incluindo estes cabeçalhos:

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

    Dica

    Se você não puder adicionar Ntddk.h, abra Configuração –> C/C++ – Geral –>> Diretórios de Inclusão Adicionais e adicione C:\Program Files (x86)\Windows Kits\10\Include\<build#>\km, substituindo <build#> pelo diretório apropriado na instalação do WDK.

    O Ntddk.h contém as principais definições de kernel do Windows para todos os drivers, enquanto o Wdf.h contém definições para drivers com base no WDF (Windows Driver Framework).

  2. Em seguida, forneça declarações para os dois retornos de chamada que você usará:

    DRIVER_INITIALIZE DriverEntry;
    EVT_WDF_DRIVER_DEVICE_ADD KmdfHelloWorldEvtDeviceAdd;
    
  3. Use o seguinte código para escrever o 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 é o ponto de entrada para todos os drivers, como Main() é para muitos aplicativos de modo de usuário. O trabalho do DriverEntry é inicializar estruturas e recursos em todo o driver. Neste exemplo, você imprimiu "Olá, Mundo" para DriverEntry, configurou o objeto driver para registrar o ponto de entrada do retorno de chamada EvtDeviceAdd e, em seguida, criou o objeto driver e retornou.

    O objeto de driver atua como o objeto pai de todos os outros objetos de estrutura que você pode criar em seu driver, que incluem objetos de dispositivo, filas de E/S, temporizadores, spinlocks e muito mais. Para obter mais informações sobre objetos de estrutura, consulte Introdução aos objetos framework.

    Dica

    Para DriverEntry, é altamente recomendável manter o nome como "DriverEntry" para ajudar na análise e depuração de código.

  4. Em seguida, use o seguinte código para escrever seu 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 é invocado pelo sistema quando detecta que seu dispositivo chegou. Seu trabalho é inicializar estruturas e recursos para esse dispositivo. Neste exemplo, você simplesmente imprimiu uma mensagem "Olá, Mundo" para EvtDeviceAdd, criou o objeto do dispositivo e retornou. Em outros drivers que você escreve, você pode criar filas de E/S para seu hardware, configurar um espaço de armazenamento de contexto de dispositivo para informações específicas do dispositivo ou executar outras tarefas necessárias para preparar seu dispositivo.

    Dica

    Para o dispositivo adicionar retorno de chamada, observe como você o nomeou com o nome do driver como um prefixo (KmdfHelloWorldEvtDeviceAdd). Em geral, recomendamos nomear as funções do driver dessa maneira para diferenciá-las das funções de outros drivers. DriverEntry é o único que você deve nomear exatamente isso.

  5. Seu Driver.c completo agora tem esta aparência:

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

Este exemplo ilustra um conceito fundamental de drivers: eles são uma "coleção de retornos de chamada" que, uma vez inicializados, sentam-se e esperam que o sistema os chame quando precisar de algo. Uma chamada do sistema pode ser um novo evento de chegada do dispositivo, uma solicitação de E/S de um aplicativo de modo de usuário, um evento de desligamento de energia do sistema, uma solicitação de outro driver ou um evento de remoção surpresa quando um usuário desconecta o dispositivo inesperadamente. Felizmente, para dizer "Olá, Mundo", você só precisava se preocupar com a criação de driver e dispositivo.

Em seguida, você criará seu driver.

Criar o driver

  1. Na janela Gerenciador de Soluções, selecione e segure (ou selecione à direita) Solução 'KmdfHelloWorld' (1 projeto) e escolha Configuration Manager. Escolha uma configuração e uma plataforma para o projeto de driver. Para este exercício, escolhemos Depurar e x64.

  2. Na janela Gerenciador de Soluções, selecione e segure (ou selecione à direita) KmdfHelloWorld e escolha Propriedades. Em Rastreamento de Todas as Opções do Wpp>, defina Executar rastreamento Wpp como Não. Selecione Aplicar e, em seguida, OK.

  3. Para criar o driver, escolha Compilar Solução no menu Compilar . O Visual Studio mostra o progresso do build na janela Saída . (Se a janela Saída não estiver visível, escolha Saída no menu Exibir .) Depois de verificar se a solução foi criada com êxito, você pode fechar o Visual Studio.

  4. Para ver o driver criado, em Explorador de Arquivos, vá para a pasta KmdfHelloWorld e, em seguida, para x64\Debug\KmdfHelloWorld. A pasta inclui:

    • KmdfHelloWorld.sys – o arquivo de driver do modo kernel
    • KmdfHelloWorld.inf – um arquivo de informações que o Windows usa quando você instala o driver
    • KmdfHelloWorld.cat – um arquivo de catálogo que o instalador usa para verificar a assinatura de teste do driver

Dica

Se você vir DriverVer set to a date in the future ao criar o driver, altere as configurações do projeto do driver para que o Inf2Cat defina /uselocaltime. Para fazer isso, use Propriedades de Configuração-Inf2Cat-General-Use>>> Local Time. Agora , Tanto Stampinf quanto Inf2Cat usam a hora local.

Implantar o driver

Normalmente, quando você testa e depura um driver, o depurador e o driver são executados em computadores separados. O computador que executa o depurador é chamado de computador host e o computador que executa o driver é chamado de computador de destino. O computador de destino também é chamado de computador de teste.

Até agora, você usou o Visual Studio para criar um driver no computador host. Agora você precisa configurar um computador de destino.

  1. Siga as instruções em Provisionar um computador para implantação e teste de driver (WDK 10).

    Dica

    Ao seguir as etapas para provisionar o computador de destino automaticamente usando um cabo de rede, anote a porta e a chave. Você os usará posteriormente na etapa de depuração. Neste exemplo, usaremos 50000 como a porta e 1.2.3.4 como a chave.

    Em cenários reais de depuração de driver, é recomendável usar uma chave gerada por KDNET. Para obter mais informações sobre como usar o KDNET para gerar uma chave aleatória, consulte o tópico Depurar Drivers – Laboratório Passo a Passo (Modo Kernel do Sysvad).

  2. No computador host, abra sua solução no Visual Studio. Você pode clicar duas vezes no arquivo de solução, KmdfHelloWorld.sln, na pasta KmdfHelloWorld.

  3. Na janela Gerenciador de Soluções, selecione e segure (ou clique com o botão direito do mouse) no projeto KmdfHelloWorld e escolha Propriedades.

  4. Na janela Páginas de Propriedades kmdfHelloWorld, vá para Configuração Propriedades > Driver Instalação Implantação>, conforme mostrado aqui.

  5. Verifique Remover versões anteriores do driver antes da implantação.

  6. Em Nome do Dispositivo de Destino, selecione o nome do computador que você configurou para teste e depuração. Neste exercício, usamos um computador chamado MyTestComputer.

  7. Selecione Atualização do Driver de ID de Hardware e insira a ID de hardware do driver. Para este exercício, a ID de hardware é Root\KmdfHelloWorld. Selecione OK.

    Captura de tela da janela de páginas de propriedades kmdfhelloworld com a opção de instalação do driver de implantação selecionada.

    Observação

    Neste exercício, a ID de hardware não identifica uma parte real do hardware. Ele identifica um dispositivo imaginário que receberá um local na árvore de dispositivos como um filho do nó raiz. Para hardware real, não selecione Atualização do Driver de ID de Hardware; Em vez disso, selecione Instalar e Verificar. Você verá a ID de hardware no arquivo INF (informações do driver). Na janela Gerenciador de Soluções, vá para Arquivos de Driver KmdfHelloWorld >e clique duas vezes em KmdfHelloWorld.inf. A ID de hardware está localizada em [Standard.NT$ARCH$].

    [Standard.NT$ARCH$]
    %KmdfHelloWorld.DeviceDesc%=KmdfHelloWorld_Device, Root\KmdfHelloWorld
    
  8. No menu Compilar , escolha Implantar Solução. O Visual Studio copia automaticamente os arquivos necessários para instalar e executar o driver no computador de destino. A implantação pode levar um minuto ou dois.

    Quando você implanta um driver, os arquivos de driver são copiados para a pasta %Systemdrive%\drivertest\drivers no computador de teste. Se algo der errado durante a implantação, você poderá marcar para ver se os arquivos são copiados para o computador de teste. Verifique se os arquivos .inf, .cat, certificado de teste e .sys e quaisquer outros arquivos necessários estão presentes na pasta %systemdrive%\drivertest\drivers.

    Para obter mais informações sobre como implantar drivers, consulte Implantando um driver em um computador de teste.

Instalar o driver

Com o driver Olá, Mundo implantado no computador de destino, agora você instalará o driver. Quando você provisionou anteriormente o computador de destino com o Visual Studio usando a opção automática , o Visual Studio configurou o computador de destino para executar drivers assinados de teste como parte do processo de provisionamento. Agora você só precisa instalar o driver usando a ferramenta DevCon.

  1. No computador host, navegue até a pasta Ferramentas na instalação do WDK e localize a ferramenta DevCon. Por exemplo, examine a seguinte pasta:

    C:\Arquivos de Programas (x86)\Windows Kits\10\Tools\x64\devcon.exe

    Copie a ferramenta DevCon para seu computador remoto.

  2. No computador de destino, instale o driver navegando até a pasta que contém os arquivos de driver e, em seguida, executando a ferramenta DevCon.

    1. Aqui está a sintaxe geral da ferramenta devcon que você usará para instalar o driver:

      devcon install <INF file><hardware ID>

      O arquivo INF necessário para instalar esse driver é KmdfHelloWorld.inf. O arquivo INF contém a ID de hardware para instalar o binário do driver ,KmdfHelloWorld.sys. Lembre-se de que a ID de hardware, localizada no arquivo INF, é Root\KmdfHelloWorld.

    2. Abra uma janela do Prompt de Comando como Administrador. Navegue até a pasta que contém o arquivo de .sys do driver criado e insira este comando:

      devcon install kmdfhelloworld.inf root\kmdfhelloworld

      Se você receber uma mensagem de erro sobre o desenvolvimento não ser reconhecido, tente adicionar o caminho à ferramenta de desenvolvimento . Por exemplo, se você copiou para uma pasta no computador de destino chamada C:\Tools, tente usar o seguinte comando:

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

      Uma caixa de diálogo será exibida indicando que o driver de teste é um driver sem sinal. Selecione Instalar este driver de qualquer maneira para continuar.

      Captura de tela do aviso de segurança exibido durante o processo de instalação do driver.

Depurar o driver

Agora que instalou o driver KmdfHelloWorld no computador de destino, você anexará um depurador remotamente do computador host.

  1. No computador host, abra uma janela do Prompt de Comando como Administrador. Altere para o diretório WinDbg.exe. Usaremos a x64version de WinDbg.exe do WDK (Kit de Driver do Windows) que foi instalado como parte da instalação do kit do Windows. Este é o caminho padrão para WinDbg.exe:

    C:\Arquivos de Programas (x86)\Kits do Windows\10\Depuradores\x64

  2. Inicie o WinDbg para se conectar a uma sessão de depuração de kernel no computador de destino usando o comando a seguir. O valor da porta e da chave deve ser o mesmo que você usou para provisionar o computador de destino. Usaremos 50000 para a porta e 1.2.3.4 para a chave, os valores que usamos durante a etapa de implantação. O sinalizador k indica que esta é uma sessão de depuração de kernel.

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

  3. No menu Depurar , escolha Interromper. O depurador no computador host invadirá o computador de destino. Na janela Comando do Depurador , você pode ver o prompt de comando de depuração do kernel: kd>.

  4. Neste ponto, você pode experimentar o depurador inserindo comandos no prompt kd> . Por exemplo, você pode experimentar estes comandos:

  5. Para permitir que o computador de destino seja executado novamente, escolha Ir no menu Depurar ou pressione "g" e pressione "enter".

  6. Para interromper a sessão de depuração, escolha Desanexar Depurador no menu Depurar .

    Importante

    Use o comando "ir" para permitir que o computador de destino seja executado novamente antes de sair do depurador ou se o computador de destino permanecerá sem resposta à entrada do mouse e do teclado porque ele ainda está falando com o depurador.

Para obter um passo a passo detalhado do processo de depuração do driver, consulte Depurar Drivers Universais – Laboratório Passo a Passo (Echo Kernel-Mode).

Para obter mais informações sobre depuração remota, consulte Depuração remota usando WinDbg.

Ferramentas de Depuração para Windows

Depurar Drivers Universais – Laboratório Passo a Passo (Echo Kernel-Mode)

Escrever seu primeiro driver