Compartilhar via


Gerenciamento de Identificadores

Uma fonte significativa de problemas de segurança dentro dos drivers é o uso de identificadores passados entre componentes do modo de usuário e do modo kernel. Há vários problemas conhecidos com o uso de manipuladores no ambiente do kernel, incluindo os seguintes:

  • Um aplicativo que passa o tipo errado de identificador para um driver de kernel. O driver do kernel pode falhar ao tentar usar um objeto de evento em que um objeto de arquivo é necessário.

  • Um aplicativo passa um identificador para um objeto ao qual não tem o acesso necessário. O driver do kernel pode executar uma operação que funciona porque a chamada vem do modo kernel, mesmo que o usuário não tenha permissões adequadas para fazer isso.

  • Um aplicativo que passa um valor que não é um identificador válido em seu espaço de endereço, mas é marcado como um identificador do sistema para executar uma operação mal-intencionada no sistema.

  • Um aplicativo que passa um valor que não é um identificador apropriado para o objeto do dispositivo (um identificador que esse driver não criou).

Para proteger contra esses problemas, um driver de kernel deve ter especial cuidado para garantir que os identificadores passados para ele sejam válidos. A política mais segura é criar os identificadores necessários no contexto do driver. Esses identificadores, criados por drivers de kernel, devem especificar a opção OBJ_KERNEL_HANDLE, que criará um identificador válido em um contexto de processo arbitrário e que só pode ser acessado por um chamador em modo kernel.

Para os drivers que usam identificadores criados por um programa de aplicativo, o uso desses identificadores deve ser feito com extremo cuidado:

  • A melhor prática é converter o identificador em um ponteiro de objeto chamando ObReferenceObjectByHandle, especificando os parâmetros AccessMode corretos (geralmente de Irp-RequestorMode>), DesiredAccess e ObjectType , como IoFileObjectType ou ExEventObjectType.

  • Se um identificador precisar ser usado diretamente em uma chamada, é melhor usar as variantes Nt de funções em vez das variantes Zw de funções. Isso imporá a verificação de parâmetros e manipulará a validação pelo sistema operacional, pois o modo anterior será UserMode e, portanto, não confiável. Note que os parâmetros passados para funções Nt, que são ponteiros, podem falhar na validação se o modo anterior for UserMode. As rotinas Nt e Zw retornam um parâmetro IoStatusBlock com informações de erro que você deve verificar se há erros.

  • Os erros devem ser adequadamente capturados usando __try e __except, conforme necessário. Muitas das rotinas do gerenciador de cache (Cc), do gerenciador de memória (Mm) e da biblioteca de runtime do sistema de arquivos (FsRtl) geram uma exceção quando ocorre um erro.

Nenhum driver deve depender de identificadores ou parâmetros passados de um aplicativo em modo de usuário sem tomar as precauções apropriadas.

Observe que, se a variante Nt for usada para abrir um arquivo, a variante Nt também deverá ser usada para fechar o arquivo.