Le format de fichier CustomDestinations-ms expliqué
automaticDestinations-ms est ce que Windows a écrit sur l'utilisateur. customDestinations-ms est ce que l'application a écrit sur elle-même. Même dossier, mêmes fichiers nommés par AppID, conteneurs complètement différents. Le côté Automatic est MS-CFB avec des flux LNK numérotés. Le côté Custom est un flux d'octets plat avec des catégories et des shell links dos à dos, et aucun fichier composé à l'horizon. Si vous nourrissiez un lecteur OLE avec l'un en attendant l'autre, vous le sauriez déjà.
Pourquoi CustomDestinations existe-t-il ?
Le fichier Automatic répond à "qu'est-ce que l'utilisateur a touché récemment ?". Le fichier Custom répond à "que veut l'application sur sa Jump List ?". La division est par auteur, pas par contenu. Un développeur qui veut l'entrée "Nouvelle fenêtre de navigation privée" de Chrome, ou la tâche "Nouveau document" de Word, appelle ICustomDestinationList : BeginList, ajoute des instances IObjectCollection via AppendCategory, AppendKnownCategory ou AddUserTasks, puis CommitList. La shell sérialise cela dans %AppData%\Microsoft\Windows\Recent\CustomDestinations\<AppID>.customDestinations-ms.
Contenu typique :
- Une catégorie Tasks, des verbes que le développeur a câblés. "Nouvelle fenêtre de navigation privée", "Nouvelle fenêtre privée", "Rédiger un e-mail", "Quitter". Ceux-ci pointent vers l'exécutable propre de l'application avec des arguments spécifiques de ligne de commande.
- Une catégorie Known,
FrequentouRecent, déléguée à la shell. L'app dit "afficher la liste standard ici" ; la shell la remplit. - Une ou plusieurs catégories Custom, noms UTF-16LE arbitraires. Les navigateurs, IDE, lecteurs multimédias l'utilisent pour des choses comme "Plus visités", "Projets récents", "Récemment joués".
Structure sur disque
Le fichier s'ouvre avec un en-tête fixe portant une valeur magique, une version de format et un nombre d'entrées de catégorie. Chaque entrée de catégorie encode :
- Un champ type :
0pour une catégorie personnalisée (nommée par l'app),1pour une catégorie connue comme Frequent ou Recent,2pour la catégorie Tasks. - Pour le type
0, un nom UTF-16LE préfixé par sa longueur en caractères (pas en octets). - Un nombre d'entrées.
- Autant de structures LNK sérialisées, écrites dos à dos.
[ header: magic + version + category_count ]
repeat category_count times:
[ type (0=custom | 1=known | 2=tasks) ]
[ if type==0: name_len_chars + UTF-16LE name ]
[ entry_count ]
[ LNK #1 ][ LNK #2 ] ... [ LNK #entry_count ]
[ trailing footer 0xBABFFBAB / padding ]
Il n'y a pas de préfixe de longueur par entrée devant chaque LNK. Vous analysez le shell link sur place et laissez la structure LNK vous dire où elle se termine. C'est pourquoi un parser LNK fonctionnel n'est pas négociable pour ce format.
Le terminateur 0xBABFFBAB est un test de reconnaissance aussi bon que la magie MS-CFB côté Automatic. Si vous voyez ces quatre octets en fin de fichier et aucun en-tête de fichier composé au début, vous regardez un fichier CustomDestinations.
La charge utile LNK, octet par octet
Chaque destination est un shell link standard MS-SHLLINK, identique en forme aux flux numérotés à l'intérieur d'un fichier Automatic. Même parser, pas de cas particulier. Vous obtenez ShellLinkHeader, LinkTargetIDList optionnelle, LinkInfo avec VolumeID et LocalBasePath, les chaînes StringData (NAME_STRING, RELATIVE_PATH, WORKING_DIR, COMMAND_LINE_ARGUMENTS, ICON_LOCATION) et tous les blocs ExtraData. Le TrackerDataBlock survit ici aussi avec ses identifiants de volume et de machine et ses GUID droid. Le chemin cible, le répertoire de travail, les arguments de ligne de commande, l'emplacement de l'icône et les trois horodatages FILETIME proviennent tous de l'intérieur du LNK.
Ce dernier point compte : le conteneur CustomDestinations ne duplique pas les horodatages ni les compteurs d'accès par entrée. Il n'y a pas de DestList. L'ordre est par position de fichier, et les seuls horodatages sont ceux à l'intérieur des LNK incorporés.
Éléments épinglés et catégorie Tasks
La catégorie Tasks (type = 2) contient des verbes définis par le développeur, pas des documents utilisateur. "Ouvrir une nouvelle fenêtre", "Rédiger un mail", "Démarrer une session privée". Ces LNK pointent presque toujours vers le binaire de l'application avec des arguments, chrome.exe --incognito, winword.exe /n, outlook.exe /c ipm.note. Utile pour comprendre ce qu'une application offrait ; pas utile comme enregistrement d'activité utilisateur.
Les entrées épinglées sont différentes. Elles sont stockées aux côtés d'autres catégories mais elles survivent aux réinitialisations de Jump List et à l'effacement explicite des éléments récents. En triage, une entrée épinglée est une intention. L'utilisateur a activement cliqué sur "Épingler à cette liste". Traitez cela comme un signal plus fort qu'un accès MRU transitoire.
Notes pratiques de parsing
- Lisez strictement de manière séquentielle. Il n'y a pas de répertoire central ; les offsets sont implicites dans la position du curseur. Un parsing échoué à l'offset N invalide tout après N.
- Pas de couche OLE. N'essayez pas d'ouvrir le fichier avec un lecteur Compound File. Traitez-le comme un flux d'octets bruts.
- Les chaînes UTF-16LE sont préfixées par leur longueur en caractères. Multipliez par deux avant d'avancer le curseur.
- Un pied de page ou un padding final est normal. Tolérez une queue courte plutôt que de la traiter comme de la corruption. Le marqueur
0xBABFFBAB, lorsqu'il est présent, est la fin. - Réutilisez votre parser LNK. La forme est documentée dans [MS-SHLLINK] et a été rétro-ingénierée à plusieurs reprises : Volatility, libforensics, JLECmd d'Eric Zimmerman, Jumplist-Browser de Kacos2000, les publications de recherche de fox-it. N'écrivez pas le vôtre à partir de zéro à moins de le faire pour le plaisir.
Pourquoi cela compte pour le triage
Un fichier customDestinations-ms avec une liste Tasks obsolète n'a rien de remarquable, c'est ce que l'application a livré. Un fichier customDestinations-ms dont les LNK épinglés pointent vers des partages réseau, des clés USB ou des exécutables inattendus est intéressant. L'épinglage a survécu parce que l'utilisateur l'a placé là. Croisez l'AppID avec le catalogue d'applications (les fichiers LNK dans Recent\ et le automaticDestinations-ms correspondant le confirmeront), puis regardez les champs LinkInfo et TrackerDataBlock dans chaque LNK épinglé pour le chemin, le nom d'hôte et le GUID droid.
Pour voir tout cela sur un vrai échantillon sans écrire de code, déposez-en un dans le parser sur la page d'accueil, il décode les deux types de fichiers côte à côte.
Pour aller plus loin
- La documentation ICustomDestinationList de Microsoft, le côté développeur du format.
- La spécification [MS-SHLLINK] de Microsoft, la charge utile LNK dans chaque catégorie.
- JLECmd d'Eric Zimmerman, parser hors ligne canonique.
- Jumplist-Browser de Kacos2000, visualiseur PowerShell qui expose les octets bruts.
- Pour corroboration, le parser LNK, le parser Prefetch et le parser de registre.