El formato de archivo AutomaticDestinations-ms explicado
Un archivo automaticDestinations-ms es un OLE Compound File (CFB / MS-CFB).
Cada flujo numerado contiene un shell link (LNK) de un elemento usado
recientemente, y un único flujo DestList aporta el orden, las marcas de tiempo
y el nombre de host de origen.
El contenedor externo: OLE Compound File
El archivo sigue el formato Microsoft Compound File Binary (MS-CFB), el mismo contenedor de almacenamiento estructurado que hay detrás de los documentos de Office anteriores a 2007. Internamente, el archivo se divide en sectores de tamaño fijo (512 bytes para la mayoría de jump lists), enhebrados a través de una FAT, con un mini-stream para entradas menores de 4096 bytes. Un directorio en un índice de sector conocido enumera los flujos con nombre ("storages" y "streams" en la terminología CFB).
Para una Jump List solo importa el listado del directorio. Verás un conjunto
plano de flujos: uno por elemento reciente, nombrados con un número hexadecimal,
más un único flujo DestList. No hay storages anidados.
Flujos numerados: un LNK por entrada
Cada nombre de flujo numerado es un entero hexadecimal en minúsculas — 1,
2, a, 1f, etc. — y contiene un shell link estándar de Windows, tal y como
se documenta en [MS-SHLLINK]. Eso significa que el mismo parser LNK que ya
usas para los archivos .lnk en disco lo decodificará: ShellLinkHeader,
LinkTargetIDList opcional, LinkInfo, los bloques StringData (NAME_STRING,
RELATIVE_PATH, WORKING_DIR, COMMAND_LINE_ARGUMENTS, ICON_LOCATION) y los
bloques ExtraData (TrackerDataBlock, PropertyStoreDataBlock y compañía).
El nombre del flujo es el ID de la entrada, y es lo que enlaza cada LNK con una
fila del DestList.
El flujo DestList
DestList es el índice que convierte el conjunto de LNKs en una lista de uso
reciente ordenada y fechada. Empieza con una cabecera de 32 bytes seguida de
una secuencia de registros de entrada de longitud variable.
DestList header (32 bytes, little-endian)
offset size field
0x00 4 version (0x01..0x04 observed; Win7=1, Win10+=3/4)
0x04 4 number of entries
0x08 4 number of pinned entries
0x0C 4 unused / reserved (often a float; varies by version)
0x10 4 last entry ID issued
0x14 4 unused / reserved
0x18 4 last revision number
0x1C 4 unused / reserved
Cada entrada que sigue lleva — en orden — los metadatos necesarios para reconstruir de dónde procede el LNK:
DestList entry (variable length)
field size notes
checksum / reserved 8 version-dependent
NewVolumeID (GUID) 16 target volume
NewObjectID (GUID) 16 target object (file)
BirthVolumeID (GUID) 16 original volume
BirthObjectID (GUID) 16 original object
NetBIOS hostname 16 ASCII, NUL-padded
entry ID 4 matches the numbered stream name
reserved 4
access count 4 IEEE-754 float; sentinel = pinned
last access FILETIME 8 100-ns ticks since 1601-01-01 UTC
entry pin status / reserved 4 layout differs by version
name length (chars) 2 UTF-16LE code units
name 2*N UTF-16LE, not NUL-terminated
trailer 4 only on DestList v3/v4
Las versiones 3 y 4 (Windows 10 y posteriores) añaden un pequeño trailer por
entrada y ajustan algunos campos reservados; los nombres y propósitos de
los campos anteriores son estables, pero los desplazamientos exactos cambian
ligeramente entre versiones. Ramifica siempre según version de la cabecera
antes de procesar las entradas.
Entradas ancladas frente a no ancladas
La cabecera indica el número de entradas ancladas, pero el marcador de anclaje
por entrada se codifica en el campo de número de accesos. Los elementos no
anclados almacenan un float normalizado que representa con qué frecuencia el
usuario abrió el destino. Los elementos anclados almacenan un valor centinela
— históricamente 0xFFFFFFFF interpretado como un float de 32 bits (NaN) —
para marcar que la entrada está anclada por el usuario y que el recuento no es
significativo. El centinela exacto y la posición de cualquier flag adicional de
anclaje varía entre versiones de DestList, así que trata el recuento como
opaco hasta haber comprobado la versión.
Notas prácticas de parseo
- Endianness. Todo es little-endian, incluidos los GUIDs (
Data1/Data2/Data3son little-endian;Data4es un array de bytes). - FILETIME. Convierte con
unix_seconds = (filetime / 10_000_000) - 11_644_473_600. Mantenlos en UTC; las jump lists no registran zona horaria. - Hostname. El nombre NetBIOS de 16 bytes es la estación de trabajo que abrió el archivo. En un dominio resulta inestimable para correlacionar actividad entre equipos.
- Entry ID. Empareja el
entry IDde 4 bytes de cada fila de DestList con los nombres de flujo hexadecimales del directorio CFB; esa es tu clave de unión. - Desfase de versión. Windows 7 producía DestList v1. Windows 8/8.1 introdujo v3; Windows 10 y 11 emiten habitualmente v3 o v4. Los parsers antiguos que asumen el layout v1 leerán mal los archivos modernos: lee siempre primero la cabecera.
- Ordenación. No asumas que las filas de DestList están en orden MRU.
Ordena por
last access FILETIME(o porlast entry IDmenos desplazamiento, según lo que necesites).
Para inspeccionar un archivo real sin escribir código, suéltalo en el parser en el navegador — ejecuta un parser de DestList + LNK en Rust compilado a WebAssembly, enteramente del lado del cliente. Para conocer el origen de estos archivos y por qué importan en una investigación, consulta la introducción forense.