共用方式為


應用程式驗證器 - 偵錯應用程式驗證器停止

偵錯工具安裝和安裝

某些應用程式驗證器動作可能會導致引發例外狀況。 偵錯工具必須設定為在第二次機會攔截這些例外狀況,因為應用程式驗證器本身會處理第一個機率例外狀況。

引發的例外狀況有三種類型:

  • 如果堆積選項偵測到堆積緩衝區滿溢,就會產生存取違規例外狀況 (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 是目標應用程式的名稱,而 Flags 會指定要套用至此目標所需的應用程式驗證器選項。

旗標應該是代表所需選項的位總和。 個別位值如下所示:

旗標值 意義
00000001 堆積檢查
00000004 控制碼檢查
00000008 低資源 SIM 卡檢查
00000020 TLS 檢查
00000040 已變更堆疊
00000200 危險 API
00001000 例外狀況檢查
00002000 記憶體檢查
00020000 其他檢查
00040000 LOCK CHECKS

使用 !avrf 進行偵錯

!avrf 擴充功能會控制應用程式驗證器的設定,並顯示應用程式驗證器所產生的各種輸出。 如需 !arvrf 延伸模組的其他資訊,請參閱偵錯工具中 的 !avrf

Syntax

!avrf

不含任何參數的 !avrf 命令會顯示應用程式驗證程式設定,以及目前和先前應用程式驗證器的相關資訊會中斷。

!avrf –vs { Length | -aAddress }

顯示虛擬空間作業記錄檔。 Length 指定要從最近一次開始顯示的記錄數目。 Address 會指定虛擬位址。 將會顯示包含此虛擬位址的虛擬作業記錄。

!avrf -hp { Length | -a Address }

顯示堆積作業記錄。 Address 會指定堆積位址。 將會顯示包含此堆積位址的堆積作業記錄。

!avrf -cs { Length | -a Address }

顯示重大區段刪除記錄。 Length 指定要從最近一次開始顯示的記錄數目。 Address 會指定關鍵區段位址。 指定 Address 時,會顯示特定重要區段的記錄。

!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 ]

顯示「讀取器/寫入器」 (SRW) 記錄檔。 指定 [位址] 會顯示與該 SRW 鎖定位址相關的記錄。 當指定 Length 以及 Address 時,會顯示該位址範圍內的所有 SRW 鎖定。 -stats 選項會傾印 SRW 鎖定統計資料。

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

顯示未處理的資源記錄。 這些資源可能或可能不會在任何指定的時間點外泄。 指定 ModuleName (包括延伸模組) 會顯示指定模組中的所有未完成資源。 指定 ResourceType 會顯示該特定資源類型的未處理資源。 指定位址會傾印具有該位址之未處理資源的記錄。 ResourceType 可以是下列其中一項:

  • 堆積:使用 Win32 堆積 API 顯示堆積配置
  • 本機:顯示本機/全域配置
  • CRT:使用 CRT API 顯示配置
  • 虛擬:顯示虛擬保留
  • BSTR:顯示 BSTR 配置
  • 登錄:顯示 [登錄機碼] 隨即開啟
  • 電源:顯示電源通知物件
  • 控制碼:顯示執行緒、檔案和事件控制碼配置

!avrf –trace TraceIndex

顯示指定追蹤索引的堆疊追蹤。 某些結構會使用此 16 位索引編號來識別堆疊追蹤。 此索引指向堆疊追蹤資料庫中的位置。 如果您要分析這類結構,您會發現此語法很有用。

!avrf -cnt

顯示全域計數器的清單。

!avrf -brk [ BreakEventType ]

指定這是中斷事件命令。 當 !avrf -brk 與沒有其他參數搭配使用時,就會顯示中斷事件設定。 BreakEventType 會指定中斷事件的型別編號。 如需可能的型別清單,請使用 !avrf -brk

!avrf -flt [ EventTypeProbability ]

指定這是錯誤插入命令。 當 與沒有其他參數搭配使用時 !avrf -flt ,會顯示目前的錯誤插入設定。 EventType 會指定事件的型別編號。 Probability 會指定事件將會失敗的頻率。 這可以是介於 0 到 1,000,000 (0xF4240) 之間的任何整數。

!avrf -flt break EventType

導致每次插入此錯誤時,應用程式驗證器都會中斷偵錯工具。

!avrf -flt stacks Length

顯示最新錯誤插入作業的堆疊追蹤長度數目。

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

指定這是目標範圍命令。 當 -trg 與沒有其他參數搭配使用時,會顯示目前的目標範圍。 Start 會指定目標範圍或排除範圍的起始位址。 End 會指定目標範圍或排除範圍的結束位址。 模組會指定要設為目標或排除的模組名稱。 模組應包含完整的模組名稱,包括 .exe 或 .dll 延伸模組。 不應包含路徑資訊。 指定全部會導致重設所有目標範圍或排除範圍。

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

指定這是排除範圍命令。 Start 會指定目標範圍或排除範圍的起始位址。 End 會指定目標範圍或排除範圍的結束位址。 模組會指定要設為目標或排除的模組名稱。 模組應包含完整的模組名稱,包括 .exe 或 .dll 延伸模組。 不應包含路徑資訊。 指定全部會導致重設所有目標範圍或排除範圍。 指定 Time 會導致在執行繼續之後的 Time 毫秒內隱藏所有錯誤。

以下是偵錯工具中 !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 (重新開機目標應用程式) 命令。 或者,您可能偏好結束應用程式驗證器會話,並開始修正程式碼中的 Bug。

偵錯重大區段錯誤

!cs 偵錯工具延伸模組

!cs 可用於使用者模式偵錯工具和核心偵錯工具,以顯示目前進程中重要區段的相關資訊。 如需 !cs 延伸模組的其他資訊,請參閱偵錯工具檔中的 !cs

需要具有類型資訊的比對符號,特別是針對 ntdll.dll。

此延伸模組的語法如下:

!cs [-s] - 傾印目前進程中的所有作用中關鍵區段。

!cs [-s] 位址 - 在此位址傾印重要區段。

!cs [-s] -d 位址 - 傾印對應至此位址之 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,則會顯示處理常式中所有控制碼的相關資訊。 如果未指定進程,則會使用目前的進程。

使用者模式偵錯工具語法為:

!htrace [handle]

使用者模式偵錯工具延伸模組一律會顯示目前偵錯者進程的相關資訊。

範例:

處理 815328b0 中處理 7CC 的傾印資訊

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.

偵錯堆積錯誤

堆積驗證器偵錯工具擴充功能

堆積驗證器偵錯工具擴充功能是 !堆積延伸模組的一部分, (NT 堆積偵錯工具擴充功能) 。 您可以使用 !heap 取得簡單的說明 -? 或 !heap -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

損毀的區塊訊息

在配置 (配置存留期期間,使用者免費、實際免費) 頁面堆積管理員會檢查區塊是否完整填滿模式,且區塊標頭具有一致的資料。 如果這不是這種情況,您將會收到驗證程式停止。

例如,如果區塊是完整頁面堆積區塊 (,如果您知道已針對所有配置啟用完整頁面堆積,) 您可以使用 「!heap –p –a ADDRESS」 來找出區塊的特性。

如果區塊是淺色頁面堆積區塊,則需要找出區塊標頭的起始位址。 您可以在回報的位址下方傾印 30-40 個位元組,並尋找區塊標頭的魔術開始/結束模式, (ABCDAAAA、ABCDBB、ABCDAAA9、ABCDBBBA) 。

標頭會提供您瞭解失敗所需的所有資訊。 特別是,魔術模式會告知區塊是否已配置,或釋放區塊是否為淺色頁面堆積或完整頁面堆積區塊。 此處的資訊必須與違規呼叫仔細比對。

例如,如果使用區塊位址加上四個位元組來呼叫 HeapFree,您就會收到損毀的訊息。 區塊標頭看起來會正常,但您必須注意到標頭結尾後的第一個位元組 (0xDCBAXXXX magic 值之後的第一個位元組,) 呼叫中的位址不同。

特殊填滿指標

頁面堆積管理員會將看起來為核心指標的值填入使用者配置。 當區塊釋出 (填滿值為 F0) ,而區塊已配置,但未對區塊進行零的要求, (填滿值為 E0 表示淺色頁面堆積,而完整頁面堆積則為 C0 時) 。 非零配置通常適用于 malloc/new 使用者。 如果發生失敗 (存取違規,) 在F0F0F0F0、E0E0E0E0等位址嘗試讀取/寫入,C0C0C0C0則最有可能遇到其中一種情況。

讀取/寫入F0F0F0F0表示在釋放區塊之後已使用。 不幸的是,您需要一些偵測工作來找出造成此原因的區塊。 您需要取得失敗的堆疊追蹤,然後檢查堆疊上函式的程式碼。 其中一個可能會對正在運作的配置做出錯誤的假設。

讀取/寫入E0E0E0E0/C0C0C0C0表示應用程式未正確初始化配置。 這也需要程式碼檢查目前堆疊追蹤中的函式。 以下是這類失敗的範例。 在測試程式中,發現在位址上執行 HeapFree 時發生存取違規E0E0E0E0。 結果顯示測試已配置結構、未正確初始化結構,然後呼叫 物件的解構函式。 因為特定欄位不是 null, (它E0E0E0E0它) 它稱為 delete。

頁面堆積技術詳細資料

若要偵測堆積損毀, (溢位或下溢) ,AppVerifier 會修改記憶體配置的方式,方法是將要求記憶體填補為完整不可寫入的頁面,或在配置記憶體前後加上特殊標記。 AppVerifier 會藉由將 Verifier.dll 載入已驗證的進程,並將應用程式所呼叫的一些 Win32 堆積 API 重新導向至對應的 Verifier.dll API 來執行這項作業。

當在頁面堆積屬性區段中以完整不可寫入頁面填補要求記憶體時, (FULL 設定會在頁面堆積屬性區段中啟用,而且是預設設定) ,AppVerifier 會耗用大量的虛擬記憶體,但有一個優點是,當溢位或下溢發生時,堆積損毀事件會即時快取。 請記住,此模式中的記憶體看起來會像這樣 [AppVerifier Read-Only 堆積頁面 (4k) ] [受測應用程式所要求的記憶體數量] ,或類似 [AppVerifier Read-Only 堆積頁面 (4k) ]。

堆積檢查會根據向後屬性,將防護頁面放在配置的開頭或結尾。 如果向後設定為 False,這是預設值,則會在配置結尾放置防護頁面,以攔截緩衝區滿溢。 如果設定為 True,則會將防護頁面放在配置開頭,以攔截緩衝區不足。

當清除堆積屬性) 中的 [完整] 核取方塊專案,以啟用 (特殊標籤填補要求的記憶體時,AppVerifier 會在釋放此記憶體時檢查併發出警示。 使用這項技術的主要問題是,在某些情況下,只有在釋放記憶體時,才會偵測到記憶體損毀 (記憶體區塊的最小數量是 8 個位元組) ,因此當 3 位元組變數或 5 位元組溢位發生時,將不會立即偵測到。

在下溢事件上,將會嘗試寫入 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配置了認可頁面的大型區域。 稍後它會取消認可頁面,然後釋放整個虛擬區域。

另請參閱

應用程式驗證器 - 概觀

應用程式驗證器 - 測試應用程式

應用程式驗證器 - 應用程式驗證器內的測試

應用程式驗證器 - 停止程式碼和定義

應用程式驗證程式 - 常見問題