具有標記的物件參考追蹤
核心物件 是 Windows 核心在系統記憶體中實作的基本資料物件。 它們代表裝置、驅動程式、檔案、登錄機碼、事件、旗號、進程和執行緒等實體。
大部分的核心物件不是永久的。 為了避免 Windows 在使用核心模式驅動程式時刪除非Permanent 核心物件,驅動程式會取得物件的計數參考。 當驅動程式不再需要 物件時,驅動程式會釋放其物件參考。
如果驅動程式未釋放物件的所有參考,物件的參考計數永遠不會達到零,而物件管理員永遠不會刪除它。 在作業系統重新開機之前,您無法重複使用流失的資源。
如果參考 物件的驅動程式 , 就會發生另一種類型的參考錯誤。 在此情況下,驅動程式會釋出比驅動程式實際保留更多的物件參考。 此錯誤可能會導致物件管理員提前刪除物件,而其他用戶端仍嘗試存取物件。
核心物件的外泄和參考不足可能是難以追蹤的錯誤。 例如,進程物件或裝置物件可能有數千個參考。 在這些情況下,很難識別物件參考 Bug 的來源。
在 Windows 7 和更新版本的 Windows 中,您可以提供物件參考的標記,讓這些 Bug 更容易找到。 下列常式會將標籤與核心物件的參考取得和發行建立關聯:
ObDereferenceObjectDeferDeleteWithTag
ObReferenceObjectByHandleWithTag
ObReferenceObjectByPointerWithTag
例如,在 Windows 7 和更新版本中提供的 ObReferenceObjectWithTag 和 ObDereferenceObjectWithTag是 Windows 2000 和更新版本的 ObReferenceObject 和 ObDereferenceObject 常式的增強版本。 這些增強常式可讓您提供四位元組的自訂標籤值作為輸入參數。 您可以使用 Windows 偵錯工具來 檢查 物件參考追蹤 ,其中包含每個呼叫 的標記值。 ObReferenceObject 和 ObDereferenceObject 不會讓呼叫端指定自訂標籤,但在 Windows 7 和更新版本中,這些常式會將具有標籤值 「Dflt」) 的預設標籤新增至追蹤 (。 因此,對 ObReferenceObject 或 ObDereferenceObject 的呼叫與對 ObReferenceObjectWithTag 或 ObDereferenceObjectWithTag 的呼叫相同,指定 「Dflt」 的標記值。 (在您的程式中,此標記值會顯示為 0x746c6644 或 'tlfD'.)
若要追蹤潛在的物件外泄或參考不足,請在驅動程式中識別一組相關聯的 ObReferenceObjectXxxWithTag 和 ObDereferenceObjectXxxWithTag 呼叫,以遞增和遞減特定物件的參考計數。 (選擇一般標籤值,例如「Lky8」) 用於此集合中的所有呼叫。 當您的驅動程式使用 物件完成之後,遞減的數目應該完全符合遞增的數目。 如果這些數位不相符,您的驅動程式就會有物件參考 Bug。 偵錯工具可以比較每個標記值的遞增和遞減數目,並告訴您它們是否不相符。 使用這項功能,您可以快速找出參考計數不符的來源。
若要在 Windows 偵錯工具中檢視物件參考追蹤,請使用 !obtrace 核心模式偵錯工具延伸模組。 如果物件參考追蹤開啟,您可以使用 !obtrace 延伸模組來顯示物件參考標記。 根據預設,物件參考追蹤已關閉。 使用 全域旗標編輯器 (Gflags) 來啟用物件參考追蹤。 如需 Gflags 的詳細資訊,請參閱 設定物件參考追蹤。
!obtrace延伸模組的輸出包含 「Tag」 資料行,如下列範例所示:
0: kd> !obtrace 0x8a226130
Object: 8a226130
Image: leakyapp.exe
Sequence (+/-) Tag Stack
-------- ----- ---- --------------------------------------------
36 +1 Dflt nt!ObCreateObject+1c4
nt!NtCreateEvent+93
nt!KiFastCallEntry+12a
37 +1 Dflt nt!ObpCreateHandle+1c1
nt!ObInsertObjectEx+d8
nt!ObInsertObject+1e
nt!NtCreateEvent+ba
nt!KiFastCallEntry+12a
38 -1 Dflt nt!ObfDereferenceObjectWithTag+22
nt!ObInsertObject+1e
nt!NtCreateEvent+ba
nt!KiFastCallEntry+12a
39 +1 Lky8 nt!ObReferenceObjectByHandleWithTag+254
leakydrv!LeakyCtlDeviceControl+6c
nt!IofCallDriver+63
nt!IopSynchronousServiceTail+1f8
nt!IopXxxControlFile+6aa
nt!NtDeviceIoControlFile+2a
nt!KiFastCallEntry+12a
3a -1 Dflt nt!ObfDereferenceObjectWithTag+22
nt!ObpCloseHandle+7f
nt!NtClose+4e
nt!KiFastCallEntry+12a
-------- ----- ---- --------------------------------------------
References: 3, Dereferences 2
Tag: Lky8 References: 1 Dereferences: 0 Over reference by: 1
此範例中的最後一行表示與 「Lky8」 標記相關聯的參考和取值計數不相符,而且此不相符的結果是由一個 (過度參考,也就是流失) 。
如果結果改為參考不足, !obtrace 輸出的最後一行可能如下所示:
Tag: Lky8 References: 1 Dereferences: 2 Under reference by: 1
根據預設,作業系統會在釋放物件之後刪除物件的物件參考追蹤,以節省記憶體。 即使系統釋放物件之後,仍可將追蹤保留在記憶體中,在追蹤參考不足時也很有用。 為了達到此目的,Gflags 工具會提供「永久」選項,可在電腦關閉後再次啟動時,保留記憶體中的追蹤。
Windows XP 引進了物件參考追蹤。 由於一開始追蹤未包含標記,因此開發人員必須使用較不方便的技術來識別物件參考 Bug。 偵錯工具可以追蹤物件群組的參考,而開發人員依物件類型所選取。 開發人員唯一可以識別物件參考和取值的各種來源,就是比較其呼叫堆疊。 雖然先前 的 !obtrace 範例只包含五個堆疊,但某些類型的物件,例如 程式 (EPROCESS) 物件,可能會被參考並取值數千次。 使用數千個堆疊來檢查時,可能很難識別物件外泄或參考不足的來源,而不使用標記。