Tutorial: Escrever um Driver do Windows Hello World (Kernel-Mode Driver Framework)
Este artigo descreve como escrever um pequeno driver Universal do Windows usando Kernel-Mode Driver Framework (KMDF) e, em seguida, implantar e instalar o driver em um computador separado.
Pré-requisitos
Siga as etapas para instalar do Windows Driver Kit (WDK). Ferramentas de Depuração para Windows são incluídas quando você instala o WDK.
Instale Visual Studio 2022. Ao instalar o Visual Studio 2022, selecione a carga de trabalho Desenvolvimento desktop com C++ e, em Componentes individuais adicione:
- MSVC v143 - VS 2022 C++ ARM64/ARM64EC Spectre-mitigated libs (Latest)
- MSVC v143 – bibliotecas com mitigação de Spectre VS 2022 C++ x64/x86 (mais recente)
- ATL do C++ para as mais recentes ferramentas de build v143 com Mitigações de Espectro (ARM64/ARM64EC)
- ATL do C++ para as mais recentes ferramentas de build v143 com Mitigações de Espectro (x86 & x64)
- C++ MFC for latest v143 build tools with Spectre Mitigations (ARM64/ARM64EC)
- MFC do C++ para as mais recentes ferramentas de build v143 com Mitigações de Espectro (x86 & x64)
- Windows Driver Kit
Criar e compilar um driver
Abra o Microsoft Visual Studio. No menu Arquivo, escolha Novo > Projeto.
Na caixa de diálogo Criar um novo projeto, selecione C++ na lista suspensa esquerda, escolha Windows no menu suspenso do meio e escolha Driver Driver na lista suspensa à direita.
Selecione KMDF (Kernel Mode Driver, Driver de modo Kernel), Vazio na lista de tipos de projeto. Selecione Avançar.
Dica
Se você não conseguir encontrar modelos de projeto de driver no Visual Studio, a extensão do WDK do Visual Studio não foi instalada corretamente. Para resolver isso, execute o Instalador do Visual Studio, selecione Modificar, adicione Kits de driver do Windows na guia Componente Individual e selecione Modificar.
Na caixa de diálogo Configure o seu novo projeto, insira "KmdfHelloWorld" no campo nome do projeto.
Nota
Ao criar um novo driver KMDF ou UMDF, você deve selecionar um nome de driver com 32 caracteres ou menos. Esse limite de comprimento é definido em wdfglobals.h.
No campo Local, insira o diretório no qual você deseja criar o novo projeto.
Verifique Colocar solução e projeto no mesmo diretório e selecione Criar.
O Visual Studio cria um projeto e uma solução. Você pode vê-los na janela do Gerenciador de Soluções. (Se a janela do 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.
Na janela Gerenciador de Soluções, clique com o botão direito em solução 'KmdfHelloWorld' (1 de 1 projeto) e escolha Gerenciador de Configurações. Escolha uma configuração e uma plataforma para o projeto de driver. Por exemplo, escolha Debug e x64.
Na janela do Gerenciador de Soluções, selecione com o botão direito do mouse o projeto KmdfHelloWorld, escolha Adicionare selecione Novo Item.
Na caixa de diálogo Adicionar Novo Item, insira "Driver.c".
Nota
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.
Escreva seu primeiro código de driver
Agora que você criou seu projeto Hello World vazio e adicionou o arquivo de origem Driver.c, escreva 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.
No Driver.c, comece incluindo estes cabeçalhos:
#include <ntddk.h> #include <wdf.h>
Dica
Se você não puder adicionar
Ntddk.h
, abra Configuration -> C/C++ -> Geral -> Diretórios de Inclusão Adicionais e adicioneC:\Program Files (x86)\Windows Kits\10\Include\<build#>\km
, substituindo<build#>
pelo diretório apropriado na instalação do WDK.Ntddk.h contém as principais definições de kernel do Windows para todos os drivers, enquanto Wdf.h contém definições para drivers com base no WDF (Windows Driver Framework).
Em seguida, forneça declarações para os dois retornos de chamada:
DRIVER_INITIALIZE DriverEntry; EVT_WDF_DRIVER_DEVICE_ADD KmdfHelloWorldEvtDeviceAdd;
Use o seguinte código para escrever seu 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 de DriverEntry é inicializar estruturas e recursos do driver como um todo. Neste exemplo, você imprimiu "Hello World" para DriverEntry, configurou o objeto driver para registrar seu ponto de entrada de retorno de chamadaEvtDeviceAdd e, em seguida, criou o objeto driver e retornou.O objeto 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.
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 o dispositivo chegou. Seu trabalho é inicializar estruturas e recursos para esse dispositivo. Neste exemplo, você imprimiu uma mensagem "Olá, Mundo" para EvtDeviceAdd, criou o objeto do dispositivo e retornou. Em outros drivers que você escreve, é possível que você crie filas de entrada e saída para seu hardware, configure um espaço de armazenamento no contexto do dispositivo para informações específicas do dispositivo, ou execute outras tarefas necessárias para preparar o 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 forma para diferenciá-las das funções de outros drivers. DriverEntry é o único que você deve nomear exatamente dessa forma.
O 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; }
Salvar Driver.c.
Este exemplo ilustra um conceito fundamental de drivers: eles são um "conjunto de chamadas de retorno" que, uma vez inicializadas, ficam esperando que o sistema os acione 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 drivers e dispositivos.
Em seguida, você cria seu driver.
Criar o driver
Na janela Gerenciador de Soluções, clique com o botão direito em solução 'KmdfHelloWorld' (1 de 1 projeto) e escolha Gerenciador de Configurações. Escolha uma configuração e uma plataforma para o projeto de driver. Para este exercício, escolha Depuração e x64.
Na janela Gerenciador de Soluções, selecione com o botão direito KmdfHelloWorld e escolha Propriedades. Em Rastreamento do Wpp > Todas as Opções, defina Executar rastreamento do Wpp como Não. Selecione Aplicar e OK.
Para compilar o driver, escolha Compilar Solução no menu Compilar. O Visual Studio mostra o progresso da compilação na janela Saída. (Se a janela Saída não estiver visível, escolha Saída do menu Exibição.) Ao verificar que a solução foi criada com êxito, você pode fechar o Visual Studio.
Para ver o driver compilado, no Explorador de Arquivos, acesse a pasta KmdfHelloWorld e, em seguida, vá para x64\Debug\KmdfHelloWorld. A pasta inclui:
- KmdfHelloWorld.sys -- o arquivo de driver no 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->Geral->Usar Hora Local. Agora Stampinf e 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 hoste 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.
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, você usa 50000 como porta e 1.2.3.4 como a chave.
Em cenários reais de depuração de driver, recomendamos 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 Drivers de Depuração – Laboratório Passo a Passo (Modo Kernel do Sysvad).
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.
Na janela Gerenciador de Soluções, clique com o botão direito no projeto KmdfHelloWorld e escolha Propriedades.
Vá para Instalação do Driver > Implantação.
Para 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.
Para garantir que você esteja testando a versão mais recente do driver, verifique Remover versões anteriores do driver antes da implantação.
Selecione Atualização de 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.
Nota
Neste exercício, a ID de hardware não identifica uma parte real do hardware. Ela identifica um dispositivo imaginário que receberá um lugar na árvore de dispositivos como filho do nó raiz. Para hardware real, não selecione a opção 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, acesse KmdfHelloWorld > Arquivos do driver, 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
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 ou dois minutos.
Quando você implanta um driver, os arquivos de driver são copiados para a pasta \drivertest\drivers do %Systemdrive%no computador de teste. Se algo der errado durante a implantação, você poderá verificar se os arquivos são copiados para o computador de teste. Verifique se os arquivos .inf, .cat, test cert e .sys e quaisquer outros arquivos necessários estão presentes na pasta \drivertest\drivers do %systemdrive%.
Para obter mais informações sobre como implantar drivers, consulte Implantando um driver em um computador de teste.
Instalar o driver
Com o driver Hello World implantado no computador de destino, agora você instala 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.
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:\Program Files (x86)\Windows Kits\10\Tools\x64\devcon.exe
Copie a ferramenta DevCon para seu computador remoto.
No computador de destino, instale o driver navegando até a pasta que contém os arquivos de driver e executando a ferramenta DevCon.
Aqui está a sintaxe geral da ferramenta de desenvolvimento que você usará para instalar o driver:
instalar devcon <arquivo INF><ID de hardware>
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.
Abra uma janela do Prompt de Comando como Administrador. Navegue até a pasta que contém o arquivo .sys do driver compilado e insira este comando:
devcon install kmdfhelloworld.inf root\kmdfhelloworld
Se você obtiver uma mensagem de erro sobre devcon não reconhecida, tente adicionar o caminho para a ferramenta devcon. Por exemplo, se você copiá-la 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 aparecerá indicando que o driver de teste é um driver não atribuído. Selecione Instalar esse driver de qualquer maneira continuar.
Depurar o driver
Agora que instalou o driver KmdfHelloWorld no computador de destino, você conectará um depurador remotamente do computador host.
No computador host, abra uma janela do Prompt de Comando como Administrador. Mude para o diretório WinDbg.exe. Você usará a versão x64 do WinDbg.exe do WDK (Windows Driver Kit, 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)\Windows Kits\10\Depuradores\x64
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. Você usa 50000 para a porta e 1.2.3.4 para a chave, os valores usados 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
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>.
Neste ponto, você pode experimentar o depurador inserindo comandos no prompt kd>. Por exemplo, você pode experimentar estes comandos:
Para permitir que o computador de destino seja executado novamente, escolha Ir no menu Depurar ou pressione "g" e "enter".
Para interromper a sessão de depuração, escolha Desanexar Depurador no menu Depuração.
Importante
Use o comando "go" para permitir que o computador de destino volte a funcionar antes de sair do depurador, ou o computador de destino não responderá às entradas do mouse e do teclado porque ele ainda está se comunicando com o depurador.
Para obter um passo a passo detalhado do processo de depuração do driver, consulte Depuração de Drivers Universais - Laboratório Passo a Passo (Echo Kernel-Mode).
Para obter mais informações sobre depuração remota, consulte Depuração Remota usando o WinDbg.
Artigos relacionados
Ferramentas de Depuração para Windows
Depurar drivers universais ― Laboratório passo a passo (Echo Kernel-Mode)