← Voltar ao blog

Master File Table (MFT): o $MFT do NTFS explicado

· 13 min de leitura

O $MFT é o índice que o NTFS usa para acompanhar todos os ficheiros e directórios de um volume. É também o artefacto isolado mais informativo que se pode puxar de uma máquina Windows. Todos os outros ficheiros do volume, a própria tabela incluída, têm pelo menos um registo dentro dele. As ferramentas forenses respondem a perguntas difíceis sobre ficheiros que o Windows já não admite que existem porque os registos sobrevivem à eliminação. Os caçadores de ameaças perseguem persistência através dele porque os atacantes não conseguem mover ficheiros em NTFS sem escrever nele. Se aprender apenas um artefacto do Windows ao nível do byte, é este.

Esta é a referência que eu próprio gostaria de ter tido quando comecei. Cobre o que é a tabela, como é um registo, que atributos um registo pode transportar, o que significam os ficheiros de sistema reservados no início, o que significa de facto o temido erro "Windows cannot recover the master file table", e como ler a tabela.

O que é a Master File Table

O $MFT é um ficheiro. Um único ficheiro. Está num offset conhecido perto do início de cada volume NTFS. O boot sector no offset 0 da partição (os 512 bytes do BIOS Parameter Block) contém um campo chamado MftStartLcn que aponta para o primeiro cluster do $MFT. Leia esses 1.024 bytes e tem o registo 0, a auto-descrição da tabela.

Cada linha da tabela tem exactamente 1.024 bytes e descreve um ficheiro ou um directório. Cada registo guarda o nome, carimbos temporais, flags ao estilo DOS, referência ao descritor de segurança, e ou os próprios dados (ficheiros pequenos) ou uma lista de clusters em disco onde vivem os dados (ficheiros grandes), tudo codificado como uma sequência de atributos tipados.

O NTFS foi introduzido com o Windows NT 3.1 em 1993 e tem sido o sistema de ficheiros Windows por omissão em todos os discos fixos desde o Windows XP. Substituiu o FAT, que mantém uma pequena tabela de alocação e guarda os nomes de ficheiros dentro das entradas de directório. O NTFS coloca quase todas as peças de metadados sobre todos os ficheiros numa tabela estruturada, o $MFT. Esse desenho tem duas consequências que vale a pena memorizar:

  1. Todos os metadados estão num só sítio. Um único seek ao $MFT enumera todos os ficheiros do volume. É por isso que ferramentas forenses, motores de antivírus, serviços de indexação e software de backup o lêem todos. É também por isso que um $MFT corrompido é um problema muito maior do que um FAT corrompido.
  2. Os ficheiros eliminados deixam para trás os seus metadados. Quando o NTFS elimina um ficheiro, limpa um bit no cabeçalho do registo e marca os clusters do ficheiro como livres em $Bitmap. O resto do registo (nome, carimbos temporais, frequentemente os dados) fica onde está até que o slot do registo seja reutilizado. Veja o que sobrevive a uma eliminação.

O acrónimo MFT significa Master File Table. Escrito $MFT em disco porque, no NTFS, o cifrão prefixa os nomes dos ficheiros de metadados.

Como o $MFT está disposto em disco

Quando o NTFS formata um volume, reserva uma região chamada a zona MFT perto do início da partição. Os primeiros 16 registos da tabela são reservados para ficheiros de metadados do NTFS (descritos abaixo); o registo 0 é a entrada da própria tabela, apontando de volta para os seus próprios clusters.

O $MFT cresce estendendo-se para a sua zona reservada sempre que precisa de mais registos. Se o volume encher antes de a zona se esgotar, o Windows reduz a zona para dar espaço a dados de utilizador, motivo pelo qual um $MFT muito fragmentado é comum em sistemas de ficheiros envelhecidos. A tabela nunca encolhe. Uma vez criado um slot, fica no $MFT; eliminar apenas limpa a flag de em-uso. É por isso que registos eliminados antigos bem acima da marca actual podem sobreviver durante anos num volume pouco usado.

Uma cópia de segurança dos primeiros registos vive em $MFTMirr, colocada no meio do volume. Se o próprio $MFT for ilegível, o NTFS usa $MFTMirr para arrancar a recuperação. Veja $MFTMirr e quando o NTFS o usa.

Anatomia de um registo FILE

Cada registo do MFT começa com a assinatura ASCII de quatro bytes FILE (46 49 4C 45). Os registos corrompidos transportam em vez disso BAAD, uma lápide escrita pelo chkdsk quando não conseguiu reparar o registo. Depois da assinatura vem um cabeçalho de 56 bytes, depois o fixup array, depois um fluxo de atributos tipados terminado por 0xFFFFFFFF.

O cabeçalho transporta os campos a que mais frequentemente recorre:

  • Assinatura. FILE para um registo válido, BAAD para irreparável.
  • Update sequence (fixup) array. O truque de detecção de torn writes. Os dois últimos bytes de cada bloco de 512 bytes do registo são substituídos por um USN; os originais ficam guardados neste array. Na leitura, o NTFS verifica o USN e repõe os bytes originais.
  • Número de sequência do $LogFile. Um ponteiro para o $LogFile para recuperação de crash.
  • Número de sequência. Incrementado sempre que o slot do registo é reutilizado. Combinado com o número de registo, forma a referência de ficheiro de 64 bits que identifica univocamente uma encarnação particular de um ficheiro.
  • Contador de hard links. Número de atributos $FILE_NAME que apontam para o registo.
  • Flags. Bit 0 é IN_USE (limpo significa eliminado). Bit 1 é DIRECTORY.
  • Referência ao base file record. Não zero em registos de extensão que pertencem a um registo base noutro sítio da tabela.
  • Tamanhos used e allocated. Used é quanto do slot de 1.024 bytes este registo consome de facto; allocated é o tamanho do slot (sempre 1.024 em volumes padrão).

Para um passeio byte a byte pelo cabeçalho e pelo fluxo de atributos, veja dentro de um registo MFT.

Depois do cabeçalho vêm os atributos. Cada um tem o seu próprio cabeçalho curto (tipo, comprimento, flag de residente/não residente, nome opcional) seguido pelos dados. Não há ordem fixa, mas na prática $STANDARD_INFORMATION é o primeiro e $DATA o último. Um registo que fica sem espaço (demasiados fragmentos, demasiados ADS, um nome invulgarmente longo) cria um atributo $ATTRIBUTE_LIST que aponta para um ou mais registos de extensão noutro sítio da tabela. Os parsers têm de seguir a cadeia para reconstruir o ficheiro.

Atributos de ficheiro guardados no $MFT

Esta é a lista canónica de tipos de atributo NTFS, com códigos hex:

| Tipo | Hex | Finalidade | |------|-----|---------| | $STANDARD_INFORMATION | 0x10 | Quatro carimbos temporais (criado, modificado, acedido, MFT-modificado), flags DOS, owner ID, ID de segurança, ponteiro USN. | | $ATTRIBUTE_LIST | 0x20 | Ponteiros para registos de extensão quando os atributos de um ficheiro transbordam um registo. | | $FILE_NAME | 0x30 | Um nome de ficheiro, referência ao directório-pai, tamanho alocado e real, e um segundo conjunto de quatro carimbos temporais. Um ficheiro pode ter vários (um por hard link, mais o nome curto 8.3 em volumes com essa funcionalidade ligada). | | $OBJECT_ID | 0x40 | Identificador de objecto de 128 bits usado pelo Distributed Link Tracking. | | $SECURITY_DESCRIPTOR | 0x50 | ACL legada por ficheiro. O NTFS moderno guarda ACLs de forma central em $Secure e referencia-as por ID a partir de $STANDARD_INFORMATION. | | $VOLUME_NAME | 0x60 | Apenas no registo 3 ($Volume). Contém o rótulo do volume. | | $VOLUME_INFORMATION | 0x70 | Versão NTFS, flag dirty. | | $DATA | 0x80 | O conteúdo do ficheiro. Residente para ficheiros muito pequenos; não residente (uma runlist de clusters) caso contrário. Um ficheiro pode carregar vários $DATA; o sem nome é o fluxo primário, os com nome são Alternate Data Streams. | | $INDEX_ROOT | 0x90 | Raiz de uma árvore B+. Usada por directórios ($I30), índices de reparse-point, e outras estruturas indexadas. | | $INDEX_ALLOCATION | 0xA0 | Continuação não residente de um índice grande. | | $BITMAP | 0xB0 | Bitmap de alocação para o próprio $MFT ou para directórios grandes. | | $REPARSE_POINT | 0xC0 | Symlinks, junctions, mount points, placeholders do OneDrive, stubs de dedup. | | $EA_INFORMATION / $EA | 0xD0 / 0xE0 | Atributos estendidos da era OS/2. Raros em Windows moderno. O WSL1 usava-os para metadados POSIX, que é o único contexto que vale a pena considerar. | | $LOGGED_UTILITY_STREAM | 0x100 | Metadados de encriptação EFS ($EFS), dados de transacção TxF. |

Um registo carrega sempre pelo menos $STANDARD_INFORMATION, um $FILE_NAME e um $DATA. Tudo o resto é opcional e dependente de funcionalidade.

Dados residentes vs não residentes

A maioria dos atributos $DATA num volume real são não residentes: o cabeçalho do atributo carrega uma lista compacta de cluster runs (um LCN inicial mais um comprimento, repetidos), e os bytes do ficheiro vivem noutro sítio do disco. O cabeçalho do atributo é em si pequeno.

Se o ficheiro for suficientemente pequeno (tipicamente abaixo de ~700 bytes, contabilizados os outros atributos), o NTFS guarda os bytes inline dentro do registo. Isso são dados residentes, e é um dos artefactos mais úteis em trabalho forense: o conteúdo de um pequeno ficheiro de texto eliminado há semanas pode ainda estar, byte a byte, dentro de um registo $MFT não alocado. Veja resident data para o limite de tamanho e o que procurar.

Ficheiros de metadados NTFS nos primeiros dezasseis registos

Os primeiros 16 registos do $MFT estão reservados para a contabilidade do próprio NTFS. Começam com $ para não colidir com nomes de ficheiros de utilizador. Os que valem a pena conhecer:

| Rec # | Ficheiro | O que é | |-------|------|------------| | 0 | $MFT | A própria tabela. A sua runlist $DATA aponta para os seus próprios clusters. | | 1 | $MFTMirr | Cópia parcial dos primeiros registos do $MFT. | | 2 | $LogFile | Log de transacções usado para desfazer ou refazer operações incompletas após um crash. | | 3 | $Volume | Rótulo do volume e flag dirty. | | 4 | $AttrDef | Esquema dos tipos de atributo válidos. | | 5 | . | O directório raiz. | | 6 | $Bitmap | Um bit por cluster no volume; segue a alocação. | | 7 | $Boot | Cópia do boot sector. | | 8 | $BadClus | Ficheiro sparse cujas runs apontam para todos os clusters marcados como maus pelo sistema de ficheiros. | | 9 | $Secure | Armazém central de descritores de segurança. | | 10 | $UpCase | Tabela de mapeamento Unicode para letras maiúsculas, usada para comparação de nomes sem distinção de maiúsculas e minúsculas. | | 11 | $Extend | Directório com ficheiros de sistema mais recentes: $ObjId, $Quota, $Reparse, $UsnJrnl, $RmMetadata. |

O change journal $UsnJrnl (sob $Extend) é especialmente útil em forense; regista cada alteração de metadados no volume e complementa o $MFT para reconstrução de timeline. Veja emparelhar journal com a file table.

Quando o $MFT corre mal

O erro "Windows cannot recover master file table. CHKDSK aborted" aparece quando o chkdsk não consegue ler o $MFT e também não consegue recorrer a $MFTMirr. A esta altura, o NTFS já tentou e falhou na sua auto-reparação interna. As causas raiz que tenho visto, ordenadas pela frequência com que acabam por ser cada uma:

  • Suporte físico a falhar. Sectores maus na zona MFT devolvem lixo na leitura. Os dados SMART geralmente corroboram. Faça uma imagem do disco com ddrescue, não dd, e trabalhe na imagem.
  • Perda súbita de energia durante uma operação intensiva em metadados. Normalmente o log de transacções desfaz isto, mas um $LogFile corrompido derrota o rollback.
  • Corrupção ao nível do driver ou filtro. Pilhas de encriptação de disco mal comportadas, minifilters de sistema de ficheiros, ou drivers de armazenamento com bugs podem escrever registos inconsistentes. Comum em hosts com vários agentes de segurança a brigar uns com os outros.
  • Sobrescritas maliciosas. Wipers e algumas famílias de ransomware (notavelmente o Petya e a onda inicial NotPetya) deliberadamente garatujam o $MFT para tornar o volume impossível de montar. Veja padrões de ransomware no MFT.

A resposta forense sólida:

  1. Pare imediatamente de escrever no volume. Cada nova escrita reduz as hipóteses de recuperação.
  2. Faça imagem do disco com FTK Imager, dd ou ddrescue para um destino confiável. Verifique o hash.
  3. Trabalhe na imagem, não no original. Tente testdisk, R-Studio, ou uma análise manual que encontre registos FILE por varredura por assinatura directamente do volume. Mesmo que o ponteiro em disco para $MFT tenha desaparecido, os próprios registos ainda são geralmente reconhecíveis.
  4. Se o objectivo é repor o volume online em vez de recuperar dados, só então corra chkdsk /f na imagem.

O chkdsk /b num volume escrevível pode limpar marcadores de cluster mau, mas também pode descartar registos que não consegue compreender. Corra-o no original apenas depois de ter uma imagem e de ter decidido que a disponibilidade pesa mais do que a fidelidade forense.

Como ler o $MFT

Tem três opções realistas:

  • MFTECmd (Eric Zimmerman). Um CLI Windows em .NET que produz CSV na disposição amiga de bodyfile que a maioria das ferramentas de timeline espera. O padrão de facto para respondentes a incidentes.
  • omerbenamram/mft. Uma crate Rust e CLI (mft_dump). O parser que este site usa, útil quando se quer programar análise ou embebê-la num pipeline maior.
  • O parser de browser deste site. Largue o $MFT na página inicial e corre o mesmo parser Rust, compilado para WebAssembly, inteiramente no seu browser. Nada é enviado.

Para uma comparação com prós e contras concretos, veja ferramentas de parser de MFT. Para fluxos de trabalho práticos num $MFT analisado, veja construir uma timeline, ficheiros eliminados, e extrair o $MFT.

Perguntas frequentes

O que significa MFT?

MFT significa Master File Table. Escrito $MFT em disco porque, no NTFS, o cifrão prefixa os nomes dos ficheiros de metadados.

Para que serve a master file table?

É o índice que o NTFS usa para encontrar todos os ficheiros e directórios de um volume. Cada entrada guarda o nome do ficheiro, carimbos temporais, informação de segurança, atributos, e a localização dos seus dados em disco.

Que atributos de ficheiro estão guardados na master file table?

No mínimo, cada registo carrega $STANDARD_INFORMATION (carimbos temporais, flags DOS), $FILE_NAME (nome e um segundo conjunto de carimbos temporais), e $DATA (o conteúdo do ficheiro ou um ponteiro para ele). Os registos também podem carregar $ATTRIBUTE_LIST, $OBJECT_ID, $SECURITY_DESCRIPTOR, $INDEX_ROOT, $INDEX_ALLOCATION, $BITMAP, $REPARSE_POINT, $EA e $LOGGED_UTILITY_STREAM dependendo do ficheiro. A referência completa está na tabela de atributos acima.

Quão grande é a master file table?

Cada registo tem 1.024 bytes. A tabela reserva por omissão cerca de 12,5% do volume (a zona MFT) mas só consome o espaço que realmente precisa. Um volume com um milhão de ficheiros tem aproximadamente 1 GB de $MFT.

O $MFT é o mesmo que o $MFTMirr?

Não. O $MFTMirr é uma cópia parcial dos primeiros registos do $MFT, colocada noutro local do disco para que o NTFS possa arrancar a recuperação se o cabeçalho da tabela principal estiver corrompido.

Como corrijo uma master file table corrompida?

Faça primeiro uma imagem do disco. Depois ou corra chkdsk /f contra a imagem (rápido, pode descartar registos), ou use uma ferramenta de recuperação que consiga varrer por assinaturas FILE e reagrupar a tabela a partir dos clusters brutos (lento, preserva mais evidência). Nunca corra chkdsk contra o volume original antes de fazer imagem.

Posso ler o $MFT em Linux ou macOS?

Sim. O $MFT é apenas um ficheiro. Qualquer parser que aceite um dump bruto de $MFT funciona em qualquer SO: omerbenamram/mft, analyzeMFT, a ferramenta de browser deste site. Só precisa de Windows para extrair o ficheiro de um volume montado em vivo.

Leituras adicionais

Recursos externos