1120 Alameda Orquidea, Atibaia, SP,  BRA

contact@driverentry.com.br

Começar de novo

Alguns drivers precisam iniciar logo que o sistema carrega, ou melhor, enquanto o sistema carrega. Quando configuramos nosso driver com Start = 0 (Boot), nosso driver carrega junto com drivers bem básicos, tais como File Systems, Bus Drivers e por aí vai. Certa vez, precisei que um driver de Boot abrisse um arquivo para obter algumas informações do sistema. Infelizmente coisas como partições, volumes e File Systems ainda não estavam trabalhando, assim, tive que adiar um pouco este processo. Neste post vou comentar sobre como dar continuidade à inicialização do seu driver depois que a função DriverEntry terminou.

Para exemplificar o caso, escrevi um driver que ilustra claramente essa situação. O projeto completo está em um link para download no final deste post. Olhando para a implementação da nossa DriverEntry temos:

/****
***     DriverEntry
**
**      Ponto de entrada do driver, que será executado enquanto
**      o sistema Boota (do verbo "acabei de ligar a máquina").
*/
extern "C" 
NTSTATUS DriverEntry(IN PDRIVER_OBJECT  pDriverObj,
                     IN PUNICODE_STRING pusRegistryPath)
{
    NTSTATUS    nts;
 
    //-f--> Ói nóis aqui traveis...
    KdPrint(("Starting DrvReinit...\n"));
 
    //-f--> Registra rotina de descarga do driver
    pDriverObj->DriverUnload = OnDriverUnload;
 
    //-f--> Tenta abrir o arquivo o quanto antes, afinal
    //      eu nasci de sete meses mesmo.
    nts = TryOpenFile();
 
    //-f--> E como vocês podem ver...
    if (!NT_SUCCESS(nts))
    {
        //-f--> O arquivo não foi aberto
        KdPrint(("Scheduling reinitialization.\n"));
 
        //-f--> E eu até imagino qual seja o erro.
        ASSERT(nts == STATUS_OBJECT_PATH_NOT_FOUND);
 
        //-f--> Registra OnReinitialize para ser chamada mais tarde.
        //      Mais tarde significa quando todos os drivers de boot
        //      foram carregados.
        IoRegisterBootDriverReinitialization(pDriverObj,
                                             OnReinitialize,
                                             NULL);
    }
    else
    {
        //-f--> Er... Tem certeza de que você instalou esse driver
        //      direito? Bom, então o sistema não está iniciando
        //      agora e você iniciou este driver na mão.
    }
 
    //-f--> Se a função DriverEntry não retornar STATUS_SUCCESS,
    //      a rotina agendada para reiniciar o driver não será chamada.
    return STATUS_SUCCESS;
}

A função IoRegisterBootDriverReinitialization (Ufa! Algumas destas funções exigem fôlego) registra uma rotina de callback que será chamada ao final da carga de todos os drivers de Boot. Isso vai nos dar uma segunda oportunidade de tentar fazer o que nos foi proposto. Esta rotina é normalmente utilizada por filtros que se atacham sobre dispositivos não Plug-and-Play, e assim, não podem contar com a chamada da função AddDevice para serem notificados de que um novo device foi criado.

Em nosso exemplo, o objetivo é simplesmente ler um arquivo. Nestas condições, o erro que recebemos é o STATUS_OBJECT_PATH_NOT_FOUND. Para garantir que o arquivo que estamos querendo ler realmente existe, vamos utilizar o próprio arquivo onde o driver é implementado. Logo, se nosso código está rodando, o arquivo está lá.

/****
***     TryOpenFile
**
**      Tenta abrir o arquivo onde este driver está implementado.
**      Se este código está rodando, então este arquivo tem que estar lá.
*/
NTSTATUS TryOpenFile(VOID)
{
    NTSTATUS            nts;
    IO_STATUS_BLOCK     IoStatusBlock;
    HANDLE              hFile;
    OBJECT_ATTRIBUTES   ObjAttributes;
    UNICODE_STRING      usFilePath =
        RTL_CONSTANT_STRING(L"\\??\\C:\\Windows\\System32\\drivers\\DrvReinit.sys");
 
    //-f--> Olá depurador...
    KdPrint(("Trying open the file...\n"));
 
    //-f--> Preenchendo a estrutura OBJECT_ATTRIBUTES,
    //      fazê o quê, faz parte.
    InitializeObjectAttributes(&ObjAttributes,
                               &usFilePath,
                               OBJ_CASE_INSENSITIVE,
                               NULL,
                               NULL);
 
    //-f--> Solicita a abertura do arquivo
    nts = ZwCreateFile(&hFile,
                       GENERIC_READ,
                       &ObjAttributes,
                       &IoStatusBlock,
                       NULL,
                       0,
                       FILE_SHARE_READ | FILE_SHARE_WRITE,
                       FILE_OPEN,
                       0,
                       NULL,
                       0);
 
    //-f--> Abriu ou não?
    if (!NT_SUCCESS(nts))
    {
        //-f--> Sinto muito, não foi dessa vez
        KdPrint(("Error 0x%08x opening the file.\n", nts));
    }
    else
    {
        //-f--> Até que não foi tão difícil assim
        KdPrint(("File opened OK. Er... So, let's close it now.\n"));
 
        //-f--> Fecha o handle do arquivo. Não queremos nada com ele mesmo.
        ZwClose(hFile);
    }
    return nts;
}

Inicialmente quando tentamos abrir este arquivo à partir da função DriverEntry e nos é retornado o erro indicando que o mesmo não existe, podemos cair em uma crise existencial. Afinal, se o arquivo não existe, como o driver foi carregado? Será que o driver também não existe? Será que eu também não existo? Outras crises poderiam ser mencionadas como a tão conhecida:

“Deus é amor.
O amor é cego.
Steve Wonder é cego.
Logo, Steve Wonder é Deus.

Disseram-me que eu sou ninguém.
Ninguém é perfeito.
Logo, eu sou perfeito.
Mas só Deus é perfeito.
Portanto, eu sou Deus.

Se Steve Wonder é Deus, eu sou Steve Wonder!
Meu Deus, eu sou cego!”

Mas voltando ao assunto, caso a rotina de reinicialização for executada e o serviço que o driver precisa ainda não estiver disponível, então podemos re-agendar a chamada desta rotina mais uma vez (ou quantas vezes forem necessárias). Um dos parâmetros que a rotina de reinicialização recebe, é o que informa quantas vezes ela foi chamada pelo sistema. Isso poderia ser utilizado para desistir de procurar o recurso que nunca aparece quando a milésima tentativa falhasse. Acompanhe nossa rotina de exemplo.

/****
***     OnReinitialize
**
**      Rotina que é registrada pela funçao 
**      IoRegisterBootDriverReinitialization para ser chamada mais tarde.
*/
VOID OnReinitialize(IN PDRIVER_OBJECT     pDriverObj,
                    IN PVOID              pContext,
                    IN ULONG              ulCount)
{
    NTSTATUS    nts;
 
    //-f--> Esta pode ser reagenda quantas vezes forem necessárias.
    //      Vamos mostrar quantas foram.
    KdPrint(("OnReinitialize was called %d times...\n", ulCount));
 
    //-f--> Se não houvesse este comentário, vocês nunca descobririam
    //      que a função abaixo TentaAbrirArquivo
    nts = TryOpenFile();
 
    //-f--> E aí? Deu certo?
    if (!NT_SUCCESS(nts))
    {
        //-f--> Se o erro for diferente deste abaixo, então
        //      não sei de nada. A culpa não é minha. Eu não te conheço.
        ASSERT(nts == STATUS_OBJECT_PATH_NOT_FOUND);
 
        //-f--> Não pode ser, isso não está acontecendo de verdade.
        //      Vamos tentar um cadim mais tarde então.
        IoRegisterBootDriverReinitialization(pDriverObj,
                                             OnReinitialize,
                                             NULL);
    }
}

Instalando um driver de Boot

Para assegurar que teremos a necessidade de atrasar a inicialização do nosso driver, vamos fazer com que nosso driver de exemplo seja um dos primeiros a ser carregado. Para isso, vamos utilizar o já mencionado DriverLoader para instalá-lo em um grupo específico.

Depois de compilar o código de exemplo, copie o driver para o diretório System32\drivers da máquina vítima. Execute o DriverLoader, preencha os campos como demostrado abaixo e clique em RegisterService.

Para completar a experiência, vamos reiniciar o sistema e conectar o depurador de Kernel para acompanhar. Deveriamos ter a seguinte saída.

Mas não poderiamos utilizar um work Item para isso? Na verdade sim, mas existem sutis diferenças entre essas alternativas. Utilizar um Work Item não garante que a rotina não será executada antes da sua DriverEntry terminar, e obviamente, também não garante que ela seja executada somente depois que todos os drivers de Boot forem carregados.

Até a proxima… 😉

DrvReinit.zip

3 Responses

  1. Oi!
    Estou com um problemão! Não cosigo instalar programa nenhum que o computador “diz” que só através do driver G. O meu computador não posui este driver.Ele posui o A:,C:,D; e o F:.O que eu faço? O driver G: no caso seria só qdo coloco o pen-Driver.
    E o que é DriverEntry? O meu técnico disse que só formatando a máquina?? Ele não poderia criar mais este driver, o G:??

    Obrigada,

    1. Olá Lucia,

      Embora eu não tenha detalhes suficientes para resolver seu problema definitivamente, acho que posso dar uma ou duas dicas para assim tentar resolvê-lo.

      O “driver” como você está me dizendo, são na verdade unidades, nomes que o sistema cria como forma de nos dar acesso aos arquivos contidos em um dispositivo de armazenamento, assim como o seu Pen-Drive.
      Quando colocamos um Pen-Drive em um computador, o sistema adota uma unidade a este dispositivo. Estas unidades somente estão disponíveis enquanto seu pen-drive estiver no computador.

      Pelo que você está me dizendo, imagino que você tenha instalado um programa em seu computador a partir de uma unidade criada por um pen-drive, e agora, por algum motivo, toda vez que você utilizar este programa, ele tentará utilizar a mesma unidade de onde foi instalado. Repito, isso é o que eu imagino.

      Formatar o computador sempre foi uma excelente maneira de lidar com problemas que não entendemos. Talvez seu técnico não tenha a menor idéia do que está acontecendo, e agora esteja querendo usar seu golpe final. Sinto informar que isso não é garantia de que o problema não voltará a acontecer. Tente procurar um outro técnico, talvez um com um pouco mais de experiência, busque uma segunda opinião.

      Espero ter ajudado.

      A propósito, DriverEntry é um nome que se utiliza em programação de computadores, e está um tanto distante do problema que você está relatando.

      []s,

  2. Olá Fernando, continua vivo por aqui? “Espero que sim hehe!”

    É que estou precisando de uma ajudinha sua. Estou fazendo uma função CopyFile(). Então quero primeiramente obter o tamanho em bytes do arquivo existente. Para isso estou usando ZwQueryInformationFile() com FILE_STANDARD_INFORMATION, da seguinte maneira:

    nts=ZwQueryInformationFile(hFile, &IoStatusBlock,
    &fsi,
    sizeof(fsi),
    FileStandardInformation );

    Agora, como faço pra extrair o tamanho do arquivo pelo fsi?

    Obrigado por esse ótimo artigo.
    Tiago

Leave a Reply

Your email address will not be published.