Step into Kernel (Serial)

1 de December de 2006 - Fernando Roberto

Ganhei meu primeiro computador aos 13 anos de idade, mas foi só depois de 7 anos, quando comecei meu estágio, que descobri que era possível depurar os programas que eu escrevia. Até lá, meus métodos de depuração sempre foram coisas como imprimir o valor da variável na tela. Tudo bem que nesse tempo eu não desenvolvia programas complexos o suficiente para ficar sem saída, e com o passar do tempo, acho que me acostumei a trabalhar sem esse luxo todo de breakpoint e tal.

Quando fui apresentado a um depurador, nem dei o devido valor para aquilo. Eu pensava comigo mesmo: “É só colocar um printf e beleza. Não preciso de toda essa parafernalha.”. Na época utilizávamos o CodeView, um depurador 16 bits que roda em DOS, que é exibido na tela abaixo.

Não demorou muito para eu começar a depender do depurador para fazer as coisas mais simples, como escovar os dentes por exemplo. Na verdade, algumas situações como escrever código para coletores de dados, escrever firmwares para hardwares que não tinham display, situações nas quais não podíamos contar nem com uma porta serial para saber o que se passava com o software, e a inesquecível situação onde havíamos colocado um ocsiloscópio em um determinado pino do processador para capturar sinais de vida do nosso software, enfim, situações onde daríamos um braço para ter um simples breakpoint me mostraram que o depurador não é perfumaria.

O engraçado é que hoje em dia, na faculdade, a grande maioria dos alunos da minha turma não sabe depurar software. Eu já tentei explicar para meus amigos o tamanho da vantagem de utilizar um depurador, mas eles acabam tendo a mesma reação que tive anos atrás: “Ah não Fernando, isso é muito complicado. Só de olhar a gente acaba encontrando o erro.”. Por mim tudo bem…

Bom, a gente está aqui pra beber ou pra conversar? Vamos depurar o driver gerado pelo tradicional post Getting Started. Desta vez, vamos fazer como manda a tradição. Vamos precisar de duas máquinas, um cabo serial do tipo Null Modem e por último e não menos importante, uma cópia do Windbg. Não, não adianta ler esta frase de novo, você não entendeu errado, você vai precisar realmente de duas máquinas. Não sei porque, algumas pessoas apresentam uma certa resistência em acreditar nisso. Bom, suponho que superamos essa fase e podemos continuar com o post.

Se você ainda não tem uma cópia do Windbg, você poderá baixa-lo gratuitamente da página do Windows Debugging Tools no site da Microsoft.

Vamos tomar esta imagem ao lado emprestada do help do Windbg, que mostra como devemos ter os micros para realizar o Debug de Kernel.

A máquina nomeada HOST será onde teremos o WinDbg rodando. Esta máquina é, na maioria das vezes, a máquina onde o driver foi desenvolvido. Vamos partir do princípio que esta é a máquina onde o driver foi desenvolvido e adiar os detalhes de como configurar o diretório de símbolos e fontes para um outro post. A máquina nomeada TARGET é onde teremos nosso driver carregado pelo sistema. O sistema operacional da máquina TARGET não precisa necessariamente ser Checked Build, nem é necessário instalar nenhum kit adicional ao sistema operacional. Tudo o que precisamos é do seu driver e de uma porta serial. Todos os Windows da plataforma NT (Windows NT, Windows 2000, Windows XP, Windows 2003 Server e Windows Vista) já trazem seu depurador nativo desde a sua instalação. Na verdade, essa é uma excelente característica no uso do Windbg. Imagine aquele tipo de problema que só se manifesta na máquina do cliente. Você não vai querer instalar nada agressivo ou mesmo que mude o cenário de forma que o problema não se manifeste mais. Acredite, isso não é tão raro assim. Nestes casos, basta habilitar o Debug e pronto.

Para habilitar o Debug na máquina TARGET, precisaremos editar alguns parâmetros de inicialização que estão no arquivo Boot.ini localizado na pasta raiz do sistema. Este arquivo está com os atributos de arquivo de sistema, arquivo oculto e também como somente leitura, talvez seja necessário configurar o Windows Explorer para que seja possível ver estes arquivos. Utilizando o Windows Explorer, vá até a pasta raiz, remova a propriedade de somente leitura do arquivo Boot.ini, em seguia, abra este arquivo utilizando um editor de texto como o Bloco de Notas por exemplo. O conteúdo deste arquivo é semelhante ao demonstrado baixo.

Na sessão Operating Systems você terá que duplicar a linha do sistema que você vai querer depurar. Adicione ao final desta linha os parâmetros /debugport=com1 /baudrate=115200 que irão configurar o modo Debug. O primeiro parâmetro seleciona a porta COM a ser utilizada, enquanto o segundo seleciona a velocidade da comunicação. O protocolo serial não é o meio mais rápido de comunicação entre as máquinas HOST e TARGET, e em algumas situações chega a ser desagradável ter que esperar as interações entre o WinDbg e o sistema depurado. Existem outros meios de comunicação que podem ser utilizados caso seu computador não tenha uma porta serial disponível ou caso você queria ter mais velocidade e conforto no Debug. Veja mais detalhes no post Serial Killers.

O resultado final do nosso arquivo Boot.ini deveria ser algo semelhante ao exibido abaixo. Vou pular o inicio as linhas que decrevem o sistema operacional para a melhor visualização colocando “…”.

[boot loader]
timeout=30
default=multi(0)disk(0)rdisk(0)partition(2)\WINDOWS
[operating systems]
multi(0)... /NoExecute=OptIn
multi(0)... /NoExecute=OptIn /debugport=com1 /baudrate=115200

Feitas estas alterações, você pode salvar o arquivo e restaurar os atributos originais do arquivo. No Windows XP e Windows 2003, os mesmos passos acima descritos poderiam ser feitos com a ajuda de uma ferramenta chamada Bootcfg.exe. A partir do Windows Vista este processo é bem diferente e vou descreve-los em um post futuro.

De volta à máquina HOST, inicie o WinDbg e selecione a opção Kernel Debug… do menu File. Deverá ser exibida uma janela que configura o meio de comunicação. Faça isso de forma que fique compatível com o que foi configurado no arquivo Boot.ini da máquina TARGET. Neste caso, estamos utilizando a porta serial COM1 e baud rate de 115200. Clicando em OK o WinDbg vai abrir a porta serial da máquina HOST e vai esperar até que a máquina TARGET esteja pronta. Neste ponto, teremos a seguinte mensagem na janela de comandos do WinDbg.

Enquanto a máquina HOST aguarda, certifique-se de que seu driver está instalado na máquina TARGET e a reinicie. Depois de reiniciada, quando sistema for iniciar a sua carga, o Loader do sistema irá exibir as opções encontradas no arquivo Boot.ini editadas por você. Selecione a opção onde aparece [debugger enabled] como mostra abaixo.

Selecionada esta opção, as máquinas HOST e TARGET deveriam se conectar e na janela Command seria exibiria uma saíba semelhante à mostrada abaixo.

Microsoft (R) Windows Debugger  Version 6.6.0007.5
Copyright (c) Microsoft Corporation. All rights reserved.
 
Opened \\.\com1
Waiting to reconnect...
Connected to Windows XP 2600 x86 compatible target, ptr64 FALSE
Kernel Debugger connection established.
Symbol search path is: *** Invalid ***
****************************************************************************
* Symbol loading may be unreliable without a symbol search path.           *
* Use .symfix to have the debugger choose a symbol path.                   *
* After setting your symbol path, use .reload to refresh symbol locations. *
****************************************************************************
Executable search path is:
*********************************************************************
* Symbols can not be loaded because symbol path is not initialized. *
*                                                                   *
* The Symbol Path can be set by:                                    *
*   using the _NT_SYMBOL_PATH environment variable.                 *
*   using the -y  argument when starting the debugger. *
*   using .sympath and .sympath+                                    *
*********************************************************************
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for
ntkrnlpa.exe -
Windows XP Kernel Version 2600 UP Free x86 compatible
Built by: 2600.xpsp_sp2_gdr.050301-1519
Kernel base = 0x804d7000 PsLoadedModuleList = 0x805531a0
 

Muito bem, até aqui sabemos que as máquinas estão conectadas. Pressionando as teclas Ctrl + Break no Windbg, faremos com que o sistema fique congelado na máquina TARGET e que o controle seja passado ao depurador. O depurador estará pronto para receber seus comandos quando aparecer um prompt de comandos indicado pelas letras KD no canto inferior esquerdo da janela Command.

Apenas para fazer um simples teste, digite o comando “lm” e tecle enter. Este comando lista os módulos carregados pelo sistema. Note que o driver Useless ainda não está nesta lista. Isso acontece porque o driver exemplo está configurado para ter sua execução iniciada manualmente, desta forma, o driver ainda não foi carregado pelo sistema. Em seguida, selecione o ítem Open Source File… do menu File e abra o arquivo Useless.c. Clique sobre o nome da função DriverEntry e pressione F9. Embora não haja nenhum sinal visível disso, colocamos um breakpoint nesta função. O fato de não ter nenhuma alteração visual se deve ao fato de que nosso módulo ainda não está carregado. Podemos nos certificar de que o breakpoint está instalado listando os breakpoints com o comando “bl”.

kd> bl
 0 e f9fb3430     0001 (0001) Useless!DriverEntry

Agora vamos lançar o comando “g” que fará com que o sistema volte a execução normal.

Depois que o sistema for carregado, vamos iniciar o driver através do comando net start Useless. Quando o driver for iniciado, o entry point DriverEntry será executado e por conseqüência teremos a execução interrompida pelo nosso breakpoint. A máquina TARGET ficará congelada enquanto o depurador detém o controle sobre o sistema. Repare que agora que nosso módulo está carregado, os breakpoints são facilmente identificados pelas linhas com a cor de fundo vermelha.

Bom, os primeiros passos já foram dados, os comandos e detalhes de como depurar o Kernel de Windows estariam fora do escopo deste post. Mas uma boa dica de como obter kilos de detalhes sobre este assunto é o livro Windows 2000 Kernel Debugging. O livro não é um dos meus preferidos, ele dá uma boa introdução ao assunto além de falar sobre como utilizar e construir Debug Extensions, mas não chega a ter as técnicas ninjas utilizadas pelos verdadeiros mestres do Kernel Debugging. Uma outra excelente fonte de detalhes sobre este assunto é a lista de discussão Windows Debugger Users List dedicada ao Windbg. Isso tudo sem nos esquecermos do Help.

Este foi um post inicial sobre o assunto, aguardem por posts futuros onde darei os passos de como utilizar o Windbg com a VMware e de como depurar o Kernel do Windows com apenas uma máquina utilizando o SoftIce (que Deus o tenha).

Have fun… 😉

10 Responses to “Step into Kernel (Serial)”

  1. Diego Roriz says:

    Olá Fernando.

    Sou um aluno de Ciência da Computação e estou tendo que fazer um trabalho de SO sobre drivers. Para ser mais específico, um monitor de contexto, que deve coletar informações do hardware.

    Tentei compilar seu exemplo no Getting Started mas não obtive êxito.

    errors in directory c:\documents and settings\_murdoc_\meus documentos\temp\usel
    ess
    NMAKE : warning U4006: special macro undefined : ‘$<'
    Compiling – objchk_wxp_x86\i386 for all platforms
    NMAKE : warning U4006: special macro undefined : '$<'
    Compiling – objchk_wxp_x86\i386 for all platforms
    NMAKE : warning U4006: special macro undefined : '$<'
    Compiling – objchk_wxp_x86\i386 for all platforms
    NMAKE : warning U4006: special macro undefined : '$<'
    Compiling – objchk_wxp_x86\i386 for all platforms
    BUILD: Compile errors: not linking c:\documents and settings\_murdoc_\meus docum
    entos\temp\useless directory
    BUILD: Done

    4 files compiled – 4 Errors – 0 LPS

    O diretorio contem os arquivos especificados. Se pudesse me ajudar, seria muito grato. Meu email eh murdocroriz@gmail.com

    Obrigado

    • Olá Diego,

      Já recebi um comentário de uma pessoa que teve problemas em compilar drivers em diretórios com espaços.

      Como você está tentando fazer o mesmo, dê uma olhada no comentário e sua respectiva resposta. Imagino que seja um problema semelhante.

      []s.

      • Olá Leandro,

        Segundo este link que peguei no site do DDK, existe uma lista de produtos que foram removidos das Subscriptions do MSDN por conta de um acordo entre a Sun e a Microsoft. Isso inclui o DDK do Windows 98.

        Assim, somente os Subscriptions mais antigos têm esta instalação. Você deve encontrar estas instalações com quem já é assinante a algum tempo e ainda guarda os velhos CDs. Se você os encontrar, guarde-os com carinho.

        De qualquer maneira, leia essa discussão que encontrei sobre o assunto. Tenho certeza que você econtrará uma luz.

        []s.

  2. Leandro says:

    Olá Fernando,

    Estou tentando desenvolver um driver para uma impressora especifica de minha empresa, porem o DDK do site da Microsoft so funciona em XP, onde posso arrumar um para usar em Win98Se?

  3. Marcelo says:

    Oi Fernando!
    Antes de tudo, obrigado pela dica sobre o windbg. Me ajudou muito! Uma pergunta, existe alguma maneira de proteger o nosso driver. Tipo, alguma forma de colocar uma senha ou chave para que esse não possa ser distribuído?
    Mais uma vez, obrigado!
    Abraços, Marcelo.

    • Olá Marcelo,

      Fico muito feliz em saber que outras pessoas além de minha mãe e meu cachorro lêem este blog.

      Mas respondendo à sua pergunta, não conheço nada que seja dedicado à distribuição de drivers. O que imagino é que tenhamos que aplicar os mesmos macetes que são feitos para aplicações User Mode. Entretanto, acredito que minha experiência em imaginação não deva ajudar muito.

      []s,
      Fernando.

      • Marcelo says:

        blz! Vou tentar descobrir algum jeito pra isso!
        Deixa eu te perturbar mais uma vez… criei um driver para controlar uma robótica (medium changer). Ele roda no windows 2003 e XP. Quando tento instalar no windows 2000, recebo a mensagem: “Unable to Load Device Driver : device driver could not be loaded. Error Status was 0xc0000034”. Tentei recompilar o driver usando o “Windows 2000 checked build environment”, sem sucesso (include’s diferentes e lib – mcd.lib – que existe para o 2003 e XP, que não existe para o 2000). Tentei usar o Windbg, mas não consigo nem fazer carregar esse meu driver, muito menos colocar um breakpoint em DriverEntry… vc teria alguma dica/sugestão para esse caso? Muito obrigado! Abraços!

        • Marcelo,

          Se o seu driver está com problemas para ser carregado, não adianta tentar encontrar o problema com o WinDbg. Para o driver aparecer no Windbg, ele precisa ser carregado.

          Provavelmente seu driver está utilizando alguma API nova que não é implementada no Windows 2000. Para saber que API seria esta, utilize o Dependency Walker citado neste post para ver quais funções ele está importando de outros módulos. Fazendo isso no XP, todas as dependências externas serão resolvidas, mas se você abrir seu driver em um Windows 2000, o Dependency Walker acusará qual referência externa que não foi resolvida.

          Uma outra dica seria: Se você começou seu driver a partir de um exemplo do DDK do XP, verifique se no DDK do Windows 2000 não existe o mesmo exemplo. Assim fica bem fácil de saber qual API foi utilizada no lugar desta que está faltando.

          []s,

  4. Dina says:

    Olá Fernando,
    Estou cursando Tecnologia em Segurança da Informação e precisamos desenvolver uma aplicação com arquitetura
    cliente-servidor para o ambiente Linux, escrito em linguagem C padrão,utilizando sockets para fazer a comunicação. Esta aplicação deverá permitir
    a troca de informações entre duas máquinas remotas conectadas em uma
    rede, tens alguma idéia, ou uma luz para me dar???Estou sem saber de onde partir.Se puder me dar uma ajuda, te agradeço.
    Até mais…Dina

    • Olá Dina,

      Agora fico eu sem saber por onde começar.

      Já fiz algumas das coisas que você comentou, mas ainda não programei para Linux.

      Posso te adiantar que programar sockets com C padrão, significa utilizar as funções de Berkley conforme descrito neste link.

      Arquitetura Client-Server é um pouco mais do que eu possa escrever aqui nesta simples resposta deste comentário, mas sempre temos por onde começar.

      Como ainda não programei para Linux, então só posso desejar boa sorte.

      Sinto não poder ajudar mais.

      []s,

Deixe um comentário