Les horodatages d'Amcache, décodés

Il y a au moins neuf champs dans Amcache.hve qui ressemblent à un horodatage. Deux n'en sont pas. Quatre signifient des choses différentes sur Windows 7 et sur Windows 10. Un est contrôlé par l'attaquant. Les deux autres sont utiles, parfois, avec des bémols.

Si on vous a déjà demandé "quand ce fichier est-il apparu sur l'hôte ?" et que vous avez répondu en utilisant un seul champ Amcache, ce billet est la conversation que vous auriez dû avoir avec vous-même d'abord.

Les deux formats que Windows utilise, côte à côte#

L'Amcache mélange deux conventions d'horodatage dans la même ruche, ce qui est un certain type de choix de conception.

Le premier est le FILETIME 64 bits : intervalles de 100 nanosecondes depuis le 01/01/1601 UTC, stocké little-endian. C'est ce qu'utilise le LastWrite de chaque clé de registre, et ce que stockent certains champs au niveau valeur. Les parseurs le gèrent de façon transparente. La sémantique est généralement "le registre a écrit cette entrée à ce moment".

Le second est DateTime-en-chaîne, où Microsoft a choisi de stocker un horodatage lisible par un humain dans une valeur REG_SZ. Les formats varient entre builds : MM/JJ/AAAA HH:MM:SS sur Win10, ISO-8601-ish AAAA-MM-JJ HH:MM:SS sur certaines entrées Win11. Le fuseau horaire est généralement UTC mais parfois heure locale sur les ruches Win7. Vérifiez toujours avant de bâtir une timeline ; un décalage d'un fuseau dans un rapport, c'est embarrassant.

Lisez le type de cellule vk brut en cas de doute. REG_QWORD et REG_BINARY de longueur 8 signifient FILETIME ; REG_SZ signifie parsez la chaîne et priez.

Les champs, un par un#

Key LastWrite sur InventoryApplicationFile\#

L'horodatage le plus utile de la ruche. C'est le FILETIME que le registre a écrit dans l'en-tête de la clé la dernière fois que le Compatibility Appraiser a mis à jour l'entrée de ce fichier. En pratique, c'est "Appraiser a remarqué ce fichier" ou "Appraiser a re-hashé ce fichier parce que quelque chose a changé".

Ce n'est pas la création du fichier, pas l'exécution, pas l'activité utilisateur. La première fois qu'Appraiser voit un binaire fraîchement déposé, le LastWrite est approximativement la prochaine exécution planifiée après l'apparition du fichier. La cadence d'exécution planifiée dépend de l'hôte, mais quelques heures est typique sur un système normalement configuré.

Sanity check : cet horodatage doit toujours être postérieur au temps de création NTFS propre du fichier depuis la MFT. S'il est antérieur, quelque chose cloche (timestomping, dérive d'horloge, ou un attaquant qui a édité la ruche).

LinkDate (InventoryApplicationFile, InventoryDriverBinary)#

IMAGE_FILE_HEADER.TimeDateStamp de l'en-tête PE, stocké comme chaîne DateTime. C'est l'horodatage de compilation que le linker a estampé, qui est ce que l'environnement de build a déclaré.

Contrôlé par l'attaquant. Trivialement. link.exe /timestamp ou un patch post-link peut le mettre à n'importe quoi. La moitié du malware commodity sort avec LinkDate = 1970-01-01 parce que personne ne s'embête. Les meilleurs échantillons le mettent à une valeur 2018-2020 plausible. Les polissés le calent sur le LinkDate d'un vrai binaire Microsoft pour se fondre.

Utile pour :

  • Repérer des valeurs manifestement fausses (1970, 2099, ou des valeurs qui contredisent le reste de la timeline de l'hôte).
  • Distinguer deux builds du même projet, quand les deux échantillons ont des temps de compilation honnêtes.

Pas utile pour : une timeline. Ne le mettez pas sur une timeline.

InstallDate (InventoryApplication)#

Une chaîne DateTime sous chaque clé InventoryApplication\<ProgramId>. La valeur est ce que l'installeur a écrit, généralement tiré des métadonnées MSI ou de la valeur DisplayInstallDate dans la clé Uninstall. Pour des logiciels packagés légitimement (Office, Chrome, MSI d'éditeurs) c'est honnête à la journée, parfois à la seconde.

Pour les droppers malveillants, les outils sideloadés, ou tout ce qui n'est pas passé par un vrai installeur, ce champ est soit absent, soit à zéro, soit à ce qui était commode. À traiter comme indicatif.

J'utilise InstallDate pour corroborer des applications grand public quand la question est "quand cet utilisateur a-t-il installé Chrome", pas pour de l'activité d'adversaire.

InstallDateMsi, InstallDateArpLastModified, InstallDateFromLinkFile (InventoryApplication)#

Même sens général, sources différentes. Windows 10 build 1709 et postérieurs exposent plusieurs variantes d'install-date par application :

  • InstallDateMsi : depuis la base MSI, si l'installation utilisait MSI.
  • InstallDateArpLastModified : depuis le temps de dernière modification de l'entrée registre Add/Remove Programs (ARP).
  • InstallDateFromLinkFile : depuis les métadonnées du fichier .lnk du menu Démarrer, quand l'application en a créé un.

Quand ces trois divergent de plus de quelques minutes, quelque chose d'intéressant s'est produit : une installation de réparation, une mise à niveau, ou un ré-enregistrement. Quand elles concordent, la date d'installation est solide.

Key LastWrite sur InventoryApplication\#

Différent de InstallDate. C'est la dernière écriture de la clé registre, mise à jour à chaque fois qu'Appraiser rafraîchit l'entrée d'application. Pour la plupart des applications, la première écriture est proche du moment d'installation et les suivantes sont rares. Utile comme vérification de corroboration contre InstallDate*.

Key LastWrite sur InventoryDeviceContainer\#

Ce qui se rapproche le plus de "quand Windows a-t-il vu ce périphérique pour la dernière fois" dans la ruche. Mis à jour quand Appraiser rafraîchit l'enregistrement du périphérique. Combinez avec l'histoire USB propre (SYSTEM\ControlSet001\Enum\USBSTOR, Microsoft-Windows-Partition%4Diagnostic.evtx, setupapi.dev.log) pour toute question USB ; la valeur Amcache seule est trop grossière.

Key LastWrite sur InventoryDriverBinary\ et InventoryDriverPackage\#

Même forme que les clés d'application. Le LastWrite est "Appraiser a rafraîchi cette entrée de pilote", ce qui sur un hôte normalement opérationnel se produit peu après l'arrivée du pilote. Pour les enquêtes BYOVD où l'attaquant a déposé un pilote signé vulnérable, le LastWrite d'InventoryDriverBinary est fréquemment la borne la plus serrée que vous ayez sur "quand ce pilote a-t-il été mis en place".

Key LastWrite sur InventoryApplicationShortcut\#

Quand Appraiser a indexé ce .lnk. Le shortcut lui-même a ses propres MAC times dans le corps du fichier LNK, que le parseur LNK vous donnera. Le LastWrite Amcache est un horodatage distinct, postérieur, moins intéressant. Je l'utilise comme sanity check, pas comme constat.

Ce que Win7, Win10 et Win11 font différemment#

La ruche n'est pas la même entre builds majeures, même quand les noms de clés ont l'air identiques.

Windows 7 SP1 avec le Compatibility Appraiser original avait une ruche beaucoup plus simple. Les clés de premier niveau étaient Root\File, Root\Programs, Root\Generic, et Root\Orphan. La valeur LastModified sous Root\File\<volume>\<fileid> était explicitement "fichier dernièrement modifié par NTFS au moment du scan Appraiser", une sémantique différente de tout ce qui est dans la ruche moderne. Si vous lisez une ruche Win7, vous ne pouvez pas utiliser les noms de champ Win10. Le plus ancien plugin amcache de RegRipper gère ça ; AmcacheParser gère mieux les ruches modernes que les legacy.

Windows 8 / 8.1 a introduit les clés InventoryApplication* mais le schéma était instable. Plusieurs champs apparaissent en 8.1 qui disparaissent en 10 et vice versa. Je ne recommanderais pas de construire de l'automatisation contre une ruche 8.1 sans inspecter d'abord la liste des valeurs.

Windows 10 builds jusqu'à ~1709 avaient un schéma InventoryApplication plus mince. Les trois variantes InstallDate* n'existaient pas toutes. LinkDate était parfois vide pour des fichiers PE qu'Appraiser ne pouvait pas parser entièrement.

Windows 10 1809 et postérieurs est le schéma stable que vise la plupart de l'outillage actuel. L'ensemble de colonnes d'AmcacheParser correspond à celui-là.

Windows 11 ajoute quelques valeurs sous InventoryApplicationFile (notamment quelques champs liés à la signature) et change le format de chaîne de certaines valeurs DateTime de locale US à un format ISO-ish. La sémantique des horodatages de base est inchangée.

Les mises à niveau in-place font le bazar#

Quand un hôte passe de Windows 10 à Windows 11, ou d'une build majeure 10 à une autre, l'Amcache est migrée, pas régénérée. En pratique :

  • Les vieilles entrées InventoryApplicationFile sont généralement préservées avec leur LastWrite d'origine de la passe pré-mise-à-niveau.
  • Appraiser re-scanne tout après la mise à niveau, donc en quelques heures à quelques jours la plupart des LastWrites sont rafraîchis.
  • Les changements de schéma signifient que certains champs sont peuplés sur les entrées créées après la mise à niveau et vides sur les entrées qui ont survécu d'avant.
  • Un sous-ensemble d'entrées finit parfois orphelin : le binaire n'existe plus mais la clé n'a jamais été nettoyée durant la migration.

Si la distribution LastWrite de votre ruche montre deux clusters séparés par un trou propre, vous regardez probablement une population pré-mise-à-niveau et une post-mise-à-niveau. Confirmez contre setupact.log ou le canal Setup dans le journal d'événements avant d'en lire trop.

Sanity checks pratiques#

Avant que tout horodatage n'entre dans un rapport, faites tourner ceux-ci dans votre tête :

  1. Le LastWrite de la clé InventoryApplicationFile est-il postérieur au temps de création NTFS du fichier ? Sinon, quelque chose cloche.
  2. Le LastWrite est-il cohérent avec le last-run de la tâche Compatibility Appraiser dans Microsoft-Windows-TaskScheduler%4Operational.evtx ? Les écritures Appraiser sont en lot ; vous devriez voir des clusters de LastWrites qui coïncident avec les exécutions de tâche.
  3. Le triplet InstallDate* sous InventoryApplication est-il cohérent entre lui ? Le désaccord est intéressant.
  4. LinkDate est-il plausible ? 1970, 2099 ou toute valeur hors d'environ 1995-maintenant signifie que l'en-tête PE a été estampé, pas que le fichier est si vieux.
  5. Croisez au moins un horodatage Amcache avec les temps d'exécution Prefetch, les événements Sysmon process create, ou les créations du journal USN. Si le croisement diverge, l'Amcache n'a pas forcément tort, mais vous devez expliquer l'écart avant de vous engager.

L'Amcache ne vous mentira pas exprès. Elle vous tendra cependant un horodatage parfaitement raisonnable d'apparence qui signifie quelque chose de complètement différent de ce que vous avez supposé. C'est ce qu'ont en commun tous les champs de ce billet.

Pour aller plus loin#

  • Amcache.hve in Windows 8 de Yogesh Khatri, qui documente encore la sémantique des champs mieux que toute source éditeur.
  • Le code source de l'AmcacheParser d'Eric Zimmerman pour le mapping canonique des noms de valeurs vers les colonnes.
  • La documentation du Compatibility Appraiser de Microsoft, mince côté forensique mais utile pour comprendre la cadence de la tâche.
  • Les vidéos deep-dive de 13Cubed sur Amcache et Shimcache, en particulier les segments sur les horodatages.

Articles liés

Retour à tous les articles