Bug em meu driver de Boot. Já posso formatar?

16 de July de 2007 - Fernando Roberto

Escrever drivers é uma tarefa que deve ser feita com um pouco de cuidado. Afinal, qualquer situação mal resolvida entre o seu código e o sistema operacional resultará em uma bela tela azul. Mas para tudo tem remédio nessa vida e felizmente Deus criou o depurador para enfrentar essas situações. Encontrando o problema, é só trocar o arquivo .sys e pronto. Só trocar o arquivo significa esperar que a máquina fique em um estado estável, substituir ao arquivo no sistema e reiniciar a máquina. Mas a vida é uma caixinha de surpresas e por encreça que parível, a maioria dos drivers que escrevemos é iniciado automagicamente. Bom, nesse caso é só torcer então para que o erro não aconteça até que o driver seja substituído. Caso contrário teríamos que utilizar recursos como colocar a HD da máquina vítima em outro sistema e substituir o driver doente, ou na falta de outro sistema, utilizar o console de recuperação do XP para evitar do seu driver subir. Então é só torcer para que ele não seja um filtro para algo importante como disco, vídeo ou File System. Sabe como é, se o filtro não sobe, o driver principal também não. Resumindo, existe uma série de malabarismos que você pode inventar para substituir um driver de carga automática que esteja com bug. Mas será que não existe algo que não dependa tanto da sorte ou mesmo da criatividade de cada um? Hoje vou falar um pouco sobre o sistema de substituição de drivers com WinDbg.

Mapeando arquivos de drivers

Você pode mapear os arquivos de drivers para que o WinDbg substitua o driver no momento em que este for carregado. Isso não é lindo? Para que isso ocorra, você precisa utilizar o comando .kdfiles. Com este comando, você faz um vínculo entre o driver que roda na máquina Target e o driver que foi corrigido. Uma das maneiras de fazer isso é inicialmente criando um arquivo que relacione estes dois drivers. Este arquivo deve ser um arquivo texto simples onde a sintaxe é a mostrada abaixo. Este arquivo pode ter qualquer nome e extensão.

map 
\??\C:\Windows\System32\drivers\MeuDriver.sys 
C:\Pasta_Do_Driver_Novo\MeuDriver.sys

A palavra map marca o início de um mapeamento, isso não muda. A linha seguinte é o caminho do arquivo do driver que deve ser substituído. Esta linha deve ter o mesmo formato do utilizado no valor ImagePath na chave do driver que fica no registro. A última linha é o caminho do driver novo. Este caminho pode ser na própria máquina Host ou mesmo na rede.

Este mapeamento funciona apenas no Windows XP ou superior, na máquina Target obviamente. Se você ainda não está acostumado com termos Host e Target, dê uma olhada neste post.

Depois de criado o arquivo, você utiliza o WinDbg para lançar o seguinte comando:

kd>.kdfiles C:\Pasta\MeuMap.txt

A partir desse momento, toda vez que o driver for carregado pelo sistema, o Kernel verifica se este arquivo está mapeado no depurador, e se estiver, o WinDbg envia o novo driver pelo meio de conexão entre eles, seja serial, firewire ou USB. Para arquivos grandes, recomenda-se utilizar firewire. Entenda que o arquivo é substituído no disco da máquina Target pela versão nova. Isso significa que nas próximas inicializações nosso driver novo ainda será carregado, mesmo que não tenhamos o debugger atachado ao sistema.

Será que funciona mesmo?

Para tornar as coisas um pouco mais claras, vamos ver um exemplo prático. Para isso vamos precisar de um fonte de driver bem simples, para não dizer inútil, que pode ser encontrado aqui. Vamos alterar a função DriverEntry de forma que fique como abaixo:

NTSTATUS DriverEntry(IN PDRIVER_OBJECT  pDriverObject,
                     IN PUNICODE_STRING pusRegistryPath)
{
    //-f--> Vamos utilizar __DATE__ e __TIME__ para que tenhamos
    //      uma versão nova de driver a cada rebuild
    DbgPrint("Este driver foi compilado em "__DATE__" "__TIME__"\n");
 
...

Compilamos uma versão inicial e o instalamos na maquina Target da maneira que você achar melhor, mas vamos admitir que depois de instalado teremos o registro como na figura abaixo.

Se iniciarmos o driver, teremos algo parecido com a string abaixo na saída do depurador:

Este driver foi compilado em Jul 16 2007 00:04:03

Agora vamos criar nosso arquivo de mapeamento. Aqui vou salvá-lo como Z:\Sources\DriverEntry\Useless\map.txt. Seguindo o formado de como o driver foi registrado no registro, nosso arquivo de mapeamento deveria ter o seguinte conteúdo:

map
\??\C:\Windows\System32\drivers\Useless.sys
Z:\Sources\DriverEntry\Useless\objchk_wxp_x86\i386\Useless.sys

Notem que a pasta onde salvei o meu arquivo map.txt é o mesmo onde os fontes do driver estão. Isso é apenas por uma questão de organização. O arquivo de mapeamento poderia estar em qualquer pasta. Em seguida utilizamos o comando .kdfiles como mostra abaixo. Notem que listamos os mapeamentos existentes simplesmente utilizando o mesmo comando sem qualquer parâmetro.

kd> .kdfiles Z:\Sources\DriverEntry\Useless\map.txt
KD file assocations loaded from 'Z:\Sources\DriverEntry\Useless\map.txt'
 
kd> .kdfiles
KD file assocations loaded from 'Z:\Sources\DriverEntry\Useless\map.txt'
\??\C:\Windows\System32\drivers\Useless.sys ->
Z:\Sources\DriverEntry\Useless\objchk_wxp_x86\i386\Useless.sys

Na seqüência, vamos dar um Rebuild no driver (isso deveria mudar aquele time stamp) e vamos reiniciar o driver. Se tudo estiver certo aí do seu lado, você deveria ter uma saída com um time stamp diferente do que tínhamos antes.

kd> g
Mas já? Eu nem fiz nada...
KD: Accessing 'Z:\Sources\DriverEntry\Useless\objchk_wxp_x86\i386\Useless.sys'
 (\??\C:\Windows\system32\drivers\Useless.sys)
  File size 2K.
MmLoadSystemImage: Pulled \??\C:\Windows\system32\drivers\Useless.sys from kd
Este driver foi compilado em Jul 16 2007 00:17:04

Bom, se isso funciona em um driver com start manual, então com o automático também deveria funcionar. Para vermos isso, mude o start do seu driver para System ou Automatic, dê um Rebuild mais uma vêz no driver, e por fim vamos reiniciar a máquina. Quando o driver for carregado, deveremos ter a substituição automática de sua imagem na sua carga. No WinDbg teremos o seguinte:

Connected to Windows XP 2600 x86 compatible target, ptr64 FALSE
Kernel Debugger connection established.
Symbol search path is: SRV*c:\websymbols*http://msdl.microsoft.com/download/symbols
Executable search path is: 
Windows XP Kernel Version 2600 UP Free x86 compatible
Built by: 2600.xpsp_sp2_gdr.070227-2254
Kernel base = 0x804d7000 PsLoadedModuleList = 0x805533a0
System Uptime: not available
KD: Accessing 'Z:\Sources\DriverEntry\Useless\objchk_wxp_x86\i386\Useless.sys'
 (\??\C:\Windows\system32\drivers\Useless.sys)
  File size 2K.
MmLoadSystemImage: Pulled \??\C:\Windows\system32\drivers\Useless.sys from kd
Este driver foi compilado em Jul 16 2007 00:25:38

E não é que funciona mesmo! Mas e se meu driver for um driver com start do tipo Boot? Isso significa que a imagem do driver vai ser carregada antes mesmo da conexão com o WinDbg ser estabelecida. Vocês já ouviram falar que o que não tem remédio, remediado está? Felizmente isso não se aplica aqui ainda. Existe uma maneira de fazer com que mesmo os drivers de Boot sejam mapeados e substituídos.

Mapeando drivers de Boot

Para estabelecer uma conexão com o WinDbg, precisaremos substituir o loader do sistema por uma versão especial. Esta versão faz esta conexão com o Kernel Debugger antes mesmo que arquivo Boot.ini seja lido. Por esta razão, os parâmetros de conexão são fixos com porta COM1 e baudrate de 115200. Esta versão do loader é encontrada no diretório C:\WinDDk\3790\debug do DDK com o nome de ntldr_dbg. Este arquivo deve substituir a versão original do loader do sistema que fica no raiz do drive de boot com o nome ntldr. A versão debug deve ficar com o mesmo nome do loader original.

Antes de reiniciar o sistema, devemos mudar o start do driver para Boot e remover o valor ImagePath do registro. Como vocês devem saber, drivers de boot não têm esse luxo de utilizar caminhos que tenham letras de unidades e outras frescuras. Ao final das alterações, deveríamos ter o registro como mostra abaixo.


Como falei anteriormente, o formato do caminho do arquivo do driver deve ser o mesmo de como está no registro, mas sabendo que agora não há mais caminho no registro, devemos adotar o mesmo formado adotado pelo sistema. Ah tá! O mesmo formato. E qual seria? Para ver este formato basta reiniciar o sistema com o loader debug, o que deve nos dar a seguinte tela no boot do sistema.

Este é o momento de conectar com o Windbg utilizando os parâmetros pré-determinados de conexão. Isso deveria nos resultar a seguinte saída no depurador:

Microsoft (R) Windows Debugger  Version 6.7.0005.1
Copyright (c) Microsoft Corporation. All rights reserved.
 
Opened \\.\pipe\com_1
Waiting to reconnect...
BD: Boot Debugger Initialized
BD: osloader.exe base address 00400000
Connected to Windows Boot Debugger 3790 x86 compatible target, ptr64 FALSE
Kernel Debugger connection established.
Symbol search path is: SRV*c:\websymbols*http://msdl.microsoft.com/download/symbols
Executable search path is: 
Windows Boot Debugger Kernel Version 3790 UP Checked x86 compatible
Primary image base = 0x00000000 Loaded module list = 0x00000000
System Uptime: not available
The call to LoadLibrary(bootext) failed, Win32 error 0n2
    "The system cannot find the file specified."
Please check your debugger configuration and/or network access.

Agora é apresentado o menu de seleção do Boot.ini. Selecione sua opção e continue carregando o sistema. Deve ser apresentada a lista de drivers de Boot, que são os drivers que são carregados antes do primeiro breakpoint que o debugger pode parar.

BD: \WINDOWS\system32\ntoskrnl.exe base address 804EA000
BD: \WINDOWS\system32\hal.dll base address 806FF000
BD: \WINDOWS\system32\KDCOM.DLL base address 80720000
BD: \WINDOWS\system32\BOOTVID.dll base address 80010000
BD: \WINDOWS\system32\DRIVERS\ACPI.sys base address 80124000
BD: \WINDOWS\system32\DRIVERS\WMILIB.SYS base address 80001000
BD: \WINDOWS\system32\DRIVERS\pci.sys base address 80062000
BD: \WINDOWS\system32\DRIVERS\isapnp.sys base address 80003000
BD: \WINDOWS\system32\DRIVERS\compbatt.sys base address 8000C000
BD: \WINDOWS\system32\DRIVERS\BATTC.SYS base address 80013000
BD: \WINDOWS\system32\DRIVERS\intelide.sys base address 80017000
BD: \WINDOWS\system32\DRIVERS\PCIIDEX.SYS base address 80019000
BD: \WINDOWS\System32\Drivers\MountMgr.sys base address 80152000
BD: \WINDOWS\system32\DRIVERS\ftdisk.sys base address 8015D000
BD: \WINDOWS\System32\drivers\dmload.sys base address 80073000
BD: \WINDOWS\System32\drivers\dmio.sys base address 8017C000
BD: \WINDOWS\System32\Drivers\PartMgr.sys base address 801A2000
BD: \WINDOWS\System32\Drivers\VolSnap.sys base address 801A7000
BD: \WINDOWS\system32\DRIVERS\atapi.sys base address 801B4000
BD: \WINDOWS\system32\DRIVERS\vmscsi.sys base address 801CC000
BD: \WINDOWS\system32\DRIVERS\SCSIPORT.SYS base address 801CF000
BD: \WINDOWS\system32\DRIVERS\disk.sys base address 801E7000
BD: \WINDOWS\system32\DRIVERS\CLASSPNP.SYS base address 801F0000
BD: \WINDOWS\system32\DRIVERS\fltMgr.sys base address 801FD000
BD: \WINDOWS\system32\DRIVERS\sr.sys base address 8021D000
BD: \WINDOWS\System32\Drivers\KSecDD.sys base address 8022F000
BD: \WINDOWS\System32\Drivers\Ntfs.sys base address 80246000
BD: \WINDOWS\System32\Drivers\NDIS.sys base address 802D3000
BD: \WINDOWS\System32\Drivers\Useless.sys base address 8000F000
BD: \WINDOWS\System32\Drivers\Mup.sys base address 80300000
BD: \WINDOWS\system32\DRIVERS\agp440.sys base address 8031B000
Shutdown occurred...unloading all symbol tables.
Waiting to reconnect...

Aqui a conexão com o loader é encerrada. Uma nova conexão seria estabelecida caso você tivesse selecionado a entrada de Debug do Boot.ini, mas o que temos que notar aqui é o formato utilizado para a carga dos drivers de boot pelo sistema. Notem que entre os drivers da lista acima está o nosso driver de teste. Vamos adotar este mesmo formato em nosso arquivo de mapeamento.

map
\WINDOWS\System32\Drivers\Useless.sys
Z:\Sources\DriverEntry\Useless\objchk_wxp_x86\i386\Useless.sys

Depois de modificar o arquivo de mapeamento e salvar seu conteúdo em disco, devemos atualizar o WinDbg para que ele pegue esta mudança. Em seguida vamos reiniciar o sistema.

kd> .kdfiles Z:\Sources\DriverEntry\Useless\map.txt
KD file assocations loaded from 'Z:\Sources\DriverEntry\Useless\map.txt'
 
kd> .reboot
Shutdown occurred...unloading all symbol tables.
Waiting to reconnect...
BD: Boot Debugger Initialized
Connected to Windows Boot Debugger 3790 x86 compatible target, ptr64 FALSE
Kernel Debugger connection established.  (Initial Breakpoint requested)
Symbol search path is: SRV*c:\websymbols*http://msdl.microsoft.com/download/symbols
Executable search path is: 
Module List address is NULL - debugger not initialized properly.
WARNING: .reload failed, module list may be incomplete
KdDebuggerData.KernBase < SystemRangeStart
Windows Boot Debugger Kernel Version 3790 UP Checked x86 compatible
Primary image base = 0x00000000 Loaded module list = 0x00000000
System Uptime: not available
The call to LoadLibrary(bootext) failed, Win32 error 0n2
    "The system cannot find the file specified."
Please check your debugger configuration and/or network access.

A mesma seqüência de mensagens acontece, mas desta vez, o mapeamento está feito como se deve e teremos a seguinte saída quando os drivers de boot forem carregados.

BD: osloader.exe base address 00400000
BD: \WINDOWS\system32\ntoskrnl.exe base address 804EA000
BD: \WINDOWS\system32\hal.dll base address 806FF000
BD: \WINDOWS\system32\KDCOM.DLL base address 80720000
BD: \WINDOWS\system32\BOOTVID.dll base address 80010000
BD: \WINDOWS\system32\DRIVERS\ACPI.sys base address 80124000
BD: \WINDOWS\system32\DRIVERS\WMILIB.SYS base address 80001000
BD: \WINDOWS\system32\DRIVERS\pci.sys base address 80062000
BD: \WINDOWS\system32\DRIVERS\isapnp.sys base address 80003000
BD: \WINDOWS\system32\DRIVERS\compbatt.sys base address 8000C000
BD: \WINDOWS\system32\DRIVERS\BATTC.SYS base address 80013000
BD: \WINDOWS\system32\DRIVERS\intelide.sys base address 80017000
BD: \WINDOWS\system32\DRIVERS\PCIIDEX.SYS base address 80019000
BD: \WINDOWS\System32\Drivers\MountMgr.sys base address 80152000
BD: \WINDOWS\system32\DRIVERS\ftdisk.sys base address 8015D000
BD: \WINDOWS\System32\drivers\dmload.sys base address 80073000
BD: \WINDOWS\System32\drivers\dmio.sys base address 8017C000
BD: \WINDOWS\System32\Drivers\PartMgr.sys base address 801A2000
BD: \WINDOWS\System32\Drivers\VolSnap.sys base address 801A7000
BD: \WINDOWS\system32\DRIVERS\atapi.sys base address 801B4000
BD: \WINDOWS\system32\DRIVERS\vmscsi.sys base address 801CC000
BD: \WINDOWS\system32\DRIVERS\SCSIPORT.SYS base address 801CF000
BD: \WINDOWS\system32\DRIVERS\disk.sys base address 801E7000
BD: \WINDOWS\system32\DRIVERS\CLASSPNP.SYS base address 801F0000
BD: \WINDOWS\system32\DRIVERS\fltMgr.sys base address 801FD000
BD: \WINDOWS\system32\DRIVERS\sr.sys base address 8021D000
BD: \WINDOWS\System32\Drivers\KSecDD.sys base address 8022F000
BD: \WINDOWS\System32\Drivers\Ntfs.sys base address 80246000
BD: \WINDOWS\System32\Drivers\NDIS.sys base address 802D3000
KD: Accessing 'Z:\Sources\DriverEntry\Useless\objchk_wxp_x86\i386\Useless.sys'
 (\WINDOWS\System32\Drivers\Useless.sys)
  File size 2K.BD: Loaded remote file \WINDOWS\System32\Drivers\Useless.sys
 
BlLoadImageEx: Pulled \WINDOWS\System32\Drivers\Useless.sys from Kernel Debugger
BD: \WINDOWS\System32\Drivers\Useless.sys base address 8000F000
BD: \WINDOWS\System32\Drivers\Mup.sys base address 80300000
BD: \WINDOWS\system32\DRIVERS\agp440.sys base address 8031B000
Shutdown occurred...unloading all symbol tables.
Waiting to reconnect...

E mais tarde, nossa prova de que o arquivo foi substituído com sucesso.

Connected to Windows XP 2600 x86 compatible target, ptr64 FALSE
Kernel Debugger connection established.  (Initial Breakpoint requested)
Symbol search path is: SRV*c:\websymbols*http://msdl.microsoft.com/download/symbols
Executable search path is: 
Windows XP Kernel Version 2600 UP Free x86 compatible
Built by: 2600.xpsp_sp2_gdr.070227-2254
Kernel base = 0x804ea000 PsLoadedModuleList = 0x8056d620
System Uptime: not available
Este driver foi compilado em Jul 16 2007 01:18:53

Eu lhe garanto que isso ainda poderia salvar sua vida se você tivesse um filtro de File System com um bug na DriverEntry em uma máquina de cliente. A expressão de pânico do cliente quando vê a máquina dele reiniciando em um loop infinito é interessante, mas manter seu emprego é um pouco mais interessante.

Até mais,
[]s.

2 Responses to “Bug em meu driver de Boot. Já posso formatar?”

  1. Impressionante esse artigo! Apesar de poderoso, me pergunto se não seria possível para um atacante fazer rodar seu código não-assinado em kernel mode em um Windows Vista 64 bits (que, como sabemos, deveria impedir isso). Isso procede?

    • Olá Wanderley,

      Na verdade, o fato de o atacante ter acesso ao kernel da máquina via debugger acaba com toda a segurança que ela deveria oferecer.

      Neste outro post eu demonstro como injetar um executável em um sistema via debugger. Nele, ainda aponto um outro post que se aproveita do fato de estar conectado ao Kernel para fazer logon de qualquer conta local sem precisar de senhas. Vale a pena dar uma olhada e estar preparado para quando você esquecer sua senha.

      []s.

Deixe um comentário