Dentro de OBJECTS.DATA: análisis forense del repositorio WMI
Cómo almacena objetos el repositorio CIM de WMI: páginas de OBJECTS.DATA, claves de INDEX.BTR, los tres archivos MAPPING, clases, instancias y hashes.
En resumen. El repositorio WMI es una pequeña base de datos en C:\Windows\System32\wbem\Repository. OBJECTS.DATA contiene registros en páginas de 8 KiB, INDEX.BTR es un árbol B de claves de texto construidas a partir de hashes SHA-256 de nombres, y los tres archivos MAPPING convierten los números de página lógicos en físicos. Las definiciones de clase describen la estructura; las instancias contienen los valores. Las páginas liberadas conservan sus bytes antiguos, que es lo que hace posible la recuperación.
La estructura que sigue procede de la investigación de FireEye de 2015 y del analizador python-cim de Willi Ballenthin; la codificación dentro de cada registro sigue la especificación pública [MS-WMIO] de Microsoft. Microsoft no documenta el contenedor del repositorio en sí, así que considere los nombres de campo como los de la comunidad investigadora.
Los archivos
| Archivo | Función |
|---|---|
OBJECTS.DATA | Registros: definiciones de clase e instancias, páginas de 8 KiB |
INDEX.BTR | Índice en árbol B, páginas de 8 KiB, claves que apuntan a OBJECTS.DATA |
MAPPING1.MAP, MAPPING2.MAP, MAPPING3.MAP | Mapas de páginas lógicas → físicas para ambos archivos, tres generaciones |
En Windows XP y Server 2003 los mismos archivos están en Repository\FS\ y los nombres se calculan con MD5 en lugar de SHA-256.
Páginas y tabla de contenidos
Una página de OBJECTS.DATA que inicia registros se abre con una tabla de contenidos: entradas de 16 bytes (id de registro, desplazamiento en la página, tamaño, suma de comprobación) cerradas por una entrada toda a ceros. Un registro más grande que el resto de su página continúa en las siguientes páginas lógicas, que no tienen tabla de contenidos.
Las páginas lógicas son aquellas de las que habla el índice. Dónde se sitúa físicamente una página lógica es tarea del archivo MAPPING actual: cada entrada da el número de página física de una página lógica, y un valor especial marca las no utilizadas. Las páginas físicas que ninguna entrada referencia están libres, y siguen conteniendo lo último que se escribió en ellas.
El índice: claves construidas con hashes
INDEX.BTR almacena claves de texto. Cada parte es un prefijo más el SHA-256 en hexadecimal en mayúsculas del nombre en mayúsculas codificado en UTF-16:
NS_<h("ROOT\SUBSCRIPTION")>/CD_<h("COMMANDLINEEVENTCONSUMER")>.268.2079887499.561
NS_<h("ROOT\SUBSCRIPTION")>/CI_<h("__EVENTFILTER")>/IL_<h(key)>.271.2079886610.299
NS_ es el espacio de nombres, CD_ una definición de clase, CI_ + IL_ una instancia de una clase. El sufijo es página lógica . id de registro . longitud. Con el mapa actual, eso basta para obtener cualquier objeto, que es lo que significa el "modo estructurado" en WMI Parser.
Como los nombres se convierten en hashes, el índice nunca contiene un nombre legible de espacio de nombres ni de clase. Los nombres de los espacios de nombres proceden de las instancias __NAMESPACE (cada espacio de nombres enumera sus hijos), y los nombres de clase, de las definiciones de clase.
Definiciones de clase e instancias
Un registro de definición de clase contiene el nombre de la superclase, un FILETIME y después la parte de clase descrita en [MS-WMIO]: calificadores, una tabla de propiedades (nombre, tipo CIM, orden de declaración, desplazamiento en la tabla de valores) y los valores por defecto de la clase. __EventFilter y __FilterToConsumerBinding se definen en el espacio de nombres __SystemClass; los consumidores estándar, en root\subscription.
Un registro de instancia empieza por:
- el hash del nombre de la clase, como 64 caracteres UTF-16 (32 en XP);
- dos FILETIME, no documentados (python-cim los llama
timestamp1ytimestamp2); - la parte de instancia: una tabla de 2 bits por propiedad que indica si cada valor está definido, es NULL o es el valor por defecto de la clase; una tabla de valores de tamaño fijo; calificadores; y después un heap donde residen las cadenas y los arrays.
Las cadenas del heap se almacenan como un byte indicador (0 para texto de 8 bits, 1 para UTF-16) seguido de texto terminado en NUL. Por eso una simple búsqueda de texto en OBJECTS.DATA encuentra CommandLineEventConsumer.Name="…" y las propias líneas de comandos.
Decodificar una instancia requiere la estructura de su clase: la lista de propiedades procede de la definición de clase y de todos sus ancestros (CommandLineEventConsumer → __EventConsumer → __IndicationRelated → __SystemClass).
Qué significa esto para una investigación
- Los objetos activos son los accesibles a través del índice y el mapa actual.
- Los objetos eliminados permanecen en páginas no asignadas o en la cola sin usar de una página hasta que se sobrescriben. Buscar los hashes de nombre de clase los encuentra; el archivo MAPPING le indica que ya no están referenciados. Consulte recuperar persistencia WMI eliminada.
- Todos los archivos deben proceder del mismo instante. Un
INDEX.BTRmás reciente que el archivo MAPPING apunta a páginas equivocadas. Recopile desde una instantánea de volumen: cómo recopilar el repositorio WMI. - Las marcas de tiempo son poco fiables. Las definiciones de clase llevan un FILETIME y las instancias dos no documentados; ninguno es una fecha de creación documentada.
WMI Parser implementa ambas vías —el recorrido del índice y un carving completo— y etiqueta cada objeto con la forma en que se encontró.
FAQ
¿Qué contiene OBJECTS.DATA?
Todas las definiciones de clase e instancias de clase de todos los espacios de nombres WMI, almacenadas como registros en páginas de 8 KiB. Los filtros de eventos, los consumidores y los enlaces son instancias ordinarias dentro de él.
¿Por qué hay tres archivos MAPPING?
Windows conserva tres generaciones del mapa de páginas lógicas a físicas para poder recuperarse de una escritura fallida. El de versión más alta es el actual; los demás describen estados anteriores.
¿Se puede leer OBJECTS.DATA sin INDEX.BTR?
Sí. Los registros de instancia empiezan por el hash del nombre de su clase, así que pueden encontrarse recorriendo el archivo. El índice añade los espacios de nombres y distingue los registros activos de los eliminados.