Restart Area und LSN im $LogFile erklärt
Ein Blick in die Restart-Seiten des NTFS-$LogFile: Felder der Restart Area, Client-Record von NTFS, Checkpoints – und wie aus einer LSN ein Datei-Offset wird.
TL;DR. Die ersten beiden Seiten des $LogFile sind RSTR-Restart-Seiten. Jede enthält eine Restart Area: die aktuelle LSN, die Loggröße, die Anzahl der Sequenznummer-Bits, Flags und einen Client-Record für NTFS, der auf den jüngsten Checkpoint zeigt. Eine LSN ist eine 64-Bit-Zahl, deren niederwertige Bits den Offset des Eintrags in 8-Byte-Einheiten und deren höherwertige Bits einen Umlaufzähler enthalten; mit seq_bits aus der Restart Area gilt offset = (LSN << seq_bits) >> (seq_bits − 3). LSNs wachsen nur, sie ordnen also alles im Log – und dieselben LSNs stehen auch in den Record-Headern der $MFT.
Wenn Sie jemals erklären müssen, warum ein Parser „LSN 115720 → 117880“ anzeigt, oder einen Logeintrag von Hand im Hex-Editor prüfen wollen, ist das der Teil des Formats, den Sie brauchen. Den Aufbau auf Seitenebene beschreibt $LogFile-Format 1.1 vs. 2.0: was sich mit Windows 8 ändert.
Der Header der Restart-Seite
Zwei Kopien: bei Offset 0 und eine Systemseite später (meist 0x1000). Sie sind nicht zwingend synchron (dfir.ru); ein Parser prüft beide und behält die mit der höheren aktuellen LSN. Offsets gemäß libfsntfs und ntfs3:
| Offset | Größe | Feld |
|---|---|---|
0x00 | 4 | Signatur: RSTR, oder CHKD, wenn chkdsk das Log verändert hat |
0x04 | 2 | Offset des Update Sequence Array |
0x06 | 2 | Anzahl der Update-Sequence-Array-Einträge |
0x08 | 8 | Letzte LSN von chkdsk |
0x10 | 4 | Systemseitengröße |
0x14 | 4 | Log-Seitengröße |
0x18 | 2 | Offset der Restart Area |
0x1A | 2 | Minor-Version |
0x1C | 2 | Major-Version (in der Praxis 1 oder 2) |
Wenden Sie die Update-Sequence-Fix-ups an, bevor Sie irgendetwas jenseits des ersten Sektors lesen.
Die Restart Area
An dem in 0x18 angegebenen Offset:
| Offset | Größe | Feld | Warum es wichtig ist |
|---|---|---|---|
0x00 | 8 | Aktuelle LSN | Neueste LSN zum Zeitpunkt, als diese Restart Area geschrieben wurde |
0x08 | 2 | Log-Clients | Anzahl der Client-Slots (normalerweise einer: NTFS) |
0x0A / 0x0C | 2 + 2 | Liste freier / belegter Clients | 0xFFFF = leer |
0x0E | 2 | Flags | 0x1 Single-Page-I/O (ntfs3); der Browser-Parser liest 0x2 als „sauber ausgehängt“ |
0x10 | 4 | Sequenznummer-Bits | Nötig für LSN ↔ Offset |
0x14 | 2 | Länge der Restart Area | |
0x16 | 2 | Offset des Client-Arrays | Relativ zur Restart Area |
0x18 | 8 | Größe der Logdatei | Mit Ihrer Kopie vergleichen: kürzer heißt abgeschnitten |
0x20 | 4 | Datenlänge der letzten LSN | |
0x24 | 2 | Länge des Eintrags-Headers | |
0x26 | 2 | Daten-Offset der Log-Seite | Wo die Einträge in jeder RCRD-Seite beginnen |
0x28 | 4 | Open Log Count |
Der Client-Record von NTFS
Jeder Client-Slot ist 0xA0 Bytes groß; NTFS ist normalerweise der einzige Client:
| Offset | Feld | Bedeutung |
|---|---|---|
0x00 | Oldest LSN | Ältester Eintrag, den die Wiederherstellung noch brauchen könnte |
0x08 | Client Restart LSN | LSN des jüngsten Checkpoint-Eintrags |
0x1C | Namenslänge (Bytes) | |
0x20 | Name | UTF-16: NTFS |
Checkpoints: Eintragstyp 2
Die Client Restart LSN zeigt auf einen Logeintrag vom Typ 2 (Client-Restart). Für NTFS listet seine Nutzlast (NTFS_RESTART in ntfs3) die LSNs und Längen der Tabellen-Dumps auf, die bei diesem Checkpoint geschrieben wurden:
| Offset | Feld |
|---|---|
0x00 / 0x04 | Client-Major- / -Minor-Version (0.0 oder 1.0) |
0x08 | LSN des Checkpoint-Beginns |
0x10 | LSN des Dumps der Open Attribute Table |
0x18 | LSN des Dumps der Attributnamen |
0x20 | LSN des Dumps der Dirty Page Table |
0x28 | LSN des Dumps der Transaktionstabelle |
0x30–0x3C | Längen der vier Dumps |
Hier setzt die Wiederherstellung an: Sie lädt die Open Attribute Table und die übrigen Tabellen neu und liest dann vorwärts. Für die Analyse erklärt der Checkpoint, warum regelmäßig OpenAttributeTableDump- und AttributeNamesDump-Einträge auftauchen und warum ein Parser sie braucht, um das Zielattribut eines Eintrags zu benennen.
Anatomie einer LSN
Eine LSN hat 64 Bit, zweigeteilt:
63 (64 - seq_bits) 0
+-------------------------+-----------------------------------+
| sequence number | offset in 8-byte units |
+-------------------------+-----------------------------------+
Die Sequenznummer wächst bei jedem Umlauf des Logs; dadurch bleiben LSNs streng monoton steigend, obwohl sich die Offsets wiederholen. Wo geteilt wird, hängt von der Loggröße ab. ntfs3 leitet das als file_data_bits = log2(log_size) − 3 und seq_bits = 64 − file_data_bits her:
| Loggröße | file_data_bits | seq_bits |
|---|---|---|
| 256 KiB (das synthetische Sample dieser Website) | 15 | 49 |
| 64 MiB (eine übliche Größe) | 23 | 41 |
Lesen Sie seq_bits immer aus der Restart Area, statt es zu berechnen.
Von der LSN zum Datei-Offset
offset = (LSN << seq_bits) >> (seq_bits - 3)
Die Linksverschiebung entfernt die Sequenznummer; die Rechtsverschiebung um seq_bits − 3 holt das Offset-Feld zurück und multipliziert es mit 8. dfir.ru rechnet ein Beispiel vor: Bei 44 Sequenzbits hat LSN 2124332 die Sequenznummer 2 und einen Offset von 27180 Acht-Byte-Einheiten, also Byte 217440.
Ein robuster Parser kann das auch umgekehrt nutzen. Statt Seitenketten abzulaufen, durchsucht er jede RCRD-Seite nach 8-Byte-ausgerichteten Headern, deren LSN exakt auf ihren eigenen Offset abgebildet wird. Diese Prüfung verwirft zufällige Bytes und funktioniert auch dann noch, wenn dazwischenliegende Seiten beschädigt sind – so findet der Browser-Parser für das $LogFile die Einträge. Einträge, die nicht in den Rest einer Seite passen, setzen sich hinter dem Header der nächsten Seite fort (und laufen vom Dateiende an den Anfang des zirkulären Bereichs um); ist die Folgeseite älter als der Eintrag, wurde der Rest überschrieben, und der Eintrag wird als unvollständig markiert.
LSNs außerhalb des $LogFile
- Jeder FILE-Record in der
$MFTspeichert bei Header-Offset0x08die LSN der letzten protokollierten Änderung an diesem Record – eines der Bindeglieder in David Cowens NTFS TriForce. Liegt diese LSN im aktuellen Bereich des Logs, steht die Änderung, die den aktuellen FILE-Record hervorgebracht hat, noch im Log. - Auch Indexpuffer (
INDX) tragen eine LSN in ihrem Header.
Ein Vergleich der LSN eines $MFT-Records mit der niedrigsten LSN des Logs zeigt sofort, ob seine letzte Änderung noch auffindbar ist.
Was die Zahlen in der Praxis verraten
| Beobachtung | Bedeutung |
|---|---|
| Niedrigste LSN ≈ höchste LSN | Sehr wenig Historie (Log kürzlich in der Größe geändert oder starke Aktivität) |
Restart-Seite ist CHKD | chkdsk hat an diesem Log gearbeitet |
| Aktuelle LSN in der Restart Area > höchste gefundene Eintrags-LSN | Neueste Seite fehlt: Tail- / Fast Pages prüfen, oder die Kopie ist veraltet |
| Loggröße in der Restart Area > Dateigröße | Abgeschnittene Kopie |
| „Sauber ausgehängt“ bei einer angeblichen Live-Sicherung | Sicherungsprotokoll prüfen: Die Datei stammt womöglich aus einem Image oder Snapshot |
Häufig gestellte Fragen
Was ist eine LSN in NTFS?
Eine Log Sequence Number ist eine 64-Bit-Kennung, die jeder Logeintrag im $LogFile erhält. Ihre niederwertigen Bits codieren die Position des Eintrags in der Datei in 8-Byte-Einheiten, ihre höherwertigen Bits eine Sequenznummer, die bei jedem Umlauf des Logs wächst. LSNs steigen daher immer an.
Wie rechne ich eine LSN aus dem $LogFile in einen Datei-Offset um?
Lesen Sie die Anzahl der Sequenznummer-Bits aus der Restart Area und berechnen Sie (LSN << seq_bits) >> (seq_bits - 3). Das behält die unteren 64 - seq_bits Bits und multipliziert sie mit 8 – das ergibt den Byte-Offset des Eintrags-Headers in der Datei.
Weiterführende Literatur
- Linux-Kernel,
fs/ntfs3/fslog.c–RESTART_HDR,RESTART_AREA,CLIENT_REC,NTFS_RESTART,lsn_to_vbo. - Maxim Suhanov, How the $LogFile works?