Respuesta corta: usa analyzeMFT para análisis puro en Python cuando puedas instalarlo, libmft cuando quieras un modelo de objetos tipado, o delega en omerbenamram/mft cuando la velocidad importe. El análisis en Python puro es ~10–50× más lento que el crate de Rust pero está bien para scripts puntuales.
Qué estás leyendo
La Master File Table de NTFS es una secuencia de registros de tamaño fijo de 1024 bytes. Para analizarla desde Python solo necesitas:
- Abrir el archivo
$MFT(o leerlo de una imagen de disco). - Recorrerlo en tramos de 1024 bytes.
- Aplicar el array fixup a cada registro. Véase la anatomía del registro para la disposición a nivel de byte.
- Recorrer el flujo de atributos dentro de cada registro.
Las librerías de abajo se encargan de los cuatro pasos. La mayoría de analistas solo recurren al struct.unpack crudo cuando una librería no expone un campo que necesitan.
Opción 1: analyzeMFT
analyzeMFT es el clásico parser MFT en Python puro, originalmente de David Kovar y aún mantenido. Pensado para CLI, pero importable.
# pip install analyzeMFT
from analyzeMFT.mft_analyzer import MFTAnalyzer
analyzer = MFTAnalyzer(mft_file="path/to/$MFT", output_file="out.csv")
analyzer.analyze()
El CSV que produce tiene una fila por registro con las marcas de tiempo tanto de $STANDARD_INFORMATION como de $FILE_NAME. Lo bastante bueno para triaje con hoja de cálculo.
Cuándo usarlo: archivos $MFT pequeños, scripts ad-hoc, sin dependencias nativas permitidas.
Límites: lento sobre entradas de varios gigabytes (Python puro mono-hilo), y el modelo de objetos está orientado a la emisión de CSV en lugar de a recorridos programáticos.
Opción 2: libmft (modelo de objetos tipado)
Si quieres consultar los registros como objetos Python, libmft expone un modelo tipado cercano a la estructura en 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 resuelve las referencias al padre para que puedas pedir a cada entrada su ruta completa sin escribir tú el recorrido. También gestiona de forma transparente los registros de extensión $ATTRIBUTE_LIST — algo que la capa CSV de analyzeMFT te oculta.
Cuándo usarlo: quieres escribir lógica que recorra los registros, filtre por atributo y emita una forma personalizada.
Opción 3: delegar en un parser Rust
Cuando la $MFT es grande (~1 GB+) o estás procesando muchos discos por lotes, la opción práctica más rápida es delegar desde Python en un parser nativo y leer su JSON.
import json
import subprocess
# omerbenamram/mft — `cargo install mft` o descarga 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 cero → eliminado
print(record["entry"], record["file_name"]["name"])
mft_dump emite JSON Lines — un registro por línea — que se transmite limpiamente a Python sin cargar toda la salida en memoria. Comparado con analyzeMFT sobre la misma entrada, el parser Rust es típicamente 10–50× más rápido y usa una décima parte de la memoria.
Cuándo usarlo: tuberías de producción, entradas grandes o cualquier sitio donde el tiempo de análisis importe.
Leer $MFT directamente desde una imagen de disco
Si tienes una imagen cruda .dd o .E01 en lugar de una $MFT extraída, usa pytsk3 (bindings de Python para The Sleuth Kit) para posicionarte sobre $MFT en el volumen y transmitir sus bytes:
import pytsk3
img = pytsk3.Img_Info("disk.dd")
fs = pytsk3.FS_Info(img, offset=0) # usa el desplazamiento de la partición NTFS
mft_file = fs.open_meta(inode=0) # $MFT siempre es el inode 0
size = mft_file.info.meta.size
data = mft_file.read_random(0, size)
# data ahora contiene $MFT; pásalo a libmft o escríbelo a disco
Es el enfoque más limpio cuando el volumen está cifrado a nivel de partición pero montado vía un descifrador que te entrega una imagen cruda.
Errores comunes
- Olvidar el array fixup. Leer bloques crudos de 1024 bytes sin aplicar la USA te da datos basura en los desplazamientos 510 y 1022 de cada registro. Cada librería de arriba lo hace por ti — escribe tu propio parser solo si entiendes el mecanismo de fixup (véase el artículo de anatomía del registro).
- Tratar el número de registro como identidad. Los números de registro se reutilizan. La referencia de archivo de 64 bits (número de registro más número de secuencia) es el identificador que no colisiona.
- Confundir los dos conjuntos de marcas de tiempo. Cada registro lleva marcas de tiempo tanto en
$STANDARD_INFORMATION(se actualiza con frecuencia) como en$FILE_NAME(en general estable). Para la detección de timestomping necesitas ambos — véase las cuatro marcas de tiempo MFT.
Cuándo saltarse Python por completo
Para un análisis interactivo puntual sin instalación, suelta la $MFT en el parser navegador de este sitio. Ejecuta el mismo crate omerbenamram/mft compilado a WebAssembly, filtra y busca en el cliente, y exporta CSV — sin Python.