← Torna al blog

Come analizzare $MFT in Python

· 4 min di lettura

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:

  1. Aprire il file $MFT (o leggerlo da un'immagine disco).
  2. Percorrerla a blocchi di 1.024 byte.
  3. Applicare il fixup array a ogni record. Vedi l'anatomia di un record per la disposizione a livello di byte.
  4. 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.

Risorse esterne