Sistemas de arquivos · Aula 3 de 8
NTFS: a MFT, seus registros e atributos
12 min de leitura
NTFS é o sistema de arquivos de quase todos os volumes Windows que você examinará, e sua Tabela Mestra de Arquivos (MFT) descreve cada arquivo neles: nome, pasta pai, tamanho, carimbos de data/hora e onde o conteúdo fica armazenado. Esta aula mostra como localizar a MFT, ler um registro byte a byte, decodificar onde os dados de um arquivo ficam armazenados e indicar o que um registro comprova e o que não comprova.
O setor de inicialização: localizando a MFT
O primeiro setor de um volume NTFS é seu setor de inicialização; uma cópia de backup é mantida no último setor do volume. Campos principais:
| Offset | Tamanho | Campo | Valor típico |
|---|---|---|---|
| 0x03 | 8 | ID OEM | NTFS (NTFS seguido de quatro espaços) |
| 0x0B | 2 | Bytes por setor | 512 |
| 0x0D | 1 | Setores por cluster | 8 (um cluster de 4.096 bytes); um valor acima de 0x80 é um expoente negativo, por exemplo, 0xF8 = 2^8 setores, usado para clusters maiores que 64 KiB |
| 0x28 | 8 | Total de setores no volume | varia |
| 0x30 | 8 | Cluster inicial de $MFT | varia |
| 0x38 | 8 | Cluster inicial de $MFTMirr | varia |
| 0x40 | 1 | Tamanho do registro da MFT | 0xF6 (negativo, portanto 2^10 = 1.024 bytes) |
| 0x48 | 8 | Número de série do volume | varia |
| 0x1FE | 2 | Assinatura de inicialização | 0x55 0xAA |
Os valores estão em little-endian; os números de cluster (LCNs) são contados a partir do início do volume. O byte de tamanho do registro tem sinal: um valor negativo n significa 2^|n| bytes, portanto 0xF6 (−10) significa 1.024. Exemplo: com clusters de 4.096 bytes e $MFT no cluster 786.432, a MFT começa a 786.432 × 4.096 = 3.221.225.472 bytes do início do volume.
Tudo é um arquivo
No NTFS, as próprias estruturas do sistema de arquivos são arquivos com registros na MFT, assim como os documentos do usuário; a MFT descreve a si mesma no registro 0. Os primeiros 16 registros (0 a 15) são reservados para metadados, e os registros 0 a 11 contêm estes arquivos:
| Registro | Nome | Finalidade |
|---|---|---|
| 0 | $MFT | A própria MFT; seu $DATA contém todos os registros |
| 1 | $MFTMirr | Backup de pelo menos os quatro primeiros registros da MFT |
| 2 | $LogFile | Diário de transações para alterações de metadados |
| 3 | $Volume | Nome do volume, versão do NTFS, flag de volume sujo |
| 4 | $AttrDef | Definições dos tipos de atributo |
| 5 | . (diretório raiz) | A pasta de nível superior do volume |
| 6 | $Bitmap | Estado de alocação de cada cluster |
| 7 | $Boot | O setor de inicialização e o código de inicialização |
| 8 | $BadClus | Clusters marcados como defeituosos |
| 9 | $Secure | Descritores de segurança compartilhados |
| 10 | $UpCase | Tabela de maiúsculas para comparação de nomes |
| 11 | $Extend | Pasta para arquivos de metadados posteriores |
$Extend contém $UsnJrnl (o diário de alterações), $ObjId (IDs de objeto), $Quota (dados de cotas) e $Reparse (pontos de nova análise). Diferentemente dos registros 0 a 11, esses arquivos não têm números de registro fixos. Os registros 12 a 15 são reservados e não são usados para arquivos de usuário. A aula 1.4 aborda $LogFile e $UsnJrnl.
Anatomia de um registro da MFT
Cada registro normalmente tem 1.024 bytes e começa com a assinatura ASCII FILE. Uma assinatura BAAD marca um registro identificado como corrompido, normalmente após uma falha na verificação de fixup (veja abaixo). Campos do cabeçalho:
| Offset | Tamanho | Campo |
|---|---|---|
| 0x00 | 4 | Assinatura FILE |
| 0x04 | 2 | Offset para a matriz de sequência de atualização |
| 0x06 | 2 | Número de entradas nessa matriz |
| 0x08 | 8 | Número de sequência de $LogFile (LSN) |
| 0x10 | 2 | Número de sequência |
| 0x12 | 2 | Contagem de links físicos |
| 0x14 | 2 | Offset para o primeiro atributo |
| 0x16 | 2 | Flags: 0x01 em uso, 0x02 diretório |
| 0x18 | 4 | Tamanho usado do registro |
| 0x1C | 4 | Tamanho alocado do registro (1.024) |
| 0x20 | 8 | Referência de arquivo do registro base (0 se este for o registro base) |
| 0x2C | 4 | Número deste registro (NTFS 3.1, Windows XP e posteriores) |
Valores comuns das flags: 0x0000 arquivo excluído, 0x0001 arquivo em uso, 0x0002 diretório excluído, 0x0003 diretório em uso. O registro de um arquivo excluído mantém seu conteúdo até ser reutilizado, portanto nomes e carimbos de data/hora frequentemente permanecem (aula 1.8).
Fixups
A matriz de sequência de atualização (matriz de fixup) detecta registros que foram gravados apenas parcialmente. Antes da gravação, o NTFS salva os dois últimos bytes de cada bloco de 512 bytes na matriz e os sobrescreve com um número de sequência de atualização de dois bytes. Em um registro de 1.024 bytes, os bytes em 0x1FE e 0x3FE são substituídos, portanto a matriz contém três valores: o número e dois pares originais. Na leitura, ambas as posições devem corresponder ao número, e os originais são restaurados. Uma visualização hexadecimal bruta mostra os valores substituídos, portanto restaure os fixups antes de interpretar dados que atravessem essas posições; as ferramentas forenses fazem isso automaticamente.
Referências de arquivo e o número de sequência
O NTFS faz referência a um registro com uma referência de arquivo de 8 bytes: os 48 bits menos significativos são o número do registro e os 16 bits mais significativos são o número de sequência. O Windows incrementa o número de sequência quando libera um registro, portanto um registro excluído, e qualquer reutilização posterior dele, contém um valor maior que o das referências feitas enquanto o arquivo original existia.
Exemplo: os bytes 2B 1A 00 00 00 00 03 00 são lidos em little-endian como 0x0003000000001A2B. O número do registro é 0x1A2B = 6.699 e o número de sequência é 3.
As referências aparecem em $FILE_NAME (o pai), nas entradas de índice de diretório, nos registros de $UsnJrnl e em artefatos do Windows, como arquivos LNK. Se uma referência indica o registro 6.699 com sequência 3, mas o registro 6.699 está agora em uso com sequência 5, o registro foi reutilizado para um arquivo diferente. Comparar os dois números impede que você atribua o nome, o caminho ou os carimbos de data/hora de um novo arquivo a uma referência antiga.
Atributos
Após o cabeçalho vem uma lista de atributos. Cada um começa com um código de tipo de 4 bytes e um comprimento total de 4 bytes, seguidos de uma flag de não residente, do comprimento e do offset de um nome opcional, de flags e de um ID de atributo. A lista termina com o marcador 0xFFFFFFFF; os bytes após ele ainda podem conter dados de um uso anterior do registro.
| Tipo | Nome | Conteúdo |
|---|---|---|
| 0x10 | $STANDARD_INFORMATION | Quatro carimbos de data/hora, flags de atributo DOS, ID de segurança, USN |
| 0x20 | $ATTRIBUTE_LIST | Onde estão os atributos quando não cabem em um registro |
| 0x30 | $FILE_NAME | Nome, referência ao pai, quatro carimbos de data/hora, tamanhos, espaço de nomes |
| 0x40 | $OBJECT_ID | GUID de objeto de 16 bytes (usado pelo rastreamento de links) |
| 0x50 | $SECURITY_DESCRIPTOR | Direitos de acesso; no NTFS 3.x, geralmente mantidos de forma centralizada em $Secure |
| 0x80 | $DATA | Conteúdo do arquivo; sem nome é o fluxo principal, com nome é um fluxo de dados alternativo |
| 0x90 | $INDEX_ROOT | Raiz de um índice de diretório (sempre residente) |
| 0xA0 | $INDEX_ALLOCATION | Registros de índice adicionais armazenados em clusters |
| 0xB0 | $BITMAP | Quais registros de índice estão em uso |
Quando os atributos não cabem em um registro, o NTFS usa registros de extensão: o $ATTRIBUTE_LIST do registro base indica onde está cada atributo, e cada registro de extensão contém a referência do registro base em 0x20.
Dois conjuntos de carimbos de data/hora
$STANDARD_INFORMATION ($SI) e $FILE_NAME ($FN) armazenam, cada um, quatro valores FILETIME: contagens de 64 bits de intervalos de 100 nanossegundos desde 1601-01-01 00:00:00 UTC. Na ordem: criação, modificação (conteúdo), modificação da entrada da MFT e acesso, frequentemente abreviados como MACB (modified, accessed, changed, born). Relate-os em UTC, por exemplo, 2026-03-14T09:26:53.1234567Z. Os dois conjuntos são atualizados por operações diferentes, e $SI é mais fácil de alterar, portanto compará-los é útil; o módulo 3 aborda como cada um muda.
Espaços de nomes de $FILE_NAME
Cada $FN tem um byte de espaço de nomes: 0 POSIX (diferencia maiúsculas de minúsculas, qualquer caractere Unicode exceto / e nulo), 1 Win32 (nome longo), 2 DOS (nome curto 8.3), 3 Win32 e DOS (o nome longo já é um nome 8.3 válido). Se a criação de nomes 8.3 estiver habilitada, Quarterly Report.docx terá dois atributos $FN: Win32 e DOS (QUARTE~1.DOC). Um link físico adiciona outro $FN com seu próprio pai. Os campos de tamanho de $FN não são mantidos atualizados, portanto obtenha o tamanho de $DATA.
Dados residentes e não residentes
Conteúdo pequeno é armazenado dentro do registro: o atributo $DATA é residente. Em um registro de 1.024 bytes, isso corresponde a até aproximadamente 700 bytes, dependendo do comprimento do nome e dos outros atributos. Um arquivo residente não usa clusters, portanto seu conteúdo está na MFT, não na área de dados. Conteúdo maior torna $DATA não residente: o atributo mantém os tamanhos e uma lista de execuções, e o conteúdo vai para clusters. Ele não volta para o registro se o arquivo diminuir posteriormente.
O cabeçalho de um atributo não residente registra o tamanho alocado, o tamanho real (lógico) e o tamanho inicializado. A diferença entre o tamanho alocado e o real é o espaço residual do arquivo (aula 1.2).
Decodificando uma execução de dados
Uma lista de execuções é uma série de execuções, cada uma sendo uma sequência contígua de clusters, terminando com um byte 0x00. Cada execução começa com um byte de cabeçalho: o nibble menos significativo é o número de bytes que armazenam o comprimento da execução, e o nibble mais significativo é o número de bytes que armazenam o offset inicial. O offset tem sinal e é relativo ao início da execução anterior (o da primeira execução é relativo ao cluster 0).
Exemplo resolvido: a lista de execuções 31 04 A0 3F 01 00.
- Cabeçalho 0x31: o nibble menos significativo 1 significa um comprimento de 1 byte; o nibble mais significativo 3 significa um offset de 3 bytes.
- Comprimento:
04= 4 clusters. - Offset:
A0 3F 01lido em little-endian é 0x013FA0 = 1 × 65.536 + 0x3F × 256 + 0xA0 = 65.536 + 16.128 + 160 = 81.824. A execução começa no LCN 81.824 e abrange os LCNs 81.824 a 81.827. - Próximo byte
00: fim da lista de execuções.
Com clusters de 4.096 bytes, o conteúdo começa a 81.824 × 4.096 = 335.151.104 bytes do início do volume, e 4 × 4.096 = 16.384 bytes estão alocados. Se o tamanho real for 13.000 bytes, o espaço residual do arquivo será 16.384 − 13.000 = 3.384 bytes.
Um arquivo fragmentado tem mais execuções. Uma execução seguinte 21 08 00 F0 tem um offset de 2 bytes 0xF000, que, como valor com sinal, é −4.096, portanto ela começa no LCN 81.824 − 4.096 = 77.728. Uma execução com tamanho de offset igual a 0 é esparsa: nenhum cluster é alocado e sua leitura retorna zeros.
Diretórios e o índice $I30
Um diretório tem a flag de diretório e um índice chamado $I30, ordenado por nome. Diretórios pequenos cabem em um $INDEX_ROOT residente; os maiores acrescentam $INDEX_ALLOCATION, que aponta para registros de índice (assinatura INDX, normalmente 4.096 bytes) em clusters, com $BITMAP marcando aqueles em uso. Cada entrada contém a referência de arquivo do filho e uma cópia de seu $FN, incluindo carimbos de data/hora. Entradas de arquivos excluídos ou renomeados podem permanecer no espaço não utilizado dos registros INDX.
Analisando a MFT com ferramentas
As coletas de triagem geralmente incluem o arquivo $MFT. Um comando típico é MFTECmd.exe -f "E:\Triage\C\$MFT" --csv E:\Out --csvf mft.csv. O CSV inclui EntryNumber, SequenceNumber, InUse, ParentPath, FileName e colunas separadas de carimbos de data/hora de $SI e $FN, como Created0x10 e Created0x30.
Com uma imagem, o The Sleuth Kit trabalha diretamente nos registros (-o é o setor inicial da partição): fsstat -o 2048 image.raw informa o tamanho do cluster e a localização da MFT; istat -o 2048 image.raw 6699 mostra o registro 6.699 com seu número de sequência, ambos os conjuntos de carimbos de data/hora, atributos e clusters de $DATA; icat -o 2048 image.raw 6699 retorna seu conteúdo; ffind -o 2048 image.raw 6699 retorna seu caminho. Para os achados principais, confira a saída da ferramenta com o registro bruto.
O que um registro da MFT comprova e o que não comprova
Um registro mostra que um arquivo com aquele nome, pai, tamanho e localização dos dados foi descrito no volume, se o registro está alocado e os últimos carimbos de data/hora gravados. Uma referência correspondente (número do registro e número de sequência) vincula outro artefato àquele arquivo específico.
Ele não mostra quem criou ou abriu o arquivo e não comprova que os carimbos de data/hora sejam verdadeiros: o relógio pode ter estado errado, softwares podem alterar os valores de $SI e as atualizações de último acesso podem estar desabilitadas ou ser adiadas. Os clusters de um registro excluído podem ter sido realocados, portanto verifique $Bitmap e o conteúdo antes de atribuir dados ao arquivo excluído. Corrobore com $UsnJrnl, $LogFile, entradas de $I30 e artefatos do Windows.
Foco no exame: Prepare-se para converter um número de registro e um número de sequência em uma referência de arquivo de 8 bytes e vice-versa, decodificar uma execução de dados em LCN inicial, comprimento e offset em bytes, e ler as flags de em uso e de diretório. As questões podem perguntar qual atributo fornece a data/hora de criação de $SI ou $FN de um arquivo em UTC, seu caminho completo ou seu tamanho, e como confirmar que o registro não foi reutilizado.
Pontos principais
- O setor de inicialização do NTFS (ID OEM
NTFS) fornece o tamanho do setor, o tamanho do cluster, o tamanho do registro da MFT e os clusters iniciais de $MFT e $MFTMirr. - Os registros 0 a 11 são arquivos de metadados fixos ($MFT, $MFTMirr, $LogFile, $Volume, $AttrDef, raiz, $Bitmap, $Boot, $BadClus, $Secure, $UpCase, $Extend).
- Cada registro começa com
FILE, normalmente tem 1.024 bytes, usa fixups no final de cada bloco de 512 bytes e lista atributos que terminam com 0xFFFFFFFF. - Uma referência de arquivo é um número de registro de 48 bits mais um número de sequência de 16 bits; um número de sequência diferente significa que o registro foi reutilizado.
- Arquivos pequenos são residentes dentro do registro; arquivos maiores usam listas de execuções com comprimento e offset relativo com sinal.
- $SI e $FN contêm, cada um, quatro carimbos de data/hora FILETIME em UTC; eles mudam de maneiras diferentes, o que é abordado no módulo 3.