Skip to content

Reverse-replay / NTFS $LogFile

Recover the timestamps a file carried before it was stomped.

logflip walks the NTFS $LogFile undo chain backward to reconstruct pre-tamper state, then corroborates across three independent channels before it will ever call something tampering.

809 tests passing / mypy --strict / ruff clean / Python 3.11+

No public tool performs reverse-replay of NTFS $LogFile to reconstruct pre-tamper state and identify the tampering tool family.

the gap that is the entire reason this tool exists / NTFS Log Tracker, the closest prior art, reaches ~50% fidelity and does not perform the backward LSN walk.

The timestamps you read are whatever the attacker last wrote.

Timestomping writes attacker-controlled values into the $STANDARD_INFORMATION attribute of an MFT record. After the write, $SI reflects only what the attacker last set. The original timestamps are gone from the attribute.

The before-image survives in the $LogFile undo record. Every metadata change logged by NTFS leaves a redo and an undo entry in the circular $LogFile buffer. The undo entry carries the exact bytes that were in $SI before the stomp. logflip walks that chain backward to recover them.

$STANDARD_INFORMATION

attacker writes2009-01-01T00:00:00Z
original valueoverwritten, gone from $SI

$LogFile undo record (before-image)

recoverable2026-06-07T00:38:59.2703686Z

A verdict is earned by agreement, not asserted from one signal.

The image is parsed ($MFT + $LogFile + $UsnJrnl, with USA fixup and multi-page RCRD reassembly), then three independent channels must agree.

01

Reverse-replay inversion

Backward LSN walk through the $LogFile undo chain. Each undo entry carries the before-image bytes from $STANDARD_INFORMATION. Walking backward reconstructs pre-tamper $SI without guessing.

02

SI-vs-FN delta screen

$STANDARD_INFORMATION versus $FILE_NAME - an independent source. $FILE_NAME is updated only at create, rename, or reparent, so divergence flags a likely stomp. Necessary but not sufficient on its own.

03

database

Fingerprint-DB attribution

Byte-pattern match against a signed, HMAC-verified tool-family database. The third and gating channel. Without a signed database and a real engagement key, this gate does not unlock.

GUARD

Incomplete-inversion guard: circular-buffer rollover, cross-client chains, sequence non-monotonicity, and out-of-bounds undo all resolve to INCONCLUSIVE rather than a guessed result. The tool refuses to reconstruct what it cannot reconstruct soundly.

Confirmation has to be earned. By design, it is hard to reach.

A confirmed verdict requires agreement from distinct failure modes, so a single noisy channel can never carry a conviction on its own. The shipped fingerprint families are metadata-only (every pattern_hex is empty), so an empty-pattern guard makes a spurious byte match structurally impossible.

BY DESIGN

Resting state with the stub database or a demo key: provisional. That is the honest ceiling, not a placeholder pending one.
VerdictMeaning
cleanNo byte disagreement on the target record.
provisionalDisagreement and an SI-vs-FN delta surfaced, no family confirmed. The ceiling with a stub DB or demo key.
confirmedEvery gate passed against a signed DB and a non-demo key. The stub DB and demo key can never reach this.
anomalySingle-source lead only. Surfaced by scan as investigation, never as proof.

Watch it run, then read the evidence it produced.

A real recording of the CLI, paired with the actual signed HTML report. No simulation, no upload.

The run

The evidence it produced

LOGFLIP Analysis Report

target_mft_record24
Verdictprovisional

Best-effort recovered $SI (provisional)

recovered $SI2026-06-07T00:38:59.2703686Z
leaf MACHKDF-SHA256 / RFC 8785 (JCS) / HMAC-SHA256
view the full signed report

logflip runs locally. Your evidence never leaves your machine.

BY DESIGN

The recorded run uses a demo key, so its ceiling is provisional. confirmed needs a real signed database and a real engagement key.

What it catches, honestly.

Family identifiers trace to the signed DB manifest (tool_family value). One family is detected; one is documented as non-attributable. Both are shown honestly.

psstomp

psstomp - PowerShell SI-only stomp

Updates $STANDARD_INFORMATION and leaves $FILE_NAME untouched. Detected through the SI-vs-FN delta channel: the discrepancy between SI and the independent FN timestamp surfaces the stomp.

setmace

setmace - dismount raw-write

Writes raw sectors after dismounting the volume, bypassing the log entirely. Emits no $LogFile or $UsnJrnl evidence and equalizes SI and FN timestamps. Non-attributable through any of the three pipeline channels. Documented honestly, not hidden.

Honest scope and limits.

0.000dirty-shutdown FP rate

Measured over 42 real-Windows images. A controlled-corpus methodology demonstration, consistent with Daubert reliability requirements. Not a real-world population rate.

Per-family FP rate is a structural zero.

The empty-pattern matcher guard precludes any byte match. Every shipped family carries an empty pattern_hex, making a spurious match structurally impossible.

Coverage window.

The $LogFile / $UsnJrnl rollover blind window (roughly 4 to 12h on active volumes) is the primary limit. Events older than the live window yield INCONCLUSIVE.

The honest ceiling on real input.

Reverse-replay and the SI-vs-FN delta can surface a finding, but byte-pattern tool-family attribution is unavailable without a signed DB and real key. provisional is the honest ceiling; confirmed is never reached on real input. This is the correct never-false-confirm behavior.

A standards-grade CLI. Scriptable, signed, auditable.

python -m pip install -e ".[dev]"

python -m logflip detect --image disk.img \
  --mft-record 5 --output leaf.json --report report.html

detectscanverify-leafverify-dbbuild-dbkeygeningest-captures

Exit codes: 0 clean / 2 finding / 1 error. Keys are read from raw 32-byte files via --key-file, never the command line.

Evidence integrity

HKDF-SHA256 domain-separated keys, signatures over the RFC 8785 (JCS) canonical form, constant-time HMAC comparison. A single flipped byte fails verify-db; a tampered leaf fails verify-leaf.

Quality

809 tests passing, 18 skipped (need ntfsprogs or a live volume). mypy --strict clean. ruff clean. Python 3.11+. MIT licensed.