Analisando Espaço Livre e Artefatos do Sistema de Arquivos
Quando Usar
- Ao pesquisar dados ocultos ou residuais no espaço livre do sistema de arquivos
- Para analisar entradas da Tabela Mestra de Arquivos (MFT) do NTFS para metadados de arquivos excluídos
- Ao reconstruir operações de arquivos a partir do USN Change Journal
- Para detectar Fluxos de Dados Alternativos (ADS) usados para ocultar dados ou malware
- Durante análise forense aprofundada que exige exame além da recuperação padrão de arquivos
Pré-requisitos
- Imagem de disco forense com sistema de arquivos NTFS
- Ferramentas The Sleuth Kit (TSK): istat, icat, fls, blkls, blkstat
- MFTECmd (Eric Zimmerman) para análise de MFT
- MFTExplorer para análise interativa de MFT
- Compreensão das estruturas NTFS (MFT, $UsnJrnl, $LogFile, ADS)
- Python com analyzeMFT ou biblioteca mft para análise automatizada
Fluxo de Trabalho
Etapa 1: Identificar e Extrair Artefatos do Sistema de Arquivos NTFS
# Determinar o layout da partição
mmls /cases/case-2024-001/images/evidence.dd
# Extrair arquivos-chave do sistema NTFS
# $MFT - Tabela Mestra de Arquivos
icat -o 2048 /cases/case-2024-001/images/evidence.dd 0 > /cases/case-2024-001/ntfs/MFT
# $UsnJrnl:$J - USN Change Journal
icat -o 2048 /cases/case-2024-001/images/evidence.dd 62-128 > /cases/case-2024-001/ntfs/UsnJrnl_J
# $LogFile - Log de transações
icat -o 2048 /cases/case-2024-001/images/evidence.dd 2 > /cases/case-2024-001/ntfs/LogFile
# Extrair todo o espaço livre do volume
blkls -s -o 2048 /cases/case-2024-001/images/evidence.dd > /cases/case-2024-001/ntfs/slack_space.raw
# Obter informações do sistema de arquivos
fsstat -o 2048 /cases/case-2024-001/images/evidence.dd | tee /cases/case-2024-001/ntfs/fs_info.txt
Etapa 2: Analisar a Tabela Mestra de Arquivos (MFT)
# Analisar MFT com MFTECmd (Eric Zimmerman)
MFTECmd.exe -f "C:\cases\ntfs\MFT" --csv "C:\cases\analysis\" --csvf mft_analysis.csv
# Analisar com analyzeMFT (Python)
pip install analyzeMFT
analyzeMFT.py -f /cases/case-2024-001/ntfs/MFT \
-o /cases/case-2024-001/analysis/mft_analysis.csv \
-c
# Análise MFT personalizada com Python
python3 << 'PYEOF'
from mft import PyMft
import csv
mft = PyMft(open('/cases/case-2024-001/ntfs/MFT', 'rb').read())
arquivos_excluidos = []
arquivos_suspeitos = []
for entrada in mft.entries():
if entrada is None:
continue
nome_arquivo = entrada.get_filename()
if nome_arquivo is None:
continue
excluido = not entrada.is_active()
diretorio = entrada.is_directory()
criado = entrada.get_created_timestamp()
modificado = entrada.get_modified_timestamp()
mft_modificado = entrada.get_mft_modified_timestamp()
tamanho = entrada.get_file_size()
# Sinalizar arquivos excluídos para recuperação
if excluido and not diretorio and tamanho > 0:
arquivos_excluidos.append({
'nome': nome_arquivo,
'tamanho': tamanho,
'criado': str(criado),
'modificado': str(modificado),
'numero_entrada': entrada.entry_number
})
# Detectar timestomping (tempo de modificação MFT != tempo de modificação $SI)
si_modificado = entrada.get_si_modified_timestamp()
fn_modificado = entrada.get_fn_modified_timestamp()
if si_modificado and fn_modificado:
if abs((si_modificado - fn_modificado).total_seconds()) > 86400: # >1 dia de diferença
arquivos_suspeitos.append({
'nome': nome_arquivo,
'si_modificado': str(si_modificado),
'fn_modificado': str(fn_modificado),
'delta': str(si_modificado - fn_modificado)
})
print(f"=== ARQUIVOS EXCLUÍDOS (metadados recuperáveis) ===")
print(f"Total: {len(arquivos_excluidos)}")
for f in arquivos_excluidos[:20]:
print(f" [{f['modificado']}] {f['nome']} ({f['tamanho']} bytes)")
print(f"\n=== POTENCIAL TIMESTOMPING ===")
print(f"Total suspeitos: {len(arquivos_suspeitos)}")
for f in arquivos_suspeitos[:10]:
print(f" {f['nome']}: $SI={f['si_modificado']}, $FN={f['fn_modificado']} (delta: {f['delta']})")
PYEOF
Etapa 3: Analisar Espaço Livre para Dados Ocultos
# Pesquisar strings no espaço livre
strings -a /cases/case-2024-001/ntfs/slack_space.raw > /cases/case-2024-001/analysis/slack_strings.txt
# Pesquisar padrões específicos no espaço livre
grep -iab "password\|secret\|confidential\|credit.card\|ssn" \
/cases/case-2024-001/ntfs/slack_space.raw > /cases/case-2024-001/analysis/slack_keywords.txt
# Analisar espaço livre de arquivo individual
python3 << 'PYEOF'
import struct
# O espaço livre do arquivo consiste em:
# 1. RAM slack: bytes entre o fim do arquivo e o próximo limite do setor (preenchido com conteúdo da RAM ou zeros)
# 2. Drive slack: setores restantes no cluster após o último setor do arquivo
# Analisar slack para entradas MFT específicas
# Usando Sleuth Kit para obter slack de arquivo para um arquivo específico
import subprocess
# Obter detalhes do arquivo
resultado = subprocess.run(
['istat', '-o', '2048', '/cases/case-2024-001/images/evidence.dd', '14523'],
capture_output=True, text=True
)
print(resultado.stdout)
# A saída mostra as execuções de dados - o último cluster pode conter dados de slack
# Calcular tamanho do slack: (allocated_size - file_size) bytes
PYEOF
# Pesquisar assinaturas de arquivos no espaço livre (arquivos incorporados)
foremost -t jpg,pdf,zip -i /cases/case-2024-001/ntfs/slack_space.raw \
-o /cases/case-2024-001/carved/slack_carved/
# Usar bulk_extractor para encontrar dados estruturados no slack
bulk_extractor -o /cases/case-2024-001/analysis/bulk_extract/ \
/cases/case-2024-001/ntfs/slack_space.raw
Etapa 4: Analisar o USN Change Journal
# Analisar Journal USN com MFTECmd
MFTECmd.exe -f "C:\cases\ntfs\UsnJrnl_J" --csv "C:\cases\analysis\" --csvf usn_journal.csv
# Análise Python do Journal USN
pip install pyusn
python3 << 'PYEOF'
import struct
import csv
from datetime import datetime, timedelta
def parse_usn_record(data, offset):
"""Analisar um único USN_RECORD_V2."""
if offset + 8 > len(data):
return None, offset
record_len = struct.unpack_from('<I', data, offset)[0]
if record_len < 56 or record_len > 65536 or offset + record_len > len(data):
return None, offset + 8
major_ver = struct.unpack_from('<H', data, offset + 4)[0]
if major_ver != 2:
return None, offset + record_len
mft_ref = struct.unpack_from('<Q', data, offset + 8)[0] & 0xFFFFFFFFFFFF
parent_ref = struct.unpack_from('<Q', data, offset + 16)[0] & 0xFFFFFFFFFFFF
usn = struct.unpack_from('<Q', data, offset + 24)[0]
timestamp = struct.unpack_from('<Q', data, offset + 32)[0]
reason = struct.unpack_from('<I', data, offset + 40)[0]
source_info = struct.unpack_from('<I', data, offset + 44)[0]
security_id = struct.unpack_from('<I', data, offset + 48)[0]
file_attrs = struct.unpack_from('<I', data, offset + 52)[0]
filename_len = struct.unpack_from('<H', data, offset + 56)[0]
filename_off = struct.unpack_from('<H', data, offset + 58)[0]
name = data[offset + filename_off:offset + filename_off + filename_len].decode('utf-16-le', errors='ignore')
# Converter Windows FILETIME para datetime
ts = datetime(1601, 1, 1) + timedelta(microseconds=timestamp // 10)
# Decodificar flags de razão
razoes = []
botoes_razao = {
0x01: 'DATA_OVERWRITE', 0x02: 'DATA_EXTEND', 0x04: 'DATA_TRUNCATION',
0x10: 'NAMED_DATA_OVERWRITE', 0x20: 'NAMED_DATA_EXTEND',
0x100: 'FILE_CREATE', 0x200: 'FILE_DELETE', 0x400: 'EA_CHANGE',
0x800: 'SECURITY_CHANGE', 0x1000: 'RENAME_OLD_NAME', 0x2000: 'RENAME_NEW_NAME',
0x4000: 'INDEXABLE_CHANGE', 0x8000: 'BASIC_INFO_CHANGE',
0x10000: 'HARD_LINK_CHANGE', 0x20000: 'COMPRESSION_CHANGE',
0x40000: 'ENCRYPTION_CHANGE', 0x80000: 'OBJECT_ID_CHANGE',
0x100000: 'REPARSE_POINT_CHANGE', 0x200000: 'STREAM_CHANGE',
0x80000000: 'CLOSE'
}
for flag, desc in botoes_razao.items():
if reason & flag:
razoes.append(desc)
registro = {
'timestamp': ts.strftime('%Y-%m-%d %H:%M:%S'),
'nome_arquivo': name,
'entrada_mft': mft_ref,
'entrada_pai': parent_ref,
'razoes': '|'.join(razoes),
'usn': usn
}
return registro, offset + record_len
# Analisar o journal
with open('/cases/case-2024-001/ntfs/UsnJrnl_J', 'rb') as f:
data = f.read()
registros = []
offset = 0
while offset < len(data) - 8:
registro, offset = parse_usn_record(data, offset)
if registro:
registros.append(registro)
else:
offset += 8 # Pular zeros
# Filtrar por eventos de exclusão
missoes = [r for r in registros if 'FILE_DELETE' in r['razoes']]
criacoes = [r for r in registros if 'FILE_CREATE' in r['razoes']]
renomeacoes = [r for r in registros if 'RENAME_NEW_NAME' in r['razoes']]
print(f"Total de registros USN: {len(registros)}")
print(f"Criações de arquivos: {len(criacoes)}")
print(f"Exclusões de arquivos: {len(missoes)}")
print(f"Renomeações de arquivos: {len(renomeacoes)}")
print("\n=== EXCLUSÕES RECENTES ===")
for r in missoes[-20:]:
print(f" [{r['timestamp']}] EXCLUÍDO: {r['nome_arquivo']} (MFT#{r['entrada_mft']})")
# Escrever o journal completo em CSV
with open('/cases/case-2024-001/analysis/usn_journal.csv', 'w', newline='') as f:
writer = csv.DictWriter(f, fieldnames=['timestamp', 'nome_arquivo', 'entrada_mft', 'entrada_pai', 'razoes', 'usn'])
writer.writeheader()
writer.writerows(registros)
PYEOF
Etapa 5: Detectar e Analisar Fluxos de Dados Alternativos
# Listar todos os Fluxos de Dados Alternativos na imagem
find /mnt/evidence -exec getfattr -d {} \; 2>/dev/null | grep -i "ads\|zone\|stream"
# Usando Sleuth Kit para encontrar ADS
fls -r -o 2048 /cases/case-2024-001/images/evidence.dd | grep ":" | \
tee /cases/case-2024-001/analysis/ads_list.txt
# Extrair conteúdo específico de ADS
# Formato: icat imagem inode:nome_ads
icat -o 2048 /cases/case-2024-001/images/evidence.dd 14523:hidden_stream \
> /cases/case-2024-001/analysis/extracted_ads.bin
# Verificar streams Zone.Identifier (rastreamento de origem de download)
fls -r -o 2048 /cases/case-2024-001/images/evidence.dd | grep "Zone.Identifier" | \
while read linha; do
inode=$(echo "$linha" | awk '{print $2}' | tr -d ':')
echo "=== $linha ==="
icat -o 2048 /cases/case-2024-001/images/evidence.dd "${inode}:Zone.Identifier" 2>/dev/null
echo ""
done > /cases/case-2024-001/analysis/zone_identifiers.txt
# O conteúdo de Zone.Identifier revela:
# [ZoneTransfer]
# ZoneId=3 (3 = Internet, indicando que o arquivo foi baixado)
# ReferrerUrl=https://malicious-site.com/payload.exe
# HostUrl=https://cdn.malicious-site.com/payload.exe
Conceitos-chave
| Conceito | Descrição |
|---|---|
| Espaço livre (slack) | Espaço não utilizado entre o fim do arquivo e o limite do cluster contendo dados residuais |
| RAM slack | Parte do slack do fim do arquivo até o limite do setor (historicamente preenchida com RAM) |
| MFT ($MFT) | Tabela Mestra de Arquivos - banco de dados de metadados NTFS com entradas para cada arquivo |
| Journal USN ($UsnJrnl) | Journal de alterações registrando todas as modificações de arquivos/diretórios no NTFS |
| Fluxos de Dados Alternativos | Recurso NTFS que permite múltiplos fluxos de dados por arquivo (armazenamento oculto) |
| $STANDARD_INFORMATION | Atributo MFT com timestamps modificáveis por aplicativos em modo de usuário |
| $FILE_NAME | Atributo MFT com timestamps modificáveis apenas pelo kernel |
| Timestomping | Técnica antiforense que modifica timestamps de arquivos para evitar detecção |
Ferramentas e Sistemas
| Ferramenta | Finalidade |
|---|---|
| MFTECmd | Analisador de MFT e Journal USN de Eric Zimmerman com saída CSV |
| MFTExplorer | Ferramenta GUI interativa para análise de MFT |
| analyzeMFT | Analisador Python de MFT com saída CSV/JSON |
| The Sleuth Kit | Kit de ferramentas forenses de sistema de arquivos (fls, icat, blkls, istat) |
| bulk_extractor | Extração de características de dados brutos, incluindo espaço livre |
| NTFS Log Tracker | Ferramenta para análise de registros de transação $LogFile |
| streams.exe | Ferramenta Sysinternals para listar Fluxos de Dados Alternativos NTFS |
| Plaso | Ferramenta de superlinha do tempo analisando MFT e Journal USN |
Cenários Comuns
Cenário 1: Detecção de Antiforense via Timestomping Compare os timestamps $STANDARD_INFORMATION com os timestamps $FILE_NAME nas entradas MFT, sinalize arquivos onde os timestamps $SI são anteriores aos timestamps $FN (impossível em operação normal), identifique arquivos com timestomping como evidência de manipulação deliberada, correlacione com outras evidências de linha do tempo.
Cenário 2: Dados Ocultos em Fluxos de Dados Alternativos Varra por ADS anexados a arquivos além do Zone.Identifier padrão, extraia o conteúdo do ADS para análise, verifique a existência de executáveis ou documentos ocultos armazenados em ADS, correlacione a criação do ADS com a linha do tempo de atividade do usuário, documente as descobertas como evidência.
Cenário 3: Reconstrução de Arquivos Excluídos a partir da MFT Analise a MFT em busca de entradas inativas (excluídas), extraia nomes, tamanhos e timestamps de arquivos excluídos, recupere o conteúdo do arquivo usando icat se os clusters de dados não forem sobrescritos, construa uma lista de arquivos de evidência excluídos, correlacione com eventos de exclusão do Journal USN.
Cenário 4: Reconstrução de Atividade de Arquivo a partir do Journal USN Analise o USN Change Journal para o período de investigação, identifique eventos de criação, modificação, renomeação e exclusão de arquivos, reconstrua a sequência de operações de arquivos, detecte evidências de preparação de dados (padrão criar, copiar, compactar, excluir), identifique limpeza antiforense de arquivos.
Formato de Saída
Análise de Artefatos de Sistema de Arquivos:
Volume: NTFS (Partição 2, 465 GB)
Tamanho do Cluster: 4096 bytes
Análise MFT:
Total de Entradas: 456.789
Arquivos Ativos: 234.567
Entradas Excluídas: 12.345 (8.901 com metadados recuperáveis)
Arquivos com Timestomping: 23 (incompatibilidade SI/FN detectada)
Journal USN:
Registros Analisados: 2.345.678
Intervalo de Datas: 2024-01-01 a 2024-01-20
Criações de Arquivos: 45.678
Exclusões de Arquivos: 23.456
Renomeações de Arquivos: 12.345
Fluxos de Dados Alternativos:
Total de ADS Encontrados: 1.234
Zone.Identifier: 890 (arquivos baixados)
ADS Personalizados/Suspeitos: 5 (dados ocultos detectados)
Espaço Livre:
Total de Espaço Livre: 12,3 GB
Ocorrências de Palavras-chave: 45 (senhas, cartões de crédito)
Arquivos Esculpidos: 23 do espaço livre
Descobertas Suspeitas:
- 23 arquivos com timestamps alterados (timestomping)
- 5 arquivos com ADS ocultos contendo dados
- USN mostra exclusão em massa em 2024-01-18 (antiforense)
…
[truncado para tradução]