Risposta breve: usate analyzeMFT per il parsing puro Python quando potete installarlo, libmft quando volete un modello a oggetti tipizzato, oppure invocate omerbenamram/mft quando la velocità conta. Il parsing puro Python è ~10-50× più lento del crate Rust ma va bene per script occasionali.
Cosa state leggendo
La Master File Table NTFS è una sequenza di record a dimensione fissa di 1.024 byte. Per analizzarla da Python basta:
- Aprire il file
$MFT(o leggerlo da un'immagine disco). - Percorrerla a blocchi di 1.024 byte.
- Applicare il fixup array a ogni record. Vedi l'anatomia di un record per la disposizione a livello di byte.
- Percorrere il flusso di attributi all'interno di ogni record.
Le librerie qui sotto gestiscono tutti e quattro i passi. La maggior parte degli analisti ripiega sul struct.unpack grezzo solo quando una libreria non espone un campo di cui hanno bisogno.
Opzione 1: analyzeMFT
analyzeMFT è il classico parser MFT puro Python, originariamente di David Kovar e tuttora mantenuto. Pensato come CLI, ma importabile.
# pip install analyzeMFT
from analyzeMFT.mft_analyzer import MFTAnalyzer
analyzer = MFTAnalyzer(mft_file="path/to/$MFT", output_file="out.csv")
analyzer.analyze()
Il CSV che produce ha una riga per record con i timestamp sia da $STANDARD_INFORMATION sia da $FILE_NAME. Sufficiente per un triage guidato da foglio elettronico.
Da usare quando: file $MFT piccoli, script ad-hoc, dipendenze native non consentite.
Limiti: lento su input di diversi gigabyte (puro Python a thread singolo), e il modello a oggetti è orientato all'emissione di CSV piuttosto che a percorsi programmatici.
Opzione 2: libmft (modello a oggetti tipizzato)
Se volete interrogare i record come oggetti Python, libmft espone un modello tipizzato vicino alla struttura su disco.
# pip install libmft
from libmft.api import MFT
with open("path/to/$MFT", "rb") as f:
mft = MFT(f)
for entry in mft:
if not entry.is_deleted():
continue
name = entry.get_full_path()
si = entry.get_attributes(0x10)[0] # $STANDARD_INFORMATION
print(name, si.created, si.modified)
libmft risolve le reference parent in modo che possiate chiedere a ogni entrata il suo percorso completo senza scrivere la traversata. Gestisce anche in modo trasparente i record di estensione $ATTRIBUTE_LIST — cosa che il livello CSV di analyzeMFT vi nasconde.
Da usare quando: volete scrivere logica che percorre i record, filtra per attributo ed emette una forma personalizzata.
Opzione 3: invocare un parser Rust
Quando la $MFT è grande (1 GB+) o state elaborando molti dischi in batch, l'opzione pratica più rapida è invocare da Python un parser nativo e leggere il suo JSON.
import json
import subprocess
# omerbenamram/mft — `cargo install mft` o scaricate un binario release
proc = subprocess.run(
["mft_dump", "-o", "json", "path/to/$MFT"],
capture_output=True, check=True,
)
for line in proc.stdout.splitlines():
record = json.loads(line)
if record["header"]["flags"] & 0x1 == 0: # IN_USE a zero → eliminato
print(record["entry"], record["file_name"]["name"])
mft_dump emette JSON Lines — un record per riga — che si trasferisce in modo pulito in Python senza caricare l'intero output in memoria. Confrontato con analyzeMFT sullo stesso input, il parser Rust è tipicamente 10-50× più veloce e usa un decimo della memoria.
Da usare quando: pipeline di produzione, input grandi, o dovunque il tempo di parsing conti.
Leggere $MFT direttamente da un'immagine disco
Se avete un'immagine grezza .dd o .E01 invece di un $MFT estratto, usate pytsk3 (binding Python per The Sleuth Kit) per posizionarvi su $MFT del volume e farne lo streaming dei byte:
import pytsk3
img = pytsk3.Img_Info("disk.dd")
fs = pytsk3.FS_Info(img, offset=0) # usate l'offset della partizione NTFS
mft_file = fs.open_meta(inode=0) # $MFT è sempre inode 0
size = mft_file.info.meta.size
data = mft_file.read_random(0, size)
# data ora contiene $MFT; passatelo a libmft o scrivetelo su disco
È l'approccio più pulito quando il volume è cifrato a livello di partizione ma montato tramite un decifratore che vi dà un'immagine grezza.
Insidie comuni
- Dimenticare il fixup array. Leggere blocchi grezzi di 1.024 byte senza applicare l'USA vi dà spazzatura agli offset 510 e 1022 di ogni record. Tutte le librerie sopra lo fanno per voi — scrivete il vostro parser solo se comprendete il meccanismo di fixup (vedi l'anatomia di un record).
- Trattare il numero di record come identità. I numeri di record vengono riutilizzati. La file reference a 64 bit (numero di record più sequence number) è l'identificatore che non collide.
- Confondere i due set di timestamp. Ogni record contiene timestamp sia in
$STANDARD_INFORMATION(aggiornati frequentemente) sia in$FILE_NAME(per lo più stabili). Per la rilevazione di timestomping vi servono entrambi — vedi i quattro timestamp MFT.
Quando saltare del tutto Python
Per un'analisi interattiva occasionale senza alcuna installazione, trascinate la $MFT sul parser browser di questo sito. Esegue lo stesso crate omerbenamram/mft compilato in WebAssembly, filtra e cerca lato client ed esporta CSV — nessun Python richiesto.