Средство проверки приложений — отладка остановки средства проверки приложений

Установка и установка отладчика

Некоторые действия средства проверки приложений могут привести к возникновению исключения. Отладчик должен быть настроен на перехват этих исключений при втором шансе, так как само средство проверки приложений будет обрабатывать исключения первого шанса.

Возникают исключения трех типов:

  • Исключение нарушения доступа (0xC0000005) создается, если параметр кучи обнаруживает переполнение буфера кучи. В некоторых случаях параметр Проверить использование системного пути также может привести к нарушению доступа.

  • Исключение недопустимого дескриптора (0xC0000008) создается, когда параметр Обнаружить недопустимое использование дескриптора обнаруживает недопустимую операцию дескриптора.

  • Исключение переполнения стека (0xC00000FD) создается, когда параметр Проверить адекватность стека обнаруживает, что начальный стек был слишком коротким.

Один из способов подготовки к этим событиям — запустить отладчик в командной строке следующим образом:

windbg -xd av -xd ch -xd sov ApplicationCommandLine

или

cdb -xd av -xd ch -xd sov ApplicationCommandLine

Если вы уже запустили отладчик, можно использовать команду sxd (Задать исключения), чтобы перехватывать все нарушения доступа, недопустимые дескрипторы и переполнения стека в качестве исключений второго шанса:

0:000> sxd av 

0:000> sxd ch 

0:000> sxd sov 1

Теоретически можно управлять проверкой приложений с помощью отладчика ядра. Однако это не рекомендуется— для этого требуется частое использование команд .process и .pagein, но это дает вам не больше возможностей, чем использование отладчика в пользовательском режиме.

Установка средств отладки

Чтобы скачать последнюю версию средств, см. статью Скачивание средств отладки для Windows.

Настройка оборудования для отладки User-Mode

Отладка в пользовательском режиме обычно выполняется на одном компьютере: отладчик запускается на том же компьютере, что и приложение, на котором произошел сбой.

В этом случае специальная настройка оборудования не требуется. В этом случае термины "главный компьютер" и "целевой компьютер" являются взаимозаменяемыми.

Настройка программного обеспечения для отладки User-Mode

Базовая конфигурация User-Mode. Прежде чем начать отладку в пользовательском режиме, необходимо скачать необходимые файлы символов и задать определенные переменные среды.

Файлы символов

Необходимо скачать файлы символов для отлаживаемого процесса в пользовательском режиме. Если это приложение, которое вы написали, оно должно быть создано с полными файлами символов. Если это коммерческое приложение, файлы символов могут быть доступны на веб-сервере или для скачивания обратитесь к производителю.

При удаленной отладке расположение файла символов зависит от используемого метода:

  • При удаленной отладке с помощью отладчика файлы символов должны находиться на компьютере с сервером отладки.

  • При выполнении удаленной отладки с помощью remote.exe файлы символов должны находиться на компьютере с отладчиком.

  • Если выполняется удаленная отладка через сервер обработки или сервер подключения KD, файлы символов должны находиться на компьютере со смарт-клиентом.

  • Если вы управляете отладчиком пользовательского режима из отладчика ядра, файлы символов должны находиться на обоих компьютерах.

Настройка переменных среды

Отладчик использует различные переменные среды для указания ряда важных параметров.

Дополнительные сведения об отладчиках см. в статье начало работы с помощью отладки Windows.

Настройка средства проверки приложений с помощью отладчика с помощью командной строки

Для настройки средства проверки приложений можно использовать командную строку CDB или NTSD.

Используйте следующую командную строку:

cdb OtherOptions -vf:Flags Target

Где Target — это имя целевого приложения, а Флаги указывают требуемые параметры средства проверки приложений, которые должны быть применены к этому целевому объекту.

Флаги должны быть суммой битов, представляющих нужные параметры. Ниже приведены отдельные битовые значения.

Значение флага Значение
00000001 ПРОВЕРКИ КУЧИ
00000004 ОБРАБОТКА ПРОВЕРОК
00000008 ПРОВЕРКИ SIM-КАРТЫ С НИЗКИМ УРОВНЕМ РЕСУРСОВ
00000020 ПРОВЕРКИ TLS
00000040 ГРЯЗНЫЕ СТЕКИ
00000200 ОПАСНЫЕ API
00001000 ПРОВЕРКИ ИСКЛЮЧЕНИЙ
00002000 ПРОВЕРКИ ПАМЯТИ
00020000 ПРОЧИЕ ПРОВЕРКИ
00040000 ПРОВЕРКИ БЛОКИРОВКИ

Отладка с помощью !avrf

Расширение !avrf управляет параметрами средства проверки приложений и отображает различные выходные данные, созданные средством проверки приложений. Дополнительные сведения о расширении !arvrf см. в разделе !avrf документации по отладчику.

Синтаксис

!avrf

Команда !avrf без параметров отображает параметры средства проверки приложений и сведения о текущем и предыдущем прерывании проверки приложений, если таковые есть.

!avrf –vs { Length | -aAddress }

Отображает журнал операций виртуального пространства. Length указывает количество отображаемых записей, начиная с последней. Адрес указывает виртуальный адрес. Будут отображаться записи виртуальных операций, содержащих этот виртуальный адрес.

!avrf -hp { Length | -a Address }

Отображает журнал операций кучи. Address указывает адрес кучи. Будут отображаться записи операций кучи, содержащих этот адрес кучи.

!avrf -cs { Length | -a Address }

Отображает журнал удаления критического раздела. Length указывает количество отображаемых записей, начиная с последней. Адрес указывает адрес критического раздела. Записи для конкретного критического раздела отображаются при указании адреса.

!avrf -dlls [ Length ]

Отображает журнал загрузки и выгрузки DLL. Length указывает количество отображаемых записей, начиная с последней.

!avrf -trm

Отображает журнал всех завершенных и приостановленных потоков.

!avrf -ex [ Length ]

Отображает журнал исключений. Средство проверки приложений отслеживает все исключения, происходящие в приложении.

!avrf -threads [ ThreadID ]

Отображает сведения о потоках в целевом процессе. Для дочерних потоков также отображаются размер стека и флаги CreateThread, заданные родительским элементом. При указании идентификатора потока будут отображаться сведения только для этого конкретного потока.

!avrf -tp [ ThreadID ]

Отображает журнал пула потоков. Этот журнал может содержать трассировки стека для различных операций, таких как изменение маски сходства потоков, изменение приоритета потока, публикация сообщений потока, инициализация COM и неинициализация COM из обратного вызова пула потоков. При указании идентификатора потока будут отображаться сведения только для этого конкретного потока.

!avrf -srw [ Address | Address Length ] [ -stats ]

Отображает журнал slim reader/Writer (SRW). При указании адреса будут отображаться записи, относящиеся к этому адресу блокировки SRW. Если указано значение Length вместе с адресом, отображаются все блокировки SRW в этом диапазоне адресов. Параметр -stats создает дамп статистики блокировки SRW.

!avrf -leak [ -m ModuleName ] [ -r ResourceType ] [ -a Address ] [ -t ]

Отображает журнал невыполненных ресурсов. Эти ресурсы могут быть или не могут быть утечками в любой момент. При указании ModuleName (включая расширение) отображаются все незавершенные ресурсы в указанном модуле. При указании ResourceType отображаются незавершенные ресурсы определенного типа ресурса. Указание адреса создает дампы записей о невыполненных ресурсах с этим адресом. ResourceType может иметь один из следующих вариантов:

  • Куча: отображает выделения кучи с помощью API-интерфейсов кучи Win32.
  • Локальный: отображение локальных или глобальных выделений
  • CRT: отображение выделений с помощью API CRT
  • Виртуальный: отображает виртуальные резервирования.
  • BSTR: отображение выделений BSTR
  • Реестр: отображает открытые разделы реестра.
  • Питание: отображаются объекты уведомлений о энергопотреблении.
  • Дескриптор: отображает выделение потоков, файлов и дескрипторов событий.

!avrf –trace TraceIndex

Отображает трассировку стека для указанного индекса трассировки. Некоторые структуры используют этот 16-разрядный номер индекса для идентификации трассировки стека. Этот индекс указывает на расположение в базе данных трассировки стека. Если вы анализируете такую структуру, этот синтаксис будет полезным.

!avrf -cnt

Отображает список глобальных счетчиков.

!avrf -brk [ BreakEventType ]

Указывает, что это команда break-event. Если !avrf -brk используется без дополнительных параметров, отображаются параметры события прерывания. BreakEventType указывает номер типа события прерывания. Чтобы получить список возможных типов, используйте !avrf -brk.

!avrf -flt [ EventTypeProbability ]

Указывает, что это команда внедрения сбоя. Если !avrf -flt используется без дополнительных параметров, отображаются текущие параметры внедрения ошибок. EventType указывает номер типа события. Вероятность указывает частоту, с которой событие будет завершаться сбоем. Это может быть любое целое число от 0 до 1 000 000 (0xF4240).

!avrf -flt break EventType

Приводит к тому, что средство проверки приложений прерывается в отладчике при каждом внедрении этой ошибки.

!avrf -flt stacks Length

Отображает длину трассировки стека для последних операций, внедренных сбоем.

!avrf -trg [ StartEnd | dll Module | all ]

Указывает, что это команда целевого диапазона. Если параметр -trg используется без дополнительных параметров, отображаются текущие целевые диапазоны. Start задает начальный адрес целевого диапазона или диапазона исключения. End указывает конечный адрес целевого диапазона или диапазона исключения. Module указывает имя модуля, который должен быть целевым или исключенным. Модуль должен содержать полное имя модуля, включая расширение .exe или .dll. Не следует включать сведения о пути. Если указать все, все целевые диапазоны или диапазоны исключений будут сброшены.

!avrf -skp [ StartEnd | dll Module | all | Time ]

Указывает, что это команда диапазона исключений. Start задает начальный адрес целевого диапазона или диапазона исключения. End указывает конечный адрес целевого диапазона или диапазона исключения. Module указывает имя модуля, который должен быть целевым или исключенным. Модуль должен содержать полное имя модуля, включая расширение .exe или .dll. Не следует включать сведения о пути. Если указать все, все целевые диапазоны или диапазоны исключений будут сброшены. Указание времени приводит к тому, что все ошибки будут подавляться в миллисекундах времени после возобновления выполнения.

Ниже приведены выходные данные, предоставляемые командой !avrf в отладчике.

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

Если расширение !avrf используется без параметров, оно отображает текущие параметры средства проверки приложений.

Расширение !avrf использует Exts.dll в отладчике.

Если произошла остановка средства проверки приложений, расширение !avrf без параметров покажет характер остановки и причину этой остановки.

Если отсутствуют символы для ntdll.dll и verifier.dll, расширение !avrf создаст сообщение об ошибке.

Непрерывные и непеременимые остановки

Отладка непрерывной остановки

Ниже приведен пример исключения недопустимого дескриптора, которое было вызвано параметром Обнаружить недопустимое использование дескриптора.

Во-первых, появится следующее сообщение:

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

Обратите внимание, что в сообщении говорится, что эту остановку средства проверки приложений можно продолжить. После того как вы поймете, что произошло, можно продолжить запуск целевого приложения.

Сначала следует использовать расширение !avrf. Это позволит получить сведения о текущем сбое:

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).

В последней строке этого отображения приводится сводка по проблеме.

На этом этапе может потребоваться просмотреть некоторые журналы. После завершения используйте команду g (Go), чтобы снова запустить приложение:

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

В этом случае появится сообщение о том, что эту остановку средства проверки приложений продолжить нельзя. Ошибка слишком серьезна для продолжения процесса, и средство проверки приложений не может спасти процесс.

Расширение !avrf можно использовать для предоставления сведений о текущем сбое:

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.

В последней строке этого отображения приводится сводка по проблеме.

На этом этапе также может потребоваться просмотреть некоторые журналы. На этом этапе может потребоваться использовать команду .restart (Restart Target Application). Или, возможно, вы предпочитаете завершить сеанс проверки приложений и начать исправлять ошибки в коде.

Отладка ошибок критического раздела

Расширение отладчика !cs

!cs можно использовать как в отладчике пользовательского режима, так и в отладчике ядра для отображения сведений о критически важных разделах в текущем процессе. Дополнительные сведения о расширении !cs см. в разделе !cs в документации по отладчику.

Сопоставление символов со сведениями о типе требуется, особенно для ntdll.dll.

Синтаксис этого расширения:

!cs [-s] — дампа всех активных критических разделов в текущем процессе.

Адрес !cs [-s] — критически важный раздел дампа по этому адресу.

!cs [-s] -d address — критический раздел дампа, соответствующий DebugInfo по этому адресу.

-s создает дамп трассировки стека инициализации критических разделов, если она доступна.

Примеры:

Дамп сведений о критическом разделе с использованием его адреса

0:001> ! cs 0x7803B0F8

Critical section   = 0x7803B0F8 (MSVCRT!__app_type+0x4)
DebugInfo          = 0x6A262080
NOT LOCKED
LockSemaphore      = 0x0
SpinCount          = 0x0

Дамп сведений о критическом разделе с использованием его адреса, включая трассировку стека инициализации

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

Дамп сведений о критическом разделе с использованием адреса сведений об отладке

0:001> !cs -d 0x6A262080

DebugInfo          = 0x6A262080
Critical section   = 0x7803B0F8 (MSVCRT!__app_type+0x4)
NOT LOCKED
LockSemaphore      = 0x0
SpinCount          = 0x0

Дамп сведений о критическом разделе с использованием адреса сведений об отладке, включая трассировку стека инициализации

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

Дамп сведений обо всех активных критических разделах в текущем процессе

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
.....

Дамп сведений обо всех активных критических разделах текущего процесса, включая трассировку стека инициализации


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

Отладка ошибок исключений

В журнале исключений регистрируются все исключения, возникшие в целевом процессе.

Для отображения последних нескольких исключений можно использовать команду расширения !avrf -ex Length. Length указывает количество исключений. Если параметр Length опущен, отображаются все исключения.

Вот пример:

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.

Отладка обрабатывает ошибки

!htrace можно использовать как в пользовательском режиме, так и в отладчике ядра для отображения сведений трассировки стека для одного или всех дескрипторов в процессе. Эти сведения доступны, если трассировка дескрипторов включена для процесса — автоматически включается, если проверка дескрипторов включена в средстве проверки приложений. Трассировки стека сохраняются каждый раз, когда процесс открывает или закрывает дескриптор или когда он ссылается на недопустимый дескриптор. Дополнительные сведения о расширении !htrace см. в разделе !htrace в документации по отладчику.

Синтаксис отладчика ядра для этого расширения:

!htrace [ handle [process] ]

Если дескриптор не указан или имеет значение 0, будут отображаться сведения обо всех дескрипторах в процессе. Если параметр process не указан, будет использоваться текущий процесс.

Синтаксис отладчика пользовательского режима:

!htrace [handle]

Расширение отладчика пользовательского режима всегда отображает сведения о текущем процессе отладки.

Примеры:

Сведения о дампе для дескриптора 7CC в процессе 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.

Сведения о дампе всех дескрипторов в процессе 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.

Дамп сведений о дескрипторове 7DC в текущем процессе


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.

Ошибки отладки кучи

Расширение отладчика средства проверки кучи

Расширение отладчика средства проверки кучи является частью расширения !heap (расширение отладчика кучи NT). Простую справку можно получить с помощью кучи -? или более обширный с !кучей -p -? . Текущее расширение не определяет, включена ли куча страниц для процесса, и действовать соответствующим образом. Пока пользователь расширения должен знать, что куча страниц включена, и использовать команды с префиксом !heap -p . Дополнительные сведения о расширении !htrace см. в разделе !куча документации по отладчику.

!heap -p

Создает дампы всех полностраничных кучи, созданных в процессе.

!heap -p -h ADDRESS-OF-HEAP

Полный дамп полностраничной кучи в ADDRESS-OF-HEAP.

!heap -p -a ADDRESS

Пытается выяснить, есть ли блок кучи по адресу ADDRESS. Это значение не обязательно должно быть адресом начала блока. Команда полезна, если нет никакого представления о характере области памяти.

Журнал операций кучи

Журнал операций кучи отслеживает все подпрограммы кучи. К ним относятся HeapAlloc, HeapReAlloc и HeapFree.

Вы можете использовать !avrf -hp Length команду расширения для отображения последних нескольких записей; Length указывает количество записей.

Можно использовать для !avrf -hp -a Address отображения всех операций с пространством кучи, которые повлияли на указанный адрес. Для операции выделения достаточно, чтобы адрес содержался в блоке выделенной кучи. Для свободной операции необходимо указать точный адрес начала блока.

Для каждой записи в журнале отображаются следующие сведения:

  • Вызываемая функция кучи.
  • Идентификатор потока, который вызвал подпрограмму.
  • Адрес, участвующий в вызове— это адрес, возвращенный подпрограммой выделения или переданный бесплатной подпрограмме.
  • Размер региона, участвующего в вызове.
  • Трассировка стека вызова.

В первую очередь отображаются самые последние записи.

В этом примере отображаются две самые последние записи:

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

Типичные сценарии отладки

Существует несколько сценариев сбоя. Некоторые из них требуют довольно много детективной работы, чтобы получить всю картину.

Нарушение доступа на недоступной странице

Это происходит, если полностраничная куча включена, если протестированное приложение обращается за пределами буфера. Это также может произойти, если он касается освобожденного блока. Чтобы понять характер адреса, по которому возникло исключение, необходимо использовать следующее:

!heap –p –a ADDRESS-OF-AV

Сообщение о поврежденном блоке

В течение нескольких моментов времени существования выделения (выделение, свободное от пользователя, реальное свободное) диспетчер кучи страниц проверяет, все ли шаблоны заполнения блока нетронутыми, а заголовок блока содержит согласованные данные. Если это не так, вы получите остановку проверяющего.

Если блок является блоком полностраничной кучи (например, если вы знаете, что куча полной страницы включена для всех выделений), можно использовать "!куча -p –адрес", чтобы узнать, каковы характеристики блока.

Если блок является блоком кучи светлой страницы, необходимо узнать начальный адрес заголовка блока. Вы можете найти начальный адрес, создав дамп на 30–40 байт под сообщаемым адресом, и найдите магические шаблоны начала и окончания для заголовка блока (ABCDAAAA, ABCDBBBB, ABCDAAA9, ABCDBBBA).

В заголовке будут приведены все сведения, необходимые для понимания сбоя. В частности, магические шаблоны укажут, выделен ли блок или свободен, является ли он легкой кучи страницы или блоком полностраничной кучи. Информация здесь должна быть тщательно сопоставлена с обидным вызовом.

Например, если вызов HeapFree выполняется с адресом блока плюс четыре байта, вы получите поврежденное сообщение. Заголовок блока будет выглядеть нормально, но вы должны заметить, что первый байт после конца заголовка (первый байт после магического значения 0xDCBAXXXXX) имеет другой адрес, чем в вызове.

Специальные указатели заполнения

Диспетчер кучи страниц заполняет выделение пользователем значениями, которые будут выглядеть как указатели ядра. Это происходит, когда блок освобождается (значение заполнения — F0) и когда блок выделяется, но запрос на обнуление блока не выполняется (значение заполнения — E0 для светлой кучи страниц и C0 для полностраничной кучи). Ненулевое выделение ресурсов является типичным для пользователей malloc/new. Если возникает сбой (нарушение прав доступа), когда выполняется попытка чтения и записи по таким адресам, как F0F0F0F0, E0E0E0E0 C0C0C0C0, скорее всего, вы столкнулись с одним из этих случаев.

Чтение и запись в F0F0F0F0 означает, что блок был использован после освобождения. К сожалению, вам потребуется некоторые детективные работы, чтобы выяснить, какой блок вызвал это. Необходимо получить трассировку стека сбоя, а затем проверить код функций в стеке. Один из них может сделать неправильное предположение о выделении живых.

Чтение и запись в E0E0E0E0/C0C0C0C0 означает, что приложение неправильно инициализировало выделение. Для этого также требуется проверка кода функций в текущей трассировке стека. Вот пример такого сбоя. В тестовом процессе было замечено нарушение доступа при выполнении E0E0E0E0 адреса HeapFree. Оказалось, что тест выделил структуру, неправильно инициализировал ее, а затем вызвал деструктор объекта. Так как определенное поле не было пустым (в нем было E0E0E0E0), для него было вызвано удаление.

Технические сведения о куче страниц

Для обнаружения повреждений кучи (переполнения или переполнения) AppVerifier изменит способ выделения памяти путем заполнения запрошенной памяти полными страницами, не допускающими записи, или специальными тегами до и после выделенной памяти. AppVerifier делает это, загружая Verifier.dll в проверяемый процесс и перенаправляя некоторые API кучи Win32, вызываемые приложением, в соответствующие API Verifier.dll.

При заполнении запрошенной памяти полными страницами, не поддерживающими запись (параметр FULL включен в разделе свойств кучи страниц и является параметром по умолчанию) AppVerifier будет потреблять большой объем виртуальной памяти, но имеет преимущество в том, что события повреждения кучи кэшируются в режиме реального времени при переполнении или переполнении. Помните, что память в этом режиме будет выглядеть следующим образом[AppVerifier Read-Only страница кучи (4k)] [объем памяти, запрошенный тестируемым приложением] или следующим образом[Объем памяти, запрошенный тестируемым приложением] [AppVerifier Read-Only страница кучи (4k)].

В проверка куча будет размещать страницу защиты в начале или конце выделения в зависимости от свойства Назад. Если для свойства Назад задано значение False, которое является значением по умолчанию, в конце выделения будет помещена страница защиты для перехвата переполнения буфера. Если задано значение True, страница защиты помещается в начало выделения, чтобы перехватывать переполнение буфера.

При заполнении запрошенной памяти специальными тегами (включаемых путем очистки поля "Полный" проверка в свойствах кучи) AppVerifier проверка и оповещает вас при освобождении памяти. Проблема main при использовании этого метода заключается в том, что в некоторых случаях повреждение памяти обнаруживается только при освобождении памяти (минимальный объем блока памяти составляет 8 байт), поэтому, когда в 3-байтовой переменной или 5-байтовом переполнении происходит, это не будет обнаружено немедленно.

В случае события underflow будет предпринята попытка записи на страницу Read-Only. Это приведет к возникновению исключения. Обратите внимание, что это исключение может быть перехвачено только в том случае, если целевое приложение выполняется в отладчике. Обратите внимание, что в режиме полностраничной кучи также будут обнаружены эти ошибки, так как в нем используются страницы padding+guard. Причина использования легкой кучи страниц заключается в том, что компьютер не допускает больших ограничений памяти полностраничной кучи.

Для приложений с большим объемом памяти или когда требуется использовать AppVerifier в течение длительных периодов времени (например, нагрузочное тестирование), лучше выполнять обычные (легкие) тесты кучи, а не полный режим из-за снижения производительности. Однако при возникновении проблемы включите кучу полной страницы, чтобы изучить ее.

Приложения, использующие пользовательские кучи (куча, которая обходит реализацию кучи операционной системой), могут не получить все преимущества использования кучи страниц или даже сбой, если она включена.

Ошибки отладки памяти

Расширение отладчика средства проверки памяти

Журнал операций виртуального пространства отслеживает все подпрограммы, которые каким-либо образом изменяют виртуальное пространство процесса. К ним относятся VirtualAlloc, VirtualFree, MapViewOfFile и UnmapViewOfFile.

Вы можете использовать !avrf -vs Length команду расширения для отображения последних нескольких записей; Length указывает количество записей.

Вы можете использовать !avrf -vs -a Address для отображения всех операций с виртуальным пространством, которые повлияли на указанный адрес. Для выделения достаточно, чтобы адрес содержался в выделенном блоке. Для бесплатного использования необходимо указать точный адрес начала региона.

Для каждой записи в журнале отображаются следующие сведения:

  • Функция с именем
  • Идентификатор потока, который вызвал подпрограмму
  • Адрес, участвующий в вызове— это адрес, возвращенный подпрограммой выделения или переданный бесплатной подпрограмме.
  • Размер региона, участвующего в вызове
  • Тип операции с памятью (параметр AllocationType)
  • Тип запрашиваемой защиты
  • Трассировка стека вызова

Примеры

В первую очередь отображаются самые последние записи.

В следующем примере отображаются две последние записи:

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

Из выходных данных видно, что поток 0xB4 сначала отзывали страницу, а затем освобождали весь виртуальный регион.

Ниже приведены все операции, влияющие на адресные 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

Чтобы прочитать эти выходные данные, помните, что записи создаются в дампе, начиная с последнего. Таким образом, в этом журнале показано, что поток 0xB4 выделен большой регион, в котором он фиксирует страницу. Позже страница была списана, а затем была освобождена вся виртуальная область.

См. также:

Средство проверки приложений — обзор

Средство проверки приложений — тестирование приложений

Средство проверки приложений — тесты в приложении

Средство проверки приложений — остановка кодов и определений

Средство проверки приложений — часто задаваемые вопросы