Skip to content

QuarantineEventsV2: den macOS-Downloadverlauf auswerten

Was die macOS-Datenbank QuarantineEventsV2 speichert: LSQuarantineEvent-Schema, Mac Absolute Time, Typnummern, Verknüpfung per xattr-UUID und Grenzen.

Veröffentlicht am 7 Min. Lesezeit

TL;DR. QuarantineEventsV2 ist eine SQLite-Datenbank pro Benutzer in ~/Library/Preferences/, in der macOS Downloads von Apps festhält, die an der Quarantäne teilnehmen, etwa Safari, Chrome, Mail, Nachrichten und AirDrop. Ihre einzige Tabelle, LSQuarantineEvent, verrät wann (in Mac Absolute Time), welche App, von welcher URL und liefert eine Ereignis-UUID, die auch am Ende des Attributs com.apple.quarantine der heruntergeladenen Datei steht. Den Dateinamen speichert sie nicht, und was curl, wget oder git geholt haben, sieht sie nie.

Was QuarantineEventsV2 ist

Wenn eine App, die an der Quarantäne teilnimmt, etwas aus dem Netz speichert, versieht macOS die Datei mit dem Quarantäne-Attribut und schreibt bei vielen dieser Downloads ein Ereignis in die Datenbank QuarantineEventsV2 des Benutzers. Die Datei liegt hier, eine pro Benutzerkonto:

~/Library/Preferences/com.apple.LaunchServices.QuarantineEventsV2

Zeilen überleben in der Regel das Löschen der heruntergeladenen Datei, und eine automatische Bereinigung ist nicht dokumentiert. Die Datenbank beschreibt daher oft Downloads, deren Dateien längst verschwunden sind; genau das macht sie in der Praxis zu einem Downloadverlauf. Zeilen lassen sich trotzdem mit sqlite3 löschen, und die ganze Datei kann entfernt werden.

Das Schema von LSQuarantineEvent

Dies ist das Schema, das auf einem System mit macOS 26.6 beobachtet wurde. Apple dokumentiert die Spalten nicht; die Beschreibungen unten stützen sich auf die Spaltennamen und die beobachteten Daten.

SpalteTypInhalt
LSQuarantineEventIdentifierTEXT, Primärschlüssel, nicht nullEreignis-UUID; zugleich letztes Feld des Quarantäne-Attributs der Datei
LSQuarantineTimeStampREALZeitpunkt des Ereignisses in Mac Absolute Time
LSQuarantineAgentBundleIdentifierTEXTBundle-Identifier der herunterladenden App
LSQuarantineAgentNameTEXTName der herunterladenden App
LSQuarantineDataURLStringTEXTURL der heruntergeladenen Daten
LSQuarantineSenderNameTEXTName des Absenders, wenn das Objekt von einer Person stammt
LSQuarantineSenderAddressTEXTAdresse des Absenders, im selben Fall
LSQuarantineTypeNumberINTEGERTypnummer des Ereignisses (siehe unten)
LSQuarantineOriginTitleTEXTTitel der Ursprungsseite
LSQuarantineOriginURLStringTEXTSeite oder Ort, von dem der Download ausging
LSQuarantineOriginAliasBLOBBinäre Alias-Daten

Es gibt zwei Indizes: LSQuarantineEventIndex auf dem Identifier und LSQuarantineTimeStampIndex auf dem Zeitstempel. Auf jenem System nutzte die Datenbank den Rollback-Journal-Modus (journal_mode=delete) mit einer Seitengröße von 4096 Byte. Rechnen Sie also eher mit einer -journal- als mit einer -wal-Datei, sichern Sie aber beide.

Zeitstempel: Mac Absolute Time

LSQuarantineTimeStamp zählt die Sekunden seit 2001-01-01 00:00:00 UTC, also Mac Absolute Time. Addieren Sie 978307200, um einen Unix-Zeitstempel zu erhalten. Die Spalte ist als REAL deklariert, doch SQLite kann einen REAL-Wert, der eine ganze Zahl ist, auf der Festplatte als Integer speichern; das ist eine dokumentierte Speicheroptimierung. Ein Parser, der nur 8-Byte-Gleitkommazahlen akzeptiert, liest solche Zeilen falsch oder überspringt sie; er muss beide Kodierungen verstehen.

LSQuarantineTypeNumber

Apples öffentlicher Header LSQuarantine.h führt sechs Typkonstanten in dieser Reihenfolge auf: WebDownload, OtherDownload, EmailAttachment, InstantMessageAttachment, CalendarEventAttachment, OtherAttachment. Sie in dieser Reihenfolge den Werten 0 bis 5 zuzuordnen, ist die gängige, in Quellen berichtete Deutung; von Apple dokumentiert ist sie nicht.

WertBerichtete BedeutungBeobachtet unter macOS 26.6
0WebDownloadDownloads über Chrome und Homebrew Cask
1OtherDownload
2EmailAttachment
3InstantMessageAttachmentNachrichten
4CalendarEventAttachment
5OtherAttachment
6Nicht dokumentiertsharingd (AirDrop)
7Nicht dokumentiertEine Safari-Zeile

Behandeln Sie die Nummer als Hinweis und verlassen Sie sich auf die Agent-Spalten, um zu sehen, welche App beteiligt war.

Beobachtungen auf einem echten System mit macOS 26.6

Die folgenden Punkte stammen von einem einzigen Testsystem. Es sind Beobachtungen, keine Regeln, aber sie erklären viele verwirrende Ergebnisse:

  • Viele Zeilen, vor allem von Chrome und Nachrichten, hatten überhaupt keine URL-Spalte gefüllt.
  • Safari-Downloads trugen eine UUID in ihrem Quarantäne-Attribut, hatten in den letzten Monaten aber keine passende Datenbankzeile.
  • Manche Attributwerte lauteten 0081;00000000;;: keine Zeit, kein Agent, keine UUID.
  • Im Attribut werden Leerzeichen in Agent-Namen als \x20 maskiert, etwa Microsoft\x20Word.
  • Einige UUIDs waren kleingeschrieben.

Die Lehre daraus: Eine UUID ohne Treffer oder eine leere URL ist häufig und für sich genommen nicht verdächtig.

Was eine Zeile beweist und was nicht

Eine aktive Zeile zeigt, dass eine an der Quarantäne teilnehmende App für dieses Benutzerkonto zu diesem Zeitpunkt ein Download- oder Anhangsereignis mit diesen URLs und diesem Agent registriert hat. Zusammen mit dem Attribut einer Datei verknüpft sie eine Datei auf dem Datenträger mit einer Quelle.

Sie zeigt nicht, dass die Datei noch existiert, dass sie geöffnet oder ausgeführt wurde oder wer an der Tastatur saß: Eine gestohlene Sitzung erzeugt dieselben Zeilen wie der rechtmäßige Benutzer. Sie nennt die lokale Datei nicht. Und das Fehlen einer Zeile ist ein schwaches Indiz: Downloads mit Werkzeugen, die nicht teilnehmen (curl, wget, scp, rsync, git), tauchen nie auf, Zeilen lassen sich mit sqlite3 löschen, und die Datei selbst kann gelöscht werden.

Abfrage mit sqlite3

Arbeiten Sie mit einer Kopie, nie mit dem Original (schon das Öffnen kann eine Datenbank verändern; siehe gelöschte Einträge wiederherstellen). Diese Abfrage listet die Ereignisse chronologisch mit UTC-Datum auf:

SELECT datetime(LSQuarantineTimeStamp + 978307200, 'unixepoch') AS event_utc,
       LSQuarantineAgentBundleIdentifier, LSQuarantineDataURLString,
       LSQuarantineOriginURLString, LSQuarantineEventIdentifier
FROM LSQuarantineEvent ORDER BY LSQuarantineTimeStamp;

Ereignisse und Dateien über die xattr-UUID verknüpfen

Die Datenbank kennt URLs, aber keine Dateien; die Datei kennt ihr Ereignis, aber nicht immer ihre URL. Das Bindeglied ist die UUID. Ein Quarantäne-Attribut sieht so aus:

0083;66f7c2a1;Safari;6E4C2B1A-...

Das letzte Feld entspricht LSQuarantineEventIdentifier. Vergleichen Sie ohne Beachtung der Groß- und Kleinschreibung, da manche UUIDs kleingeschrieben gespeichert sind. Ein Ereignis kann zu mehreren Dateien gehören: Das Archivierungsprogramm überträgt den Quarantäne-Wert des Archivs samt UUID auf jede Datei, die es entpackt. Das Flag-Feld und die übrigen Teile des Werts behandelt com.apple.quarantine-Flags, das von Browsern gesetzte URL-Attribut kMDItemWhereFroms und Quarantäne.

Wie Quarantine Parser die Datenbank liest

Quarantine Parser läuft vollständig in Ihrem Browser (Rust, kompiliert zu WebAssembly in einem Web Worker); nichts wird hochgeladen. Er verwendet einen eigenen, rein lesenden SQLite-Reader statt einer SQLite-Bibliothek, und das zählt in dreierlei Hinsicht:

  • WAL. Eine -wal-Datei wird so angewendet, wie SQLite es tut: Header- und Frame-Prüfsummen sowie Salts werden geprüft, und nur festgeschriebene Frames werden verwendet. Eine begleitende -journal-Datei wird ebenfalls gelesen.
  • Unvollständige Kopien. Abgeschnittene, unvollständige oder gesperrte Kopien werden so weit wie möglich gelesen, statt beim ersten Fehler abzubrechen.
  • Wiederherstellung. Nicht mehr aktive Zeilen werden per Carving aus dem freien Speicher von Tabellenseiten, aus Freelist-Seiten, älteren Seitenkopien, überholten oder nicht festgeschriebenen WAL-Frames und Rollback-Journal-Seiten gewonnen. Siehe gelöschte QuarantineEventsV2-Einträge wiederherstellen.

Sie können die Datenbanken mehrerer Benutzer gleichzeitig ablegen, auch die lsquarantine_<user>-Kopien von Aftermath, zusammen mit xattr-Listings. Die Ansichten Zeitleiste, Agents, Domains und Dateien zeigen dann jedes Ereignis mit den Dateien, die seine UUID tragen, und die Ansicht Befunde listet Ansatzpunkte auf, keine Urteile.

Weiterlesen

Häufige Fragen

Wo liegt die Datenbank QuarantineEventsV2 unter macOS?

Jeder Benutzer hat eine unter ~/Library/Preferences/com.apple.LaunchServices.QuarantineEventsV2. Es ist eine SQLite-Datenbank mit einer einzigen Tabelle, LSQuarantineEvent. Kopieren Sie sie zusammen mit einer daneben liegenden -wal- oder -journal-Datei.

Speichert QuarantineEventsV2 den Namen der heruntergeladenen Datei?

Nein. Sie speichert URLs, die herunterladende App, einen Zeitstempel und eine Ereignis-UUID, aber weder den lokalen Pfad noch den Dateinamen. Um ein Ereignis einer Datei zuzuordnen, gleichen Sie die UUID am Ende des Attributs com.apple.quarantine der Datei ab.

Wie wandle ich LSQuarantineTimeStamp in ein Datum um?

Es handelt sich um Mac Absolute Time: Sekunden seit 2001-01-01 00:00:00 UTC. Addieren Sie 978307200, um Unix-Zeit zu erhalten, zum Beispiel mit datetime(LSQuarantineTimeStamp + 978307200, 'unixepoch') in sqlite3.

Bedeutet eine leere QuarantineEventsV2, dass nichts heruntergeladen wurde?

Nein. Werkzeuge wie curl, wget, scp, rsync und git nehmen nicht an der Quarantäne teil, Zeilen lassen sich mit sqlite3 löschen und die ganze Datei kann entfernt werden. Fehlende Einträge sind ein schwaches Indiz.

Verwandte Artikel