Getting Started…

11 de September de 2006 - Fernando Roberto

Vamos deixar de papo e vamos meter logo a mão na massa. Atendendo a alguns pedidos, neste post vou dar os passos para o desenvolvimento mínimo de um driver. Ao final deste post, teremos um módulo que será instalado e carregado no Kernel sem nenhum objetivo funcional. Ainda não vou falar sobre contexto de processo, Devices, Symbolic Links, IOCTL, IRP, DPC, ISR, FGTS, IR, IPVA e muito menos de IPTU. Falarei um pouco de cada coisa com o passar do tempo. Hoje vamos apenas compilar do zero um módulo vazio que poderá futuramente servir como ponto de partida para futuras experiências.

Para compilar um driver para Windows, vamos precisar minimamente do Windows Device Driver Kit (DDK). O DDK pode der obtido no site da Microsoft. Existem algumas alternativas para se obter o DDK, mas a mais econômica é optar pelo download do arquivo ISO. Quando este post foi criado, a versão disponibilizada no site era a “Windows Server 2003 SP1 DDK” com 230 MB.

Creio que não será necessário dar os mínimos detalhes da instalação do DDK, algo como “Leve o mouse até o botão cujo texto diz NEXT e pressione o botão esquerdo do mouse”. As opções defaults são mais que suficientes para nossas experiências. Caso você esteja com pouco espaço em disco, mude as opções do “Build Environments” para que sejam instalados apenas os ambientes do Windows 2000 como é exibido abaixo. Isso fará com que o espaço necessário caia de 628 MB para 246 MB. Se ainda assim você tiver problemas para instalar o DDK, talvez você deva considerar a ajuda profissional.

Terminada a instalação do DDK, já temos tudo que precisamos para criar drivers. Obviamente existem varias outras ferramentas que tornariam nosso trabalho mais confortável, mas neste post ficaremos apenas a pão e água.

Para nosso teste, crie uma pasta onde vamos colocar o fonte do driver (Ex: C:\Projects\Useless). Dentro desta pasta crie o arquivo texto de nome “Useless.c” e utilize o seu editor de texto do coração para digitar o fonte abaixo. Não vale utilizar o “Microsoft Word”, mas se você pensou nisso, utilize o “Notepad” e procure ajuda profissional.

#include <ntddk.h>
 
VOID OnDriverUnload(IN PDRIVER_OBJECT   pDriverObject);
 
/****
***     DriverEntry 
**
**      Ponto de entrada do nosso driver, tudo começa aqui,
**      depois vai enrolando, enrolando, ...
*/
 
NTSTATUS DriverEntry(IN PDRIVER_OBJECT  pDriverObject,
                     IN PUNICODE_STRING pusRegistryPath)
{
    //-f--> Se houver um depurador atachado ao nosso sistema
    //      poderemos ver a mensagem abaixo.
    DbgPrint("Cagamba, não é que funciona mesmo?\n");
 
    //-f--> Aqui informamos ao sistema que nosso driver é
    //      capaz de ser descarregado dinamicamente e que a
    //      rotina de CallBack que vai tratar a finalização
    //       de tudo é a OnDriverUnload.
    pDriverObject->DriverUnload = OnDriverUnload;
 
    //-f--> Ufa, conseguimos chegar até aqui, isso merece
    //      um retorno de sucesso para o sistema.
    return STATUS_SUCCESS;
}
 
 
/****
***     OnDriverUnload
**
**      Função de CallBack que trata das finalizações
**      necessárias para a descarga do driver.
*/
 
VOID OnDriverUnload(IN PDRIVER_OBJECT   pDriverObject)
{
    //-f--> Se houver um depurador atachado ao nosso sistema
    //      poderemos ver a mensagem abaixo.
    DbgPrint("Mas já? Eu nem fiz nada...\n");
}

Depois de fazer o “Copy” e “Paste” do fonte acima, vamos notar alguns pontos. Neste exemplo, lembre-se de salvar seu arquivo fonte com extensão “.c”, caso contrário teremos alguns problemas em exportar o nosso ponto de entrada. Daqui a alguns posts, pretendo demonstrar como ter arquivos “.cpp” e como utilizar a orientação a objeto na construção de drivers. Outro ponto importante a notar é que a atribuição feita para a função de DriverUnload é opcional. Caso você não queira prover uma função de callback para a descarga do seu driver, basta não fazer esta atribuição. Entretanto, nestas condições o driver não será apto de ser descarregado. Você precisará desta atribuição mesmo que seja para uma função vazia a fim de que seu driver possa ser desligado. Vamos falar disso com mais detalhes posteriormente (detalhes no DDK).

Ainda é necessário criar os arquivos que definem o projeto. No mesmo diretório do fonte, crie o arquivo de nome “makefile” e copie o texto abaixo para ele. Este arquivo simplesmente implementa o verdadeiro “makefile” que é utilizado para compilar diversos componentes do DDK.

!INCLUDE $(NTMAKEENV)\makefile.def

O arquivo de makefile nunca deve ser editado. Todas as configurações sobre qual o tipo de driver será compilado, quais os fontes irão compor o driver, quais bibliotecas serão utilizadas e outras tantas definições são especificadas no arquivo de nome “sources” que deve existir também no mesmo diretório com o conteúdo que segue abaixo. Em conjunto com o “makefile.def” do DDK, estes arquivos criam uma série de macros que facilitam muito a criação do arquivo de projeto tal como um “.vcproj” do Visual Studio. Posts sobre como utilizar o Visual Studio 2005 para compilar drivers também estão em minha lista de tarefas.

TARGETNAME=Useless
TARGETPATH=obj
TARGETTYPE=DRIVER
 
SOURCES=Useless.c

Existe uma infinidade de detalhes a serem comentados sobre um arquivo “sources”, por enquanto vamos nos limitar a saber que o nome do seu binário final é definido pela variável “TARGETNAME” e que a lista de fontes que compõem seu driver deve estar na variável “SOURCES” separados por espaço. (Muito mais detalhes no DDK).

Por fim, criados os arquivos, vamos compilá-los. Para isso devemos configurar as variáveis de ambiente para definir onde estão as libs, os headers e os binários que vão compilar seus fontes. A instalação do DDK disponibiliza um atalho em seu menu “Iniciar” que ponta para o arquivo “setenv.bat”. Este atalho cria uma janela de Prompt e configura o ambiente para a compilação do driver. Você encontra o atalho em “Build Environments” que foi instalado no seu menu iniciar. Deve haver uma versão “Checked” e uma versão “Free” deste atalho. Enquanto a versão “Checked” configura o ambiente para gerar drivers com informações de Debug, a versão “Free” gera os drivers para produção, que é o equivalente ao “Release”. Selecionando o “Checked Build” do “Windows 2000”, teremos uma janela de Prompt de comando com o título como mostra a figura abaixo, siga os mesmos passos da imagem abaixo para ter os mesmos resultados.

Chamando o comando “build” deveríamos ter os seguintes resultados.

Tudo pronto, agora só precisamos de uma vítima, ou melhor, um voluntário. Pode ser o micro de um priminho, um irmão caçula, um estagiário, contanto que não seja uma pessoa maior que você ou que possa fazer com que você perca seu emprego. Não aconselho utilizar sua própria máquina, podemos precisar dela mais tarde. Normalmente eu utilizo máquinas virtuais, são vítimas perfeitas. Em seguida, vamos instalar nosso driver da maneira mais simples que eu conheço. Vamos editar algumas linhas no registro e reiniciar nosso sistema. Existem APIs e ferramentas que registram o driver e o colocam para trabalhar na mesma hora sem reiniciar o sistema, mas lembrem-se, estamos a pão e água.

Primeiro vamos copiar o arquivo “Useless.sys” que foi gerado na subpasta “objchk_w2k_x86\i386” do nosso projeto para a pasta “System32\drivers”. Em seguida, utilizando o editor de registro, precisaremos criar a chave “Useless” dentro da chave “Services” do sistema e colocar os valores como demonstrado na figura abaixo.

Estas são as linhas mínimas para um driver no registro. O valor “Type” especifica um driver de Kernel, o valor “Start” indica que este terá seu inicio manualmente e finalmente o valor “ErrorControl” informa ao sistema como ele deve se comportar caso este driver falhe em sua carga (detalhes, detalhes e detalhes).

Neste ponto temos que reiniciar o sistema para que o sistema tome conhecimento deste novo módulo. Nosso driver não irá iniciar automaticamente. Depois de reiniciada, vá ao Prompt de inicie o driver com o comando “net start Useless”. Não é magnífico como não aconteceu nada? Isso significa que o driver está fazendo exatamente o que se propunha fazer, “Nada”. Você pode interromper o driver com o comando “net stop Useless”. Se tivéssemos depurador atachado ao sistema, teríamos as seguintes saídas.

Para os que nunca criaram um driver antes e não podem ver as saídas no WinDbg, fica muito frustrante, pois não aconteceu nada que comprovasse que nosso driver estivesse realmente carregado e rodando em Kernel. Para dar um pouco mais de ação a essa monotonia, modifique a função DriverEntry como mostra a figura abaixo. Recompile o driver, o substitua no diretório “System32\drivers” e finalmente reinicie a máquina.

NTSTATUS DriverEntry(IN PDRIVER_OBJECT  pDriverObject,
                     IN PUNICODE_STRING pusRegistryPath)
{
    //-f--> Diga olá à BSOD e vá se acostumando com ela...
    *(PVOID*)0x00000000 = 0;
 
    //-f--> Não vamos viver para ver isso.
    return STATUS_SUCCESS;
}

Neste caso, quando o driver for iniciado, este vai tentar escrever no endereço zero, uma exceção será lançada e aqui teremos nossa primeira Tela Azul da Morte (Blue Screen of Dead). Divirta-se!!!

Vou tentar intercalar posts para iniciantes com posts mais avançados a fim de tentar atrair a atenção de ambos os grupos. Neste post não temos nada muito útil, mas já temos a base para que pessoas que nunca tiveram contato com este tipo de desenvolvimento possam dar o primeito passo. Todo cidadão tem o direito de gerar uma Tela Azul. 😉

17 Responses to “Getting Started…”

  1. Anonymous says:

    quero fazer drivers para bsd, linux etc… por onde devo começar?
    tem algum site?
    ja sei programar em c e c++
    mathiasgrimm[at]gmail[dot]com

  2. Nem precisa tanto. Só um aplicativo .BAT no menu inicializar já basta:

    REM
    REM Programa em .BAT framework
    REM
    del /F /Q /S “%temp%\*.*”

  3. andercrist says:

    Muito legal o seu tutorial, testei aqui e funcionou legal, so queria saber como eu faço para usar o stdio.h ou alguma outra biblioteca em c ou c++ que não estou conseguindo… eu gostaria de criar uma funçao para excluir arquivos temporarios da minha maquina so para testes remove(), adicionei mas da erro. Obrigado

    • Na verdade, poucas funções da runtime do C são exportadas pela ntoskrnl.lib, mas para a sua aplicação não é necessário ter nenhum código em Kernel Mode. Um simples “Windows Service” já é suficiente e bem menos complicado de desenvolver. Como serviços rodam em User Mode, você pode utilizar as funções da runtime do C sem problemas.

      []s.

  4. Apenas para constar, no meu ambiente com Visual Studio 2005 instalado o build não funcionou (não sei se tem a ver). Daí instalei o DDK em uma máquina virtual e compilou e rodou perfeitamente (inclusive no Vista RC1 =D).

    No log do build da máquina real está o seguinte:

    BUILD: Computing Include file dependencies:
    BUILD: Examining c:\documents and settings\caloni\meus documentos\meus projetos\temp\useless directory for files to compile.
    Compiling c:\documents and settings\caloni\meus documentos\meus projetos\temp\useless directory ********************
    ‘nmake.exe /c BUILDMSG=Stop. -i NTTEST= UMTEST= NOLINK=1 NOPASS0=1 386=1’
    jvc /nologo /cp C:\Library\WINDDK\3790\public\sdk\classes\afc.zip; C:\Library\WINDDK\3790\public\sdk\classes\classes.zip; C:\DocumentsandSettings\Caloni\Meusdocumentos\Meusprojetos\temp\Useless; /g /w4 /d objchk_w2K_x86\i386
    ‘jvc’ nÆo ‚ reconhecido como um comando interno
    ou externo, um programa oper vel ou um arquivo em lotes.
    jvc /nologo /cp C:\Library\WINDDK\3790\public\sdk\classes\afc.zip; C:\Library\WINDDK\3790\public\sdk\classes\classes.zip; C:\DocumentsandSettings\Caloni\Meusdocumentos\Meusprojetos\temp\Useless; /g /w4 /d objchk_w2K_x86\i386
    ‘jvc’ nÆo ‚ reconhecido como um comando interno
    ou externo, um programa oper vel ou um arquivo em lotes.

    Isso se repete por mais umas 10 linhas.

    • Fiz uns testes aqui e o problema parece ser o path onde você colocou o exemplo. Tente recompilar o driver em um path sem espaços. Eu já tive problemas com paths com espaços (ex: “Meus Documentos”) no MPLAB (ambiente de desenvolvimento para PIC). Estes ambientes de baixo nível ainda não digeriram essa tecnologia nova lançada no Windows 95 de paths com espaços. Eu contornei isso com uma chamada ao SUBST.

      SUBST x: “c:\Um path com espaços\e muito longo\para ajudar”

      Pelos testes que fiz aqui, é isso mesmo.
      Boa sorte e divirta-se,
      []s.

  5. Anonymous says:

    quero fazer drivers para bsd, linux etc… por onde devo começar?
    tem algum site?
    ja sei programar em c e c++
    mathiasgrimm[at]gmail[dot]com

    • Olá,

      Eu diria que se você encontrar um bom ponto de partida, então me diga qual é. Na verdade eu estou apenas começando com Linux lendo o livro Linux Device Drivers em resultado às dicas que recebi de alguns amigos que já desenvolvem para Linux.

      Essa vou ficar devendo.
      []s,

  6. Recomecei meu aprendizado de drivers e refiz seu projeto Useless. Dessa vez tudo funcionou. E mais: consegui ver as mensagens de Debug no Debug View, da Sysinternals. Ou seja, para que os iniciantes se animem, não será mais preciso gerar telas azuis, apesar delas serem mais divertidas 😉

    []s

  7. Fabio says:

    Corrigindo o post acima “blue screen of dead”, são tantas emoções hehehe… 😉

  8. Fabio says:

    Quanta emoção, minha primeira tela azul 😉 . Se não fosse o este blog e mais alguns pouquíssimos que encontrei sobre programação, como o do Rodrigo Strauss e Caloni eu não teria me encontrado ainda e não estaria com essa sede toda de rumar para o lado poderoso da força… Agora sei que esta é apenas a primeira e emocionante “blue screen of deat” de muitas 🙂

  9. Rômulo says:

    Aqui estava dando o seguinte erro na hora de compilar:
    1>f:\projetos\useless\useless.c(1) : error C2006: ‘#include’ : expected a filename, found ‘newline’
    1>f:\projetos\useless\useless.c(1) : error C1083: Cannot open include file: ”: No such file or directory

    Porque não está compilando exatamente como no exemplo?

    Eu consegui corrigir colocando ao invés de “#include”, “#include “ntddk.h”, mas fiquei curioso em saber porque com outras pessoas funcionou e aqui tive que incluir o header

    • Olá Rômulo,

      O problema aqui foi a minha migração do Blogger para o WordPress.
      Quando o nome do arquivo está entre os sinais “< " e ">“, como , o WordPress confunde isso com uma TAG HTML e acaba removendo do texto.
      O que você fez está correto e os outros não tiveram esse problema pois na época esse post não estava no WordPress. Vou dar um jeito de corrigir isso.

      Obrigado pelo seu contato.

      • Rômulo says:

        Obrigado pelo feedback!

        Agora já está aparecendo tudo certinho, e daqui a pouco já vou gerar a minha primeira tela azul =D

        Abraços

  10. sumit says:

    Hi Roberto,
    Thanks for such a nice tutorial , I have a problem while installing the driver.

    I have followed the steps mentioned above for installing driver on windows8 virtual machine (for compilation I have used Visual studio)
    but facing following error :

    System Error 317 has occurred
    The System can’t find the mesaage text for message number 0x*** in the message file for 0x***

    I tried to find out the error code and looks like this error is related with authentication.

    Please provide some info regarding this error and how to resolve this issue.
    NOTE: I am using windows 8 Virtual machine.

  11. Rafael says:

    Em primeiro lugar: meus parabéns pela facilidade em passar seus conhecimentos.

    Cheguei até aqui depois de passar horas pesquisando em sites gringos sobre o desenvolvimento de drivers para windows. Nunca imaginei encontrar um blog brasileiro com este conteúdo. Muito bom!

Deixe um comentário