REFS FORENSICS: FOREFST

Introduction π
ReFS (Resilient File System) is Microsoft’s modern file system. It appeared with Windows Server 2012, fourteen years ago. For a long time it stayed rare outside of Storage Spaces on servers but this is changing: it is the default for Dev Drive on Windows 11, increasingly common on Windows Server, and recent Insider builds can boot from it. Sooner or later, one of these volumes ends up in an investigation.
Structurally, it has nothing in common with NTFS: no Master File Table, no in-place updates, but a forest of B+-trees updated by copy-on-write, with metadata checksums, per-stream snapshots and a redo-only transaction log.
The forensic literature has not followed. The most complete public analysis of the format remains the work of Prade, GroΓ and Dewald on ReFS 3.4 (2019). It is a solid baseline, but it describes the Windows 10 1803 era, and Microsoft has changed the format several times since.
The tooling situation is similar. The open tools I know (libfsrefs, pyrefs, the Sleuth Kit extension from Prade et al., journal parsers like ARIN) each cover a slice: an early version, a single artifact, the format as it stood in 2019. The tools that are supposed to open a current volume are commercial and closed, which makes their output difficult to audit.
So I tried to close a part of that gap: I reverse engineered ReFS from version 3.4 to the current 3.14 and published everything. The result is forefst, an open-source forensic tool for ReFS volumes, and a byte-level reference of the on-disk format at xbpt.gitlab.io/forefst, with every structural claim verified against the decompiled refs.sys driver and a lab corpus of more than a hundred disk images.
This project started as my master’s thesis (Forensic analysis of the Resilient File System (ReFS) version 3.14, University of Mons, 2026) and it went a bit further than planned. Its goal was to make ReFS analysis possible, but above all defensible. It is now public, and this post presents the two sides of the work: the reference and the tools.
No Metadata File to Extract π
Point the usual forensic toolchain at a ReFS image and it returns nothing at all. The Sleuth Kit and Autopsy report an unsupported file system. MFTECmd, LogFileParser and the USN parsers have no input, since the files they consume do not exist on the volume. A few open projects read ReFS to some degree, but they provide file access rather than forensic analysis, and they target earlier versions of the format.
The reason is that NTFS forensics is organised around extracting metadata files and parsing them offline: $MFT, $UsnJrnl:$J, $LogFile, $SDS. ReFS has no equivalent. There is no fixed-location metadata file and no MFT-like table: metadata lives in B+-trees reached from the checkpoint, objects are identified by Object IDs rather than record numbers, and the layout of the rows is itself described by an on-disk Schema Table instead of by fixed structures.
The divergence runs through every layer an analyst relies on:
| NTFS | ReFS | |
|---|---|---|
| Volume bootstrap | BPB β $MFT β metadata files | VBR β SUPB β CHKP β 13 B+-tree roots |
| Address translation | one level (VCN β LCN) | two levels (VCN β VLCN β PLCN) |
| File identification | MFT record number, reusable | Object ID, monotonic, never reused |
| Metadata storage | MFT records, fixed 1 KiB | B+-tree rows, variable size |
| Update model | in-place write | copy-on-write |
| Log journal | redo + undo | redo-only |
| Resident file content | ~700 bytes | 128 KiB (β€ 3.10), 2 KiB (3.11+) |
| Snapshots | only at volume level (VSS) | per-stream $SNAPSHOT attribute |
| Change journal | USN_RECORD_V2, 64-bit file ID | USN_RECORD_V3, 128-bit |
The full comparison, difference by difference with its forensic consequence, is on the NTFS vs ReFS page.
Supporting ReFS therefore does not mean writing a parser, it means decoding and implementing the file system.
Every address on the volume is virtual, so nothing can be resolved until the Container Table is loaded, and the Container Table can only be reached by walking the whole chain of anchors first (Bootstrap Chain, Virtual Addressing). That chain has no partial mode: a break at any link produces no output rather than a degraded one. And every link is version-dependent, which is precisely what left the existing open tools behind (Version Evolution).
So the real barrier is not parsing difficulty. It is that no public byte-level map of the current format existed to implement from. Tooling can only follow documentation, and for recent ReFS the documentation stopped in 2019.
Forensic Soundness π
This is more than an inconvenience, and it was the actual subject of my thesis: the main goal was to contribute to the forensic soundness of ReFS analysis.
Forensic soundness means using transparent, documented and repeatable methods, so that the evidence keeps its integrity and provenance and can be explained, assessed and challenged afterwards. In practice, the examiner must be able to say where an artefact comes from, how it was parsed, which assumptions were made, and an independent party must be able to review the same material and reach the same result. It also implies, as Carrier puts it, understanding the underlying structures rather than running tools as black boxes β which is precisely why open, inspectable tools matter in file-system forensics.
For NTFS these conditions are met: the structural knowledge is mature and public, several independent open tools can cross-validate each other, and the procedures are established. For ReFS none of them were. The public structural knowledge was version-bound and incomplete, the open tools targeted a format from 2019, and the tools that are supposed to read a current volume cannot be inspected. An examiner facing a ReFS volume might possibly produce a result, but not defend how it was obtained.
Hence the shape of the project, and the reason it is not just a tool: without verified structural knowledge, reliable tools cannot be built; without reliable tools, forensic soundness cannot be established. The reference is what makes the tool explainable, the tool is what makes the reference usable, and both had to be released together, with the lab materials that allow the work itself to be reproduced, verified and continued.
The Reference π
That study of the format is the biggest part of the work: the ReFS on-disk format from version 3.4 to the current 3.14, published as a browsable reference at xbpt.gitlab.io/forefst and as plain Markdown in the repository.
The scope is the whole format, byte by byte: the bootstrap chain (VBR, superblock, checkpoint), the B+-tree page engine, the system tables that map every object to its bytes, the directory entries, every attribute from $STANDARD_INFORMATION to $SNAPSHOT, both journals, the security descriptors. In practice that means dozens of structure pages, the attribute set, the concept pages that give them a forensic reading (copy-on-write, what survives a delete / format / upgrade, version detection, …), worked examples with real tool output, and a glossary, because the vocabulary is entirely its own.
And it is not a compilation of existing knowledge: a large part of it did not exist in the literature. The main new elements, each with its dedicated page:
- The addressing chain, fully mapped. How ReFS locates data on disk: virtual cluster numbers resolved through the Container Table, in two hops instead of NTFS’s one. The container rows could only be partially decoded in 2019; every field now has a name β and the physical start cluster is not at the fixed offset prior work assumed, because the row grows from 160 to 224 bytes on 64 KiB-cluster or SHA-256 volumes, which shifts the field (Virtual Addressing, Container Table).
- Resident files. Prade et al. describe
$DATAas always non-resident, stored through extent pointers. In fact the driver stores small-file content inline in the B+-tree row, under a version-dependent threshold that dropped from 128 KiB to 2 KiB. This is the finding most likely to cause silent under-recovery in practice: a tool that assumes every file’s content lives in external clusters will simply not find small files, because the bytes are inside the metadata tree and not in any cluster it would carve (Resident Storage). - The page reference shrank. The structure that anchors every metadata page went from 104 bytes to 48, with a 72-byte variant when SHA-256 is in use. It is the single change most likely to break a 3.4-era parser (Page References).
- The checksum state. CRC64 is declared on newer volumes before it is actually enforced, and a SHA-256 option exists. Knowing which state a volume is in decides how a page is validated (Checksum Architecture).
- The indirect root list. A new checkpoint flag indicates whether the thirteen root tables are stored inline or reached through a separate root-list page. A parser that misses this flag reads invalid data where the root list should be (The Checkpoint).
- A native-format marker and a version echo. New checkpoint and boot-sector fields that an in-place upgrade can never set. They make it possible to classify a volume as original, upgraded or native (Version Detection, VBR).
- The transaction log grew. The redo-opcode set went from 29 operations to 44 between 3.4 and 3.14, and the new ones are decoded (MLog).
$STANDARD_INFORMATIONchanged. Eight new bytes in the one attribute every timestamp analysis depends on ($STANDARD_INFORMATION).- New system tables. A Candidate Table, a Heat Engine, a Session Activity Table that records mount sessions, and, on Insider builds, a TPM-bound attestation table (System Tables).
- Features that postdate the literature. Stream snapshots (point-in-time copies of a file’s content), WSL metadata, Dev Drive: each got its own page (Snapshots, WSL Metadata).
- Hard links exist, and they are a forensic gift. Public sources still describe ReFS as having no hard links, so each file having exactly one path. That is no longer true: native 3.14 volumes support them, and the format handles them in a way NTFS never did. NTFS keeps one
$SIshared by every name; ReFS stores an independent$SIset per name, and no hard-link count anywhere on disk, so the group has to be reconstructed from the identity fields. Two consequences: a file can carry several divergent timestamp sets, and hard-linking promotes a file out of resident storage (Hard Links). - Timestomping without
$FILE_NAME. The classic NTFS$SI-vs-$FNcheck is gone: ReFS keeps no$FILE_NAMEtimestamp copy at all. Other anchors replace it (a change time the common stomping APIs cannot set, the USN journal, the volume creation floor), and hard links additionally provide a check with no NTFS equivalent: since each name carries its own$SI, a back-dated name stands out against its own siblings (Timestomp Detection). - Deletion evidence that cannot be erased. Object IDs are monotonic and never reused, so a gap in the sequence is durable proof that an object existed and was deleted, even after a full overwrite. NTFS recycles its MFT record numbers and erases this evidence over time; ReFS cannot (Deletion & Recovery, Object IDs).
The 3.4-era ground got revisited on the way too: the checkpoint comparison that earlier work set aside is now implemented (with an honest negative result: after a clean unmount, both checkpoint copies decode to the same tree), and the attributes, barely sketched in the literature, each get a byte-level page. The complete change-by-change record is on the Version Evolution page. To my knowledge, this is the most complete public documentation of the current format.
Everything in it is verified the same way. Each structural statement carries a graded evidence level, and a byte-level claim is accepted only when two independent sources agree: the decompiled refs.sys driver and the lab corpus of more than a hundred disk images covering multiple production versions between 3.4 and 3.14. Static analysis shows what the driver expects, raw-disk analysis shows what is actually written, and requiring the two to agree discarded several plausible but version-specific hypotheses. The tools are regression-tested against that corpus, and they disclose their own uncertainty: forefst.py --provenance lists the emitted fields that are not fully certain, each with the condition that limits it and a citation into the reference. The method has its own page; when someone asks “how do you know this column is right?”, there is a written answer.
That evidence base also has edges, and they are worth stating. The corpus was produced under controlled laboratory conditions, so volumes with years of real activity, genuine crash recovery or petabyte-scale capacity may behave in ways it does not capture. Storage Spaces and tiered deployments β the production setting ReFS is most often paired with β were not exercised at all. Compression and deduplication were located in the driver and their supporting structures documented, but neither was exercised at scale: no compressed content was produced, and a single deduplication-enabled image is not a basis for dedup-aware content reconstruction. And ReFS keeps moving: the Insider build already shows a driver advertising a 3.15 format it does not yet write. None of this makes the documented layout wrong, it marks where it stops.
One honesty note, because I think it matters: the code was written with heavy LLM assistance. What makes the result trustworthy is not how it was generated but how it was verified, and that is exactly what the evidence register, the lab corpus and the regression harness are for.
The Tools π
All that knowledge would stay theoretical without something that applies it to a real image. There are two tools, for two different jobs.
forefst.py π
forefst is the forensic tool: point it at a raw image and it bootstraps the volume itself. Python 3.7+, standard library only, no pip install, read-only by design: clone and run, on Linux, macOS or Windows. But why forefst? Because forensics and refs. And because ReFS is all about trees. And also because, I think, the more forest, the better.
Where NTFS gives you one artefact per parser, here everything comes out of the same volume walk:
- Volume triage β
summary: version, cluster size, object counts, checksum state, and whether the volume is original, upgraded or native. - File listing β
files: every file and directory with 38 metadata columns, in CSV, JSON or body format. This is the ReFS answer to an$MFTdump. - Super-timeline β
timeline: ReFS scatters time across three independent artefacts, the$SIMACB times, the USN journal and the MLog transaction log, and this merges them into one chronological output with names and paths resolved from the volume. Because the three are independent, they also cross-validate each other (Artifact Timeline). - Journals β
usnreads the change journal, the counterpart of$UsnJrnl:$J, though the records are wider here.mlog --parsedoes what a$LogFileparser would do on NTFS, promoting redo records into user actions (CREATE, WRITE, RENAME, MOVE, DELETE). - Deleted files β
deletedlists them with a per-entry recoverability verdict,export deletedwrites back what can be recovered. Copy-on-write leaves far more behind than an in-place file system does. - Prior versions β
snapshots: stream snapshots, listed and extractable. These have nothing to do with VSS; they are a copy-on-write mechanism inside the file’s own attributes, so they are read from the volume rather than mounted. - Anti-forensics β
timestomp: the replacement for the$SI-versus-$FNcheck that ReFS makes impossible, combining change-time heuristics, USN corroboration and hard-link$SIdivergence, with a severity per finding. - Ownership and special files β
securityfor owners and ACLs, where NTFS would send you to$SDS, plus a tamper check on the security table;specials(ADS, reparse points, WSL metadata, hard links, sparse, EFS, compressed, integrity streams);recyclebinfor the$I/$Rpairs. - Content β
extractfor a single file,detailsfor the full attribute dump of one object. - Uncertainty disclosure β
--provenance: the emitted fields that are not fully certain, and why.
The outputs open directly in Timeline Explorer, and --body feeds mactime. A sample image is published with the repository, so the tool can be tried without a ReFS volume at hand:
python3 forefst.py sample.raw summary # volume overview
python3 forefst.py sample.raw -o files.csv # full file listing
python3 forefst.py sample.raw timeline --csv # super-timeline
summary is what you run first. It answers, in one screen, the questions that decide how everything else must be read β which format version, which checksum algorithm, and whether the volume was formatted natively or upgraded in place:
==============================================================================
ReFS Volume Summary
==============================================================================
Image: win11refs2tspecials.raw
ReFS version: 3.14
Volume GUID: 4d712af4-c2f8-4ec8-986d-ba05ef28f86b
Volume size: 2.0 TB
Cluster size: 0x1000 (4.0 KB)
Checksum: CRC64
[...]
------------------------------------------------------------------------------
File System Content
------------------------------------------------------------------------------
Objects: 230
Directories: 217
Files: 1333 (732 resident)
Oldest timestamp: 2021-05-18 07:16:04 UTC
Newest timestamp: 2026-05-16 16:41:35 UTC
Objects is not a file count: it is the number of rows in the Object ID Table, essentially the directories plus the system tables. Files do not get their own Object ID, which is why the number sits close to the directory count and far below the file count. The resident figure is worth a look too: on this volume more than half the files carry their content inside the metadata tree, where cluster-level carving would never find it.
files produces the equivalent of an $MFT dump, 38 columns per entry. A few of them, on a hard-linked file:
FileName hltest_file1.txt
IsResident False
Created 2026-05-16 16:39:02 UTC
Changed 2026-05-16 16:41:35 UTC
HardLinkCount 4
HardLinkNames hltest_dir1/link1_to_file1.txt;hltest_dir1/link3_to_file1.txt;
hltest_dir2/link2_to_file1.txt;hltest_file1.txt
HomeOid 0x600
Four names for one physical file, reconstructed from the identity fields. Each of those names carries its own $SI timestamps, which is what makes the cross-name comparison in timestomp possible.
The complete subcommand reference is on the forefst page; the source is a single file, forefst.py.
refsanalysis.py π
refsanalysis is the companion of the reference. Where forefst answers “what happened on this volume?”, refsanalysis answers “what does this structure actually contain?”: it decodes one on-disk structure at a time β boot sector, superblock, checkpoint, Object Table, schemas, containers, allocators β and prints the fields with their offsets and raw bytes.
It is the tool the reference was written with, and it is published so the reference can be checked and continued. Three uses in particular:
- Verify a documented claim against your own image, instead of taking a page at face value.
- Extend the documentation where a structure is only partially decoded, working from the same output the reference was built on.
- Study a newer ReFS version: decode the same structures on a recent build, compare with the documented baseline, and see exactly what Microsoft moved.
That last point is the answer to the problem raised earlier, that an implementation correct for one version goes silently wrong on the next. The lab materials β formatting procedures, workload scripts, analysis outputs β are released for the same reason (reference page).
More Information π
The project π
- forefst on GitHub β the tools, the docs, the lab materials
- The ReFS reference β the same documentation as a browsable site
- Methodology β how every fact was verified: the dual-evidence model, graded claims
- Sample disk images β the lab images (Git LFS), with what each one demonstrates
The literature π
- Prade, GroΓ, Dewald β Forensic Analysis of the Resilient File System (ReFS) Version 3.4 (technical report, 2019)
- Prade, GroΓ, Dewald β Forensic analysis of the Resilient File System (ReFS) Version 3.4 (DFRWS EU 2020)
- Forensic Analysis of ReFS Journaling (DFRWS APAC 2021)
Forensic soundness was the starting point of this project, and it is also the fairest way to judge it. What matters is not that a tool now opens a ReFS volume, but that an examiner can say where each field came from, point to the evidence behind it, and see what the tool itself is unsure about β and that anyone who disagrees can check the reference, re-run the analysis on their own image, and say so. That part matters to me more than the code.
If you detect an error, if you have something to add or comment, you can contact me (or open an issue on GitHub).