Jede NTFS-Datei hat ein $DATA-Attribut (Typ 0x80), das ihren Inhalt enthält. Für die meisten Dateien ist $DATA nicht-resident: Der Attribut-Header trägt eine Runliste, die auf Cluster-Runs anderswo auf dem Volume zeigt. Für kleine Dateien leben die Bytes inline im MFT-Datensatz selbst. Dieser zweite Fall ist eines der nützlichsten Artefakte in der NTFS-Forensik, und es ist der, den Analysten immer wieder vergessen.
Wie klein ist klein genug
Ein MFT-Datensatz ist 1.024 Bytes groß. Davon ziehst du den 56-Byte-Header ab, das Fixup-Array, $STANDARD_INFORMATION (etwa 72 Bytes), mindestens ein $FILE_NAME (variabel, etwa 90 Bytes für einen typischen Namen), Padding und den Attribut-End-Sentinel. Was übrig bleibt, ist das nutzbare Budget für residente $DATA und andere residente Attribute.
In der Praxis liegt die Schwelle bei etwa 700 Bytes Daten. Manche Referenzen sagen bis zu ~750 Bytes bei Dateien mit sehr kurzen Namen und ohne andere residente Attribute; manche gehen tiefer (~580 Bytes) bei Dateien mit längeren Namen oder zusätzlichen Attributen. Die Schwelle ist keine Konstante; sie hängt davon ab, was sonst noch im Datensatz ist.
Dateien, die passen und die ich ständig sehe:
- Kleine Textdateien: Notizen, To-do-Listen, Entwürfe.
- Kleine Konfigurationsdateien:
.ini,.cfg, kleine.json, kleine.xml. - PowerShell-Einzeiler und kurze Skripte.
- Kleine
.lnk-Verknüpfungsdateien (die meisten LNKs sind klein genug; siehe LNK-Forensik für das, was sie kodieren). - Registry-Export-Snippets und
.reg-Dateien. - Kleine
.bat- und.cmd-Dateien. - Zertifikatsdateien im PEM-Format, die kurz sind.
- Leere Dateien und Null-Byte-Platzhalter. Die "Daten" sind null Bytes, und
$DATAist trivial resident. - Viele
$INDEX_ROOT-Attribute für kleine Verzeichnisse.
Warum das zählt
Ein residentes $DATA-Attribut kann wiederhergestellt werden, ohne den Rest der Platte zu lesen. Wenn du eine Kopie von $MFT hast, hast du bereits:
- Den vollständigen Inhalt kleiner Textdateien.
- Viele
$INDEX_ROOT-Daten für Verzeichnisse (die Einträge des Verzeichnisses, inline aufgelistet). - Kurze Alternate Data Streams (benannte
$DATA-Attribute mit kleinem Inhalt). - Reparse Points und Symlink-Ziele (der Zielpfad lebt in
$REPARSE_POINT, das typischerweise auch resident ist).
Das ist eine überraschende Menge an Beweisen innerhalb eines einzelnen 200 MB bis 2 GB großen MFT-Extrakts. Für gelöschte kleine Dateien überleben die residenten Bytes auch, nachdem der Datenbereich überschrieben wurde, weil der Datenbereich überhaupt nie angefasst wurde; die Bytes lebten in der MFT.
Ich habe wiederhergestellt:
- Einen 320-Byte-PowerShell-Loader aus einem gelöschten Datensatz, dessen Datencluster nie existierten.
- Einen 480-Byte-
.lnk, der auf das Staging-Verzeichnis des Angreifers zeigt, drei Wochen vor der Akquise gelöscht. - Eine 700-Byte-
.configvon einem Dienst, der deinstalliert worden war. Das Verzeichnis war weg; der MFT-Datensatz saß auf Slot 412.000 unberührt.
Die MFT war der einzige Ort, an dem diese Dateien noch existierten.
Resident ist nicht stabil
Wenn eine residente Datei über den freien Platz des Datensatzes hinaus wächst, konvertiert NTFS sie in nicht-resident. Die Daten ziehen zu Clustern um und $DATA wird zu einer Runliste. Die Konvertierung wird in $LogFile protokolliert, sobald die Änderung passiert.
Das Gegenteil kann technisch passieren (eine Datei, die unter die Schwelle geschrumpft ist, könnte wieder resident werden), aber Windows tut das selten freiwillig. Sobald $DATA nicht-resident ist, bleibt es das tendenziell. Wenn du einen Datensatz mit residentem $DATA für eine Datei findest, von der du weißt, dass sie einmal größer war, ist das ungewöhnlich und untersuchenswert; es deutet entweder auf bewusste Manipulation oder einen ungewöhnlichen Codepfad hin, der die Datei an Ort und Stelle verkleinert hat.
Residente Daten beim Parsen erkennen
Jeder MFT-Parser exponiert ein "Resident"-Flag im $DATA-Attribut-Header. MFTECmds CSV-Ausgabe hat es als boolesche Spalte. mft_dumps JSON hat header.is_resident. libmft exponiert es am Attribut-Objekt. Sleuth Kits istat zeigt es als Teil der Attribut-Auflistung.
Der Browser-Parser auf dieser Seite filtert in zwei Klicks nach IN_USE=0 mit residentem $DATA. Dieser Filter ist der, den ich zuerst auf jeder extrahierten MFT ausführe, bei der die Wiederherstellung kleiner Dateien wichtig ist.
Wie die Bytes aussehen
Bei einem residenten $DATA folgt auf den Attribut-Header ein 16-Byte-Resident-Header (Inhaltsgröße, Inhaltsoffset, Indexed-Flag, Padding) und dann die rohen Bytes. Die Bytes sind genau das, was auf der Platte in der Datei stand: Text in der vom Datei verwendeten Kodierung, Binärinhalt in seiner nativen Form. Keine Transformation.
Für eine kleine UTF-8-Textdatei kannst du die Bytes herauskopieren und die Datei in jedem Editor lesen. Für Binärinhalte (eine kleine PE, eine kompilierte .pyc, ein serialisiertes Objekt) sind die Bytes ebenso brauchbar; behandle sie als eine aus dem Volume extrahierte Datei.
Die Grenzen
Die meisten Dateien sind nicht resident. Auf einer normalen Windows-Installation sind weit unter 5 % der Benutzerdateien resident. Der Rest ist alles Office, jede Browser-Cache-Datei, jedes Programm in \Program Files\, jeder Download. Residente Wiederherstellung ist eine hochwertige Nische, kein Default-Modus.
Die Schwelle ist nicht exakt. Geh nicht davon aus "Dateien unter 700 Bytes sind wiederherstellbar". Die tatsächliche Schwelle hängt von den anderen Attributen des Datensatzes ab. Teste auf der spezifischen MFT, die du analysierst.
Resident überlebt Kompression oder Verschlüsselung zu einer hinreichend großen Datei nicht. EFS-verschlüsselte Dateien verwenden $LOGGED_UTILITY_STREAM für die Verschlüsselungsmetadaten und $DATA ist der Chiffretext. Wenn der Chiffretext klein genug ist, um resident zu sein, hast du die verschlüsselten Bytes; du brauchst weiterhin den EFS-Schlüssel, um sie zu lesen.
Ein Workflow, der dies nutzt
Das nächste Mal, wenn du einen Fall der Wiederherstellung gelöschter Dateien hast und die Datei, die dich interessiert, klein ist:
- Erwirb
$MFTvom Host (oder einem VSS-Snapshot, falls du einen aktuellen hast). - Parse sie. Ziehe jeden Datensatz, bei dem
IN_USE=0und das primäre$DATAresident ist. - Filtere per
$FILE_NAME-Regex, falls du den Dateinamen kennst. Filtere per Eltern-Referenz, falls du das Verzeichnis kennst. - Inspiziere die residenten
$DATA-Bytes inline. Die Datei ist direkt da.
Fünf Minuten Arbeit für eine Wiederherstellung, die Signatur-Carving Stunden gekostet hätte (und wahrscheinlich für kleine fragmentierte Dateien sowieso gescheitert wäre).
Weiterführende Literatur
- Die Notizen zu residenten Attributen des linux-ntfs-Projekts. Klare Erklärung des Resident-Header-Layouts.
- Microsoft, NTFS-Allokationsalgorithmus. Offizielle Referenz dazu, wie die Resident-/Nicht-Resident-Entscheidung getroffen wird.
- Sleuth Kits
istatzum Inspizieren einzelner Datensätze und Sehen des Resident-Flags im Kontext.