Como a maioria dos desenvolvedores de drivers, eu tambĂ©m tenho que construir versĂ”es de drivers para vĂĄrias versĂ”es do Windows. Ainda nĂŁo vou comentar sobre VXDs neste post, mas jĂĄ posso comentar sobre ter um Ășnico arquivo .SYS que possa rodar tanto em Windows Server 2003 como em Windows NT. Sei que este sistema operacional nĂŁo Ă© mais vendido e nem conta mais com o suporte da Microsoft, mas Ă© impressionante ver como ainda encontramos Windows 95 e Windows NT tanto em ambientes corporativos como nas casas de usuĂĄrios finais. Assim temos sempre que ficar nos esquivando das limitaçÔes de cada sistema operacional quando estamos planejando desenvolver um sistema.
Uma função que recebeu uma nova versĂŁo foi a ExFreePool, sua nova versĂŁo Ă© a ExFreePoolWithTag, que Ă© implementada a partir Windows 2000. Para termos um Ășnico binĂĄrio que seja executado em ambas as versĂ”es, temos que utilizar a mais velha que ainda Ă© suportada pelos sistemas recentes.
Vamos considerar o fonte abaixo para um driver que realiza a incrĂvel tarefa de alocar memĂłria dinamicamente.
#include
Â
NTSTATUS DriverEntry(PDRIVER_OBJECTÂ pDriverObject,
               PUNICODE_STRING pusRegistryPath)
{
  PVOID pTemp;
Â
  //-f--> TÎ pensando que é fåcil alocar memória.
   if (!(pTemp = ExAllocatePool(NonPagedPool, 10))
     return STATUS_NO_MEMORY;
Â
  //-f--> Memory leak é falta de educação.
   ExFreePool(pTemp);
  return STATUS_SUCCESS;
}
Utilizando o ambiente de build para Windows XP para compilar este driver, podemos ver que o mesmo binĂĄrio funciona em Windows Server 2003, Windows XP, Windows 2000 e Windows NT. Ou pelo menos deveria. Quando tentamos iniciar nosso maravilhoso driver em Windows NT, obtemos a seguinte mensagem:

Mas como? Se dermos uma olhada mais de perto em nosso driver, vamos descobrir que ele Ă© um traidor miserĂĄvel. Como uma DLL, drivers sĂŁo mĂłdulos executĂĄveis que utilizam a clĂĄssica estrutura PE para declarar suas dependĂȘncias. Assim, podemos abrir nosso driver no “Dependency Walker” e ver o que ele espera da vida para poder ser carregado. Por encreça que parĂvel, ele realmente depende estaticamente das funçÔes novas de alocação de memĂłria. Traidor maldito!

Isso acontece porque dentro do arquivo ntddk.h existe um conjunto de defines que acabam mudando as chamadas ExFreePool para ExFreePoolWithTag. Assim podemos pegar todos aqueles 7 quilos de cĂłdigo escritos para NT e utilizĂĄ-los em builds para sistemas mais novos sem mudar uma sĂł linha, ou ainda podemos ter cĂłdigo fonte compartilhado entre drivers para Windows NT e para sistemas mais novos.
#define POOL_TAGGING 1
Â
...
Â
#ifdef POOL_TAGGING
#define ExAllocatePool(a,b) ExAllocatePoolWithTag(a,b,' kdD')
#define ExAllocatePoolWithQuota(a,b) ExAllocatePoolWithQuotaTag(a,b,' kdD')
#endif
EntĂŁo vocĂȘ me faz as seguintes perguntas: Mas qual a real diferença entre versĂŁo velha e a nova? Meu driver pode ter problemas se tentar utilizar a versĂŁo velha rodando em um sistema mais novo? Existe vida inteligente fora da terra?
A versĂŁo nova veio para ajudar a detectar memory leaks. Cada alocação de memĂłria fica associada a uma Tag. Estas Tags podem ser visualizadas utilizando ferramentas de depuração tais como o WinDbg. Por exemplo: Se toda a sua biblioteca de configuração utilizar uma Tag, e a sua biblioteca de comunicação utilizar outra, fica fĂĄcil saber quais delas estĂĄ deixando memĂłria alocada. Desta forma fica mais fĂĄcil saber qual programador do seu time vocĂȘ vai mandar pra rua. As macros no ntddk.h apenas utilizam uma Tag default para as alocaçÔes de memĂłria que nĂŁo tem uma Tag associada.
Mas o que acontece com os drivers que foram compilados com o DDK do Windows NT e que rodam sobre o sistemas posteriores? Estes drivers de fato utilizam as funçÔes velhas. Se utilizarmos o Depends neles poderemos ver isso. Por uma questão de compatibilidade, as funçÔes velhas ainda são exportadas em sistemas novos para dar suporte aos drivers antigos. Vamos dar uma olhada na implementação da ExAllocatePool do Windows 2000.

A implementação da função antiga simplesmente encaminha a alocação de memĂłria para a função nova e utiliza a Tag ‘None’. Isso nos assegura que nĂŁo morreremos de cĂąncer se continuarmos utilizando as funçÔes antigas em um sistema novo.
Bom, o que estĂĄvamos querendo mesmo? Ah sim, construir um driver novo que possa rodar tanto em Windows NT como em sistemas posteriores. PoderĂamos simplesmente utilizar o DDK do Windows NT e contar com a compatibilidade reversa para rodar em sistemas mais novos. Isso Ă© possĂvel, mas algumas das funçÔes que vemos no DDK sĂŁo definidas como macros. A função IoSetCompletionRoutine Ă© um exemplo clĂĄssico disso. Utilizar um DDK antigo significa nĂŁo ter algumas destas funçÔes definidas e terĂamos que ficar remendando definiçÔes do DDK se quisĂ©ssemos desfrutar delas. Existem ainda aqueles casos de pessoas perdidas em ilhas desertas apenas com o DDK mais novo.
#include
Â
#undef ExAllocatePool
#undef ExFreePool
Â
NTSTATUS DriverEntry(PDRIVER_OBJECTÂ pDriverObject,
               PUNICODE_STRING pusRegistryPath)
{
Â
...
Para resolver isso, logo depois do include para ntddk.h, vamos dar um #undef nas macros que redirecionam as chamadas do ExAllocatePool para a nova versão e prontio, seus pobremas se acabaram-se. Recompilando teremos um driver novo que utiliza as funçÔes antigas de alocação. Desta forma ele pode ser utilizado em sistemas desde o Windows NT.
A desvantagem deste mĂ©todo Ă© que seus drivers nĂŁo poderĂŁo utilizar as Tags quando rodando em um sistema que as suporte. Na verdade Ă© possĂvel ter um Ășnico binĂĄrio que se rodando em Windows NT utilize ExFreePool e se rodando em um sistema mais novo utilize ExFreePoolWithTag, mas isso Ă© outro Post.
Neste primeiro post, vou aproveitar que ninguém vai ler mesmo para falar sobre mim. Afinal, as pessoas que se interessarem em ler este blog podem querer conhecer meu perfil com um pouco mais de detalhes. Nascido em 1976, eu nem imaginava o que seria da vida quando aos 13 anos và um computador pela primeira vez. Meu primo tinha um CP200 e eu nem sabia que tipo de video game era aquele. Quando meu primo fez um upgrade para um poderoso MSX, eu comprei o CP200 dele. Eu simplesmente não sabia nada a respeito. Na época os jogos e aplicativos eram gravados em fita K7 e eu não tinha o gravador para desfrutar de toda essa tecnologia. Resultado, eramos eu e o manual de BASIC e foi assim que aprendi a programar.