YYYY-MM-DD HH:MM:SS.XX):
--partition-start 0) whose bootstrap chain was
hand‑zeroed. forefst summary flags the tampering and bootstraps from the redundant checkpoint
anyway. mlog --parse replays every rename / move / delete with original names (from 128‑bit
object IDs) and timestamps; object‑ID gaps prove permanent directory deletion; recyclebin
and export recyclebin recover the deleted files. All answers are proven against the handout’s
own SHA‑256 checker.
The intended challenge is to notice a broken superblock/checkpoint and repair it by recomputing Logical
Cluster Numbers in a hex editor. v1.6 turns that into a one‑line diagnosis: summary validates the
whole bootstrap chain, reports the tampering, and still boots from the good checkpoint.
$ forefst.py ReAL_File_System.001 summary --partition-start 0
Bootstrap structures
VBR primary LBA 0 — checksum OK
SUPB primary LCN 0x1e — signature OK !! CORRUPT self-reference: stored 0x0, expected own LCN 0x1e
CHKP primary LCN 0x27d0 — signature OK, vclock=68 !! CORRUPT self-reference: stored 0x0, expected 0x27d0
SUPB backup LCN 0xf7ffd/0xf7ffe — signature OK !! CORRUPT self-reference: stored 0x0
CHKP secondary LCN 0x1dc48 — signature OK, vclock=67 !! CORRUPT self-reference: stored 0x0
** BOOTSTRAP TAMPERING: 5 structure(s) carry a CORRUPT self-reference
a zeroed self-LCN does not occur normally — treat it as deliberate tampering / hand-corrupted bootstrap.
ReFS version 3.4 Directories: 7 Files: 17 (bootstrapped from checkpoint vclock=68)
ReFS keeps a durable redo log. mlog --parse decodes it into concrete actions and resolves each
object’s birth name vs current name from its 128‑bit object ID — so a renamed directory
still shows the name it was created with.
$ forefst.py ReAL_File_System.001 mlog --parse --partition-start 0
[CREATE] OID 0x704 → de34f60ab 2024-02-18T07:36:59.227Z
[CREATE] /88077a4a1370 → e88e52cac 2024-02-18T07:37:31.132Z
[CREATE] /cad090f9724d → fb828d071 2024-02-18T07:38:13.882Z
[CREATE] OID 0x707 → f8f1c218f9 2024-02-18T07:38:40.586Z
[CREATE] /$RECYCLE.BIN/…/$RPNGMPK → c062fb828 2024-02-18T07:39:14.554Z
[CREATE] /bb6de6190 2024-02-18T07:39:34.008Z
[RENAME] / → e88e52cac (same parent 0x600) 2024-02-18T07:40:20.495Z
[MOVE ] / → c062fb828 (parent 0x600 → 0x703) 2024-02-18T07:39:14.554Z
[DELETE] / → f8f1c218f9 (object table destroyed) 2024-02-18T07:41:28.575Z
[RENAME] / → fb828d071 (same parent 0x600) 2024-02-18T07:42:12.481Z
[DELETE] / → de34f60ab (object table destroyed) 2024-02-18T07:43:19.604Z
[DELETE] / (object table destroyed) 2024-02-18T07:48:22.479Z # essay.txt
[DELETE] / (object table destroyed) 2024-02-18T07:49:26.698Z # ead47cb
That output answers three questions at once:
RENAMEs: e88e52cac→88077a4a1370 @ 13:10:20.49 and fb828d071→cad090f9724d @ 13:12:12.48.MOVE into the recycle SID dir (simple delete of c062fb828) and the two DELETE (object table destroyed) (permanent).CREATE timestamps (originals resolved).$ refsanalysis.py ReAL_File_System.001 objects --partition-start 0
0x701 0x702 0x703 [0x704 —] 0x705 0x706 [0x707 —] 0x708 0x709
0x704/0x707 are
permanent proof that de34f60ab and f8f1c218f9 once existed and were destroyed — it
survives even a complete overwrite. (Files carry no object ID, so this signal is directory/system‑object only.)
v1.6’s recyclebin decodes the non‑resident $I records directly — original
paths, sizes, deletion times — and export recyclebin writes each payload back under its
original name:
$ forefst.py ReAL_File_System.001 recyclebin --partition-start 0
$IPNGMPK Original path: I:\c062fb828 Deleted: 2024-02-18 07:40:48.62 (dir, simple delete)
$I0IY71M Original path: I:\19ff211f Deleted: 2024-02-18 07:44:31.43 Size: 20,000
$I9238F6 Original path: I:\simple-pass.txt Deleted: 2024-02-18 07:45:00.51 Size: 346
$I8ZS4M3 Original path: I:\fe0c329 Deleted: 2024-02-18 07:45:52.… Size: 16,823
$ forefst.py ReAL_File_System.001 export recyclebin out/ --partition-start 0 && md5sum out/*
4c009b045056af8f9bb401c69408d2cf 19ff211f
c50c5bcb9e98537e3d63df1bc68a81d0 fe0c329
f91488b7e00c31793bd7aa98c51896d0 simple-pass.txt
Those three MD5s are Q4. Q5 = the three simple deletions above (type Simple) plus the
two permanent file deletions from the MLog: essay.txt @ 13:18:22.47 and ead47cb @
13:19:26.69 (type Permanent).
$I file’s own $SI creation time
(the recycle‑write transaction), e.g. fe0c329 → 13:15:52.49 — not the
FILETIME embedded inside the $I payload (.47, ~11 ms earlier).
The handout ships its grading hashes, so every answer list is fed through the exact grader pipeline and compared — no guessing:
Q1: VERIFIED Q2: VERIFIED Q3: VERIFIED
Q4: VERIFIED Q5: VERIFIED Q6: VERIFIED
time_update.exe (MinGW C++) writes each *.tort as "TORT" + a
uint32 plaintext length + an XOR payload:
out[i] = pt[i] ^ key1[i % |key1|] ^ key2[i % |key2|]
key2 = md5(key1).hexdigest() # 32 ASCII hex chars
key1 = base30( rotl(FILETIME) … TEA-mix … ) + 4×nonce_chars # CryptGenRandom nonce
The FILETIME is the tampered system time set right before the file’s rename —
recoverable from the same MLog rename records shown above (milliseconds = 0). Brute the 4‑char nonce
(30⁴) against each file’s magic, strip the 8‑byte TORT header to the stored length, and hash.
Full crypto detail is in the bi0s
writeup; 7/8 files reproduce directly (PDF, GIF, WAV, JPEG, PNG, ZIP/OOXML, MP4) and the eight decrypted
MD5s satisfy the checker.
srand(time(0)) every operation, and its Sleep(1000) chains the
tampered times as T_write = msvcrtLCG(unix(T_rename) + 1). A key time overwritten in the log ring
is recovered by inverting that LCG from the surviving data‑write time.
$MFT; the tool walks the checkpoint
B+‑trees and even diagnoses a hand‑zeroed bootstrap instead of failing on it.mlog --parse gives typed, timestamped
CREATE / RENAME / MOVE / DELETE with original names resolved from object IDs.$I record’s $SI time, not the
FILETIME inside the $I payload.Sleep means the
falsified times chain — and chains can be walked backwards.