Jump List Parser
Zurück zum Blog

Eine Jump-List-Zeitleiste rekonstruieren, wenn jemand sie verschleiern wollte

2026-06-158 Min. Lesezeit

Die meisten Beiträge behandeln eine Jump List wie ein sortiertes Protokoll, das man von oben nach unten liest. Das ist sie nicht. Der DestList-Stream ist eine Struktur der zuletzt verwendeten Elemente, und in dem Moment, in dem Sie annehmen, die Reihenfolge in der Datei sei die Reihenfolge der Ereignisse, ist Ihre Zeitleiste falsch und Sie wissen es nicht. In diesem Beitrag geht es darum, die Reihenfolge richtig hinzubekommen, und darum, wie das Artefakt aussieht, nachdem jemand darin war und versucht hat, ein Problem verschwinden zu lassen.

Falls Sie noch keine davon analysiert haben, beginnen Sie mit der DFIR-Anleitung und der Aufschlüsselung des automaticDestinations-Dateiformats. Hier wird vorausgesetzt, dass Sie bereits wissen, was eine DestList-Zeile enthält.

Die Reihenfolgenfalle

Jeder DestList-Eintrag trägt einen einzigen FILETIME: den letzten Zeitpunkt, zu dem die Shell das Öffnen dieses Ziels aufgezeichnet hat. Ein Wert. Er wird bei jedem Zugriff überschrieben. Ein Eintrag verrät Ihnen also das letzte Öffnen und nichts über die fünf Öffnungen davor. Das ist das Erste, was Leute falsch machen. Sie sehen "letzter Zugriff 14:07" und schreiben "der Benutzer hat die Datei einmal um 14:07 geöffnet" in den Bericht. Die Datei wurde vielleicht vierzigmal geöffnet. Sie sehen immer nur das letzte Mal.

Das Zweite, was sie falsch machen, ist die Sequenz. Die nummerierten LNK-Streams (1, 2, 3 und so weiter) werden ungefähr in Erstellungsreihenfolge zugewiesen, aber sie sind auch nicht Ihre Zeitleiste, weil Windows sie wiederverwendet und neu schreibt. Was Sie wirklich wollen, sind zwei voneinander unabhängige Sortierungen, sortiert und dann verglichen:

  • Der FILETIME des letzten Zugriffs pro Eintrag, aufsteigend sortiert. Das ist Ihre Ereignisreihenfolge.
  • Die Eintrags-ID-Werte der DestList. Der Header führt einen Zähler, den die Shell jedes Mal hochzählt, wenn sie einen neuen Eintrag vergibt. Jede Zeile speichert die ID, die sie erhalten hat. Dieser Zähler geht nur nach oben, daher sind die Eintrags-IDs eine monotone Aufzeichnung der Reihenfolge, in der Ziele zuerst in die Liste gelangt sind.

Wenn diese beiden Sortierungen übereinstimmen, haben Sie eine saubere MRU und können der Zeitleiste vertrauen. Wenn sie sich widersprechen, ist etwas Interessantes passiert, und der Widerspruch ist genau das Signal, um das es im Rest dieses Beitrags geht.

Die Sequenz rekonstruieren

Parsen Sie nach CSV und ziehen Sie drei Spalten heraus: Eintrags-ID, Zeitpunkt des letzten Zugriffs und Zielpfad. Mit JLECmd:

JLECmd.exe -d "C:\case\AutomaticDestinations" --csv "C:\case\out"

Sortieren Sie dann nach LastModified (so nennt JLECmd den DestList-FILETIME) und behalten Sie EntryNumber daneben. In einer normalen Liste werden die Eintrags-IDs in dieselbe Richtung tendieren wie die Zeitstempel. Eine kürzlich geöffnete Datei sitzt oben in der MRU und trägt eine hohe Eintrags-ID und einen jüngeren Zeitstempel. Alte, unberührte Ziele tragen niedrige IDs und alte Zeitstempel und driften auf die Verdrängung zu.

Sehen Sie sich nun den Header-Zähler im Verhältnis zur Zeilenzahl an. Wenn der DestList-Header sagt, die zuletzt vergebene Eintragsnummer sei 61, und Sie neun Zeilen halten, haben rund fünfzig Einträge diese Liste irgendwann verlassen. Bevor Sie "Beweise wurden gelöscht" schreiben, verstehen Sie zuerst die langweilige Erklärung: Die automatische Jump List ist gedeckelt. Die Obergrenze hängt mit der Shell-Einstellung "Anzahl der anzuzeigenden zuletzt verwendeten Elemente" zusammen, und wenn die Liste voll ist, wird der am längsten nicht verwendete Eintrag verdrängt, um Platz zu schaffen. Normale Fluktuation erzeugt genau diese Lücke. Ein Arbeitsplatz, der den ganzen Tag Dokumente öffnet, zeigt einen hohen Zähler und eine kurze überlebende Liste, und das ist einfach ein Dienstag.

Was nicht normal ist, sind Lücken in den überlebenden IDs, die die MRU-Logik brechen, oder ein Header-Zähler, der sich nicht bewegt, während die Zugriffszeiten weiter voranschreiten. Behalten Sie das im Hinterkopf.

Wie eine geleerte Jump List wirklich aussieht

Benutzer und Angreifer greifen zu denselben groben Werkzeugen. Der Einstellungsschalter ("Zuletzt geöffnete Elemente in Start, Sprunglisten und Datei-Explorer anzeigen"), das Löschen der AppID-Dateien oder das Ausführen eines Datenschutzreinigers. Die Anleitung zum Leeren oder Löschen behandelt die Mechanik. Hier ist der forensische Rückstand, den jeder hinterlässt.

Löschen Sie die Datei .automaticDestinations-ms rundheraus, und Windows trauert ihr nicht nach. Die nächste interaktive Nutzung dieser Anwendung erstellt die Datei von Grund auf neu. Abwesenheit ist also selten Abwesenheit. Was Sie finden, ist eine Datei, deren Erstellungszeiten in $STANDARD_INFORMATION und $FILE_NAME in der MFT verdächtig jung sind und die eine Handvoll Einträge enthält, deren Zugriffszeitstempel sich alle in einem engen Fenster nach dieser Erstellungszeit ballen. Eine Notepad-Jump-List, die behauptet, die gesamte Historie des Benutzers mit Notepad habe letzten Donnerstagnachmittag begonnen, auf einer Maschine, die vor zwei Jahren bereitgestellt wurde, sagt Ihnen, wann die alte starb, nicht wann der Benutzer anfing, Notepad zu nutzen.

Der Einstellungsschalter ist heimtückischer, weil er kürzt statt zu zerstören, und die Datei behält ihre ursprüngliche Erstellungszeit. Das Anzeichen dort ist eine kurze und flache DestList: wenige Einträge, alle jung geballt, ein Eintrags-ID-Zähler, der die Geschichte zurücksetzt. Gleichen Sie den Fußabdruck des Schalters selbst in der Registry ab (HKCU\...\Explorer\Advanced\Start_TrackDocs), und der Registry-Parser datiert die Änderung für Sie über den letzten Schreibzeitpunkt des Schlüssels.

Das Löschen selbst wird an einem Ort aufgezeichnet, an den die Person fast nie zu bereinigen denkt. Schreibvorgänge in die Jump-List-Datei laufen durch das NTFS-Änderungsjournal. Ziehen Sie das USN-Journal, und Sie sehen die Grundkennzeichen DataOverwrite, DataExtend und häufig FileDelete/FileCreate gegen den Namen .automaticDestinations-ms, mit Zeitstempel. Die Jump List kann weg sein, und das Journal sagt trotzdem, wann sie ging.

Chirurgische Manipulation, und warum sie leicht zu erwischen ist

Großflächiges Löschen ist laut. Der leisere Schachzug besteht darin, die Verbunddatei zu öffnen und einen unbequemen Eintrag zu entfernen, während der Rest intakt bleibt. Hier zahlt sich die Kenntnis des Containerformats aus.

Die .automaticDestinations-ms ist eine OLE-Verbunddatei (CFBF, das alte Structured-Storage-Format). Sie hat eine FAT, ein Verzeichnis benannter Streams und Sektor-Slack wie jede Allokationsstruktur. Zwei Wege, auf denen die Operation schiefgeht:

Ein selbstgebauter Editor entfernt den nummerierten LNK-Stream, rührt aber die DestList nicht an. Jetzt haben Sie eine DestList-Zeile, deren Eintrag auf eine Stream-Nummer verweist, die im Verzeichnis nicht vorhanden ist. Ein ins Leere zeigender Verweis. Parsen Sie die Streams, parsen Sie die DestList, vergleichen Sie die beiden Mengen. Jeder DestList-Eintrag sollte sich zu einem Stream auflösen lassen; jeder Stream (außer DestList) sollte von einem Eintrag beansprucht werden. Alles, was auf einer der beiden Seiten verwaist ist, ist eine strukturelle Inkonsistenz, die gesunde, von der Shell gepflegte Dateien nicht erzeugen.

Der andere Fehlermodus ist das direkte Bearbeiten der DestList-Bytes. Jeder Eintrag trägt einen Integritätswert, und von Hand bearbeitete Zeilen aus Skripten, die ihn nicht neu berechnen, stimmen nicht überein. Windows ist in der Praxis nachlässig bei dessen Validierung, daher lädt die gefälschte Datei trotzdem, was für den Angreifer der ganze Sinn ist, aber die falsche Prüfsumme überlebt als Anzeichen für Sie. Behandeln Sie eine Prüfsummen-Diskrepanz als verdächtig, nicht als schlüssig, denn auch Parser-Fehler und Versionsabweichungen erzeugen sie.

Und dann gibt es die Wiederherstellung. Weil es eine Verbunddatei mit einer FAT ist, gehen die Sektoren eines entfernten Streams in den nicht zugewiesenen Bereich, nicht auf null. Sie bleiben dort, bis sie neu zugewiesen werden. Das Carving der freien Sektorkette des CFBF kann einen verworfenen LNK-Stream zurückholen, oder genug davon, um den Zielpfad und die eingebettete Volume-GUID wiederherzustellen. Es ist nicht garantiert, die Sektoren werden wiederverwendet, aber ich habe einen vollständigen UNC-Pfad aus dem Slack einer Verbunddatei geholt, bei einer Datei, die der Benutzer für bereinigt hielt.

Die Schattenkopie gewinnt fast immer

All das oben ist interessant, und meistens brauchen Sie es nicht, weil Volume Shadow Copy die Jump List leise gesichert hat, bevor irgendjemand sie anrührte. Mounten Sie die Schattenkopien, ziehen Sie dieselbe .automaticDestinations-ms aus jeder und vergleichen Sie die DestLists zwischen den Snapshots. Der Eintrag, der im Dienstags-Snapshot existiert und im Freitags-Snapshot fehlt, sagt Ihnen sowohl, was entfernt wurde, als auch das Zeitfenster, in dem es geschah. Selektives Löschen auf dem aktiven Volume wird vollständig durch eine Kopie zunichtegemacht, von deren Existenz der Benutzer nichts wusste und die er nicht erreichen kann.

Das ist das Erste, was zu prüfen ist, nicht das Letzte. Ich beginne mit den Interna der Verbunddatei, weil Sie sie verstehen sollten, aber operativ ist die Reihenfolge: Schattenkopien, dann USN-Journal, dann Carving, dann Analyse dessen, was überlebt hat, auf Byte-Ebene.

Eine Zeitleiste, die Sie verteidigen können

Eine Rekonstruktion ist nur etwas wert, wenn sie hält, also zwei Gewohnheiten.

Fixieren Sie Ihre Uhr. Der DestList-FILETIME ist UTC und die LNK-Header-Zeiten sind UTC, aber das System, das Sie rekonstruieren, hat seine Uhr vielleicht verstellt: NTP-Korrekturen, eine manuelle Änderung, eine auf einen älteren Snapshot zurückgesetzte virtuelle Maschine. Wenn sich Eintrags-ID-Reihenfolge und Zeitstempel-Reihenfolge widersprechen, ist eine Uhrverschiebung eine häufigere und ehrlichere Erklärung als Fälschung, und Sie prüfen sie, bevor Sie eine Manipulation behaupten. Suchen Sie die Zeitänderungsereignisse in System.evtx (Ereignis-ID 4616 in Security für "Die Systemzeit wurde geändert") über den EVTX-Parser und gleichen Sie ab.

Verlassen Sie sich nicht auf ein einziges Artefakt. Eine Jump List stellt einem Benutzer zu einem Zeitpunkt eine Datei vor Augen. UserAssist bestätigt, dass die Anwendung tatsächlich in einer GUI-Sitzung gestartet wurde. Prefetch bestätigt, dass der Prozess ausgeführt wurde. Das USN-Journal bestätigt, dass die Zieldatei existierte und berührt wurde. SRUM bestätigt, dass die Anwendung lief und Bytes verschob. Die Jump List ist ein starker Faden, und sie ist ein Faden. Die Fälle, die im Kreuzverhör auseinanderfallen, sind die, die auf einem einzigen wörtlich gelesenen Artefakt aufgebaut sind.

Für eine einmalige Sichtung, ohne die Datei vom Arbeitsplatz zu bewegen, legen Sie sie auf der Startseite ab, und sie dekodiert die Streams und die DestList clientseitig, nichts wird hochgeladen. Wenn Sie Stapelausgabe und Zeitleisten-Ereignisse brauchen, sind JLECmd und Plaso nach wie vor die Werkzeuge. Der Werkzeugvergleich gibt den Überblick.

Weiterführende Literatur

  • Yogesh Khatri und Mike Stevens, "Jumping into the Past", das Reverse Engineering der DestList, auf dem all das beruht.
  • Eric Zimmerman, JLECmd und JumpListExplorer.
  • Joachim Metz, libolecf, Referenz für den OLE-Verbunddatei-Container, wenn Sie von Hand carven.
  • MITRE ATT&CK T1070 Indicator Removal, um geleerte Jump Lists im größeren Bild einzuordnen.