Chaque fichier NTFS a un attribut $DATA (type 0x80) contenant son contenu. Pour la plupart des fichiers, $DATA est non résident : l'en-tête de l'attribut porte une runlist pointant vers des cluster runs ailleurs sur le volume. Pour les petits fichiers, les octets vivent en ligne dans l'enregistrement MFT lui-même. Ce second cas est l'un des artefacts les plus utiles en forensique NTFS, et c'est celui que je continue à voir oublier par les analystes.
À quel point est-ce assez petit
Un enregistrement MFT fait 1 024 octets. Vous en soustrayez l'en-tête de 56 octets, le tableau fixup, $STANDARD_INFORMATION (environ 72 octets), au moins un $FILE_NAME (variable, environ 90 octets pour un nom typique), le padding et la sentinelle de fin d'attributs. Ce qui reste est le budget utilisable pour $DATA résident et tout autre attribut résident.
En pratique, le seuil est d'environ 700 octets de données. Certaines références disent jusqu'à ~750 octets pour des fichiers à très court nom et sans autres attributs résidents ; d'autres descendent (~580 octets) pour des fichiers à nom plus long ou avec des attributs supplémentaires. Le seuil n'est pas une constante ; il dépend de ce qu'il y a d'autre dans l'enregistrement.
Les fichiers qui rentrent, et que je vois constamment :
- Petits fichiers texte : notes, listes de choses à faire, brouillons.
- Petits fichiers de configuration :
.ini,.cfg, petits.json, petits.xml. - One-liners PowerShell et scripts courts.
- Petits fichiers
.lnkde raccourci (la plupart des LNK sont assez petits ; voir forensique LNK pour ce qu'ils encodent). - Snippets d'export registre et fichiers
.reg. - Petits fichiers
.batet.cmd. - Fichiers de certificat au format PEM qui sont courts.
- Fichiers vides et placeholders de zéro octet. Les « données » font zéro octet, et
$DATAest trivialement résident. - Beaucoup d'attributs
$INDEX_ROOTpour des petits répertoires.
Pourquoi cela importe
Un attribut $DATA résident peut être récupéré sans lire le reste du disque. Si vous avez une copie de $MFT, vous avez déjà :
- Le contenu complet des petits fichiers texte.
- Beaucoup de données
$INDEX_ROOTpour les répertoires (les entrées du répertoire, listées en ligne). - Des alternate data streams courts (attributs
$DATAnommés à petit contenu). - Des reparse points et cibles de symlinks (le chemin cible vit dans
$REPARSE_POINT, qui est aussi typiquement résident).
C'est une quantité surprenante de preuves à l'intérieur d'un seul extrait MFT de 200 Mo à 2 Go. Pour les petits fichiers supprimés, les octets résidents survivent même après que la zone de données a été écrasée, parce que la zone de données n'a jamais été touchée en premier lieu ; les octets vivaient dans la MFT.
J'ai récupéré :
- Un loader PowerShell de 320 octets depuis un enregistrement supprimé dont les clusters de données n'ont jamais existé.
- Un
.lnkde 480 octets pointant vers le répertoire de staging de l'attaquant, supprimé trois semaines avant l'acquisition. - Un
.configde 700 octets d'un service qui avait été désinstallé. Le répertoire avait disparu ; l'enregistrement MFT était à l'emplacement 412 000, intact.
La MFT était le seul endroit où ces fichiers existaient encore.
Résident n'est pas stable
Quand un fichier résident grandit au-delà de l'espace libre de l'enregistrement, NTFS le convertit en non résident. Les données se déplacent vers des clusters et $DATA devient une runlist. La conversion est journalisée dans $LogFile au fur et à mesure du changement.
L'inverse peut techniquement arriver (un fichier réduit en dessous du seuil pourrait redevenir résident), mais Windows le fait rarement volontairement. Une fois que $DATA est non résident, il tend à le rester. Si vous trouvez un enregistrement avec $DATA résident pour un fichier dont vous savez qu'il était plus grand, c'est inhabituel et ça mérite enquête ; cela suggère soit une manipulation délibérée, soit un chemin de code peu commun ayant réduit le fichier sur place.
Détecter les données résidentes pendant le parsing
Chaque parser MFT expose un drapeau « résident » dans l'en-tête de l'attribut $DATA. La sortie CSV de MFTECmd l'a comme colonne booléenne. Le JSON de mft_dump a header.is_resident. libmft l'expose sur l'objet attribut. istat du Sleuth Kit le montre dans le listing d'attributs.
Le parser navigateur de ce site filtre IN_USE=0 avec $DATA résident en deux clics. Ce filtre est le premier que je lance sur toute MFT extraite où la récupération de petits fichiers compte.
À quoi ressemblent les octets
Pour un $DATA résident, l'en-tête de l'attribut est suivi d'un en-tête résident de 16 octets (taille du contenu, offset du contenu, drapeau indexé, padding) puis des octets bruts. Les octets sont exactement ce qui était sur disque dans le fichier : du texte dans l'encodage utilisé par le fichier, du contenu binaire sous sa forme native. Aucune transformation.
Pour un petit fichier texte en UTF-8, vous pouvez copier les octets dehors et lire le fichier dans n'importe quel éditeur. Pour du contenu binaire (une petite PE, un .pyc compilé, un objet sérialisé), les octets sont également utilisables ; traitez-les comme un fichier extrait du volume.
Les limites
La plupart des fichiers ne sont pas résidents. Sur une installation Windows normale, bien moins de 5 % des fichiers utilisateurs sont résidents. Le reste, c'est tout Office, chaque fichier de cache du navigateur, chaque programme dans \Program Files\, chaque téléchargement. La récupération résidente est une niche à haute valeur, pas le mode par défaut.
Le seuil n'est pas exact. Ne supposez pas « les fichiers de moins de 700 octets sont récupérables ». Le seuil réel dépend des autres attributs de l'enregistrement. Testez sur la MFT spécifique que vous analysez.
Résident ne survit pas à la compression ou au chiffrement vers un fichier suffisamment grand. Les fichiers chiffrés EFS utilisent $LOGGED_UTILITY_STREAM pour les métadonnées de chiffrement et $DATA est le chiffré. Si le chiffré est assez petit pour être résident, vous avez les octets chiffrés ; il vous faut toujours la clé EFS pour les lire.
Un flux qui utilise cela
La prochaine fois que vous avez un cas de récupération de fichier supprimé et que le fichier qui vous intéresse est petit :
- Acquérez
$MFTdepuis l'hôte (ou un snapshot VSS, si vous en avez un récent). - Parsez-la. Sortez chaque enregistrement où
IN_USE=0et le$DATAprincipal est résident. - Filtrez par regex
$FILE_NAMEsi vous connaissez le nom du fichier. Filtrez par référence parent si vous connaissez le répertoire. - Inspectez les octets
$DATArésidents en ligne. Le fichier est là.
Cinq minutes de travail pour une récupération qui aurait pris des heures avec du carving de signature (et qui aurait probablement échoué de toute façon pour les petits fichiers fragmentés).
Lectures complémentaires
- Les notes sur les attributs résidents du projet linux-ntfs. Explication claire de la disposition de l'en-tête résident.
- Microsoft, Algorithme d'allocation NTFS. Référence officielle pour la décision résident/non résident.
istatdu Sleuth Kit pour inspecter des enregistrements individuels et voir le drapeau résident en contexte.