Depurar drivers do Windows de laboratório passo a passo (modo kernel Echo)

Este laboratório apresenta o depurador do kernel WinDbg. Você usa o WinDbg para depurar o código de driver de exemplo do modo kernel Echo.

Objetivos do laboratório

Este laboratório inclui exercícios que apresentam as ferramentas de depuração, ensinam comandos comuns de depuração, ilustram o uso de pontos de interrupção e mostram como usar as extensões de depuração.

Neste laboratório, você usa uma conexão de depuração de kernel ao vivo para explorar as seguintes ações:

  • Usar os comandos do depurador do Windows
  • Usar comandos padrão (pilhas de chamadas, variáveis, threads, IRQL)
  • Usar comandos de depuração de driver avançados (!commands)
  • Usar símbolos
  • Definir pontos de interrupção na depuração ao vivo
  • Exibir pilhas de chamadas
  • Exibir a árvore de dispositivos Plug and Play
  • Trabalhar com contexto de thread e de processo

Depuração do usuário e do modo kernel

Ao trabalhar com o depurador do Windows, você pode fazer dois tipos de depuração:

Modo de usuário - aplicativos e subsistemas executados no computador no modo de usuário. Os processos executados no modo de usuário fazem isso dentro de seus próprios espaços de endereço virtual. Eles estão impedidos de obter acesso direto a muitas partes do sistema, incluindo hardware do sistema, memória que não está alocada para seu uso e outras partes do sistema que podem comprometer a integridade do sistema. Como os processos executados no modo de usuário são efetivamente isolados do sistema e de outros processos de modo de usuário, eles não podem interferir nesses recursos.

modo kernel - o sistema operacional e os programas privilegiados são executados no modo kernel. O código do modo kernel tem permissão para acessar qualquer parte do sistema. Ele não é restrito como o código do modo de usuário. Ele pode obter acesso a qualquer parte de outro processo em execução no modo de usuário ou no modo kernel. Grande parte da funcionalidade principal do sistema operacional e muitos drivers de dispositivo de hardware são executados no modo kernel.

Este exercício aborda comandos de depuração que são usados com frequência durante o modo de usuário e a depuração no modo kernel. O exercício também abrange extensões de depuração, às vezes denominadas !commands, que são usadas para depuração no modo kernel.

Configuração do laboratório

Você precisa do seguinte hardware para concluir o laboratório:

  • Um notebook ou computador desktop (host) com o Windows 10
  • Um segundo laptop ou computador desktop (destino) executado no Windows 10
  • Um hub de rede ou roteador e cabos de rede para conectar os dois computadores
  • Acesso à Internet para baixar arquivos de símbolos

Você precisa do seguinte software para concluir o laboratório:

  • Visual Studio
  • SDK (Software Development Kit) do Windows para o Windows 10
  • WDK (Kit de driver do Windows) para o Windows 10
  • O driver de eco de exemplo para o Windows 10

O laboratório tem as seguintes seções:

Conectar-se a uma sessão WinDbg no modo kernel

Nesta seção, configure a depuração de rede no host e no sistema de destino.

Os computadores neste laboratório precisam ser configurados para usar uma conexão de rede Ethernet para depuração do kernel.

Este laboratório usa dois computadores. O depurador do Windows é executado no sistema host e o driver de eco KMDF (Kernel Mode Driver Framework) é executado no sistema de destino.

Use um hub de rede ou roteador e cabos de rede para conectar os dois computadores.

Diagrama ilustrando dois computadores conectados via um hub de rede ou roteador.

Para trabalhar com aplicativos de modo kernel e usar o WinDbg, é recomendável usar o KDNET em vez do transporte Ethernet. Para saber mais sobre como usar o protocolo de transporte Ethernet, confira Introdução ao WinDbg (modo kernel). Para saber mais sobre como configurar o computador de destino, confira Preparar um computador para implantação manual de driver e Configurar automaticamente a depuração do kernel de rede KDNET.

Configurar a depuração no modo kernel usando a Ethernet

Para habilitar a depuração no modo kernel no sistema de destino:

  1. No sistema host, abra uma janela do Prompt de Comando e insira ipconfig para determinar o endereço IP.

    Windows IP Configuration
    Ethernet adapter Ethernet:
       Connection-specific DNS Suffix  . :
       Link-local IPv6 Address . . . . . : fe80::c8b6:db13:d1e8:b13b%3
       Autoconfiguration IPv4 Address. . : 169.182.1.1
       Subnet Mask . . . . . . . . . . . : 255.255.0.0
       Default Gateway . . . . . . . . . :
    
  2. Registre o endereço IP do sistema host: ______________________________________

  3. No sistema de destino, abra uma janela do Prompt de Comando e use o ping comando para confirmar a conectividade de rede entre os dois sistemas.

    ping 169.182.1.1
    

    Use o endereço IP real do sistema host que você gravou em vez de 169.182.1.1 que é mostrado na saída de exemplo.

    Pinging 169.182.1.1 with 32 bytes of data:
    Reply from 169.182.1.1: bytes=32 time=1ms TTL=255
    Reply from 169.182.1.1: bytes=32 time<1ms TTL=255
    Reply from 169.182.1.1: bytes=32 time<1ms TTL=255
    Reply from 169.182.1.1: bytes=32 time<1ms TTL=255
    
    Ping statistics for 169.182.1.1:
        Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
    Approximate round trip times in milli-seconds:
        Minimum = 0ms, Maximum = 1ms, Average = 0ms
    

Conclua a etapas a seguir para habilitar a depuração no modo kernel no sistema de destino.

Importante

Antes de usar o BCDEdit para alterar as informações de inicialização, talvez seja necessário suspender temporariamente os recursos de segurança do Windows, como BitLocker e Inicialização Segura, no computador de teste. Ao término do teste, reabilite esses recursos de segurança. Gerencie o computador de teste de forma adequada quando os recursos de segurança estiverem desabilitados. A inicialização segura costuma ser desabilitada na UEFI. Para acessar a configuração UEFI, Usar sistema, Recuperação, Inicialização avançada. Na reinicialização, selecione Solução de problemas, Opções avançadas, Configurações de firmware UEFI. Tenha cuidado porque a definição incorreta de opções da UEFI ou a desabilitação do BitLocker pode tornar o sistema inoperante.

  1. No computador de destino, abra uma janela do prompt de comando como Administrador. Insira este comando para habilitar a depuração:

    bcdedit /set {default} DEBUG YES
    
  2. Insira este comando para habilitar a assinatura de teste:

    bcdedit /set TESTSIGNING ON 
    
  3. Insira este comando para definir o endereço IP do sistema host. Use o endereço IP do sistema host gravado anteriormente, não o mostrado.

    bcdedit /dbgsettings net hostip:192.168.1.1 port:50000 key:2steg4fzbj2sz.23418vzkd4ko3.1g34ou07z4pev.1sp3yo9yz874p
    

    Aviso

    Para aumentar a segurança da conexão e reduzir o risco de solicitações de conexão aleatórias do depurador de cliente, use uma chave aleatória gerada automaticamente. Para obter mais informações, consulte Como configurar a depuração automática do kernel de rede KDNET.

  4. Insira este comando para confirmar se os valores de dbgsettings estão definidos corretamente:

    bcdedit /dbgsettings
    
    key                     2steg4fzbj2sz.23418vzkd4ko3.1g34ou07z4pev.1sp3yo9yz874p
    debugtype               NET
    hostip                  169.168.1.1
    port                    50000
    dhcp                    Yes
    The operation completed successfully.
    

    Observação

    Se você receber uma mensagem do firewall e quiser usar o depurador, selecione as três caixas.

    Captura de tela da caixa de diálogo Alerta de Segurança do Windows indicando que o Firewall do Windows bloqueou alguns recursos de um aplicativo.

  5. No computador host, abra uma janela do Prompt de Comando como administrador. Este laboratório usa a versão x64 do WinDbg.exe do WDK (Kit de Driver do Windows) que foi instalado como parte da instalação do kit do Windows. Altere para o diretório padrão WinDbg; o local padrão é mostrado abaixo.

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

    Esse laboratório pressupõe que ambos os computadores executam uma versão de 64 bits do Windows no destino e no host. Se esse não for o caso, a melhor abordagem será executar o mesmo número de bits de ferramentas executado pelo destino no host. Por exemplo, se o destino executar o Windows de 32 bits, execute uma versão de 32 bits do depurador no host. Para saber mais, confira Escolher as ferramentas de depuração de 32 ou 64 bits.

  6. Abra o WinDbg com depuração de usuário remoto por meio do comando a seguir. Os valores da chave e da porta coincidem com os valores definidos antes por meio do BCDEdit no computador de destino.

    WinDbg –k net:port=50000,key=2steg4fzbj2sz.23418vzkd4ko3.1g34ou07z4pev.1sp3yo9yz874p
    
  7. Reinicie o sistema de destino.

  8. Em um ou dois minutos, a saída da depuração deve ser exibida no sistema host.

    Microsoft (R) Windows Debugger Version 10.0.17074.1002 AMD64
    Copyright (c) Microsoft Corporation. All rights reserved.
    
    Using NET for debugging
    Opened WinSock 2.0
    Waiting to reconnect...
    Connected to target 169.182.1.1 on port 50005 on local IP 169.182.1.2
    You can get the target MAC address by running .kdtargetmac command.
    Connected to Windows 10 16299 x64 target at (Wed Feb 28 17:16:23.051 2018 (UTC - 8:00)), ptr64 TRUE
    Kernel Debugger connection established.  (Initial Breakpoint requested)
    Symbol search path is: srv*
    Executable search path is: 
    Windows 10 Kernel Version 16299 MP (4 procs) Free x64
    Product: WinNt, suite: TerminalServer SingleUserTS
    Built by: 16299.15.amd64fre.rs3_release.170928-1534
    Machine Name:
    Kernel base = 0xfffff800`9540d000 PsLoadedModuleList = 0xfffff800`95774110
    Debug session time: Wed Feb 28 17:16:23.816 2018 (UTC - 8:00)
    System Uptime: 0 days 0:00:20.534
    

A janela Comando do Depurador é a principal janela de informações da depuração no WinDbg. Você pode inserir comandos do depurador e exibir a saída do comando nesta janela.

A janela Comando do Depurador é dividida em dois painéis. Insira comandos no painel menor, que é o painel de entrada de comando na parte inferior da janela, e exiba a saída do comando no painel maior na parte superior da janela.

No painel de entrada de comandos, use as teclas de seta para cima e para baixo para rolar pelo histórico de comandos. Quando um comando aparece, você pode editá-lo ou pressionar Enter para executá-lo.

Comandos e técnicas de depuração no modo kernel

Nesta seção, use comandos de depuração para exibir informações sobre o sistema de destino.

Alguns comandos de depuração exibem texto usando a DML (Linguagem de Marcação de Depurador) a ser selecionada para coletar rapidamente mais informações.

  1. No sistema host, use Ctrl+Scroll Lock no WinDBg para entrar no código em execução no sistema de destino. O sistema de destino pode demorar um pouco para responder.

    Tela principal no depurador mostrando a saída da janela de comando de uma conexão de kernel ao vivo.

  2. Insira o seguinte comando para habilitar a DML na janela Comando do Depurador:

    0: kd> .prefer_dml 1
    DML versions of commands on by default
    
  3. Você pode acessar a ajuda do comando de referência por meio do comando .hh. Insira o seguinte comando para exibir a ajuda de referência de comando para .prefer_dml:

    0: kd> .hh .prefer_dml
    

    O arquivo de ajuda do Depurador exibe ajuda para o comando .prefer_dml.

    Captura de tela do aplicativo de ajuda do depurador exibindo ajuda para o comando .prefer-dml.

  4. Para exibir informações detalhadas sobre a versão do sistema de destino, insira o comando vertarget (Mostrar Versão do Computador de Destino) na janela do WinDbg:

    0: kd> vertarget
    Windows 10 Kernel Version 9926 MP (4 procs) Free x64
    Product: WinNt, suite: TerminalServer SingleUserTS
    Built by: 9926.0.amd64fre.fbl_awesome1501.150119-1648
    Machine Name: ""
    Kernel base = 0xfffff801`8d283000 PsLoadedModuleList = 0xfffff801`8d58aef0
    Debug session time: Fri Feb 20 10:15:17.807 2015 (UTC - 8:00)
    System Uptime: 0 days 01:31:58.931
    
  5. Para verificar se você está trabalhando com o processo certo no modo kernel, insira o comando lm (listar módulos carregados) na janela WinDbg para exibir os módulos carregados:

    0: Kd> lm
    start             end                 module name
    fffff801`09200000 fffff801`0925f000   volmgrx    (no symbols)
    fffff801`09261000 fffff801`092de000   mcupdate_GenuineIntel   (no symbols)
    fffff801`092de000 fffff801`092ec000   werkernel   (export symbols)       werkernel.sys
    fffff801`092ec000 fffff801`0934d000   CLFS       (export symbols)       CLFS.SYS
    fffff801`0934d000 fffff801`0936f000   tm         (export symbols)       tm.sys
    fffff801`0936f000 fffff801`09384000   PSHED      (export symbols)       PSHED.dll
    fffff801`09384000 fffff801`0938e000   BOOTVID    (export symbols)       BOOTVID.dll
    fffff801`0938e000 fffff801`093f7000   spaceport   (no symbols)
    fffff801`09400000 fffff801`094cf000   Wdf01000   (no symbols)
    fffff801`094d9000 fffff801`09561000   CI         (export symbols)       CI.dll
    ...
    

    A saída que foi omitida é indicada com "..." neste laboratório.

  6. Para solicitar informações detalhadas sobre um módulo específico, use a opção v (detalhada):

    0: Kd> lm v m tcpip
    Browse full module list
    start             end                 module name
    fffff801`09eeb000 fffff801`0a157000   tcpip      (no symbols)           
        Loaded symbol image file: tcpip.sys
        Image path: \SystemRoot\System32\drivers\tcpip.sys
        Image name: tcpip.sys
        Browse all global symbols  functions  data
        Timestamp:        Sun Nov 09 18:59:03 2014 (546029F7)
        CheckSum:         00263DB1
        ImageSize:        0026C000
        Translations:     0000.04b0 0000.04e4 0409.04b0 0409.04e4
    
    Unable to enumerate user-mode unloaded modules, Win32 error 0n30
    

    Não há um caminho de símbolo definido e símbolos carregados; portanto, as informações limitadas estão disponíveis no depurador.

Baixar e compilar o driver de eco KMDF

Nesta seção, baixe e compile o driver de eco KMDF.

Em geral, você trabalharia com seu próprio código de driver ao usar o WinDbg. Para se familiarizar com a operação WinDbg, este laboratório usa o driver de exemplo "Echo" do modelo KMDF. O código-fonte está disponível para ajudar a entender as informações exibidas no WinDbg. Este exemplo também é usado para ilustrar como você pode passar por uma única etapa por meio do código nativo do modo kernel. Essa técnica pode ser valiosa para depurar problemas de código no modo kernel complexos.

Para baixar e criar o driver de áudio de exemplo de eco:

  1. Baixe e extraia o exemplo de eco KMDF do GitHub.

    O exemplo de eco KMDF está localizado na pasta geral.

    Captura de tela da página windows-driver-samples do GitHub que destaca a pasta geral e o botão baixar zip.

    1. Baixe os exemplos de driver em um arquivo zip: Exemplos de driver

    2. Baixe o arquivo zip no disco rígido local.

    3. Selecione e segure ou clique com o botão direito do mouse no arquivo zip e selecione Extrair Tudo. Especifique uma nova pasta ou navegue até uma existente para armazenar os arquivos extraídos. Por exemplo, você pode especificar C:\DriverSamples\ como a nova pasta na qual os arquivos serão extraídos.

    4. Após extrair os arquivos, vá para a seguinte subpasta: C:\DriverSamples\general\echo\kmdf

  2. No Microsoft Visual Studio, selecione Arquivo>Abrir>Projeto/Solução... e vá para a pasta que contém os arquivos extraídos; por exemplo, C:\DriverSamples\general\echo\kmdf. Clique duas vezes no arquivo de solução kmdfecho para abri-lo.

    No Visual Studio, localize o Gerenciador de Soluções. Se essa janela ainda não estiver aberta, selecione Gerenciador de Soluções no menu Exibir. No Gerenciador de Soluções, você pode ver uma solução com três projetos.

    Captura de tela do Visual Studio exibindo o arquivo device.c que foi carregado do projeto kmdfecho.

  3. Defina a configuração e a plataforma do exemplo. No Gerenciador de Soluções, selecione e segure ou clique com o botão direito do mouse em Solução 'kmdfecho' (3 projetos) e selecione Gerenciador de Configurações. Verifique se a configuração e as definições da plataforma são iguais para os três projetos. Por padrão, a configuração é definida como Depuração do Win10 e a plataforma é definida como Win64 para todos os projetos. Se você fizer alteração na configuração ou na plataforma de um projeto, faça as mesmas alterações nos três projetos restantes.

  4. É necessário modificar os drivers de exemplo para usar valores que não sobrepõem os drivers existentes. Consulte Do código de exemplo ao driver de produção: o que alterar nos exemplos sobre como criar um driver de exemplo exclusivo que coexistirá com drivers reais existentes instalados no Windows.

  5. Defina a biblioteca de runtime. Abra a página de propriedades do driver de eco e localize C/C++>Geração de Código. Altere Biblioteca em Runtime para Depuração Multi-threaded (/MTd). Para saber mais sobre as opções de build, confira /MD, /MT, /LD (Usar Biblioteca em Runtime).

    Captura de tela da página de propriedades de eco no Visual Studio que destaca a configuração da biblioteca de runtime.

  6. Nas propriedades do driver, verifique se Assinatura de Driver>Modo de Assinatura está definido como Testar Assinatura.

    Captura de tela da página de propriedades de eco no Visual Studio que destaca a configuração do modo de assinatura.

  7. No Visual Studio, selecione Criar>Criar Solução.

    As janelas de build devem exibir uma mensagem que indica que o build dos três projetos teve êxito.

Dica

Se você encontrar uma mensagem de erro de compilação, use o número do erro de compilação para determinar uma correção. Por exemplo, o erro MSB8040 do MSBuild descreve como trabalhar com bibliotecas mitigadas de espectro.

  1. No Gerenciador de Arquivos, acesse a pasta que contém os arquivos extraídos para o exemplo. Por exemplo, acesse C:\DriverSamples\general\echo\kmdf se essa for a pasta especificada anteriormente. Nessa pasta, o local dos arquivos de driver compilados varia de acordo com a configuração e as definições da plataforma selecionadas no Gerenciador de Configurações. Se você deixou as configurações padrão inalteradas, os arquivos de driver compilados serão salvos em uma pasta denominada \x64\Debug para um build de depuração de 64 bits.

    Acesse a pasta contendo os arquivos criados para o driver de sincronização automática: C:\DriverSamples\general\echo\kmdf\driver\AutoSync\x64\Debug.

    A pasta deve conter estes arquivos:

    Arquivo Descrição
    Echo.sys O arquivo do driver.
    Echo.inf Um arquivo de informações (INF) contendo informações necessárias para instalar o driver.

    Além disso, o arquivo echoapp.exe foi criado e deve estar localizado aqui: C:\DriverSamples\general\echo\kmdf\exe\x64\Debug.

    Arquivo Descrição
    EchoApp.exe Um arquivo de teste executável do prompt de comando que se comunica com o driver echo.sys.
  2. Localize um pen drive USB ou configure um compartilhamento de rede para copiar os arquivos de driver criados e o EchoApp de teste do host para o sistema de destino.

Na próxima seção, copie o código no sistema de destino, e instale e teste o driver.

Instalar o exemplo de driver de eco KMDF no sistema de destino

Nesta seção, use a ferramenta DevCon para instalar o driver de exemplo de eco.

O computador em que você instala o driver é chamado de computador de destino ou o computador de teste. Em geral, este computador é separado do computador em que você desenvolve e compila o pacote de driver. O computador onde você desenvolve e compila o driver é chamado de computador host.

O processo de mover o pacote de driver para o computador de destino e instalar o driver é denominado implantação do driver.

Antes de implantar um driver assinado de teste, habilite a assinatura de teste para preparar o computador de destino. Você também precisa localizar a ferramenta DevCon na instalação do WDK e copiá-la no sistema de destino.

Para instalar o driver no sistema de destino, execute as etapas a seguir.

No sistema de destino, habilite os drivers de teste assinados:

  1. Abra as Configurações do Windows.

  2. Em Atualização e Segurança, selecione Recuperação.

  3. Em Inicialização avançada, selecione Reiniciar Agora.

  4. Quando o computador for reiniciado, selecione Opções de inicialização. No Windows 10, selecione Solucionar problemas>Opções avançadas>Configurações de Inicialização e, depois, Reiniciar.

  5. Pressione a tecla F7 para selecionar Desativar imposição de assinatura de driver.

  6. Reinicie o computador de destino.

No sistema host, vá para a pasta Ferramentas na instalação do WDK e localize a ferramenta DevCon. Por exemplo, procure na seguinte pasta: C:\Program Files (x86)\Windows Kits\10\Tools\x64\devcon.exe.

Crie uma pasta no destino para o pacote de driver criado; por exemplo, C:\EchoDriver. Copie devcon.exe no sistema de destino. Localize o certificado .cer no sistema host. Ele está na mesma pasta no computador host que contém os arquivos de driver criados. Copie todos os arquivos do driver criado, que foi descrito anteriormente no computador host, e salve-os na mesma pasta que você criou no computador de destino.

No computador de destino, selecione e segure ou clique com o botão direito do mouse no arquivo de certificado e selecione Instalar. Depois, siga os prompts para instalar o certificado de teste.

Se você precisar de instruções mais detalhadas para configurar o computador de destino, confira Preparar um computador para implantação de driver manual.

As instruções a seguir mostram como instalar e testar o driver de exemplo. Aqui está a sintaxe geral da ferramenta devcon que você usa para instalar o driver:

devcon install <INF file> <hardware ID>

O arquivo INF necessário para instalar este driver é echo.inf. O arquivo inf contém a ID de hardware para instalar o echo.sys. Para o exemplo de eco, a ID de hardware é root\ECHO.

No computador de destino, abra uma janela do prompt de comando como Administrador. Acesse a pasta do pacote de driver e insira o seguinte comando:

devcon install echo.inf root\ECHO

Se você obtiver uma mensagem de erro sobre devcon não reconhecida, tente adicionar o caminho para a ferramenta devcon. Por exemplo, se você copiou para uma pasta chamada C:\Tools, tente usar o seguinte comando:

c:\tools\devcon install echo.inf root\ECHO

Uma caixa de diálogo aparece, indicando que o driver de teste é um driver não assinado. Selecione Instalar este driver mesmo assim para prosseguir.

Captura de tela do aviso de Segurança do Windows indicando que o Windows não pode verificar o editor do software do driver.

Dica

 Em caso de problema com a instalação, verifique o arquivo a seguir para obter mais informações. %windir%\inf\setupapi.dev.log

Após a instalação bem-sucedida do driver de exemplo, você está pronto para testá-lo.

No computador de destino, em uma janela Prompt de Comando, insira devmgmt para abrir o Gerenciador de Dispositivos. No Gerenciador de Dispositivos, no menu Exibir, escolha Dispositivos por tipo. Na árvore de dispositivos, localize Driver de Eco WDF de Exemplo no nó Dispositivo de Exemplo.

Captura de tela da árvore do Gerenciador de Dispositivos destacando o driver de eco WDF de exemplo.

Insira echoapp para iniciar o aplicativo de eco de teste para confirmar se o driver é funcional.

C:\Samples\KMDF_Echo_Sample> echoapp
DevicePath: \\?\root#sample#0005#{cdc35b6e-0be4-4936-bf5f-5537380a7c1a}
Opened device successfully
512 Pattern Bytes Written successfully
512 Pattern Bytes Read successfully
Pattern Verified successfully
30720 Pattern Bytes Written successfully
30720 Pattern Bytes Read successfully
Pattern Verified successfully

Usar o WinDbg para exibir informações sobre o driver

Nesta seção, defina o caminho do símbolo e use os comandos do depurador do kernel para exibir informações sobre o driver de eco KMDF de exemplo.

Para exibir informações sobre o driver:

  1. No sistema host, se você fechou o depurador, reabra-o por meio do comando a seguir na janela Prompt de Comando do administrador.

    WinDbg -k net:port=50000,key=2steg4fzbj2sz.23418vzkd4ko3.1g34ou07z4pev.1sp3yo9yz874p
    
  2. Use Ctrl+Break (Scroll Lock) para entrar no código em execução no sistema de destino.

  3. Para definir o caminho de símbolos para o servidor de símbolos da Microsoft no ambiente WinDbg, use o comando .symfix.

    0: kd> .symfix
    
  4. Para adicionar a localidade do símbolo local para usar os símbolos locais, adicione o caminho usando .sympath+ e, depois, .reload /f.

    0: kd> .sympath+ C:\DriverSamples\general\echo\kmdf
    0: kd> .reload /f
    

    O comando .reload com a /f opção force exclui todas as informações de símbolo para o módulo especificado e recarrega os símbolos. Em alguns casos, esse comando também recarrega ou descarrega o próprio módulo.

Você deve carregar os símbolos apropriados para usar a funcionalidade avançada fornecida pelo WinDbg. Se você não tiver símbolos configurados corretamente, quando tentar usar a funcionalidade dependente de símbolos, receberá mensagens indicando que os símbolos estão indisponíveis.

0:000> dv
Unable to enumerate locals, HRESULT 0x80004005
Private symbols (symbols.pri) are required for locals.
Type “.hh dbgerr005” for details.

Há várias abordagens que podem ser usadas para trabalhar com símbolos. Em muitas situações, você pode configurar o computador para acessar símbolos de um servidor de símbolos que a Microsoft fornece quando eles são necessários. Este laboratório usa essa abordagem. Se os símbolos em seu ambiente estiverem em um local distinto, modifique as etapas para usar esse local. Para saber mais, confira Caminho do símbolo para depurador do Windows.

Para executar a depuração de origem, você deve criar uma versão verificada (depuração) de seus binários. O compilador cria arquivos de símbolo (arquivos .pdb). Esses arquivos de símbolo mostram ao depurador como as instruções binárias correspondem às linhas de origem. Os próprios arquivos de origem reais também devem estar acessíveis ao depurador.

Os arquivos de símbolo não contêm o texto do código-fonte. Para depuração, convém que o vinculador não otimize seu código. A depuração de origem e o acesso a variáveis locais serão mais difíceis e, às vezes, quase impossíveis se o código tiver sido otimizado. Se estiver com problemas para exibir variáveis locais ou linhas de origem, defina as seguintes opções de build:

set COMPILE_DEBUG=1
set ENABLE_OPTIMIZER=0
  1. Digite o seguinte comando na área de comando do depurador para exibir informações sobre o driver de eco:

    0: kd> lm m echo* v
    Browse full module list
    start             end                 module name
    fffff801`4ae80000 fffff801`4ae89000   ECHO       (private pdb symbols)  C:\Samples\KMDF_ECHO_SAMPLE\echo.pdb
        Loaded symbol image file: ECHO.sys
        Image path: \SystemRoot\system32\DRIVERS\ECHO.sys
        Image name: ECHO.sys
    ...  
    

    Para saber mais, confira lm.

  2. Como esse laboratório definiu prefer_dml antes, alguns elementos da saída são links frequentes a serem selecionados. Selecione o link Procurar todos os símbolos globais na saída de depuração para exibir informações sobre símbolos de itens iniciados com a letra "a".

    0: kd> x /D Echo!a*
    
  3. O exemplo de eco não contém símbolos iniciados com a letra "a"; então, digite x ECHO!Echo* para exibir informações sobre todos os símbolos associados ao driver de eco iniciados com "Eco".

    0: kd> x ECHO!Echo*
    fffff801`0bf95690 ECHO!EchoEvtIoQueueContextDestroy (void *)
    fffff801`0bf95000 ECHO!EchoEvtDeviceSelfManagedIoStart (struct WDFDEVICE__ *)
    fffff801`0bf95ac0 ECHO!EchoEvtTimerFunc (struct WDFTIMER__ *)
    fffff801`0bf9b120 ECHO!EchoEvtDeviceSelfManagedIoSuspend (struct WDFDEVICE__ *)
    ...
    

    Para saber mais, confira x (Examinar símbolos.

  4. A extensão !lmi exibe informações detalhadas sobre um módulo. Digite !lmi echo. Sua saída deve ser semelhante ao texto mostrado neste exemplo:

    0: kd> !lmi echo
    Loaded Module Info: [echo] 
             Module: ECHO
       Base Address: fffff8010bf94000
         Image Name: ECHO.sys
    … 
    
  5. Use a extensão !dh para exibir informações de cabeçalho, conforme mostrado neste exemplo:

    0: kd> !dh echo
    
    File Type: EXECUTABLE IMAGE
    FILE HEADER VALUES
         14C machine (i386)
           6 number of sections
    54AD8A42 time date stamp Wed Jan 07 11:34:26 2015
    ...
    
  6. Insira o seguinte para alterar a máscara de bits de depuração padrão para que todas as mensagens de depuração do sistema de destino sejam exibidas no depurador:

    0: kd> ed nt!Kd_DEFAULT_MASK 0xFFFFFFFF
    

    Alguns drivers exibem informações adicionais quando a máscara de 0xFFFFFFFF é usada. Defina a máscara como 0x00000000 se desejar reduzir a quantidade de informações exibidas.

    0: kd> ed nt!Kd_DEFAULT_MASK 0x00000000
    

    Use o comando dd para confirmar se a máscara está definida para exibir todas as mensagens do depurador.

    0: kd> dd nt!kd_DEFAULT_MASK 
    fffff802`bb4057c0  ffffffff 00000000 00000000 00000000
    fffff802`bb4057d0  00000000 00000000 00000000 00000000
    fffff802`bb4057e0  00000001 00000000 00000000 00000000
    fffff802`bb4057f0  00000000 00000000 00000000 00000000
    fffff802`bb405800  00000000 00000000 00000000 00000000
    fffff802`bb405810  00000000 00000000 00000000 00000000
    fffff802`bb405820  00000000 00000000 00000000 00000000
    fffff802`bb405830  00000000 00000000 00000000 00000000
    

Exibir informações da árvore de dispositivos Plug and Play

Nesta seção, exiba informações sobre o driver de dispositivo de exemplo de eco e onde ele reside na árvore de dispositivos Plug and Play.

As informações sobre o driver de dispositivo na árvore de dispositivos Plug and Play podem ser úteis para solução de problemas. Por exemplo, se um driver de dispositivo não residir na árvore de dispositivos, talvez exista um problema com a instalação do driver de dispositivo.

Para saber mais sobre a extensão de depuração do nó de dispositivo, confira !devnode.

  1. No sistema host, para ver todos os nós de dispositivo na árvore de dispositivos Plug and Play, insira o comando !devnode 0 1.

    0: kd> !devnode 0 1
    Dumping IopRootDeviceNode (= 0xffffe0005a3a8d30)
    DevNode 0xffffe0005a3a8d30 for PDO 0xffffe0005a3a9e50
      InstancePath is "HTREE\ROOT\0"
      State = DeviceNodeStarted (0x308)
      Previous State = DeviceNodeEnumerateCompletion (0x30d)
      DevNode 0xffffe0005a3a3d30 for PDO 0xffffe0005a3a4e50
        InstancePath is "ROOT\volmgr\0000"
        ServiceName is "volmgr"
        State = DeviceNodeStarted (0x308)
        Previous State = DeviceNodeEnumerateCompletion (0x30d)
        DevNode 0xffffe0005a324560 for PDO 0xffffe0005bd95ca0…
    …
    
  2. Use Ctrl+F para pesquisar na saída gerada para procurar o nome do driver de dispositivo, eco.

    Captura de tela da caixa de diálogo Localizar na pesquisa do WinDbg pelo termo 'eco'.

  3. É necessário carregar o driver do dispositivo de eco. Use o comando !devnode 0 1 echo para exibir informações Plug and Play associadas ao driver de dispositivo de eco, conforme mostrado neste exemplo:

    0: Kd> !devnode 0 1 echo
    Dumping IopRootDeviceNode (= 0xffffe0007b725d30)
    DevNode 0xffffe0007b71a630 for PDO 0xffffe0007b71a960
      InstancePath is "ROOT\SAMPLE\0000"
      ServiceName is "ECHO"
      State = DeviceNodeStarted (0x308)
      Previous State = DeviceNodeEnumerateCompletion (0x30d)
    …
    
  4. A saída exibida no comando anterior inclui o PDO associado à instância em execução do driver; neste exemplo, 0xffffe0007b71a960. Insira o comando !devobj <PDO address> para exibir informações do Plug and Play associadas ao driver de dispositivo de eco. Use o endereço PDO que !devnode exibe no computador, não o que é mostrado aqui.

    0: kd> !devobj 0xffffe0007b71a960
    Device object (ffffe0007b71a960) is for:
     0000000e \Driver\PnpManager DriverObject ffffe0007b727e60
    Current Irp 00000000 RefCount 0 Type 00000004 Flags 00001040
    Dacl ffffc102c9b36031 DevExt 00000000 DevObjExt ffffe0007b71aab0 DevNode ffffe0007b71a630 
    ExtensionFlags (0x00000800)  DOE_DEFAULT_SD_PRESENT
    Characteristics (0x00000180)  FILE_AUTOGENERATED_DEVICE_NAME, FILE_DEVICE_SECURE_OPEN
    AttachedDevice (Upper) ffffe000801fee20 \Driver\ECHO
    Device queue is not busy.
    
  5. A saída exibida no comando !devnode 0 1 inclui o endereço PDO associado à instância em execução do driver; neste exemplo, ele é 0xffffe0007b71a960. Insira o comando !devstack <PDO address> para exibir informações do Plug and Play associadas ao driver de dispositivo de eco. Use o endereço PDO que !devnode exibe no computador, não o que é mostrado neste exemplo.

    0: kd> !devstack 0xffffe0007b71a960
      !DevObj           !DrvObj            !DevExt           ObjectName
      ffffe000801fee20  \Driver\ECHO       ffffe0007f72eff0  
    > ffffe0007b71a960  \Driver\PnpManager 00000000  0000000e
    !DevNode ffffe0007b71a630 :
      DeviceInst is "ROOT\SAMPLE\0000"
      ServiceName is "ECHO"
    

A saída mostra que você tem uma pilha de driver de dispositivo muito simples. O driver de eco é um filho do nó PnPManager. PnPManager é um nó raiz.

\Driver\ECHO
\Driver\PnpManager

Este diagrama mostra uma árvore mais complexa de nós de dispositivo.

Diagrama que ilustra uma árvore de nós do dispositivo consistindo em cerca de 20 nós.

Para saber mais sobre pilhas de drivers mais complexas, confira Pilhas de drivers e Nós de dispositivo e pilhas de dispositivos.

Trabalhar com pontos de interrupção e código-fonte

Nesta seção, defina pontos de interrupção e etapa única por meio do código-fonte do modo kernel.

Para poder percorrer o código e verificar os valores de variáveis em tempo real, habilite pontos de interrupção e defina um caminho para o código-fonte.

Os pontos de interrupção param a execução de código em uma linha de código específica. Avance no código a partir desse ponto para depurar essa seção de código específica.

Para definir um ponto de interrupção por meio de um comando de depuração, use um dos seguintes comandos b.

Comando Descrição
bp Define um ponto de interrupção que fica ativo até que o módulo em que reside seja descarregado.
bu Define um ponto de interrupção que não é resolvido quando o módulo é descarregado e é reativado quando o módulo é recarregado.
bm Define um ponto de interrupção para um símbolo. Esse comando usa bu ou bp de forma adequada e permite o uso de curingas (*) para definir pontos de interrupção em cada símbolo correspondente, como todos os métodos em uma classe.

Para saber mais, confira Depuração de código-fonte no WinDbg.

  1. No sistema host, use a interface do usuário do WinDbg para confirmar se Depurar>Modo de Origem está habilitado na sessão atual do WinDbg.

  2. Insira o seguinte comando para adicionar a localização do código local ao caminho de origem:

    .srcpath+ C:\DriverSamples\KMDF_Echo_Sample\driver\AutoSync
    
  3. Insira o seguinte comando para adicionar a localização do símbolo local ao caminho do símbolo:

    .sympath+ C:\DriverSamples\KMDF_Echo_Sample\driver\AutoSync
    
  4. Use o comando x para examinar os símbolos associados ao driver de eco para determinar o nome da função a ser usado para o ponto de interrupção. É possível usar um curinga ou Ctrl+F para localizar o nome da função DeviceAdd.

    0: kd> x ECHO!EchoEvt*
    8b4c7490          ECHO!EchoEvtIoQueueContextDestroy (void *)
    8b4c7000          ECHO!EchoEvtDeviceSelfManagedIoStart (struct WDFDEVICE__ *)
    8b4c7820          ECHO!EchoEvtTimerFunc (struct WDFTIMER__ *)
    8b4cb0e0          ECHO!EchoEvtDeviceSelfManagedIoSuspend (struct WDFDEVICE__ *)
    8b4c75d0          ECHO!EchoEvtIoWrite (struct WDFQUEUE__ *, struct WDFREQUEST__ *, unsigned int)
    8b4cb170          ECHO!EchoEvtDeviceAdd (struct WDFDRIVER__ *, struct 
    …
    

    A saída mostra que o método DeviceAdd para o driver de eco é ECHO!EchoEvtDeviceAdd.

    Outra alternativa é revisar o código-fonte para localizar o nome da função para seu ponto de interrupção.

  5. Defina o ponto de interrupção com o comando bm usando o nome do driver, seguido do nome da função; por exemplo, AddDevice, onde você deseja definir o ponto de interrupção, separado por um ponto de exclamação. Este laboratório usa AddDevice para observar o carregamento do driver.

    0: kd> bm ECHO!EchoEvtDeviceAdd
      1: fffff801`0bf9b1c0 @!"ECHO!EchoEvtDeviceAdd"
    

    Você pode usar outra sintaxe junto com variáveis de configuração como <module>!<symbol>, <class>::<method>,'<file.cpp>:<line number>' ou ignorar várias vezes <condition> <#>. Para saber mais, consulte Pontos de interrupção condicionais no WinDbg e em outros depuradores do Windows.

  6. Liste os pontos de interrupção atuais para confirmar que o ponto de interrupção foi definido, inserindo o comando bl:

    0: kd> bl
    1 e fffff801`0bf9b1c0     0001 (0001) ECHO!EchoEvtDeviceAdd
    

    O "e" na saída mostrada aqui indica que o ponto de interrupção número 1 está habilitado para disparo.

  7. Reinicie a execução de código no sistema de destino inserindo o comando g (ir).

  8. No sistema de destino, no Windows, abra o Gerenciador de Dispositivos usando o ícone ou inserindo mmc devmgmt.msc. No Gerenciador de Dispositivos, expanda o nó Exemplos.

  9. Selecione e segure ou clique com o botão direito do mouse na entrada do driver de eco KMDF e selecione Desabilitar no menu.

  10. Selecione e segure ou clique com o botão direito do mouse novamente na entrada do driver de eco KMDF e selecione Habilitar no menu.

  11. No sistema host, quando o driver estiver habilitado, o ponto de interrupção de depuração AddDevice deverá ser acionado. A execução do código do driver no sistema de destino deve ser interrompida. Quando atinge-se o ponto de interrupção, a execução deve ser interrompida no início da rotina AddDevice. A saída do comando de depuração exibe Breakpoint 1 hit.

    Captura de tela do WinDbg exibindo locais de código de exemplo e janelas de comando.

  12. Para explorar o código linha por linha, insira o comando p ou pressione F10 até atingir o seguinte final da rotina AddDevice. O caractere de chave (}) é destacado conforme mostrado.

    Captura de tela da janela de código com o caractere de chave destacado no início da rotina AddDevice.

Na próxima seção, verifique o estado das variáveis após a execução do código DeviceAdd.

Você pode alterar os pontos de interrupção existentes com os comandos a seguir:

Comando Descrição
bl Lista pontos de interrupção.
bc Limpa um ponto de interrupção da lista. Use bc * para limpar todos os pontos de interrupção.
bd Desabilita um ponto de interrupção. Use bd * para desabilitar todos os pontos de interrupção.
be Habilita um ponto de interrupção. Use be * para habilitar todos os pontos de interrupção.

Como alternativa, você também pode alterar pontos de interrupção na interface do usuário do WinDbg.

Também é possível definir pontos de interrupção acionados quando um local de memória é acessado. Use o comando ba (interrupção no acesso) com a seguinte sintaxe:

ba <access> <size> <address> {options}
Opção Descrição
e executar: quando a CPU busca uma instrução do endereço
r leitura/gravação: quando a CPU lê ou grava no endereço
w gravação: quando a CPU grava no endereço

Só é possível definir quatro pontos de interrupção de dados em determinado momento. Cabe a você verificar se está alinhando seus dados de forma correta para disparar o ponto de interrupção. As palavras devem terminar em endereços divisíveis por 2, dwords devem ser divisíveis por 4 e palavras quádruplas por 0 ou 8.

Por exemplo, para definir um ponto de interrupção de leitura/gravação em um endereço de memória específico, você pode usar um comando como este exemplo.

ba r 4 0x0003f7bf0

Você pode usar os comandos a seguir para acompanhar seu código com os atalhos de teclado associados que aparecem entre parênteses.

  • Interromper (Ctrl+Break). Esse comando interrompe um sistema que está em execução e em comunicação com o WinDbg. A sequência no Depurador de Kernel é Ctrl+C.
  • Executar até o cursor (F7 ou Ctrl+F10). Coloque o cursor em uma janela de origem ou desmontagem onde deseje que a execução seja interrompida e pressione F7. A execução de código ocorre até esse ponto. Se o fluxo da execução de código não atingir o ponto indicado pelo cursor, o WinDbg não será interrompido. Essa situação poderá ocorrer se uma instrução IF não for executada.
  • Executar (F5). Execute até encontrar um ponto de interrupção ou até que ocorra um evento como uma verificação de depuração.
  • Contornar (F10). Esse comando faz com que a execução de código prossiga com uma instrução por vez. Se uma chamada for encontrada, a execução de código passará a chamada sem entrar na rotina que foi chamada. Se a linguagem de programação for C ou C++ e o WinDbg estiver no modo de origem, o modo de origem poderá ser ativado ou desativado usando Depurar>Modo de Origem.
  • Entrar (F11). Esse comando é como um passo a passo, exceto pelo fato da execução de uma chamada entrar na rotina chamada.
  • Sair (Shift+F11). Esse comando faz com que a execução ocorra e saia da rotina atual ou do local atual na pilha de chamadas. Este comando será útil se você já tiver visto o suficiente da rotina.

Para saber mais, confira Depuração de código-fonte no WinDbg.

Exibir variáveis e pilhas de chamadas

Nesta seção, exiba informações sobre variáveis e pilhas de chamadas.

Este laboratório pressupõe que parou na rotina AddDevice usando o processo descrito antes. Para exibir a saída mostrada aqui, repita as etapas descritas antes, se necessário.

No sistema host, para exibir variáveis, use o item de menu Exibir>Local para exibir variáveis locais.

Captura de tela do WinDbg mostrando a janela variáveis locais.

Para encontrar o local de um endereço de variável global, insira ? <variable name>.

  • Sair (Shift+F11): esse comando faz com que a execução aconteça e saia da rotina atual (local atual na pilha de chamadas). Isso será útil se você já tiver visto o suficiente da rotina.

Para saber mais, confira Depuração de código-fonte no WinDbg (clássico) na documentação de referência de depuração.

Seção 8: Exibir variáveis e pilhas de chamadas

Na Seção 8, você exibirá informações sobre variáveis e pilhas de chamadas.

Este laboratório pressupõe que você parou na rotina AddDevice usando o processo descrito antes. Para exibir a saída mostrada aqui, repita as etapas descritas antes, se necessário.

<- No sistema host

Exibir variáveis

Use o item de menu Exibir>Local para exibir variáveis locais.

Captura de tela do WinDbg mostrando a janela variáveis locais.

Variáveis globais

Você pode encontrar o local de um endereço de variável global digitando ? <nome da variável>.

Variáveis locais

Você pode exibir os nomes e valores de todas as variáveis locais para um quadro específico digitando o comando dv. Para exibir os nomes e os valores de todas as variáveis locais de um quadro específico, insira o comando dv:

0: kd> dv
         Driver = 0x00001fff`7ff9c838
     DeviceInit = 0xffffd001`51978190
         status = 0n0

A pilha de chamadas é a cadeia de chamadas de função que levaram ao local atual do contador de programas. A função superior na pilha de chamadas é a função atual, próxima função é a que chamou a função atual e assim por diante.

Para exibir a pilha de chamadas, use os comandos k*.

Comando Descrição
kb Exibe a pilha e os três primeiros parâmetros.
kp Exibe as pilhas e a lista completa de parâmetros.
kn Permite que você veja a pilha com as informações do quadro ao lado dela.
  1. No sistema host, se desejar manter a pilha de chamadas disponível, selecione a pilha view>call para exibi-la. Selecione as colunas na parte superior da janela para alternar a exibição de informações adicionais.

    Captura de tela do WinDbg mostrando a janela Pilhas de chamadas.

  2. Use o comando kn para mostrar a pilha de chamadas enquanto depura o código do adaptador de exemplo em um estado de interrupção.

    3: kd> kn
    # Child-SP          RetAddr           Call Site
    00 ffffd001`51978110 fffff801`0942f55b ECHO!EchoEvtDeviceAdd+0x66 [c:\Samples\kmdf echo sample\c++\driver\autosync\driver.c @ 138]
    01 (Inline Function) --------`-------- Wdf01000!FxDriverDeviceAdd::Invoke+0x30 [d:\wbrtm\minkernel\wdf\framework\shared\inc\private\common\fxdrivercallbacks.hpp @ 61]
    02 ffffd001`51978150 fffff801`eed8097d Wdf01000!FxDriver::AddDevice+0xab [d:\wbrtm\minkernel\wdf\framework\shared\core\km\fxdriverkm.cpp @ 72]
    03 ffffd001`51978570 fffff801`ef129423 nt!PpvUtilCallAddDevice+0x35 [d:\9142\minkernel\ntos\io\pnpmgr\verifier.c @ 104]
    04 ffffd001`519785b0 fffff801`ef0c4112 nt!PnpCallAddDevice+0x63 [d:\9142\minkernel\ntos\io\pnpmgr\enum.c @ 7397]
    05 ffffd001`51978630 fffff801`ef0c344f nt!PipCallDriverAddDevice+0x6e2 [d:\9142\minkernel\ntos\io\pnpmgr\enum.c @ 3390]
    ...
    

A pilha de chamadas mostra que o kernel (nt) chamou o código Plug and Play (PnP) que chamou o WDF (código de estrutura do driver) que, depois, chamou a função de driver de eco DeviceAdd.

Exibir processos e threads

Nesta seção, exiba informações sobre os processos e threads em execução no modo kernel.

Processos

Você pode exibir ou definir informações de processo por meio da extensão do depurador !process. Defina um ponto de interrupção para examinar o processo que é usado quando um som é reproduzido.

  1. No sistema host, insira o comando dv para examinar as variáveis de localidade associadas à rotina EchoEvtIo:

    0: kd> dv ECHO!EchoEvtIo*
    ECHO!EchoEvtIoQueueContextDestroy
    ECHO!EchoEvtIoWrite
    ECHO!EchoEvtIoRead         
    
  2. Limpe os pontos de interrupção anteriores por meio de bc *:

    0: kd> bc *  
    
  3. Defina um ponto de interrupção de símbolo nas rotinas EchoEvtIo por meio do seguinte comando:

    0: kd> bm ECHO!EchoEvtIo*
      2: aade5490          @!”ECHO!EchoEvtIoQueueContextDestroy”
      3: aade55d0          @!”ECHO!EchoEvtIoWrite”
      4: aade54c0          @!”ECHO!EchoEvtIoRead”
    
  4. Liste os pontos de interrupção para confirmar se o ponto de interrupção está definido de forma adequada:

    0: kd> bl
    1 e aabf0490 [c:\Samples\kmdf echo sample\c++\driver\autosync\queue.c @ 197]    0001 (0001) ECHO!EchoEvtIoQueueContextDestroy
    ...
    
  5. Insira g para reiniciar a execução do código:

    0: kd> g
    
  6. No sistema de destino, execute o programa de teste de driver EchoApp.exe no sistema de destino.

  7. No sistema host, quando o aplicativo de teste é executado, a rotina de E/S no driver é chamada. Essa chamada leva ao acionamento do ponto de interrupção e à interrupção na execução do código do driver no sistema de destino.

    Breakpoint 2 hit
    ECHO!EchoEvtIoWrite:
    fffff801`0bf95810 4c89442418      mov     qword ptr [rsp+18h],r8
    
  8. Use o comando !process para exibir o processo atual envolvido na execução do echoapp.exe:

    0: kd> !process
    PROCESS ffffe0007e6a7780
        SessionId: 1  Cid: 03c4    Peb: 7ff7cfec4000  ParentCid: 0f34
        DirBase: 1efd1b000  ObjectTable: ffffc001d77978c0  HandleCount:  34.
        Image: echoapp.exe
        VadRoot ffffe000802c79f0 Vads 30 Clone 0 Private 135. Modified 5. Locked 0.
        DeviceMap ffffc001d83c6e80
        Token                             ffffc001cf270050
        ElapsedTime                       00:00:00.052
        UserTime                          00:00:00.000
        KernelTime                        00:00:00.000
        QuotaPoolUsage[PagedPool]         33824
        QuotaPoolUsage[NonPagedPool]      4464
        Working Set Sizes (now,min,max)  (682, 50, 345) (2728KB, 200KB, 1380KB)
        PeakWorkingSetSize                652
        VirtualSize                       16 Mb
        PeakVirtualSize                   16 Mb
        PageFaultCount                    688
        MemoryPriority                    BACKGROUND
        BasePriority                      8
        CommitCharge                      138
    
            THREAD ffffe00080e32080  Cid 03c4.0ec0  Teb: 00007ff7cfece000 Win32Thread: 0000000000000000 RUNNING on processor 1
    

    A saída mostra que o processo está associado ao thread echoapp.exe, que estava em execução ao atingir o ponto de interrupção no evento de gravação do driver. Para saber mais, confira !process.

  9. Use o !process 0 0 para exibir informações resumidas de todos os processos. Na saída, use Ctrl+F para localizar o mesmo endereço de processo para o processo associado à imagem echoapp.exe. No exemplo, o endereço do processo é ffffe0007e6a7780.

    ...
    
    PROCESS ffffe0007e6a7780
        SessionId: 1  Cid: 0f68    Peb: 7ff7cfe7a000  ParentCid: 0f34
        DirBase: 1f7fb9000  ObjectTable: ffffc001cec82780  HandleCount:  34.
        Image: echoapp.exe
    
    ...
    
  10. Registre a ID do processo associada ao echoapp.exe para usar mais adiante neste laboratório. Você também pode usar Ctrl+C para copiar o endereço para o buffer de cópia para uso posterior.

    _____________________________________________________(endereço do processo echoapp.exe)

  11. Insira g conforme necessário no depurador para executar o código até terminar a execução de echoapp.exe. Ele atinge o ponto de interrupção várias vezes no evento de leitura e gravação. Quando echoapp.exe terminar, pressione Ctrl+ScrLk (Ctrl+Break) para entrar no depurador.

  12. Use o comando !process para confirmar que você está executando um processo distinto. Na saída mostrada aqui, o processo com o valor de Imagem System difere do valor de Imagem Echo.

    1: kd> !process
    PROCESS ffffe0007b65d900
        SessionId: none  Cid: 0004    Peb: 00000000  ParentCid: 0000
        DirBase: 001ab000  ObjectTable: ffffc001c9a03000  HandleCount: 786.
        Image: System
        VadRoot ffffe0007ce45930 Vads 14 Clone 0 Private 22. Modified 131605. Locked 64.
        DeviceMap ffffc001c9a0c220
        Token                             ffffc001c9a05530
        ElapsedTime                       21:31:02.516
    ...
    

    A saída mostra que um processo do sistema ffffe0007b65d900 estava em execução quando você interrompeu o sistema operacional.

  13. Use o comando !process para tentar examinar a ID do processo que foi associada a echoapp.exe que você gravou anteriormente. Forneça o endereço do processo echoapp.exe que você registrou antes, e não o endereço de processo mostrado neste exemplo.

    0: kd> !process ffffe0007e6a7780
    TYPE mismatch for process object at 82a9acc0
    

    O objeto de processo não está mais disponível porque o processo echoapp.exe não está mais em execução.

Threads

Os comandos para exibir e definir threads são similares aos comandos de processos. Use o comando !thread para exibir threads. Use .thread para definir os threads atuais.

  1. No sistema host, insira g no depurador para reiniciar a execução de código no sistema de destino.

  2. No sistema de destino, execute o programa de teste de driver EchoApp.exe.

  3. No sistema host, quando o ponto de interrupção é atingido, interrompe-se a execução do código.

    Breakpoint 4 hit
    ECHO!EchoEvtIoRead:
    aade54c0 55              push    ebp
    
  4. Para exibir os threads em execução, insira !thread. Devem aparecer informações semelhantes ao seguinte exemplo:

    0: kd>  !thread
    THREAD ffffe000809a0880  Cid 0b28.1158  Teb: 00007ff7d00dd000 Win32Thread: 0000000000000000 RUNNING on processor 0
    IRP List:
        ffffe0007bc5be10: (0006,01f0) Flags: 00060a30  Mdl: 00000000
    Not impersonating
    DeviceMap                 ffffc001d83c6e80
    Owning Process            ffffe0008096c900       Image:         echoapp.exe
    ...
    

    Anote o nome da imagem de echoapp.exe. Isso indica que você está verificando o thread associado ao aplicativo de teste.

  5. Use o comando !process para determinar se esse thread é o único thread em execução no processo associado a echoapp.exe. O número do thread em execução no processo equivale ao thread em execução exibido pelo comando !thread.

    0: kd> !process
    PROCESS ffffe0008096c900
        SessionId: 1  Cid: 0b28    Peb: 7ff7d00df000  ParentCid: 0f34
        DirBase: 1fb746000  ObjectTable: ffffc001db6b52c0  HandleCount:  34.
        Image: echoapp.exe
        VadRoot ffffe000800cf920 Vads 30 Clone 0 Private 135. Modified 8. Locked 0.
        DeviceMap ffffc001d83c6e80
        Token                             ffffc001cf5dc050
        ElapsedTime                       00:00:00.048
        UserTime                          00:00:00.000
        KernelTime                        00:00:00.000
        QuotaPoolUsage[PagedPool]         33824
        QuotaPoolUsage[NonPagedPool]      4464
        Working Set Sizes (now,min,max)  (681, 50, 345) (2724KB, 200KB, 1380KB)
        PeakWorkingSetSize                651
        VirtualSize                       16 Mb
        PeakVirtualSize                   16 Mb
        PageFaultCount                    686
        MemoryPriority                    BACKGROUND
        BasePriority                      8
        CommitCharge                      138
    
            THREAD ffffe000809a0880  Cid 0b28.1158  Teb: 00007ff7d00dd000 Win32Thread: 0000000000000000 RUNNING on processor 0
    
  6. Use o comando !process 0 0 para localizar o endereço de dois processos relacionados e registre esses endereços de processos aqui.

    Cmd.exe: ____________________________________________________________

    EchoApp.exe: _______________________________________________________

    0: kd> !process 0 0 
    
    …
    
    PROCESS ffffe0007bbde900
        SessionId: 1  Cid: 0f34    Peb: 7ff72dfa7000  ParentCid: 0c64
        DirBase: 19c5fa000  ObjectTable: ffffc001d8c2f300  HandleCount:  31.
        Image: cmd.exe
    …
    PROCESS ffffe0008096c900
        SessionId: 1  Cid: 0b28    Peb: 7ff7d00df000  ParentCid: 0f34
        DirBase: 1fb746000  ObjectTable: ffffc001db6b52c0  HandleCount:  34.
        Image: echoapp.exe
    …
    

    Uma alternativa é usar !process 0 17 para exibir informações detalhadas sobre cada processo. A saída desse comando pode ser longa. Para pesquisar a saída, use Ctrl+F.

  7. Use o comando !process para listar informações para ambos os processos executados no seu computador. Forneça o endereço do processo de sua saída !process 0 0, não o endereço mostrado neste exemplo.

    Esta saída de exemplo se destina à ID do processo cmd.exe que foi registrada antes. O nome da imagem dessa ID de processo é cmd.exe.

    0: kd>  !process ffffe0007bbde900
    PROCESS ffffe0007bbde900
        SessionId: 1  Cid: 0f34    Peb: 7ff72dfa7000  ParentCid: 0c64
        DirBase: 19c5fa000  ObjectTable: ffffc001d8c2f300  HandleCount:  31.
        Image: cmd.exe
        VadRoot ffffe0007bb8e7b0 Vads 25 Clone 0 Private 117. Modified 20. Locked 0.
        DeviceMap ffffc001d83c6e80
        Token                             ffffc001d8c48050
        ElapsedTime                       21:33:05.840
        UserTime                          00:00:00.000
        KernelTime                        00:00:00.000
        QuotaPoolUsage[PagedPool]         24656
        QuotaPoolUsage[NonPagedPool]      3184
        Working Set Sizes (now,min,max)  (261, 50, 345) (1044KB, 200KB, 1380KB)
        PeakWorkingSetSize                616
        VirtualSize                       2097164 Mb
        PeakVirtualSize                   2097165 Mb
        PageFaultCount                    823
        MemoryPriority                    FOREGROUND
        BasePriority                      8
        CommitCharge                      381
    
            THREAD ffffe0007cf34880  Cid 0f34.0f1c  Teb: 00007ff72dfae000 Win32Thread: 0000000000000000 WAIT: (UserRequest) UserMode Non-Alertable
                ffffe0008096c900  ProcessObject
            Not impersonating
    ...
    

    Esta saída de exemplo se destina à ID do processo echoapp.exe que foi registrada antes.

    0: kd>  !process ffffe0008096c900
    PROCESS ffffe0008096c900
        SessionId: 1  Cid: 0b28    Peb: 7ff7d00df000  ParentCid: 0f34
        DirBase: 1fb746000  ObjectTable: ffffc001db6b52c0  HandleCount:  34.
        Image: echoapp.exe
        VadRoot ffffe000800cf920 Vads 30 Clone 0 Private 135. Modified 8. Locked 0.
        DeviceMap ffffc001d83c6e80
        Token                             ffffc001cf5dc050
        ElapsedTime                       00:00:00.048
        UserTime                          00:00:00.000
        KernelTime                        00:00:00.000
        QuotaPoolUsage[PagedPool]         33824
        QuotaPoolUsage[NonPagedPool]      4464
        Working Set Sizes (now,min,max)  (681, 50, 345) (2724KB, 200KB, 1380KB)
        PeakWorkingSetSize                651
        VirtualSize                       16 Mb
        PeakVirtualSize                   16 Mb
        PageFaultCount                    686
        MemoryPriority                    BACKGROUND
        BasePriority                      8
        CommitCharge                      138
    
            THREAD ffffe000809a0880  Cid 0b28.1158  Teb: 00007ff7d00dd000 Win32Thread: 0000000000000000 RUNNING on processor 0
            IRP List:
                ffffe0007bc5be10: (0006,01f0) Flags: 00060a30  Mdl: 00000000
            Not impersonating
    ...
    
  8. Registre o primeiro endereço de thread associado aos dois processos aqui.

    Cmd.exe: ____________________________________________________

    EchoApp.exe: _________________________________________________

  9. Use o comando !Thread para exibir informações sobre o thread atual.

    0: kd>  !Thread
    THREAD ffffe000809a0880  Cid 0b28.1158  Teb: 00007ff7d00dd000 Win32Thread: 0000000000000000 RUNNING on processor 0
    IRP List:
        ffffe0007bc5be10: (0006,01f0) Flags: 00060a30  Mdl: 00000000
    Not impersonating
    DeviceMap                 ffffc001d83c6e80
    Owning Process            ffffe0008096c900       Image:         echoapp.exe
    Attached Process          N/A            Image:         N/A
    ...
    

    Conforme esperado, o thread atual é o thread associado a echoapp.exe e se encontra em estado de execução.

  10. Use o comando !Thread para exibir informações sobre o thread associado ao processo cmd.exe. Forneça o endereço de thread gravado antes.

    0: kd> !Thread ffffe0007cf34880
    THREAD ffffe0007cf34880  Cid 0f34.0f1c  Teb: 00007ff72dfae000 Win32Thread: 0000000000000000 WAIT: (UserRequest) UserMode Non-Alertable
        ffffe0008096c900  ProcessObject
    Not impersonating
    DeviceMap                 ffffc001d83c6e80
    Owning Process            ffffe0007bbde900       Image:         cmd.exe
    Attached Process          N/A            Image:         N/A
    Wait Start TickCount      4134621        Ticks: 0
    Context Switch Count      4056           IdealProcessor: 0             
    UserTime                  00:00:00.000
    KernelTime                00:00:01.421
    Win32 Start Address 0x00007ff72e9d6e20
    Stack Init ffffd0015551dc90 Current ffffd0015551d760
    Base ffffd0015551e000 Limit ffffd00155518000 Call 0
    Priority 14 BasePriority 8 UnusualBoost 3 ForegroundBoost 2 IoPriority 2 PagePriority 5
    Child-SP          RetAddr           : Args to Child                                                           : Call Site
    ffffd001`5551d7a0 fffff801`eed184fe : fffff801`eef81180 ffffe000`7cf34880 00000000`fffffffe 00000000`fffffffe : nt!KiSwapContext+0x76 [d:\9142\minkernel\ntos\ke\amd64\ctxswap.asm @ 109]
    ffffd001`5551d8e0 fffff801`eed17f79 : ffff03a5`ca56a3c8 000000de`b6a6e990 000000de`b6a6e990 00007ff7`d00df000 : nt!KiSwapThread+0x14e [d:\9142\minkernel\ntos\ke\thredsup.c @ 6347]
    ffffd001`5551d980 fffff801`eecea340 : ffffd001`5551da18 00000000`00000000 00000000`00000000 00000000`00000388 : nt!KiCommitThreadWait+0x129 [d:\9142\minkernel\ntos\ke\waitsup.c @ 619]
    ...
    

    Esse thread está associado a cmd.exe, em estado de espera.

  11. Forneça o endereço do thread de CMD.exe em espera para alterar o contexto desse thread em espera.

    0: kd> .Thread ffffe0007cf34880
    Implicit thread is now ffffe000`7cf34880
    
  12. Use o comando k para exibir a pilha de chamadas associada ao thread em espera.

    0: kd> k
      *** Stack trace for last set context - .thread/.cxr resets it
    # Child-SP          RetAddr           Call Site
    00 ffffd001`5551d7a0 fffff801`eed184fe nt!KiSwapContext+0x76 [d:\9142\minkernel\ntos\ke\amd64\ctxswap.asm @ 109]
    01 ffffd001`5551d8e0 fffff801`eed17f79 nt!KiSwapThread+0x14e [d:\9142\minkernel\ntos\ke\thredsup.c @ 6347]
    02 ffffd001`5551d980 fffff801`eecea340 nt!KiCommitThreadWait+0x129 [d:\9142\minkernel\ntos\ke\waitsup.c @ 619]
    03 ffffd001`5551da00 fffff801`ef02e642 nt!KeWaitForSingleObject+0x2c0 [d:\9142\minkernel\ntos\ke\wait.c @ 683]
    ...
    

    Elementos de pilha de chamadas, como KiCommitThreadWait, indicam que a execução desse thread não ocorreu conforme o esperado.

Para saber mais sobre threads e processos, confira as seguintes referências:

IRQL, registros e término da sessão WinDbg

Nesta seção, exiba o IRQL (nível de solicitação de interrupção) e o conteúdo dos registros.

Exibir o IRQL salvo

O IRQL é usado para gerenciar a prioridade de interrupção de atendimento. Cada processador tem uma configuração de IRQL que pode aumentar ou diminuir por meio de threads. As interrupções que ocorrem na configuração IRQL do processador ou abaixo dela são mascaradas e não interferem na operação atual. As interrupções acima da configuração IRQL do processador têm precedência sobre a operação atual.

No sistema host, a extensão !irql exibe o IRQL no processador atual do computador de destino antes da interrupção do depurador. Quando o computador de destino invade o depurador, o IRQL é alterado, mas o IRQL que estava ativo antes da interrupção do depurador é salvo e exibido por !irql.

0: kd> !irql
Debugger saved IRQL for processor 0x0 -- 2 (DISPATCH_LEVEL)

Exibir os registros

No sistema host, exiba o conteúdo dos registros do thread atual no processador atual por meio do comando r (Registros).

0: kd> r
rax=000000000000c301 rbx=ffffe00173eed880 rcx=0000000000000001
rdx=000000d800000000 rsi=ffffe00173eed8e0 rdi=ffffe00173eed8f0
rip=fffff803bb757020 rsp=ffffd001f01f8988 rbp=ffffe00173f0b620
 r8=000000000000003e  r9=ffffe00167a4a000 r10=000000000000001e
r11=ffffd001f01f88f8 r12=0000000000000000 r13=ffffd001f01efdc0
r14=0000000000000001 r15=0000000000000000
iopl=0         nv up ei pl nz na pe nc
cs=0010  ss=0018  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
nt!DbgBreakPointWithStatus:
fffff803`bb757020 cc              int     3

Outra alternativa é exibir o conteúdo dos registros selecionando Exibir>Registros. Para saber mais, confira r (Registros).

A exibição do conteúdo dos registros pode ser útil ao percorrer a execução do código de linguagem assembly e em outros cenários. Para saber mais sobre a desmontagem de linguagem assembly, confira Desmontagem x86 anotada e Desmontagem x64 anotada.

Para saber mais sobre o conteúdo do registro, confira arquitetura x86 e arquitetura x64.

Encerrar a sessão do WinDbg

Se você quiser deixar o depurador conectado, mas desejar trabalhar no destino, limpe todos os pontos de interrupção por meio de bc * para que o computador de destino não tente se conectar ao depurador do computador host. Use o comando g para permitir a reexecução do computador de destino.

Para encerrar a sessão de depuração, no sistema host, entre no depurador e insira o qd comando (Sair e desanexar) ou selecione Parar Depuração no menu.

0: kd> qd

Para saber mais, confira Encerrar uma sessão de depuração no WinDbg.

Recursos de depuração do Windows

Há mais informações disponíveis sobre a depuração do Windows. Alguns desses livros usam versões anteriores do Windows, como o Windows Vista nos exemplos, mas os conceitos discutidos se aplicam à maioria das versões do Windows.

Confira também