Das Dateiformat automaticDestinations-ms erklärt
Eine automaticDestinations-ms-Datei ist eine OLE Compound File (CFB / MS-CFB).
Jeder nummerierte Stream enthält eine Shell-Verknüpfung (LNK) für ein zuletzt
verwendetes Element, und ein einzelner DestList-Stream liefert Reihenfolge,
Zeitstempel und den ursprünglichen Hostnamen.
Der äußere Container: OLE Compound File
Die Datei folgt dem Microsoft Compound File Binary Format (MS-CFB), demselben Structured-Storage-Container, der hinter Office-Dokumenten vor 2007 steckt. Intern ist die Datei in Sektoren fester Größe unterteilt (512 Bytes bei den meisten Jump Lists), die über eine FAT verkettet sind, mit einem Mini-Stream für Einträge kleiner als 4096 Bytes. Ein Directory an einem bekannten Sektorindex listet die benannten Streams auf ("Storages" und "Streams" in der CFB-Terminologie).
Für eine Jump List interessiert nur das Directory-Listing. Sie sehen einen
flachen Satz von Streams: einen pro zuletzt verwendetem Element, benannt mit
einer hexadezimalen Zahl, plus einen einzigen DestList-Stream. Es gibt keine
verschachtelten Storages.
Nummerierte Streams: ein LNK pro Eintrag
Jeder Stream-Name ist ein kleingeschriebener Hex-Integer — 1, 2, a, 1f
und so weiter — und enthält eine standardmäßige Windows-Shell-Verknüpfung, wie
in [MS-SHLLINK] dokumentiert. Das bedeutet: derselbe LNK-Parser, den Sie
bereits für .lnk-Dateien auf der Festplatte einsetzen, dekodiert sie:
ShellLinkHeader, optionale LinkTargetIDList, LinkInfo, die StringData-Blöcke
(NAME_STRING, RELATIVE_PATH, WORKING_DIR, COMMAND_LINE_ARGUMENTS,
ICON_LOCATION) und die ExtraData-Blöcke (TrackerDataBlock,
PropertyStoreDataBlock und Verwandte).
Der Stream-Name ist die Entry-ID und stellt die Verknüpfung jedes LNK zurück zu
einer Zeile in der DestList her.
Der DestList-Stream
DestList ist der Index, der den Beutel von LNKs in eine geordnete, datierte
Liste zuletzt verwendeter Elemente verwandelt. Er beginnt mit einem 32-Byte-
Header, gefolgt von einer Folge von Eintragsdatensätzen variabler Länge.
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
Jeder folgende Eintrag trägt — in dieser Reihenfolge — die Metadaten, die nötig sind, um zu rekonstruieren, woher der LNK stammt:
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
Die Versionen 3 und 4 (Windows 10 und später) fügen pro Eintrag einen kleinen
Trailer hinzu und passen einige reservierte Felder an; die Namen und Zwecke
der obigen Felder sind stabil, aber die genauen Offsets verschieben sich leicht
zwischen den Versionen. Verzweigen Sie immer anhand der Header-version, bevor
Sie Einträge parsen.
Angeheftete vs. nicht angeheftete Einträge
Der Header gibt die Anzahl der angehefteten Einträge an, aber der Pin-Marker
pro Eintrag ist im Access-Count-Feld kodiert. Nicht angeheftete Elemente
speichern einen normalisierten Float, der angibt, wie oft der Benutzer das Ziel
geöffnet hat. Angeheftete Elemente speichern einen Sentinel-Wert — historisch
0xFFFFFFFF, als 32-Bit-Float interpretiert (NaN) — um zu kennzeichnen, dass
der Eintrag vom Benutzer angeheftet wurde und der Zähler nicht aussagekräftig
ist. Der genaue Sentinel und die Position eines zusätzlichen Pin-Flags
variieren zwischen DestList-Versionen, behandeln Sie den Zähler daher als
opak, bis Sie die Version geprüft haben.
Praktische Parsing-Hinweise
- Endianness. Alles ist Little-Endian, auch GUIDs (
Data1/Data2/Data3sind Little-Endian;Data4ist ein Byte-Array). - FILETIME. Umrechnung mit
unix_seconds = (filetime / 10_000_000) - 11_644_473_600. In UTC belassen; Jump Lists zeichnen keine Zeitzone auf. - Hostname. Der 16-Byte-NetBIOS-Name ist die Workstation, die die Datei geöffnet hat. In einer Domäne ist er unschätzbar wertvoll, um Aktivitäten über mehrere Hosts hinweg zu korrelieren.
- Entry ID. Vergleichen Sie die 4-Byte-
entry IDaus jeder DestList-Zeile mit den Hex-Stream-Namen im CFB-Directory; das ist Ihr Join-Key. - Versionsabweichungen. Windows 7 erzeugte DestList v1. Windows 8/8.1 führte v3 ein; Windows 10 und 11 emittieren üblicherweise v3 oder v4. Ältere Parser, die ein v1-Layout annehmen, lesen moderne Dateien falsch — lesen Sie immer zuerst den Header.
- Reihenfolge. Nehmen Sie nicht an, dass die DestList-Zeilen in
MRU-Reihenfolge vorliegen. Sortieren Sie nach
last access FILETIME(oder nachlast entry IDminus Offset, je nachdem was Sie benötigen).
Um eine reale Datei zu untersuchen, ohne Code zu schreiben, legen Sie eine im In-Browser-Parser ab — er führt einen zu WebAssembly kompilierten Rust- DestList- und LNK-Parser aus, vollständig clientseitig. Für Hintergründe dazu, woher diese Dateien stammen und warum sie für eine Untersuchung wichtig sind, siehe die forensische Einführung.