Seguimiento de referencia de objetos con etiquetas
Los objetos kernel son objetos de datos primitivos que el kernel de Windows implementa en la memoria del sistema. Representan entidades como dispositivos, controladores, archivos, claves del Registro, eventos, semáforos, procesos y subprocesos.
La mayoría de los objetos kernel no son permanentes. Para evitar que Windows elimine un objeto kernel nopermanente mientras se usa un controlador en modo kernel, el controlador obtiene una referencia contada al objeto. Cuando el controlador ya no necesita el objeto , el controlador libera su referencia al objeto .
Si un controlador no libera todas sus referencias a un objeto, el recuento de referencias del objeto nunca alcanza cero y el Administrador de objetos nunca lo elimina. No se pueden reutilizar los recursos filtrados hasta que se reinicie el sistema operativo.
Otro tipo de error de referencia se produce si un controlador bajo hace referencia a un objeto . En este caso, el controlador libera más referencias a un objeto que el controlador contiene realmente. Este error puede hacer que el Administrador de objetos elimine el objeto prematuramente, mientras que otros clientes siguen intentando acceder al objeto.
Las fugas y las referencias inferiores de objetos kernel pueden ser errores difíciles de rastrear. Por ejemplo, un objeto de proceso o un objeto de dispositivo podría tener decenas de miles de referencias. Puede ser difícil identificar el origen de un error de referencia de objeto en estas circunstancias.
En Windows 7 y versiones posteriores de Windows, puedes proporcionar una etiqueta para las referencias a objetos para que estos errores sean más fáciles de encontrar. Las rutinas siguientes asocian etiquetas con la adquisición y liberación de referencias a objetos kernel:
ObDereferenceObjectDeferDeleteWithTag
ObReferenceObjectByHandleWithTag
ObReferenceObjectByPointerWithTag
Por ejemplo, ObReferenceObjectWithTag y ObDereferenceObjectWithTag, que están disponibles en Windows 7 y versiones posteriores de Windows, son versiones mejoradas de las rutinas ObReferenceObject y ObDereferenceObject , que están disponibles en Windows 2000 y versiones posteriores de Windows. Estas rutinas mejoradas permiten proporcionar un valor de etiqueta personalizada de cuatro bytes como parámetro de entrada. Puede usar las herramientas de depuración de Windows para inspeccionar un seguimiento de referencia de objeto que contiene el valor de etiqueta para cada llamada a . ObReferenceObject y ObDereferenceObject no permiten al autor de la llamada especificar etiquetas personalizadas, pero, en Windows 7 y versiones posteriores de Windows, estas rutinas agregan etiquetas predeterminadas (con el valor de etiqueta "Dflt") al seguimiento. Por lo tanto, una llamada a ObReferenceObject o ObDereferenceObject tiene el mismo efecto que una llamada a ObReferenceObjectWithTag o ObDereferenceObjectWithTag que especifica un valor de etiqueta de "Dflt". (En el programa, este valor de etiqueta aparece como 0x746c6644 o "tlfD".
Para realizar un seguimiento de una posible fuga de objetos o infrareferencia, identifique un conjunto de llamadas ObReferenceObjectXxxWithTag y ObDereferenceObjectXxxWithTag en el controlador que incrementen y decrezcan el recuento de referencias de un objeto determinado. Elija un valor de etiqueta común (por ejemplo, "Lky8") que se usará para todas las llamadas de este conjunto. Una vez que el controlador termine de usar un objeto , el número de decrementos debe coincidir exactamente con el número de incrementos. Si estos números no coinciden, el controlador tiene un error de referencia de objeto. El depurador puede comparar el número de incrementos y decrementos para cada valor de etiqueta e indicarle si no coinciden. Con esta funcionalidad, puede identificar rápidamente el origen del error de coincidencia de recuento de referencias.
Para ver un seguimiento de referencia de objetos en las herramientas de depuración de Windows, use la extensión del depurador de modo kernel-obtrace . Si el seguimiento de referencia de objetos está activado, puede usar la extensión !obtrace para mostrar etiquetas de referencia de objeto. De forma predeterminada, el seguimiento de referencia de objetos está desactivado. Use el Editor de marcas globales (Gflags) para habilitar el seguimiento de referencia de objetos. Para obtener más información sobre Gflags, vea Configuring Object Reference Tracing.
La salida de la extensión !obtrace incluye una columna "Tag", como se muestra en el ejemplo siguiente:
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
La última línea de este ejemplo indica que los recuentos de referencia y desreferencia asociados a la etiqueta "Lky8" no coinciden y que el resultado de esta discrepancia es una referencia excesiva por una (es decir, una fuga).
Si el resultado fuera en su lugar una referencia inferior, la última línea de la salida !obtrace podría ser la siguiente:
Tag: Lky8 References: 1 Dereferences: 2 Under reference by: 1
De forma predeterminada, el sistema operativo conserva la memoria eliminando el seguimiento de referencia de objeto de un objeto después de liberar el objeto. Mantener un seguimiento en la memoria incluso después de que el sistema libere un objeto puede ser útil cuando se realiza un seguimiento de una referencia insuficiente. Para ello, la herramienta Gflags proporciona una opción "Permanente", que conserva el seguimiento en la memoria mientras el equipo se apaga y se inicia de nuevo.
Windows XP introdujo el seguimiento de referencia de objetos. Dado que inicialmente el seguimiento no incluía etiquetas, los desarrolladores tenían que usar técnicas menos cómodas para identificar errores de referencia de objetos. El depurador podría realizar un seguimiento de las referencias de grupos de objetos, que el desarrollador seleccionó por tipo de objeto. La única manera en que el desarrollador podía identificar los distintos orígenes de referencias y desreferencias de objetos era comparar sus pilas de llamadas. Aunque el ejemplo !obtrace anterior contiene solo cinco pilas, ciertos tipos de objeto, como un objeto de proceso (EPROCESS), se pueden hacer referencia a ellos y desreferenciar muchas miles de veces. Con miles de pilas que se van a inspeccionar, puede ser difícil identificar el origen de una fuga de objetos o una referencia inferior sin usar etiquetas.