Objektverweisablaufverfolgung mit Tags

Kernelobjekte sind primitive Datenobjekte, die der Windows-Kernel im Systemspeicher implementiert. Sie stellen Entitäten wie Geräte, Treiber, Dateien, Registrierungsschlüssel, Ereignisse, Semaphore, Prozesse und Threads dar.

Die meisten Kernelobjekte sind nicht dauerhaft. Um zu verhindern, dass Windows ein nicht fähiges Kernelobjekt löscht, während es von einem Kernelmodustreiber verwendet wird, ruft der Treiber einen gezählten Verweis auf das -Objekt ab. Wenn der Treiber das -Objekt nicht mehr benötigt, gibt der Treiber seinen Verweis auf das -Objekt frei.

Wenn ein Treiber nicht alle Verweise auf ein Objekt freigibt, erreicht die Verweisanzahl des Objekts nie null, und der Objekt-Manager löscht es nie. Sie können kompromittierte Ressourcen erst wiederverwenden, wenn das Betriebssystem neu gestartet wird.

Ein anderer Verweisfehlertyp tritt auf, wenn ein Treiber unter auf ein Objekt verweist. In diesem Fall gibt der Treiber mehr Verweise auf ein Objekt frei, als der Treiber tatsächlich enthält. Dieser Fehler kann dazu führen, dass der Objekt-Manager das Objekt vorzeitig löscht, während andere Clients weiterhin versuchen, auf das Objekt zuzugreifen.

Lecks und Unterverweise von Kernelobjekten können schwierig zu findende Fehler sein. Beispielsweise kann ein Prozessobjekt oder ein Geräteobjekt Zehntausende von Verweisen aufweisen. Unter diesen Umständen kann es schwierig sein, die Quelle eines Objektverweisfehlers zu identifizieren.

In Windows 7 und höheren Versionen von Windows können Sie ein Tag für Objektverweise bereitstellen, um die Auffindbarkeit dieser Fehler zu erleichtern. Die folgenden Routinen ordnen Tags dem Abrufen und Freigeben von Verweisen auf Kernelobjekte zu:

ObDereferenceObjectDeferDeleteWithTag

ObDereferenceObjectWithTag

ObReferenceObjectByHandleWithTag

ObReferenceObjectByPointerWithTag

ObReferenceObjectWithTag

Beispielsweise sind ObReferenceObjectWithTag und ObDereferenceObjectWithTag, die in Windows 7 und höheren Versionen von Windows verfügbar sind, erweiterte Versionen der Routinen ObReferenceObject und ObDereferenceObject , die in Windows 2000 und höheren Versionen von Windows verfügbar sind. Diese erweiterten Routinen ermöglichen es Ihnen, einen benutzerdefinierten Tagwert mit vier Byte als Eingabeparameter anzugeben. Sie können die Windows-Debugtools verwenden, um eine Objektverweisablaufverfolgung zu überprüfen, die den Tagwert für jeden Aufruf enthält. ObReferenceObject und ObDereferenceObject ermöglichen es dem Aufrufer nicht, benutzerdefinierte Tags anzugeben, aber in Windows 7 und höheren Versionen von Windows fügen diese Routinen der Ablaufverfolgung Standardtags (mit dem Tagwert "Dflt") hinzu. Daher hat ein Aufruf von ObReferenceObject oder ObDereferenceObject die gleiche Auswirkung wie ein Aufruf von ObReferenceObjectWithTag oder ObDereferenceObjectWithTag , der den Tagwert "Dflt" angibt. (In Ihrem Programm wird dieser Tagwert als 0x746c6644 oder "tlfD" angezeigt.)

Um ein potenzielles Objektleck oder einen Unterverweis nachzuverfolgen, identifizieren Sie eine Reihe zugeordneter ObReferenceObjectXxxWithTag - und ObDereferenceObjectXxxWithTag-Aufrufe in Ihrem Treiber, die die Verweisanzahl eines bestimmten Objekts erhöhen und verringern. Wählen Sie einen allgemeinen Tagwert (z. B. "Lky8") aus, der für alle Aufrufe in diesem Satz verwendet werden soll. Nachdem Der Treiber die Verwendung eines -Objekts abgeschlossen hat, sollte die Anzahl der Dekremente genau mit der Anzahl der Inkremente übereinstimmen. Wenn diese Zahlen nicht übereinstimmen, weist ihr Treiber einen Objektverweisfehler auf. Der Debugger kann die Anzahl der Inkremente und Dekremente für jeden Tagwert vergleichen und Ihnen mitteilen, ob sie nicht übereinstimmen. Mit dieser Funktion können Sie schnell die Ursache für die Nichtübereinstimmung der Verweisanzahl ermitteln.

Um eine Objektverweisablaufverfolgung in den Windows-Debugtools anzuzeigen, verwenden Sie die Debuggererweiterung !obtrace im Kernelmodus. Wenn die Objektverweisablaufverfolgung aktiviert ist, können Sie die Erweiterung !obtrace verwenden, um Objektverweistags anzuzeigen. Standardmäßig ist die Objektverweisablaufverfolgung deaktiviert. Verwenden Sie den Global Flags Editor (Gflags), um die Objektverweisablaufverfolgung zu aktivieren. Weitere Informationen zu Gflags finden Sie unter Konfigurieren der Ablaufverfolgung für Objektverweis.

Die Ausgabe der Erweiterung !obtrace enthält eine Spalte "Tag", wie im folgenden Beispiel gezeigt:

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

Die letzte Zeile in diesem Beispiel gibt an, dass die Verweis- und Dereferenzierungsanzahl, die dem "Lky8"-Tag zugeordnet sind, nicht übereinstimmen und dass das Ergebnis dieses Konflikts ein Überverweis um eins ist (d.a. ein Leak).

Wenn das Ergebnis stattdessen ein Unterverweis war, könnte die letzte Zeile der !obtrace-Ausgabe wie folgt lauten:

Tag: Lky8 References: 1 Dereferences: 2 Under reference by: 1

Standardmäßig spart das Betriebssystem Arbeitsspeicher, indem die Objektverweisablaufverfolgung für ein Objekt gelöscht wird, nachdem es das Objekt freigegeben hat. Das Beibehalten einer Ablaufverfolgung im Arbeitsspeicher, auch nachdem das System ein Objekt freigegeben hat, kann bei der Nachverfolgung eines Unterverweiss hilfreich sein. Zu diesem Zweck bietet das Gflags-Tool eine Option "Permanent", die die Ablaufverfolgung im Arbeitsspeicher beibewahrt, während der Computer heruntergefahren und wieder gestartet wird.

In Windows XP wurde die Objektverweisablaufverfolgung eingeführt. Da die Ablaufverfolgung anfänglich keine Tags enthielt, mussten Entwickler weniger praktische Techniken verwenden, um Objektverweisfehler zu identifizieren. Der Debugger kann die Verweise von Objektgruppen nachverfolgen, die der Entwickler nach Objekttyp ausgewählt hat. Die einzige Möglichkeit, wie der Entwickler die verschiedenen Quellen von Objektverweise und Dereferenzen identifizieren konnte, bestand darin, ihre Aufrufstapel zu vergleichen. Obwohl das vorherige !obtrace-Beispiel nur fünf Stapel enthält, können bestimmte Objekttypen, z. B. ein Prozessobjekt (EPROCESS), viele Tausend Male referenziert und dereferenziert werden. Bei Tausenden zu untersuchenden Stapeln kann es schwierig sein, die Quelle eines Objektlecks oder eines Unterverweiss zu identifizieren, ohne Tags zu verwenden.