Le format de fichier CustomDestinations-ms expliqué
Contrairement à automaticDestinations-ms, que Windows assemble automatiquement
à partir des données MRU par application, un fichier customDestinations-ms
est écrit par l'application elle-même via l'API COM
ICustomDestinationList.
Son rôle est de publier ce que l'application veut explicitement voir dans sa
Jump List — "Tasks", éléments épinglés et catégories nommées arbitraires. Le
format sur disque est un flux plat et séquentiel de raccourcis shell
catégorisés, et non un OLE Compound File : un petit en-tête suivi d'une série
de blocs de catégories, chacun contenant un compteur puis autant de structures
LNK sérialisées.
Pourquoi CustomDestinations existe
AutomaticDestinations répond à « qu'est-ce que l'utilisateur a touché
récemment ? ». CustomDestinations répond à « qu'est-ce que l'application veut
proposer ? ». Le développeur appelle BeginList, ajoute des instances
IObjectCollection via AppendCategory, AppendKnownCategory ou
AddUserTasks, puis finalise avec CommitList. Le shell sérialise le résultat
dans %AppData%\Microsoft\Windows\Recent\CustomDestinations\<AppID>.customDestinations-ms.
Le contenu typique inclut une catégorie "Tasks" (verbes définis par l'application tels que « New Incognito Window » ou « New Private Window »), une catégorie known comme Frequent ou Recent que l'application délègue au shell, et une ou plusieurs catégories custom avec des noms UTF-16 arbitraires (les navigateurs, IDE et lecteurs multimédias en font un usage intensif).
Structure sur disque
Le fichier s'ouvre par un en-tête fixe contenant une valeur magique et une version de format, suivi d'un nombre d'entrées de catégorie. Chaque entrée de catégorie encode :
- un champ type —
0pour une catégorie custom (nommée par l'application),1pour une catégorie known telle que Frequent ou Recent, et2pour la catégorie "Tasks" ; - pour les catégories custom, un nom UTF-16LE préfixé par sa longueur en caractères (pas en octets) ;
- un compteur d'entrées ;
- ce même nombre de structures LNK sérialisées écrites bout à bout.
[ 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 / padding ]
Il n'y a aucun préfixe de longueur par entrée devant chaque LNK — vous analysez le raccourci shell sur place et laissez la structure LNK indiquer où elle se termine.
La charge utile LNK
Chaque destination est un raccourci shell MS-SHLLINK standard, identique en forme aux flux numérotés trouvés dans AutomaticDestinations. Cela signifie que le même parseur LNK est réutilisé : ShellLinkHeader, LinkTargetIDList optionnel, LinkInfo, les blocs StringData et les éventuels blocs ExtraData (notamment le TrackerDataBlock avec ses identifiants volume/machine et ses GUID droid). Chemin cible, répertoire de travail, arguments de ligne de commande, emplacement d'icône et les trois horodatages FILETIME proviennent tous du LNK lui-même — le conteneur CustomDestinations ne les duplique pas.
Éléments épinglés et catégorie "Tasks"
La catégorie "Tasks" (type 2) contient des verbes de lancement définis
par le développeur plutôt que des documents utilisateur — « Open new window »,
« Compose mail », « Start a private session ». Ces LNK pointent généralement
vers l'exécutable de l'application avec des arguments de ligne de commande
spécifiques.
Les entrées épinglées sont stockées aux côtés des autres catégories mais survivent aux réinitialisations de Jump List et à la suppression explicite des éléments récents, ce qui en fait un signal de persistance utile en triage : un chemin qu'un utilisateur a activement épinglé implique une intention, et pas seulement une activité MRU accessoire.
Notes pratiques d'analyse
- 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.
- Il n'y a aucune couche OLE — n'essayez pas d'ouvrir le fichier avec un lecteur Compound File. Traitez-le comme un flux d'octets brut.
- Les chaînes UTF-16LE sont préfixées en longueur en caractères, donc multipliez par deux avant d'avancer le curseur.
- Attendez-vous à des octets de footer ou de padding en fin après la dernière catégorie ; tolérez une courte queue plutôt que de la traiter comme une corruption.
- Réutilisez votre parseur LNK existant tel quel ; le format est documenté par Microsoft dans la documentation développeur du shell et a été rétro-ingénieré par Volatility, libforensics, fox-it et kacos2000.
Si vous avez un fichier d'exemple, essayez-le dans le parseur dans le navigateur — il décode les deux types de fichiers côte à côte.