Skip to content

Le format .bcf, octet par octet

Une lecture pratique du format binaire RecentFileCache.bcf avec un dump hexadécimal, un enregistrement décodé et les cas limites qu'un parser doit gérer.

Publié le 4 min de lecture

Le format RecentFileCache.bcf est assez petit pour s'apprendre autour d'un café. La référence autorisée est libyal/dtformats. Ce billet parcourt la même spec avec un dump hex réel et les cas limites qui font trébucher les parsers en pratique.

Endianness et encodage

  • Tous les entiers sont little-endian.
  • Les chaînes sont UTF-16LE, terminées par NUL.
  • Pas de pied, pas de corps préfixé par longueur. Les enregistrements vont jusqu'à EOF.

Si vous avez déjà écrit un parser pour un des formats binaires de l'ère AppCompat Windows, rien de tout cela ne vous surprendra.

L'en-tête de 20 octets

OffsetTailleValeurDescription
040x0fffeefeSignature. Le seul champ d'en-tête à traiter comme porteur.
440x00002211Inconnu. Constante observée.
840x00000003Inconnu. Constante observée.
1240x00000001Inconnu. Constante observée.
164variablePossiblement une somme de contrôle. Aucun parser public ne la vérifie.

Validez la signature. Si les octets à l'offset 0 ne sont pas fe ef ff 0f little-endian, vous ne regardez pas un RecentFileCache.bcf. Sortez et avertissez. Les trois autres constantes ont eu la même valeur sur tous les échantillons que j'ai vus en une décennie d'hôtes Win7. Traitez-les comme des constantes pour des vérifications de cohérence ; ne conditionnez pas le parsing à elles.

Un enregistrement

offset  size  field
   0     4    char_count   ; nombre d'unités de code UTF-16, INCLUT le NUL final
   4    var   path         ; UTF-16LE, terminé par NUL

Un char_count à 5 signifie 4 unités de code de chemin plus le NUL, soit 10 octets de charge utile plus 4 pour le compteur.

Un char_count à 0 n'est pas un enregistrement vide légitime. Tous les parsers que j'ai vus le traitent soit comme une corruption soit comme un terminateur et s'arrêtent. Faites pareil. N'essayez pas de lire 0 octet en boucle infinie.

Un dump hex commenté

Fichier minimal contenant C:\foo.exe et D:\bar :

00000000  fe ef ff 0f 11 22 00 00  03 00 00 00 01 00 00 00   en-tête (signature + 3 inconnus)
00000010  00 00 00 00                                         possible checksum (à zéro)

00000014  0b 00 00 00                                         char_count = 11 (10 chars + NUL)
00000018  43 00 3a 00 5c 00 66 00  6f 00 6f 00 2e 00 65 00   "C:\foo.e"
00000028  78 00 65 00 00 00                                   "xe" + NUL

0000002e  07 00 00 00                                         char_count = 7  (6 chars + NUL)
00000032  44 00 3a 00 5c 00 62 00  61 00 72 00 00 00           "D:\bar" + NUL

Décodé :

Enregistrementchar_countChemin
011C:\foo.exe
17D:\bar

Taille totale : 64 octets.

Notez que les chemins enregistrés dans les échantillons réels portent presque toujours le préfixe NT \??\ (par exemple \??\C:\Users\bob\AppData\Local\Temp\setup.exe). C'est la notation de chemin DOS du NT object manager. Enlevez-la pendant la normalisation. N'essayez pas de la « corriger » avant de valider la signature.

Cas limites qu'un vrai parser doit gérer

Un parser robuste ne fait pas confiance au fichier. Les .bcf réels dans la nature arrivent avec tous ces problèmes.

  • Mauvaise signature. Les copies forensiques sont rembourrées, préfixées d'en-têtes d'acquisition ou tirées d'une région mal alignée. Validez la signature ; avertissez mais continuez si vous voulez un comportement indulgent.
  • char_count = 0. Traitez comme terminateur. Stop.
  • char_count plus grand que les octets restants. Fichier tronqué ou enregistrement corrompu. Stop et avertissement. Ne lisez pas au-delà d'EOF.
  • Octets de queue après le dernier enregistrement bien formé. Courant, surtout sur des fichiers acquis en pleine réécriture. Émettez un avertissement pour que l'analyste sache qu'il y a des données de queue inexpliquées.
  • Corps de chaîne vide (char_count = 1). Juste le NUL. Techniquement valide. Émettez un chemin vide ; ne plantez pas.
  • Paires de substituts UTF-16. Le format les permet. Décodez de façon permissive avec remplacement des séquences malformées. Ne faites pas échouer tout le fichier pour un point de code laid.

L'implémentation de référence dans ce dépôt fait tout ce qui précède et émet un tableau structuré d'avertissements à côté des enregistrements.

Ce que le format ne vous dit pas

Après tout ce parsing soigné, vous avez une liste de chemins. Il n'y a pas :

  • d'horodatages par entrée,
  • de tailles de fichiers,
  • de hashes,
  • de signataires,
  • de métadonnées PE,
  • ni d'indication du moment où une entrée a été ajoutée par rapport au dernier passage de ProgramDataUpdater.

Si vous voulez répondre « quand » ou « qu'était ce binaire », couplez chaque hit BCF intéressant avec Amcache.hve pour la même époque (ou Prefetch, ou le fichier sur disque, ou l'enregistrement MFT) et triangulez. Le BCF vous donne une liste de pistes. Les métadonnées vivent ailleurs.

Pour aller plus loin

Articles liés

Le format BCF est un en-tête, une liste de chemins UTF-16LE préfixés de leur longueur, et c'est tout. Voici ce que signifie chaque octet et pourquoi rien n'a changé depuis 2009.
Le cycle de vie du BCF tient en une seule tâche planifiée. Comprenez quand ProgramDataUpdater s'exécute, quand il vide le fichier, et comment repérer un appraiser désactivé.
Un endpoint Win7 signalé par le SOC, 14 chemins dans le BCF, trois méritant un second regard. Une chaîne de corroboration réaliste et combien le BCF a réellement contribué.