← Voltar ao blog

Como analisar a $MFT em Python

· 4 min de leitura

Resposta curta: use analyzeMFT para análise em Python puro quando puder instalá-lo, libmft quando quiser um modelo de objetos tipado, ou chame omerbenamram/mft externamente quando a velocidade importar. Análise em Python puro é ~10–50× mais lenta que o crate Rust, mas serve para scripts pontuais.

O que você está lendo

A Master File Table do NTFS é uma sequência de registros de tamanho fixo de 1.024 bytes. Para analisá-la a partir do Python, você só precisa:

  1. Abrir o arquivo $MFT (ou lê-lo de uma imagem de disco).
  2. Percorrê-lo em blocos de 1.024 bytes.
  3. Aplicar o fixup array a cada registro. Veja a anatomia do registro para o layout em nível de byte.
  4. Percorrer o fluxo de atributos dentro de cada registro.

As bibliotecas abaixo cuidam dos quatro passos. A maioria dos analistas só recorre a struct.unpack bruto quando uma biblioteca não expõe um campo que precisam.

Opção 1: analyzeMFT

analyzeMFT é o parser MFT clássico em Python puro, originalmente de David Kovar e ainda mantido. Voltado para CLI, mas importável.

# pip install analyzeMFT
from analyzeMFT.mft_analyzer import MFTAnalyzer

analyzer = MFTAnalyzer(mft_file="path/to/$MFT", output_file="out.csv")
analyzer.analyze()

O CSV que ele produz tem uma linha por registro com carimbos de tempo tanto de $STANDARD_INFORMATION quanto de $FILE_NAME. Bom o suficiente para triagem orientada a planilha.

Quando usar: arquivos $MFT pequenos, scripts ad-hoc, dependências nativas proibidas.

Limites: lento em entradas de vários gigabytes (Python puro mono-thread), e o modelo de objetos é orientado para emissão de CSV em vez de percursos programáticos.

Opção 2: libmft (modelo de objetos tipado)

Se você quer consultar registros como objetos Python, libmft expõe um modelo tipado próximo à estrutura em 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 resolve as referências de pai para que você possa pedir a cada entrada seu caminho completo sem escrever a travessia você mesmo. Também lida com registros de extensão $ATTRIBUTE_LIST de forma transparente — algo que a camada CSV do analyzeMFT esconde de você.

Quando usar: você quer escrever lógica que percorre registros, filtra por atributo e emite um formato customizado.

Opção 3: chamar um parser Rust externamente

Quando a $MFT é grande (~1 GB+) ou você está processando em lote muitos discos, a opção prática mais rápida é chamar do Python um parser nativo e ler seu JSON.

import json
import subprocess

# omerbenamram/mft — `cargo install mft` ou baixe um binário 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 zerado → excluído
        print(record["entry"], record["file_name"]["name"])

mft_dump emite JSON Lines — um registro por linha — que faz streaming limpo para o Python sem carregar toda a saída em memória. Comparado com analyzeMFT na mesma entrada, o parser Rust é tipicamente 10–50× mais rápido e usa um décimo da memória.

Quando usar: pipelines de produção, entradas grandes, ou qualquer lugar onde o tempo de análise importa.

Lendo a $MFT direto de uma imagem de disco

Se você tem uma imagem .dd ou .E01 em vez de um arquivo $MFT extraído, use pytsk3 (bindings Python para o The Sleuth Kit) para se posicionar em $MFT no volume e fazer streaming dos bytes:

import pytsk3

img = pytsk3.Img_Info("disk.dd")
fs = pytsk3.FS_Info(img, offset=0)  # use o offset da partição NTFS
mft_file = fs.open_meta(inode=0)    # $MFT é sempre o inode 0
size = mft_file.info.meta.size
data = mft_file.read_random(0, size)
# data agora contém $MFT; passe para libmft ou grave em disco

Essa é a abordagem mais limpa quando o volume é criptografado em nível de partição mas montado via um descriptografador que lhe dá uma imagem bruta.

Armadilhas comuns

  • Esquecer o fixup array. Ler blocos brutos de 1.024 bytes sem aplicar o USA dá lixo nos offsets 510 e 1022 de cada registro. Cada biblioteca acima faz isso por você — só escreva seu próprio parser se entender o mecanismo de fixup (veja o post de anatomia do registro).
  • Tratar o número do registro como identidade. Números de registro são reutilizados. A referência de arquivo de 64 bits (número do registro mais número de sequência) é o identificador que não colide.
  • Confundir os dois conjuntos de carimbos de tempo. Cada registro carrega carimbos de tempo em $STANDARD_INFORMATION (atualizados com frequência) e em $FILE_NAME (em sua maioria estáveis). Para detecção de timestomping, você precisa dos dois — veja os quatro carimbos de tempo da MFT.

Quando pular o Python por completo

Para uma análise interativa pontual sem nenhuma instalação, solte a $MFT no parser navegador deste site. Ele roda o mesmo crate omerbenamram/mft compilado para WebAssembly, filtra e busca no lado do cliente, e exporta CSV — sem Python necessário.

Recursos externos