Verificador de Aplicativos – Depuração de Paradas do Verificador de Aplicativo
Instalação e instalação do depurador
Algumas ações do Verificador de Aplicativo podem resultar na geração de uma exceção. O depurador deve ser definido para capturar essas exceções na segunda chance, pois o próprio Verificador de Aplicativo manipulará as primeiras exceções de chance.
As exceções geradas são de três tipos:
Uma exceção de violação de acesso (0xC0000005) será gerada se a opção heap detectar um estouro de buffer de heap. Em alguns casos, a opção Verificar o uso do caminho do sistema também pode causar uma violação de acesso.
Uma exceção de identificador inválida (0xC0000008) é gerada quando a opção Detectar uso de identificador inválido detecta uma operação de identificador inválida.
Uma exceção de estouro de pilha (0xC00000FD) é gerada quando a opção Verificar a pilha adequada detecta que a pilha inicial era muito curta.
Uma maneira de se preparar para esses eventos é iniciar o depurador em uma linha de comando da seguinte maneira:
windbg -xd av -xd ch -xd sov ApplicationCommandLine
ou
cdb -xd av -xd ch -xd sov ApplicationCommandLine
Se você já tiver iniciado o depurador, poderá usar o comando sxd (Definir Exceções) para capturar todas as violações de acesso, identificadores inválidos e estouros de pilha como exceções de segunda chance:
0:000> sxd av
0:000> sxd ch
0:000> sxd sov 1
Teoricamente, é possível controlar o Verificador de Aplicativos por meio de um depurador de kernel. No entanto, isso não é recomendado – requer o uso frequente dos comandos .process e .pagein, mas não oferece mais energia do que usar um depurador no modo de usuário.
Instalando as ferramentas de depuração
Para baixar a versão mais recente das ferramentas, consulte Baixar Ferramentas de Depuração para Windows.
Configurando hardware para depuração de User-Mode
A depuração no modo de usuário geralmente é feita em um único computador: o depurador é executado no mesmo computador que o aplicativo que falhou.
Nesse caso, nenhuma configuração de hardware específica é necessária. Ao longo deste tópico, os termos computador host e computador de destino são intercambiáveis nesse caso.
Configurando software para depuração de User-Mode
Configuração básica de User-Mode – antes de iniciar a depuração do modo de usuário, você deve baixar os arquivos de símbolo necessários e definir determinadas variáveis de ambiente.
Arquivos de Símbolo
Você deve baixar os arquivos de símbolo para o processo de modo de usuário que está sendo depurado. Se este for um aplicativo que você escreveu, ele deverá ser criado com arquivos de símbolo completos. Se for um aplicativo comercial, os arquivos de símbolo poderão estar disponíveis em um servidor Web ou para download, entre em contato com o fabricante.
se você estiver executando a depuração remota, o local do arquivo de símbolo dependerá do método que você está usando:
Se você estiver executando a depuração remota por meio do depurador, os arquivos de símbolo deverão estar no computador com o servidor de depuração.
Se você estiver executando a depuração remota por meio de remote.exe, os arquivos de símbolo deverão estar no computador com o depurador.
Se você estiver executando a depuração remota por meio de um servidor de processo ou um servidor de conexão KD, os arquivos de símbolo deverão estar no computador com o cliente inteligente.
Se você estiver controlando o depurador do modo de usuário do depurador de kernel, os arquivos de símbolo precisarão estar em ambos os computadores.
Configurando variáveis de ambiente
O depurador usa uma variedade de variáveis de ambiente para indicar várias configurações importantes.
Para obter mais informações sobre depuradores, consulte Introdução com a Depuração do Windows
Configurando o Verificador de Aplicativos com o Depurador usando a linha de comando
Para configurar o Verificador de Aplicativos, você pode usar a linha de comando CDB ou NTSD.
Use a seguinte linha de comando:
cdb OtherOptions -vf:Flags Target
Em que Target é o nome do aplicativo de destino, e Flags especifica as opções desejadas do Verificador de Aplicativo que devem ser aplicadas a esse destino.
Os sinalizadores devem ser uma soma dos bits que representam as opções desejadas. Os valores de bits individuais são os seguintes:
Valor de sinalizador | Significado |
---|---|
00000001 | VERIFICAÇÕES DE HEAP |
00000004 | VERIFICAÇÕES DE IDENTIFICADOR |
00000008 | VERIFICAÇÕES DE SIM DE BAIXO RECURSO |
00000020 | VERIFICAÇÕES DE TLS |
00000040 | PILHAS SUJAS |
00000200 | APIS PERIGOSAS |
00001000 | VERIFICAÇÕES DE EXCEÇÃO |
00002000 | VERIFICAÇÕES DE MEMÓRIA |
00020000 | VERIFICAÇÕES DIVERSAS |
00040000 | VERIFICAÇÕES DE BLOQUEIO |
Depuração com !avrf
A extensão !avrf controla as configurações do Verificador de Aplicativo e exibe uma variedade de saídas produzidas pelo Verificador de Aplicativo. Para obter informações adicionais sobre a extensão !arvrf, consulte !avrf nos documentos do depurador.
Syntax
!avrf
O comando !avrf sem parâmetros mostra as configurações do Verificador de Aplicativo e as informações sobre as quebras atuais e anteriores do Verificador de Aplicativo, se houver.
!avrf –vs { Length | -aAddress }
Exibe o log de operações de espaço virtual. Length especifica o número de registros a serem exibidos a partir do mais recente. O endereço especifica o endereço virtual. Os registros das operações virtuais que contêm esse endereço virtual serão exibidos.
!avrf -hp { Length | -a Address }
Exibe o log de operações de heap. O endereço especifica o endereço heap. Os registros das operações de heap que contêm esse endereço heap serão exibidos.
!avrf -cs { Length | -a Address }
Exibe o log de exclusão de seção crítica. Length especifica o número de registros a serem exibidos a partir do mais recente. O endereço especifica o endereço da seção crítica. Os registros da seção crítica específica são exibidos quando Address é especificado.
!avrf -dlls [ Length ]
Exibe o log de carregamento/descarregamento da DLL. Length especifica o número de registros a serem exibidos a partir do mais recente.
!avrf -trm
Exibe um log de todos os threads encerrados e suspensos.
!avrf -ex [ Length ]
Exibe o log de exceções. O Verificador de Aplicativos rastreia todas as exceções que ocorrem no aplicativo.
!avrf -threads [ ThreadID ]
Exibe informações sobre threads no processo de destino. Para threads filho, o tamanho da pilha e os sinalizadores CreateThread especificados pelo pai também são exibidos. Fornecer uma ID de thread exibirá informações somente para esse thread específico.
!avrf -tp [ ThreadID ]
Exibe o log do pool de threads. Esse log pode conter rastreamentos de pilha para várias operações, como alterar a máscara de afinidade de thread, alterar a prioridade do thread, postar mensagens de thread, inicializar COM e não inicializar COM de dentro do retorno de chamada do pool de threads. Fornecer uma ID de thread exibirá informações somente para esse thread específico.
!avrf -srw [ Address | Address Length ] [ -stats ]
Exibe o log do LEITOR/Gravador Fino (SRW). Especificar Endereço exibirá registros relativos a esse endereço de bloqueio SRW. Quando Length é especificado junto com o Endereço , todos os bloqueios SRW dentro desse intervalo de endereços são exibidos. A opção -stats despeja as estatísticas de bloqueio SRW.
!avrf -leak [ -m ModuleName ] [ -r ResourceType ] [ -a Address ] [ -t ]
Exibe o log de recursos pendentes. Esses recursos podem ou não ser vazamentos em um determinado ponto. Especificar ModuleName (incluindo a extensão) exibe todos os recursos pendentes no módulo especificado. Especificar ResourceType exibe recursos pendentes desse tipo de recurso específico. Especificar registros de despejos de endereço de recursos pendentes com esse endereço. ResourceType pode ser um dos seguintes:
- Heap: exibe alocações de heap usando APIs de Heap do Win32
- Local: exibe alocações locais/globais
- CRT: exibe alocações usando APIs CRT
- Virtual: exibe reservas virtuais
- BSTR: exibe alocações BSTR
- Registro: exibe a chave do Registro aberta
- Power: exibe objetos de notificação de energia
- Identificador: exibe alocações de thread, arquivo e identificador de evento
!avrf –trace TraceIndex
Exibe um rastreamento de pilha para o índice de rastreamento especificado. Algumas estruturas usam esse número de índice de 16 bits para identificar um rastreamento de pilha. Esse índice aponta para um local dentro do banco de dados de rastreamento de pilha. Se você estiver analisando essa estrutura, achará essa sintaxe útil.
!avrf -cnt
Exibe uma lista de contadores globais.
!avrf -brk [ BreakEventType ]
Especifica que esse é um comando de evento de interrupção. Quando !avrf -brk
é usado sem parâmetros adicionais, as configurações de evento de interrupção são exibidas. BreakEventType especifica o número de tipo do evento de interrupção. Para obter uma lista de tipos possíveis, use !avrf -brk
.
!avrf -flt [ EventTypeProbability ]
Especifica que esse é um comando de injeção de falha. Quando !avrf -flt
é usado sem parâmetros adicionais, as configurações atuais de injeção de falha são exibidas. EventType especifica o número de tipo do evento. A probabilidade especifica a frequência com que o evento falhará. Isso pode ser qualquer inteiro entre 0 e 1.000.000 (0xF4240).
!avrf -flt break EventType
Faz com que o Verificador de Aplicativo interrompa o depurador sempre que essa falha é injetada.
!avrf -flt stacks Length
Exibe o Número de comprimento de rastreamentos de pilha para as operações injetadas por falha mais recentes.
!avrf -trg [ StartEnd | dll Module | all ]
Especifica que esse é um comando de intervalo de destino. Quando -trg é usado sem parâmetros adicionais, os intervalos de destino atuais são exibidos. Start especifica o endereço inicial do intervalo de destino ou do intervalo de exclusão. End especifica o endereço final do intervalo de destino ou do intervalo de exclusão. O módulo especifica o nome de um módulo a ser direcionado ou excluído. O módulo deve incluir o nome completo do módulo, incluindo a extensão .exe ou .dll. As informações do caminho não devem ser incluídas. Especificar tudo faz com que todos os intervalos de destino ou intervalos de exclusão sejam redefinidos.
!avrf -skp [ StartEnd | dll Module | all | Time ]
Especifica que esse é um comando de intervalo de exclusão. Start especifica o endereço inicial do intervalo de destino ou do intervalo de exclusão. End especifica o endereço final do intervalo de destino ou do intervalo de exclusão. O módulo especifica o nome de um módulo a ser direcionado ou excluído. O módulo deve incluir o nome completo do módulo, incluindo a extensão .exe ou .dll. As informações do caminho não devem ser incluídas. Especificar tudo faz com que todos os intervalos de destino ou intervalos de exclusão sejam redefinidos. Especificar Tempo faz com que todas as falhas sejam suprimidas para milissegundos de tempo após a retomada da execução.
Veja a seguir a saída fornecida pelo comando !avrf no depurador.
0:000> !avrf
Application verifier settings (816431A7):
- full page heap
- COM
- RPC
- Handles
- Locks
- Memory
- TLS
- Exceptions
- Threadpool
- Leak
- SRWLock
No verifier stop active.
Note: Sometimes bugs found by verifier manifest themselves as raised
exceptions (access violations, stack overflows, invalid handles),
and it is not always necessary to have a verifier stop.
!avrf extension comments
Quando a extensão !avrf é usada sem parâmetros, ela exibe as opções atuais do Verificador de Aplicativo.
A extensão !avrf usa o Exts.dll no depurador.
Se uma Parada do Verificador de Aplicativo tiver ocorrido, a extensão !avrf sem parâmetros revelará a natureza da parada e o que a causou.
Se os símbolos para ntdll.dll e verifier.dll estiverem ausentes, a extensão !avrf gerará uma mensagem de erro.
Paradas contínuas e não contínuas
Depurando uma parada contínua
Aqui está um exemplo de uma exceção de identificador inválida que foi gerada pela opção Detectar uso de identificador inválido.
Primeiro, a seguinte mensagem é exibida:
Invalid handle - code c0000008 (first chance)
===================================================
VERIFIER STOP 00000300: pid 0x558: invalid handle exception for current stack trace
C0000008 : Exception code.
0012FBF8 : Exception record. Use .exr to display it.
0012FC0C : Context record. Use .cxr to display it.
00000000 :
===================================================
This verifier stop is continuable.
After debugging it use 'go' to continue.
===================================================
Break instruction exception - code 80000003 (first chance)
eax=00000000 ebx=6a27c280 ecx=6a226447 edx=0012fa4c esi=00942528 edi=6a27c260
eip=6a22629c esp=0012facc ebp=0012faf0 iopl=0 nv up ei pl zr na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
ntdll!DbgBreakPoint:
6a22629c cc int 3
Observe que a mensagem informa que esta Parada do Verificador de Aplicativo pode continuar. Depois de entender o que ocorreu, você pode continuar executando o aplicativo de destino.
Primeiro, você deve usar a extensão !avrf. Isso fornece informações sobre a falha atual:
0:000> !avrf
Global flags: 00000100
Application verifier global flag is set.
Application verifier settings (00000004):
- no heap checking enabled!
- handle checks
Page heap is not active for this process.
Current stop 00000300 : c0000008 0012fbf8 0012fc0c 00000000 .
Using an invalid handle (either closed or simply bad).
A linha final dessa exibição resume o problema.
Talvez você queira examinar alguns logs neste momento. Depois de terminar, use o comando g (Go) para iniciar o aplicativo novamente:
0:000> g
## Debugging a Non-Continuable Stop
Here is an example of an access violation that has been raised by the page heap option.
First, the following message appears:
Access violation - code c0000005 (first chance)
===================================================
VERIFIER STOP 00000008: pid 0x504: exception raised while verifying block header
00EC1000 : Heap handle
00F10FF8 : Heap block
00000000 : Block size
00000000 :
===================================================
This verifier stop is not continuable. Process will be terminated when you use the 'go' debugger command.
===================================================
Break instruction exception - code 80000003 (first chance)
eax=00000000 ebx=00000000 ecx=6a226447 edx=0012fab7 esi=00f10ff8 edi=00000008
eip=6a22629c esp=0012fb5c ebp=0012fb80 iopl=0 nv up ei pl zr na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
ntdll!DbgBreakPoint:
6a22629c cc int 3
Nesse caso, a mensagem informa que esta Parada do Verificador de Aplicativo não pode continuar. O erro é muito grave para que o processo continue em execução e não há como o Verificador de Aplicativo salvar o processo.
A extensão !avrf pode ser usada para fornecer informações sobre a falha atual:
0:000> !avrf
Global flags: 02000100
Application verifier global flag is set.
Page heap global flag is set.
Application verifier settings (00000001):
- full page heap
Page heaps active in the process (format: pageheap, lightheap, flags):
00941000 , 00a40000 , 3 (pageheap traces )
00b41000 , 00c40000 , 3 (pageheap traces )
00cb1000 , 00db0000 , 3 (pageheap traces )
00ec1000 , 00fc0000 , 3 (pageheap traces )
Current stop 00000008 : 00ec1000 00f10ff8 00000000 00000000 .
Corrupted heap block.
A linha final dessa exibição resume o problema.
Talvez você também queira examinar alguns logs neste momento. Talvez você queira usar o comando .restart (Reiniciar Aplicativo de Destino) neste ponto. Ou talvez você prefira encerrar a sessão do Verificador de Aplicativos e começar a corrigir os bugs em seu código.
Depurando erros críticos da seção
Extensão do depurador !cs
!cs podem ser usados no depurador do modo usuário e no depurador de kernel para exibir informações sobre seções críticas no processo atual. Para obter informações adicionais sobre a extensão !cs, consulte !cs nos documentos do depurador.
Símbolos correspondentes com informações de tipo são necessários, especialmente para ntdll.dll.
A sintaxe dessa extensão é:
!cs [-s] – despejo de todas as seções críticas ativas no processo atual.
!cs [-s] endereço – despejo de seção crítica neste endereço.
!cs [-s] -d address - dump critical section correspondente a DebugInfo neste endereço.
-s despejará o rastreamento de pilha de inicialização de seção crítica se ele estiver disponível.
Exemplos:
Despejar informações sobre uma seção crítica usando seu endereço
0:001> ! cs 0x7803B0F8
Critical section = 0x7803B0F8 (MSVCRT!__app_type+0x4)
DebugInfo = 0x6A262080
NOT LOCKED
LockSemaphore = 0x0
SpinCount = 0x0
Despejar informações sobre uma seção crítica usando seu endereço, incluindo o rastreamento de pilha de inicialização
0:001> !cs -s 0x7803B0F8
Critical section = 0x7803B0F8 (MSVCRT!__app_type+0x4)
DebugInfo = 0x6A262080
NOT LOCKED
LockSemaphore = 0x0
SpinCount = 0x0
Stack trace for DebugInfo = 0x6A262080:
0x6A2137BD: ntdll!RtlInitializeCriticalSectionAndSpinCount+0x9B
0x6A207A4C: ntdll!LdrpCallInitRoutine+0x14
0x6A205569: ntdll!LdrpRunInitializeRoutines+0x1D9
0x6A20DCE1: ntdll!LdrpInitializeProcess+0xAE5
Despejar informações sobre uma seção crítica usando seu endereço de informações de depuração
0:001> !cs -d 0x6A262080
DebugInfo = 0x6A262080
Critical section = 0x7803B0F8 (MSVCRT!__app_type+0x4)
NOT LOCKED
LockSemaphore = 0x0
SpinCount = 0x0
Despejar informações sobre uma seção crítica usando seu endereço de informações de depuração, incluindo o rastreamento de pilha de inicialização
0:001> !cs -s -d 0x6A262080
DebugInfo = 0x6A262080
Critical section = 0x7803B0F8 (MSVCRT!__app_type+0x4)
NOT LOCKED
LockSemaphore = 0x0
SpinCount = 0x0
Stack trace for DebugInfo = 0x6A262080:
0x6A2137BD: ntdll!RtlInitializeCriticalSectionAndSpinCount+0x9B
0x6A207A4C: ntdll!LdrpCallInitRoutine+0x14
0x6A205569: ntdll!LdrpRunInitializeRoutines+0x1D9
0x6A20DCE1: ntdll!LdrpInitializeProcess+0xAE
Despejar informações sobre todas as seções críticas ativas no processo atual
0:001> !cs
-----------------------------------------
DebugInfo = 0x6A261D60
Critical section = 0x6A262820 (ntdll!RtlCriticalSectionLock+0x0)
LOCKED
LockCount = 0x0
OwningThread = 0x460
RecursionCount = 0x1
LockSemaphore = 0x0
SpinCount = 0x0
-----------------------------------------
DebugInfo = 0x6A261D80
Critical section = 0x6A262580 (ntdll!DeferedCriticalSection+0x0)
NOT LOCKED
LockSemaphore = 0x7FC
SpinCount = 0x0
-----------------------------------------
DebugInfo = 0x6A262600
Critical section = 0x6A26074C (ntdll!LoaderLock+0x0)
NOT LOCKED
LockSemaphore = 0x0
SpinCount = 0x0
.....
Despejar informações sobre todas as seções críticas ativas no processo atual, incluindo o rastreamento de pilha de inicialização
0:001> !cs -s
...
-----------------------------------------
DebugInfo = 0x6A261EA0
Critical section = 0xA8001C (+0xA8001C)
NOT LOCKED
LockSemaphore = 0x0
SpinCount = 0x0
No stack trace saved
-----------------------------------------
DebugInfo = 0x6A261EC0
Critical section = 0x6A263560 (ntdll!RtlpDphTargetDllsLock+0x0)
NOT LOCKED
LockSemaphore = 0x0
SpinCount = 0x0
No stack trace saved
-----------------------------------------
DebugInfo = 0x6A261EE0
Critical section = 0xA90608 (+0xA90608)
NOT LOCKED
LockSemaphore = 0x7EC
SpinCount = 0x0
Stack trace for DebugInfo = 0x6A261EE0:
0x6A2137BD: ntdll!RtlInitializeCriticalSectionAndSpinCount+0x9B
0x6A20B0DC: ntdll!CsrpConnectToServer+0x1BE
0x6A20B2AA: ntdll!CsrClientConnectToServer+0x148
0x77DBE83F: KERNEL32!BaseDllInitialize+0x11F
0x6A207A4C: ntdll!LdrpCallInitRoutine+0x14
0x6A205569: ntdll!LdrpRunInitializeRoutines+0x1D9
0x6A20DCE1: ntdll!LdrpInitializeProcess+0xAE5
-----------------------------------------
DebugInfo = 0x6A261F00
Critical section = 0x77E1AEB8 (KERNEL32!BaseDllRegistryCache+0x18)
NOT LOCKED
LockSemaphore = 0x0
SpinCount = 0x0
Stack trace for DebugInfo = 0x6A261F00:
0x6A2137BD: ntdll!RtlInitializeCriticalSectionAndSpinCount+0x9B
0x6A207A4C: ntdll!LdrpCallInitRoutine+0x14
0x6A205569: ntdll!LdrpRunInitializeRoutines+0x1D9
0x6A20DCE1: ntdll!LdrpInitializeProcess+0xAE5
Erros de exceção de depuração
O log de exceções registra todas as exceções que ocorreram no processo de destino.
Você pode usar o comando de extensão !avrf -ex Length para exibir as últimas várias exceções; Length especifica o número de exceções. Se Length for omitido, todas as exceções serão exibidas.
Veja um exemplo:
0:000> !avrf -ex 4
=================================
Thread ID: 0000052c
Exception code: c0000008
Exception address: 6a226663
Exception record: 0012fb50
Context record: 0012fb64
Displayed 1 exception log entries.
Erros de identificadores de depuração
!htrace pode ser usado no depurador de modo de usuário e no depurador de kernel para exibir informações de rastreamento de pilha para um ou todos os identificadores em um processo. Essas informações estarão disponíveis se o rastreamento de identificador estiver habilitado para o processo – habilitado automaticamente se a verificação de identificador estiver habilitada no verificador de aplicativos. Os rastreamentos de pilha são salvos sempre que o processo está abrindo ou fechando um identificador ou quando ele está fazendo referência a um identificador inválido. Para obter informações adicionais sobre a extensão !htrace, consulte !htrace nos documentos do depurador.
A sintaxe do depurador de kernel para essa extensão é:
!htrace [ handle [process] ]
Se handle não for especificado ou for 0, as informações sobre todos os identificadores no processo serão exibidas. Se o processo não for especificado, o processo atual será usado.
A sintaxe do depurador no modo de usuário é:
!htrace [handle]
A extensão do depurador no modo de usuário sempre exibe informações sobre o processo de depuração atual.
Exemplos:
Despejar informações sobre o identificador 7CC no processo 815328b0
kd> !htrace 7CC 815328b0
Loaded \\...\kdexts extension DLL
Process 0x815328B0
ObjectTable 0xE15ECBB8
--------------------------------------
Handle 0x7CC - CLOSE:
0x8018FCB9: ntoskrnl!ExDestroyHandle+0x103
0x801E1D12: ntoskrnl!ObpCloseHandleTableEntry+0xE4
0x801E1DD9: ntoskrnl!ObpCloseHandle+0x85
0x801E1EDD: ntoskrnl!NtClose+0x19
0x77DBFCD6: KERNEL32!GetLocaleFileInfo+0x3D
0x77DBF942: KERNEL32!NlsProcessInitialize+0x11D
0x77E0C6DF: KERNEL32!NlsDllInitialize+0x35
0x6A20785C: ntdll!LdrpCallInitRoutine+0x14
0x6A205393: ntdll!LdrpRunInitializeRoutines+0x1D9
0x6A20DD80: ntdll!LdrpInitializeProcess+0xAF6
--------------------------------------
Handle 0x7CC - OPEN:
0x8018F44A: ntoskrnl!ExCreateHandle+0x94
0x801E3180: ntoskrnl!ObpCreateHandle+0x304
0x801E1563: ntoskrnl!ObOpenObjectByName+0x1E9
0x77DBFCD6: KERNEL32!GetLocaleFileInfo+0x3D
0x77DBF942: KERNEL32!NlsProcessInitialize+0x11D
0x77E0C6DF: KERNEL32!NlsDllInitialize+0x35
0x6A20785C: ntdll!LdrpCallInitRoutine+0x14
0x6A205393: ntdll!LdrpRunInitializeRoutines+0x1D9
0x6A20DD80: ntdll!LdrpInitializeProcess+0xAF6
--------------------------------------
Parsed 0x1CA stack traces.
Dumped 0x2 stack traces.
Despejar informações sobre todos os identificadores no processo 815328b0
kd> !htrace 0 81400300
Process 0x81400300
ObjectTable 0xE10CCF60
--------------------------------------
Handle 0x7CC - CLOSE:
0x8018FCB9: ntoskrnl!ExDestroyHandle+0x103
0x801E1D12: ntoskrnl!ObpCloseHandleTableEntry+0xE4
0x801E1DD9: ntoskrnl!ObpCloseHandle+0x85
0x801E1EDD: ntoskrnl!NtClose+0x19
0x010012C1: badhandle!mainCRTStartup+0xE3
0x77DE0B2F: KERNEL32!BaseProcessStart+0x3D
--------------------------------------
Handle 0x7CC - OPEN:
0x8018F44A: ntoskrnl!ExCreateHandle+0x94
0x801E3390: ntoskrnl!ObpCreateUnnamedHandle+0x10C
0x801E7317: ntoskrnl!ObInsertObject+0xC3
0x77DE23B2: KERNEL32!CreateSemaphoreA+0x66
0x010011C5: badhandle!main+0x45
0x010012C1: badhandle!mainCRTStartup+0xE3
0x77DE0B2F: KERNEL32!BaseProcessStart+0x3D
--------------------------------------
Handle 0x7DC - BAD REFERENCE:
0x8018F709: ntoskrnl!ExMapHandleToPointerEx+0xEA
0x801E10F2: ntoskrnl!ObReferenceObjectByHandle+0x12C
0x801902BE: ntoskrnl!NtSetEvent+0x6C
0x80154965: ntoskrnl!_KiSystemService+0xC4
0x010012C1: badhandle!mainCRTStartup+0xE3
0x77DE0B2F: KERNEL32!BaseProcessStart+0x3D
--------------------------------------
Handle 0x7DC - CLOSE:
0x8018FCB9: ntoskrnl!ExDestroyHandle+0x103
0x801E1D12: ntoskrnl!ObpCloseHandleTableEntry+0xE4
0x801E1DD9: ntoskrnl!ObpCloseHandle+0x85
0x801E1EDD: ntoskrnl!NtClose+0x19
0x010012C1: badhandle!mainCRTStartup+0xE3
0x77DE0B2F: KERNEL32!BaseProcessStart+0x3D
--------------------------------------
Handle 0x7DC - OPEN:
0x8018F44A: ntoskrnl!ExCreateHandle+0x94
0x801E3390: ntoskrnl!ObpCreateUnnamedHandle+0x10C
0x801E7317: ntoskrnl!ObInsertObject+0xC3
0x77DE265C: KERNEL32!CreateEventA+0x66
0x010011A0: badhandle!main+0x20
0x010012C1: badhandle!mainCRTStartup+0xE3
0x77DE0B2F: KERNEL32!BaseProcessStart+0x3D
--------------------------------------
Parsed 0x6 stack traces.
Dumped 0x5 stack traces.
Despejar informações sobre o identificador 7DC no processo atual
kd> !htrace 7DC
Process 0x81400300
ObjectTable 0xE10CCF60
--------------------------------------
Handle 0x7DC - BAD REFERENCE:
0x8018F709: ntoskrnl!ExMapHandleToPointerEx+0xEA
0x801E10F2: ntoskrnl!ObReferenceObjectByHandle+0x12C
0x801902BE: ntoskrnl!NtSetEvent+0x6C
0x80154965: ntoskrnl!_KiSystemService+0xC4
0x010012C1: badhandle!mainCRTStartup+0xE3
0x77DE0B2F: KERNEL32!BaseProcessStart+0x3D
--------------------------------------
Handle 0x7DC - CLOSE:
0x8018FCB9: ntoskrnl!ExDestroyHandle+0x103
0x801E1D12: ntoskrnl!ObpCloseHandleTableEntry+0xE4
0x801E1DD9: ntoskrnl!ObpCloseHandle+0x85
0x801E1EDD: ntoskrnl!NtClose+0x19
0x010012C1: badhandle!mainCRTStartup+0xE3
0x77DE0B2F: KERNEL32!BaseProcessStart+0x3D
--------------------------------------
Handle 0x7DC - OPEN:
0x8018F44A: ntoskrnl!ExCreateHandle+0x94
0x801E3390: ntoskrnl!ObpCreateUnnamedHandle+0x10C
0x801E7317: ntoskrnl!ObInsertObject+0xC3
0x77DE265C: KERNEL32!CreateEventA+0x66
0x010011A0: badhandle!main+0x20
0x010012C1: badhandle!mainCRTStartup+0xE3
0x77DE0B2F: KERNEL32!BaseProcessStart+0x3D
--------------------------------------
Parsed 0x6 stack traces.
Dumped 0x3 stack traces.
Depurando erros de heap
Extensão do depurador do verificador de heap
A extensão do depurador do verificador de heap faz parte da extensão !heap (extensão do depurador de heap NT). Ajuda simples pode ser obtida com !heap -? ou mais extenso com !heap -p -? . A extensão atual não detectará por conta própria se o heap de página estiver habilitado para um processo e agir de acordo. Por enquanto, o usuário da extensão precisa saber que o heap de página está habilitado e usar comandos prefixados por !heap -p . Para obter informações adicionais sobre a extensão !htrace, consulte !heap nos documentos do depurador.
!heap -p
Despeja endereços de todos os heaps de página inteira criados no processo.
!heap -p -h ADDRESS-OF-HEAP
Despejo completo do heap de página inteira em ADDRESS-OF-HEAP.
!heap -p -a ADDRESS
Tenta descobrir se há um bloco de heap em ADDRESS. Esse valor não precisa ser o endereço do início do bloco. O comando será útil se não houver nenhuma pista sobre a natureza de uma área de memória.
Log de operações de heap
O log de operações de heap rastreia todas as rotinas de heap. Isso inclui HeapAlloc, HeapReAlloc e HeapFree.
Você pode usar o !avrf -hp Length
comando de extensão para exibir os últimos registros; Length especifica o número de registros.
Você pode usar !avrf -hp -a Address
para exibir todas as operações de espaço de heap que afetaram o Endereço especificado. Para uma operação de alocação, é suficiente que Address esteja contido no bloco de heap alocado. Para uma operação gratuita, o endereço exato do início do bloco deve ser dado.
Para cada entrada no log, as seguintes informações são exibidas:
- A função heap chamada.
- A ID do thread que chamou a rotina.
- O endereço envolvido na chamada — esse é o endereço que foi retornado por uma rotina de alocação ou que foi passado para uma rotina gratuita.
- O tamanho da região envolvida na chamada.
- O rastreamento de pilha da chamada.
As entradas mais recentes são exibidas primeiro.
Neste exemplo, as duas entradas mais recentes são exibidas:
0:001> !avrf -hp 2
alloc (tid: 0xFF4):
address: 00ea2fd0
size: 00001030
00403062: Prymes!_heap_alloc_dbg+0x1A2
00402e69: Prymes!_nh_malloc_dbg+0x19
00402e1e: Prymes!_malloc_dbg+0x1E
00404ff3: Prymes!_stbuf+0xC3
00401c23: Prymes!printf+0x43
00401109: Prymes!main+0xC9
00402039: Prymes!mainCRTStartup+0xE9
77e7a278: kernel32!BaseProcessStart+0x23
alloc (tid: 0xFF4):
address: 00ea07d0
size: 00000830
00403062: Prymes!_heap_alloc_dbg+0x1A2
00402e69: Prymes!_nh_malloc_dbg+0x19
00402e1e: Prymes!_malloc_dbg+0x1E
00403225: Prymes!_calloc_dbg+0x25
00401ad5: Prymes!__initstdio+0x45
00401f38: Prymes!_initterm+0x18
00401da1: Prymes!_cinit+0x21
00402014: Prymes!mainCRTStartup+0xC4
77e7a278: kernel32!BaseProcessStart+0x23
Cenários típicos de depuração
Há vários cenários de falha que podem ser encontrados. Alguns deles precisam de um pouco de trabalho de detetive para ter toda a imagem.
Violação de acesso na página não acessível
Isso acontece quando o heap de página inteira está habilitado se o aplicativo testado acessa além do fim do buffer. Isso também pode acontecer se tocar em um bloco liberado. Para entender qual é a natureza do endereço no qual a exceção ocorreu, você precisa usar:
!heap –p –a ADDRESS-OF-AV
Mensagem de bloqueio corrompida
Em vários momentos durante o tempo de vida de uma alocação (alocação, livre do usuário, livre de real) o gerenciador de heap de página verifica se o bloco tem todos os padrões de preenchimento intactos e se o cabeçalho do bloco tem dados consistentes. Se esse não for o caso, você receberá uma parada do verificador.
Se o bloco for um bloco de heap de página inteira (por exemplo, se você tiver certeza de que o heap de página inteira está habilitado para todas as alocações), poderá usar "!heap –p –a ADDRESS" para descobrir quais são as características do bloco.
Se o bloco for um bloco de heap de página leve, você precisará descobrir o endereço inicial do cabeçalho do bloco. Você pode encontrar o endereço inicial despejando de 30 a 40 bytes abaixo do endereço relatado e procurar os padrões de início/término mágicos para um cabeçalho de bloco (ABCDAAAA, ABCDBBBB, ABCDAAA9, ABCDBBBA).
O cabeçalho fornecerá todas as informações necessárias para entender a falha. Particularmente, os padrões mágicos informarão se o bloco está alocado ou livre se for um heap de página leve ou um bloco de heap de página inteira. As informações aqui devem ser comparadas cuidadosamente com a chamada ofensiva.
Por exemplo, se uma chamada para HeapFree for feita com o endereço de um bloco mais quatro bytes, você receberá a mensagem corrompida. O cabeçalho de bloco parecerá bom, mas você precisará observar que o primeiro byte após o final do cabeçalho (primeiro byte após o valor mágico 0xDCBAXXXX) tem um endereço diferente e, em seguida, aquele na chamada.
Ponteiros de preenchimento especiais
O gerenciador de heap de página preenche a alocação do usuário com valores que serão exibidos como ponteiros de kernel. Isso acontece quando o bloco é liberado (o valor de preenchimento é F0) e quando o bloco é alocado, mas nenhuma solicitação é feita para que o bloco seja zerado (o valor de preenchimento é E0 para heap de página leve e C0 para heap de página inteira). As alocações não zero são típicas para usuários malloc/novos. Se houver uma falha (violação de acesso) em que uma tentativa de leitura/gravação em endereços como F0F0F0F0, E0E0E0E0 C0C0C0C0 provavelmente você atingiu um desses casos.
Uma leitura/gravação em F0F0F0F0 significa que um bloco foi usado depois que ele foi liberado. Infelizmente você vai precisar de algum trabalho de detetive para descobrir qual bloco causou isso. Você precisa obter o rastreamento de pilha da falha e inspecionar o código para as funções na pilha. Um deles pode fazer uma suposição errada sobre uma alocação estar viva.
Uma leitura/gravação em E0E0E0E0/C0C0C0C0 significa que o aplicativo não inicializava corretamente a alocação. Isso também requer a inspeção de código das funções no rastreamento de pilha atual. Aqui está um exemplo para esse tipo de falha. Em um processo de teste, uma violação de acesso ao fazer um HeapFree no endereço E0E0E0E0 foi notada. Descobriu-se que o teste alocava uma estrutura, não a inicializava corretamente e, em seguida, chamou o destruidor do objeto. Como um determinado campo não era nulo (tinha E0E0E0E0 nele), ele chamou delete nele.
Detalhes técnicos do heap de página
Para detectar corrupção de heap (estouros ou subfluxos), o AppVerifier modificará a maneira como a memória é alocada preenchendo a memória solicitada com páginas completas não graváveis ou com marcas especiais antes e depois da memória alocada. O AppVerifier faz isso carregando Verifier.dll no processo que está sendo verificado e redirecionando algumas das APIs de Heap do Win32 chamadas pelo aplicativo para APIs de Verifier.dll correspondentes.
Ao preencher a memória solicitada com páginas completas não graváveis (a configuração FULL é habilitada na seção de propriedades de heap de página e é a configuração padrão), o AppVerifier consumirá uma grande quantidade de memória virtual, mas tem a vantagem de que os eventos de corrupção de heap são armazenados em cache em tempo real quando ocorre o estouro ou o fluxo inferior. Lembre-se de que a memória nesse modo terá esta aparência [AppVerifier Read-Only Heap Page (4k)] [Quantidade de memória solicitada pelo Aplicativo em teste] ou assim [Quantidade de memória solicitada pelo Aplicativo em teste] [AppVerifier Read-Only Página heap (4k)].
O heap marcar colocará uma página de proteção no início ou no final da alocação, dependendo da propriedade Backward. Se Backward for definido como False, que é o padrão, ele colocará uma página de proteção no final da alocação para capturar estouros de buffer. Se estiver definida como True, a página de proteção será colocada no início da alocação para capturar subexecutações de buffer.
Ao preencher a memória solicitada com marcas especiais (habilitada limpando o item de caixa "Completo" marcar nas propriedades do heap), o AppVerifier marcar e alertará você quando essa memória for liberada. O main problema ao usar essa técnica é que há alguns casos em que a memória corrompida só será detectada quando a memória for liberada (a quantidade mínima de bloco de memória é de 8 bytes), portanto, quando em uma variável de 3 bytes ou um estouro de 5 bytes ocorrer, ela não será detectada imediatamente.
Em um evento de fluxo inferior, será feita uma tentativa de gravar em uma página Read-Only. Isso disparará uma exceção. Observe que essa exceção só poderá ser capturada se o aplicativo de destino estiver sendo executado em um depurador. Observe que o modo heap de página inteira também detectará esses erros porque ele usa páginas de preenchimento+proteção. O motivo pelo qual você usaria o heap de página leve é se o computador não puder tolerar as restrições de memória alta do heap de página inteira.
Para aplicativos com uso intensivo de memória ou quando for necessário usar o AppVerifier durante longos períodos de tempo (por exemplo, teste de estresse), é melhor executar testes de heap normais (leves) em vez do modo completo devido à degradação do desempenho. No entanto, quando você encontrar um problema, ative o heap de página inteira para investigar mais detalhadamente.
Os aplicativos que estão usando heaps personalizados (um heap que ignora a implementação do heap do sistema operacional) podem não obter o benefício completo de usar o heap de página ou podem até funcionar mal quando ele está habilitado.
Depurando erros de memória
A extensão do depurador do verificador de memória
O log de operações de espaço virtual rastreia todas as rotinas que modificam o espaço virtual de um processo de qualquer forma. Eles incluem VirtualAlloc, VirtualFree, MapViewOfFile e UnmapViewOfFile.
Você pode usar o !avrf -vs Length
comando de extensão para exibir os últimos registros; Length especifica o número de registros.
Você pode usar !avrf -vs -a Address para exibir todas as operações de espaço virtual que afetaram o Endereço especificado. Para uma alocação, é suficiente que Address esteja contido no bloco alocado. Para um gratuito, o endereço exato do início da região deve ser dado.
Para cada entrada no log, as seguintes informações são exibidas:
- A função chamada
- A ID do thread que chamou a rotina
- O endereço envolvido na chamada — esse é o endereço que foi retornado por uma rotina de alocação ou que foi passado para uma rotina gratuita
- O tamanho da região envolvida na chamada
- O tipo de operação de memória (o parâmetro AllocationType)
- O tipo de proteção solicitada
- O rastreamento de pilha da chamada
Exemplos
As entradas mais recentes são exibidas primeiro.
No exemplo a seguir, as duas entradas mais recentes são exibidas:
0:001> !avrf -vs 2
VirtualFree (tid: 0xB4): addr:04bb0000 sz:00400000 op:8000 prot:0
00aa1ac2: verifier!VsLogCall+0x42
00aa19c1: verifier!AVrfpNtFreeVirtualMemory+0x30
68925d17: kernel32!VirtualFreeEx+0x35
6892611c: kernel32!VirtualFree+0x13
75ef6525: mshtml+0x116525
75ef68af: mshtml+0x1168AF
6a20787c: ntdll!LdrpCallInitRoutine+0x14
6a211c6f: ntdll!LdrUnloadDll+0x39A
689275c1: kernel32!FreeLibrary+0x3B
77b22d69: ole32!CoQueryReleaseObject+0x1E6
77b02bd2: ole32!SetErrorInfo+0x1ED
VirtualFree (tid: 0xB4): addr:04bb0000 sz:00001000 op:4000 prot:0
00aa1ac2: verifier!VsLogCall+0x42
00aa19c1: verifier!AVrfpNtFreeVirtualMemory+0x30
68925d17: kernel32!VirtualFreeEx+0x35
6892611c: kernel32!VirtualFree+0x13
75ef65ae: mshtml+0x1165AE
75ef68af: mshtml+0x1168AF
6a20787c: ntdll!LdrpCallInitRoutine+0x14
6a211c6f: ntdll!LdrUnloadDll+0x39A
689275c1: kernel32!FreeLibrary+0x3B
77b22d69: ole32!CoQueryReleaseObject+0x1E6
77b02bd2: ole32!SetErrorInfo+0x1ED
Ele pode ser visto na saída em que o thread 0xB4 primeiro descompactou uma página e, em seguida, liberou toda a região virtual.
Aqui está uma exibição de todas as operações que afetam o endereço 0x4BB1000:
0:001> !avrf -vs -a 4bb1000
Searching in vspace log for address 04bb1000 ...
VirtualFree (tid: 0xB4): addr:04bb0000 sz:00400000 op:8000 prot:0
00aa1ac2: verifier!VsLogCall+0x42
00aa19c1: verifier!AVrfpNtFreeVirtualMemory+0x30
68925d17: kernel32!VirtualFreeEx+0x35
6892611c: kernel32!VirtualFree+0x13
75ef6525: mshtml+0x116525
75ef68af: mshtml+0x1168AF
6a20787c: ntdll!LdrpCallInitRoutine+0x14
6a211c6f: ntdll!LdrUnloadDll+0x39A
689275c1: kernel32!FreeLibrary+0x3B
77b22d69: ole32!CoQueryReleaseObject+0x1E6
77b02bd2: ole32!SetErrorInfo+0x1ED
VirtualFree (tid: 0xB4): addr:04bb1000 sz:00001000 op:4000 prot:0
00aa1ac2: verifier!VsLogCall+0x42
00aa19c1: verifier!AVrfpNtFreeVirtualMemory+0x30
68925d17: kernel32!VirtualFreeEx+0x35
6892611c: kernel32!VirtualFree+0x13
75ef65ae: mshtml+0x1165AE
75ef68af: mshtml+0x1168AF
6a20787c: ntdll!LdrpCallInitRoutine+0x14
6a211c6f: ntdll!LdrUnloadDll+0x39A
689275c1: kernel32!FreeLibrary+0x3B
77b22d69: ole32!CoQueryReleaseObject+0x1E6
77b02bd2: ole32!SetErrorInfo+0x1ED
VirtualAlloc (tid: 0xB4): addr:04bb0000 sz:00010000 op:1000 prot:4
00aa1ac2: verifier!VsLogCall+0x42
00aa1988: verifier!AVrfpNtAllocateVirtualMemory+0x37
68925ca3: kernel32!VirtualAllocEx+0x61
68926105: kernel32!VirtualAlloc+0x16
75ef63f3: mshtml+0x1163F3
VirtualAlloc (tid: 0xB4): addr:04bb0000 sz:00400000 op:2000 prot:4
00aa1ac2: verifier!VsLogCall+0x42
00aa1988: verifier!AVrfpNtAllocateVirtualMemory+0x37
68925ca3: kernel32!VirtualAllocEx+0x61
68926105: kernel32!VirtualAlloc+0x16
75ef63d9: mshtml+0x1163D9
Para ler essa saída, lembre-se de que as entradas são despejadas começando com a mais recente. Portanto, esse log mostra que o thread 0xB4 alocado uma região grande na qual ele comprometeu uma página. Posteriormente, ele descompactou a página e, em seguida, liberou toda a região virtual.
Consulte Também
Verificador de Aplicativos – Visão geral
Verificador de Aplicativos – Testando aplicativos
Verificador de Aplicativos – Testes no Verificador de Aplicativos