Jump List Parser
Back to blog

The AutomaticDestinations-ms File Format Explained

2026-05-253 min read

An automaticDestinations-ms file is an OLE Compound File (CFB / MS-CFB). Each numbered stream holds a shell link (LNK) for a recently-used item, and a single DestList stream provides ordering, timestamps, and the originating hostname.

The outer container: OLE Compound File

The file follows the Microsoft Compound File Binary format (MS-CFB), the same structured-storage container behind pre-2007 Office documents. Internally, the file is divided into fixed-size sectors (512 bytes for most jump lists), threaded through a FAT, with a mini-stream for entries smaller than 4096 bytes. A directory at a known sector index lists the named streams ("storages" and "streams" in CFB terminology).

For a Jump List you only care about the directory listing. You will see a flat set of streams: one per recent item, named with a hexadecimal number, plus a single DestList stream. There are no nested storages.

Numbered streams: one LNK per entry

Each numbered stream name is a lowercase hex integer — 1, 2, a, 1f, and so on — and contains a standard Windows shell link, as documented in [MS-SHLLINK]. That means the same LNK parser you already use for .lnk files on disk will decode it: ShellLinkHeader, optional LinkTargetIDList, LinkInfo, the StringData blocks (NAME_STRING, RELATIVE_PATH, WORKING_DIR, COMMAND_LINE_ARGUMENTS, ICON_LOCATION), and the ExtraData blocks (TrackerDataBlock, PropertyStoreDataBlock, and friends).

The stream name is the entry ID, and it is what links each LNK back to a row in the DestList.

The DestList stream

DestList is the index that turns the bag of LNKs into an ordered, dated recently-used list. It begins with a 32-byte header followed by a sequence of variable-length entry records.

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

Each entry that follows carries — in order — the metadata needed to reconstruct where the LNK came from:

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

Versions 3 and 4 (Windows 10 and later) add a small trailer per entry and tweak a few reserved fields; the field names and purposes above are stable, but exact offsets shift slightly between versions. Always branch on the header version before parsing entries.

Pinned vs unpinned entries

The header reports the number of pinned entries, but the per-entry pin marker is encoded into the access-count field. Unpinned items store a normalised float representing how often the user opened the target. Pinned items store a sentinel value — historically 0xFFFFFFFF interpreted as a 32-bit float (NaN) — to mark that the entry is user-pinned and that the count is not meaningful. The exact sentinel and the position of any additional pin flag varies between DestList versions, so treat the count as opaque until you have checked the version.

Practical parsing notes

  • Endianness. Everything is little-endian, including GUIDs (Data1/Data2 /Data3 are little-endian; Data4 is a byte array).
  • FILETIME. Convert with unix_seconds = (filetime / 10_000_000) - 11_644_473_600. Keep them in UTC; jump lists do not record timezone.
  • Hostname. The 16-byte NetBIOS name is the workstation that opened the file. On a domain it is invaluable for correlating activity across hosts.
  • Entry ID. Match the 4-byte entry ID from each DestList row against the hex stream names in the CFB directory; that is your join key.
  • Version skew. Windows 7 produced DestList v1. Windows 8/8.1 introduced v3; Windows 10 and 11 commonly emit v3 or v4. Older parsers that assume v1 layout will misread modern files — always read the header first.
  • Ordering. Do not assume the DestList rows are in MRU order. Sort by last access FILETIME (or by last entry ID minus offset, depending on what you need).

To inspect a real file without writing any code, drop one on the in-browser parser — it runs a Rust DestList + LNK parser compiled to WebAssembly, entirely client-side. For background on where these files come from and why they matter for an investigation, see the forensic primer.