Skip to content

Au cœur d'OBJECTS.DATA : analyse forensique du dépôt WMI

Comment le dépôt CIM de WMI stocke ses objets : pages d'OBJECTS.DATA, clés d'INDEX.BTR, fichiers MAPPING, définitions de classes, instances et hachages.

Publié le 5 min de lecture

En bref. Le dépôt WMI est une petite base de données située dans C:\Windows\System32\wbem\Repository. OBJECTS.DATA contient des enregistrements dans des pages de 8 Kio, INDEX.BTR est un arbre B de clés textuelles construites à partir de hachages SHA-256 de noms, et les trois fichiers MAPPING convertissent les numéros de pages logiques en numéros physiques. Les définitions de classes décrivent la structure ; les instances contiennent les valeurs. Les pages libérées conservent leurs anciens octets, ce qui rend la récupération possible.

La structure décrite ci-dessous provient des travaux de FireEye de 2015 et du parseur python-cim de Willi Ballenthin ; l'encodage à l'intérieur de chaque enregistrement suit la spécification publique de Microsoft [MS-WMIO]. Microsoft ne documente pas le conteneur du dépôt lui-même : considérez les noms de champs comme ceux de la communauté de recherche.

Les fichiers

FichierRôle
OBJECTS.DATAEnregistrements : définitions de classes et instances, pages de 8 Kio
INDEX.BTRIndex en arbre B, pages de 8 Kio, les clés pointent dans OBJECTS.DATA
MAPPING1.MAP, MAPPING2.MAP, MAPPING3.MAPTables de correspondance pages logiques → physiques pour les deux fichiers, trois générations

Sous Windows XP et Server 2003, les mêmes fichiers se trouvent dans Repository\FS\ et les noms sont hachés en MD5 au lieu de SHA-256.

Pages et table des matières

Une page d'OBJECTS.DATA qui commence des enregistrements débute par une table des matières : des entrées de 16 octets (identifiant d'enregistrement, décalage dans la page, taille, somme de contrôle) terminées par une entrée entièrement nulle. Un enregistrement plus grand que le reste de sa page se poursuit sur les pages logiques suivantes, qui n'ont pas de table des matières.

Ce sont les pages logiques dont parle l'index. L'emplacement physique d'une page logique relève du fichier MAPPING courant : chaque entrée donne le numéro de page physique d'une page logique, et une valeur spéciale signale les pages inutilisées. Les pages physiques qu'aucune entrée ne référence sont libres, et contiennent toujours ce qui y a été écrit en dernier.

L'index : des clés construites à partir de hachages

INDEX.BTR stocke des clés textuelles. Chaque partie est un préfixe suivi du SHA-256 en hexadécimal majuscule du nom mis en majuscules, encodé 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_ désigne l'espace de noms, CD_ une définition de classe, CI_ + IL_ une instance d'une classe. Le suffixe correspond à page logique . identifiant d'enregistrement . longueur. Avec la table de correspondance courante, cela suffit pour extraire n'importe quel objet : c'est ce que signifie le « mode structuré » dans WMI Parser.

Comme les noms sont hachés, l'index ne contient jamais de nom d'espace de noms ou de classe lisible. Les noms des espaces de noms proviennent des instances __NAMESPACE (chaque espace de noms liste ses enfants), et les noms de classes des définitions de classes.

Définitions de classes et instances

Un enregistrement de définition de classe contient le nom de la superclasse, un FILETIME, puis la partie classe décrite dans [MS-WMIO] : qualificateurs, table des propriétés (nom, type CIM, ordre de déclaration, décalage dans la table des valeurs) et valeurs par défaut de la classe. __EventFilter et __FilterToConsumerBinding sont définis dans l'espace de noms __SystemClass ; les consommateurs standard dans root\subscription.

Un enregistrement d'instance commence par :

  1. le hachage du nom de la classe, sur 64 caractères UTF-16 (32 sous XP) ;
  2. deux FILETIME non documentés (python-cim les nomme timestamp1 et timestamp2) ;
  3. la partie instance : une table à 2 bits par propriété indiquant si chaque valeur est définie, NULL ou égale à la valeur par défaut de la classe ; une table des valeurs de taille fixe ; des qualificateurs ; puis un tas où résident les chaînes et les tableaux.

Les chaînes du tas sont stockées sous la forme d'un octet d'indicateur (0 pour du texte 8 bits, 1 pour de l'UTF-16) suivi d'un texte terminé par NUL. C'est pourquoi une simple recherche textuelle dans OBJECTS.DATA trouve CommandLineEventConsumer.Name="…" ainsi que les lignes de commande elles-mêmes.

Décoder une instance nécessite la structure de sa classe : la liste des propriétés provient de la définition de la classe et de tous ses ancêtres (CommandLineEventConsumer → __EventConsumer → __IndicationRelated → __SystemClass).

Ce que cela implique pour une investigation

  • Les objets actifs sont ceux qui sont accessibles par l'index et la table de correspondance courante.
  • Les objets supprimés restent dans des pages non mappées ou dans la fin inutilisée d'une page jusqu'à ce qu'ils soient écrasés. Une recherche des hachages de noms de classes les retrouve ; le fichier MAPPING indique qu'ils ne sont plus référencés. Voir récupérer une persistance WMI supprimée.
  • Tous les fichiers doivent provenir du même instant. Un INDEX.BTR plus récent que le fichier MAPPING pointe vers les mauvaises pages. Collectez depuis un cliché instantané : comment collecter le dépôt WMI.
  • Les horodatages sont peu fiables. Les définitions de classes portent un FILETIME et les instances deux FILETIME non documentés ; aucun n'est une date de création documentée.

WMI Parser implémente les deux approches, le parcours de l'index et un carving complet, et indique pour chaque objet comment il a été trouvé.

FAQ

Que contient OBJECTS.DATA ?

Toutes les définitions de classes et toutes les instances de classes de chaque espace de noms WMI, stockées sous forme d'enregistrements dans des pages de 8 Kio. Les filtres d'événements, les consommateurs et les liaisons y sont des instances ordinaires.

Pourquoi y a-t-il trois fichiers MAPPING ?

Windows conserve trois générations de la table de correspondance entre pages logiques et physiques afin de pouvoir se rétablir après une écriture échouée. Celle dont la version est la plus élevée est la version courante ; les autres décrivent des états antérieurs.

Peut-on lire OBJECTS.DATA sans INDEX.BTR ?

Oui. Les enregistrements d'instance commencent par le hachage du nom de leur classe, on peut donc les retrouver en parcourant le fichier. L'index ajoute les espaces de noms et distingue les enregistrements actifs des enregistrements supprimés.

Articles liés

Articles liés

Lire CCM_RecentlyUsedApps dans le dépôt WMI : quels programmes chaque utilisateur a lancés, quand et combien de fois, y compris les anciennes copies d'OBJECTS.DATA.
Pourquoi filtres, consommateurs et liaisons WMI supprimés subsistent dans OBJECTS.DATA : parcours d'index, carving, python-cim et PyWMIPersistenceFinder.
Persistance WMI : fonctionnement de __EventFilter, des consommateurs et de __FilterToConsumerBinding, où ils sont stockés et comment les détecter.