ntfs-forensic
Hand it a raw MFT record, get back severity-graded NTFS anomalies — timestomping, alternate data streams, deleted records, and record slack as forensicnomicon::report::Findings.
[]
= "0.5" # pulls in ntfs-core
use audit_record;
use Source;
let src = Source ;
for anomaly in audit_record
audit_record parses the record header and attributes, extracts $STANDARD_INFORMATION/$FILE_NAME, and grades what it finds. A record whose header does not parse yields no anomalies (structural corruption is surfaced by the reader/carver, never a panic). Already have parsed components? Skip the re-parse and call audit_components(record_number, header, record, attributes, si, fname).
The anomaly codes
Each anomaly is an observation ("consistent with …"); the examiner draws the conclusions. Codes are a stable, published contract.
| Code | Severity | What it observes |
|---|---|---|
NTFS-TIMESTOMP |
High | $STANDARD_INFORMATION times show forgery tells vs. the harder-to-forge $FILE_NAME times ($SI predates $FN, or lands on a whole second) |
NTFS-ADS |
Low | A named $DATA attribute — an alternate data stream (also used benignly, e.g. Zone.Identifier) |
NTFS-SLACK-RESIDUE |
Low | Non-zero residue in an MFT record's slack, past its used size |
NTFS-DELETED-RECORD |
Info | An MFT record not in use — a recoverable deleted file |
NTFS-MFTMIRR-MISMATCH |
High | A system record in $MFT differs from its $MFTMirr copy |
NTFS-LOGFILE-CLEARED |
Medium | $LogFile shows restart-area gaps consistent with the journal having been cleared |
The first four come from audit_record / audit_components. The volume-level pair come from audit_mft_mirror($MFT, $MFTMirr) and audit_logfile($LogFile), returning ArtifactAnomalys that also convert via to_finding(source).
The building blocks
audit_record is composed from pure, side-effect-free primitives you can call directly:
detect_timestomp(si, file_name)→TimestompIndicators { si_created_before_fn, created_mismatch, si_whole_second }alternate_data_streams(attributes)→ the named$DATAattributesrecord_slack(record, header)→ the bytes past the record's used sizeis_deleted(header)→ record not currently allocatedcarve_file_records(mft, record_size)→ offsets ofFILE/BAADrecords in a raw$MFTregion
$UsnJrnl:$J change-journal analysis
Beyond MFT-record anomalies, this crate analyses the USN change journal (decoded by ntfs-core):
rules— a configurable rule engine (RuleSet/Rule, glob + regex filename and reason-flag matching) whose hits convert to gradedreport::Findings viaRuleMatch::to_finding.analysis— pattern detectors for secure deletion (SDelete / cipher), USN-journal clearing, ransomware, and timestomping.correlation— cross-references USN ↔$LogFile↔$MFTto surface ghost records, coverage gaps, entry reuse, and timestamp conflicts.triage— aTriageEnginewith 12 built-in investigative questions over reconstructed records.
The "reconstructed records" these analyse come from ntfs-core's RewindEngine, which rebuilds the full path of every journal event — even for deleted, MFT-reused files — via the Rewind algorithm pioneered by CyberCX (NTFS Usnjrnl Rewind · CyberCX-DFIR/usnjrnl_rewind).
These power the thin usnjrnl-forensic CLI, which adds output formats (JSON / CSV / SQLite / TLN / body) and live monitoring on top.
The two-crate split
This crate is the analyzer; the reader is ntfs-core ($MFT, attributes, indexes, data runs, LZNT1, the full $UsnJrnl:$J reader stack — streaming reader, carver, RewindEngine path reconstruction, MftData — and NtfsFs path navigation over any Read + Seek source). The split mirrors vmdk-core/vmdk-forensic. Together they back issen and usnjrnl-forensic.
Trust, but verify
Built for untrusted disk images from potentially compromised systems: #![forbid(unsafe_code)]; panic-free on crafted input (the workspace denies clippy::unwrap_used / expect_used in production code); ntfs-core is fuzzed with seven cargo-fuzz targets, cross-validated against The Sleuth Kit and the mft crate on real disk images, and held at 100% line coverage in CI.
Privacy Policy · Terms of Service · © 2026 Security Ronin Ltd