Compartilhar via


Verificador de aplicativos - Perguntas frequentes (FAQs)

Perguntas Gerais

Veja a seguir uma lista de perguntas recebidas sobre o uso geral do Application Verifier.

O que é o Application Verifier?

O Verificador de Aplicativos é uma ferramenta de verificação de tempo de execução usada para encontrar bugs em aplicativos do Microsoft Windows. Por ser uma ferramenta de tempo de execução, o código do aplicativo precisa ser exercitado para ser verificado. Uma boa cobertura de teste é, portanto, essencial.

O cenário de uso típico do Verificador de Aplicativos é habilitá-lo para os aplicativos de interesse (consulte as perguntas abaixo para saber como fazer isso) e, em seguida, executar todos os testes que você escreveu para seu aplicativo. Você receberá uma notificação para qualquer bug encontrado na forma de uma interrupção do depurador ou uma entrada de log do verificador.

Como faço para desinstalar o Verificador de Aplicativos?

Para desinstalar o Verificador de Aplicativos, acesse o painel de controle clicando em Iniciar, selecione Adicionar ou Remover Programas, Remover um programa, clique em Verificador de Aplicativos e clique em Remover.

Como faço para iniciar o Verificador de Aplicativos?

Depois de instalar o Application Verifier, você pode iniciá-lo acessando-o em sua lista de programas OU digitando Appverif.exe em uma linha de comando. Para fazer isso, vá para um prompt de comando ou para a caixa Executar do menu Inicialização. Digite appverif.exe e pressione Enter. Isso iniciará o Application Verifier.

O binário Appverifer.exe é instalado no diretório do sistema e é usado para fazer as configurações da ferramenta.

Onde os logs são armazenados?

Os logs são armazenados em %USERPROFILE%\AppVerifierLogs

O que devo fazer se tiver problemas ao usar o Verificador de Aplicativos?

Verifique se você está executando a versão mais recente. Considere experimentar o mesmo aplicativo em um PC diferente ou até mesmo em uma versão do Windows.

O Verificador de Aplicativos verifica o código gerenciado?

O AppVerifier se preocupa com as interfaces entre o sistema operacional e o aplicativo. Como resultado, a menos que seu código gerenciado esteja executando interoperabilidade em APIs nativas que tenham a ver com Heaps, Handles, Critical Section, etc., seus casos de teste não fornecerão nenhuma interação com as interfaces verificadas.

Recomendamos aproveitar os Assistentes de Depuração Gerenciados para verificar seu código gerenciado. Leia mais sobre eles em Depurando código gerenciado usando o Windows Debugger.

Há suporte para ARM64EC?

O Verificador de Aplicativos não oferece suporte a ARM64EC.

Perguntas sobre o depurador

A seguir está a lista de perguntas recebidas sobre o depurador.

Por que recebi uma mensagem de erro informando que preciso de um depurador?

A camada de verificação Básico no Verificador de Aplicativos exige que você execute seu aplicativo em um depurador. Se você não tiver um depurador associado ao aplicativo antes de selecionar o teste, receberá uma caixa de diálogo lembrando que precisará executar seu aplicativo em um depurador para obter as informações registradas.

Como executo meu aplicativo em um depurador?

Consulte os tópicos de instalação e configuração do Depurador – Introdução à depuração do Windows

Como faço para testar a expansão da pilha sem qualquer outra instrumentação?

Em geral, a expansão da pilha deve ser realmente testada isoladamente de outras camadas de verificação, incluindo heap. O motivo é o seguinte: cada camada de verificação "destrói" uma API ou um ponto exportado com alguma rotina.

Por exemplo, uma chamada para CreateFileA será uma chamada para appvocre! NS_SecurityChecks::CreateFileA, que pode chamar appvcore! NS_FillePaths::CreateFileA que pode chamar kernel32! CreateFileA, que pode chamar verificador! AVrfpNtCreateFile, que chamará ntdll! NtCreateFile. Você pode ver que a instrumentação adicionou mais 3 chamadas de função "empilhadas", cada uma delas pode e consumirá mais pilha.

No caso abaixo, o LH-verifier.dll é "thunking" cada DllMain e o caminho de código de heap "instrumentado" adicionará mais uso de pilha. Como o thread injetado do depurador não usa os padrões IMAGE_NT_HEADERS, a pilha inicialmente confirmada não será suficiente para concluir o estado APC de um thread (um thread no estado APC executou o código de inicialização).

Se você quiser usar Stack-Ckecs, provavelmente a única outra camada de verificação que você deve usar é FirstChanceAccessViolation.

Ao usar a extensão !avrf, recebo 'O verificador de aplicativos não está habilitado para este processo...'

O erro completo recebido: Application verifier is not enabled for this process. Use appverif.exe tool to enable it.

Você provavelmente tem apenas as camadas de verificação de shim habilitadas e/ou o heap no modo "puro" habilitado. Estas são algumas das possíveis causas.

Perguntas sobre o cenário de teste

A seguir está uma lista de perguntas recebidas sobre diferentes cenários de teste.

Como posso habilitar o Verificador de Aplicativos no meu serviço, mas não em outros?

Faça uma cópia do svchost.exe no diretório System32 e chame a cópia de "Mysvchost.exe".

Usando regedit, abra HKLM\System\CurrentControlSet\Services\MyService.

Edite o valor "ImagePath", que será algo como "%SystemRoot%\system32\svchost.exe -k myservice" e altere svchost.exe para "Mysvchost.exe".

Adicione "Mysvchost.exe" à lista AppVerifier e verifique os testes desejados.

Reinicialize.

Como executo o Verificador de Aplicativos em um aplicativo de 64 bits iniciado a partir de um aplicativo de 32 bits em execução no WOW64?

Versão simples: a regra de ouro para habilitar as configurações do verificador em um determinado aplicativo é corresponder o número de bits da ferramenta e o processo de destino. Ou seja: use o appverif.exe de 32 bits para um aplicativo de 32 bits (ambos em execução no WoW64) e use o AppVerif.exe de 64 bits para o destino nativo de 64 bits.

Versão Longa: as configurações do Verificador de Aplicativos são a união adequada das configurações "principais" e das configurações de "correção".

Configurações principais - As configurações principais são armazenadas em Opções de execução de arquivo de imagem.

O valor "Depurador" é lido do aplicativo de inicialização. Portanto, se você quiser que o devenv.exe de 32 bits inicie o my.exe de 64 bits e executá-lo no depurador, deverá usar a chave do Registro de 32 bits em WoW6432Node. Os outros valores, para um processo de 32 bits, são lidos de ambos os locais, tanto o IFEO nativo quanto o WoW6432Node.

O raciocínio é o seguinte: um processo de 32 bits em execução no WoW é um processo de 64 bits que executa o loop de emulação Wow64. Portanto, cada processo de 32 bits é primeiro um processo de 64 bits e, em seguida, um processo de 32 bits. O IFEO de 64 bits habilitará o verificador no código Wow64cpu.dll, enquanto o IFEO de 32 bits habilitará o verificador no código de 32 bits.

Do ponto de vista do usuário final, verifier.dll é carregado duas vezes (uma vez no mundo de 64 bits, uma vez no mundo de 32 bits). Como a maioria das pessoas não se preocupa em verificar wow64cpu.dll, o comportamento mais aceito para processos de 32 bits é verificar apenas a parte de 32 bits. É por isso que a regra de ouro de "sempre combine o bit-ness" se aplica.

Como depurar meu serviço que é executado em uma estação de janela não interativa

Para depurar um serviço executado em uma estação de janela não interativa, faça o seguinte (aplica-se somente se você estiver usando ntsd/windbg):

Adicione uma chave ao registro em HKLM\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options. O nome dessa chave deve ser o nome do processo (service.exe).

Crie um valor de REG_SZ chamado Depurador e defina esse valor como o caminho em que o depurador reside. Ele deve conter o caminho completo, não apenas o nome do depurador. O comando deve incluir a opção –server e uma porta específica ou intervalo de portas que o depurador deve escutar. Um exemplo é c:\debuggers\ntsd.exe –server tcp:port=5500:5600 –g –G.

Conecte-se ao servidor do depurador executando o depurador com uma opção –remote. Um exemplo é: windbg.exe –remote tcp:=localhost,port=55xx onde 'xx' é algum número de 00 a 99 se você usou um intervalo no servidor.

O AppVerifier faz detecção de vazamentos? No Windows 7 e superior, há uma opção de verificação de vazamentos que detectará quando um processo vazar memória. Em sistemas operacionais anteriores, o AppVerifier não testa o aplicativo para detecção de vazamento, mas procura outros problemas de memória.

Quais testes são recomendados para questões de segurança?

  • Heaps
  • Alças
  • Bloqueios
  • Pilhas (somente para serviços e processos importantes que podem derrubar a máquina)

Lembre-se de que ObsoleteAPICalls apenas exibirá um aviso para cada chamada que vir para uma API listada como obsoleta ou preterida no MSDN. Você deve decidir caso a caso se é importante que seu aplicativo mude para as novas APIs. Algumas das APIs são perigosas e algumas foram apenas substituídas por uma API mais recente com mais opções. Dê uma olhada na seção "APIs perigosas" de Escrevendo código seguro, 2ª adição para saber mais.

Para aplicativos que precisam ser altamente confiáveis, como serviços e programas de servidor, você também deve habilitar a verificação de pilhas. Isso verifica se o tamanho do commit da pilha é adequado, desativando o crescimento da pilha. Se o aplicativo for encerrado imediatamente com um estouro de pilha, isso significa que o aplicativo precisa ser recompilado com um tamanho de confirmação de pilha maior. Se você for um testador e encontrar um problema com um aplicativo ao usar a verificação de pilhas, registre um bug, atribua-o ao seu desenvolvedor e continue testando.

Perguntas específicas do teste

A seguir está uma lista de perguntas sobre testes. Clique na pergunta para ver a resposta:

Os vazamentos de seções críticas são importantes?

Sempre que você vaza uma seção crítica, você vaza o seguinte: um identificador de evento, uma pequena quantidade de pool de kernel e uma pequena alocação de heap. Eles serão limpos se o processo for encerrado.

Se o seu processo deve permanecer vivo por muito tempo, esses vazamentos podem mordê-lo. Como as correções são muito fáceis em 99% dos casos (o desenvolvedor simplesmente esqueceu de chamar RtlDeleteCriticalSection), você deve resolvê-las.

Podemos lidar programaticamente com estouros de pilha?

Não há garantia de que o estabelecimento de um manipulador de exceção na função de thread inicial capturará os possíveis estouros de pilha que podem ser gerados. Isso ocorre porque o código que despacha exceções também precisa de um pouco de pilha para ser executado sobre o registro de ativação atual. Como acabamos de falhar na extensão da pilha, é muito provável que passemos pelo final da pilha confirmada e levantemos uma segunda exceção ao tentar despachar a primeira. Uma exceção de falha dupla encerrará o processo incondicionalmente.

O teste LoaderLock está dando um erro ao chamar DestroyWindow. Por que não consigo chamar DestroyWindow em DllMain? Você não controla qual thread vai se desconectar. Se não for o mesmo thread que criou a janela, você não poderá destruir a janela. Então você vaza a janela e na próxima vez que a janela receber uma mensagem, você trava porque o Wndproc foi descarregado.

Você precisa destruir a janela antes de obter a desconexão do processo. O perigo não é que o user32 seja descarregado. O perigo é que você está sendo descarregado. Portanto, a próxima mensagem que a janela receber travará o processo porque o user32 entregará a mensagem ao seu Wndproc, que não existe mais.

O sistema operacional Microsoft Windows tem afinidade de thread. Process-detach não. A trava do carregador não é realmente o grande problema; o problema é Dllmain. A desanexação de processo é a última vez que sua DLL executa código. Você deve se livrar de tudo antes de retornar. Mas como o Windows tem afinidade de thread, você não pode limpar a janela se estiver no thread errado.

O bloqueio do carregador entra em cena se alguém tiver um gancho global instalado (por exemplo, spy++ está em execução). Nesse caso, você entra em um cenário de impasse potencial. Novamente, a solução é destruir a janela antes de obter a desanexação do processo.

É caro aumentar os commits iniciais da pilha para evitar estouros?

Quando você confirma a pilha, está apenas reservando espaço no arquivo de página. Não há impacto no desempenho. Nenhuma memória física é realmente usada. O único custo adicional acontece se você realmente tocar no espaço da pilha que você comprometeu. Mas isso acontecerá de qualquer maneira, mesmo que você não confirme a pilha antecipadamente.

Vejamos qual seria o custo para tornar todos os serviços executados em svchost.exe à prova de balas. Em uma máquina de teste, obtenho 9 processos svchost.exe com um total de 139 threads. Se definirmos a pilha padrão para cada thread em 32K, precisaremos de aproximadamente 32K x 200 ~ 6,4 Mb de espaço no arquivo de página para confirmar todas as pilhas antecipadamente.

Este é um preço muito pequeno a pagar pela confiabilidade.

E quanto ao tamanho da pilha reservada?

Existem itens interessantes, como o despacho de exceção no IA64 / AMD64 que requer uma pilha extra "inesperada". Pode haver algum processamento acontecendo em threads de trabalho RPC cujos requisitos de pilha são tentativas razoáveis de medi-los.

Em primeiro lugar, você deve ter uma ideia de todos os pools de threads que vivem no processo. O NT-Thread-Pool, com os threads de espera alertáveis, às vezes é especial, porque, por exemplo, se você usar um componente de banco de dados do SQL, ele usará suspensões alertáveis em um thread que é um destino do usuário-APC. Isso pode causar problemas com chamadas aninhadas.

Depois de conhecer todos os pools de threads, tenha uma ideia de como controlar seus requisitos de pilha. Por exemplo, o RPC lê uma chave do Registro para a confirmação da pilha. Os threads da bomba WDM obtêm isso da imagem. Para outros pools de threads, a milhagem pode variar.

Quando todos os tópicos estiverem limpos, você poderá realizar alguma ação. Não ter um espaço reservado enorme ajuda a resolver a fragmentação do espaço somente se os threads vierem e irem com muita frequência. Se você tiver um pool de threads estável que esteja sob seu controle, poderá ter uma vantagem em reduzir o espaço reservado também. Isso realmente ajudará a economizar espaço de endereço para os heaps e espaço de endereço para os usuários.

Existem recomendações sobre como escolher o tamanho certo para LINKER_STACKCOMMITSIZE=?

O valor deve ser divisível pelo tamanho da página (4k/8k dependendo da CPU). Aqui estão algumas diretrizes para determinar o tamanho que você precisa:

  1. Converta quaisquer funções recursivas com profundidade potencial não vinculada (ou pelo menos alta profundidade induzível pelo usuário) em iterativas.

  2. Reduza o uso de alocação. Use heap ou safealloca.

  3. Execute o Prefast com verificação de tamanho de pilha reduzido (digamos 8k). Corrija as funções sinalizadas como usando muita pilha.

  4. Defina o commit da pilha como 16k.

  5. Execute em um grupo de testes do depurador com a verificação "Pilhas" do Verificador de Aplicativos ativada.

  6. Quando você vir o estouro de pilha, determine os piores infratores e corrija-os. (Consulte a etapa 5.)

  7. Quando você não pode reduzir mais o uso da pilha, aumente em 8k. Se você tem > 64k, há algo errado, diminua de volta para 64k e veja a etapa 6. Caso contrário, vá para a etapa 5.

Quais são os requisitos de memória para o teste de heap?

Para testes de heap completos, você precisará de 256 MB de RAM e pelo menos um arquivo de paginação de 1 GB. Para testes de heap normais, você precisará de pelo menos 128 MB de RAM. Não há requisitos específicos de processador ou disco.

Por que estou recebendo uma parada ALL_ACCESS?

Qualquer aplicativo que use _ALL_ACCESS torna o objeto que está acessando não auditável porque o log de auditoria não refletirá o que você realmente fez com o objeto, apenas o que você pediu para fazer com o objeto.

Essa condição cria uma camuflagem para um ataque mais tortuoso. Um administrador que verifica uma atividade de ataque em andamento não verá nada de errado com a pessoa que solicita ALL_ACCESS na chave X, porque um aplicativo específico sempre faz isso. O administrador pensará que "a pessoa provavelmente está apenas executando o Word". O administrador não pode dizer que um hacker penetrou na minha conta e agora está sondando o sistema para determinar qual acesso eu tenho, que ele pode explorar para seus fins nefastos. As possibilidades são infinitas.

O problema da ACL com ALL_ACCESS é que você sempre deve recebê-lo. Se quiséssemos um dia negar a você o acesso DELETE a uma determinada chave, não conseguiríamos. Mesmo que você não estivesse realmente excluindo a chave, estaríamos interrompendo seu aplicativo porque você solicitaria acesso de exclusão.

Por que não obtenho nenhum log dos testes de heap e bloqueio?

Esses testes são camadas de verificação criadas no sistema operacional (e não no pacote) e relatam erros em um depurador. Se você executar um aplicativo com esses testes ativados e não tiver falhas, eles não estarão relatando nenhum problema.

Se você tiver falhas, será necessário executar em um depurador ou passar o aplicativo para um desenvolvedor para testar mais de perto.

Por que a injeção de falhas não está funcionando?

A probabilidade de injeção de falha foi alterada para partes por milhão em compilações do AppVerifier lançadas após fevereiro de 2007 com base nos comentários dos clientes. Portanto, uma probabilidade de 0n20000 é 2%, 0n500000 é 50% e assim por diante.

A extensão do depurador !avrf –flt pode ser usada para alterar a probabilidade em tempo real no depurador. No entanto, a verificação de Simulação de Poucos Recursos para o processo deve ser ativada para que isso funcione.

A extensão do depurador !avrf é parte exts.dll que acompanha o pacote do depurador. As alterações em !avrf que dão suporte à alteração de probabilidade estão no pacote de depurador mais recente. Se você estiver tendo problemas com a injeção de falhas, atualize seus depuradores e o pacote AppVerifier.

Por que o Verificador de Vazamento não relata determinados vazamentos de recursos?

O Verificador de Vazamento não relata nenhum vazamento de recursos enquanto um módulo DLL ou EXE é carregado. Quando um módulo é descarregado, o Verificador de Vazamento emite uma parada se algum dos recursos alocados pelo módulo não tiver sido liberado.

Para inspecionar recursos alocados por uma DLL ou EXE carregada, use a extensão do depurador !avrf -leak.

Confira também

Application Verifier — Visão geral

Application Verifier - Recursos

Application Verifier - Testando aplicativos

Application Verifier - Testes dentro do Application Verifier

Application Verifier - Códigos de parada e definições

Application Verifier -Depurando paradas do Application Verifier