Comprender el USN Journal de NTFS ($UsnJrnl:$J)

Una introducción práctica al Update Sequence Number Journal de NTFS: qué es, cómo está estructurado en disco y por qué es tan valioso en la forense de Windows.

Por 6 min de lectura

El Update Sequence Number Journal de NTFS es el artefacto al que recurro primero en cualquier host Windows cuando necesito saber qué tocó el disco y cuándo. Está habilitado por defecto en cada volumen NTFS que Windows ha enviado desde Vista, casi nadie lo desactiva y casi nadie lo lee antes de que algo vaya mal. Esa asimetría es lo que lo hace útil.

Cada creación, eliminación, renombrado, truncamiento, cambio de atributo y cierre de archivo en el volumen añade un registro a $UsnJrnl:$J. Los registros son pequeños, unas pocas docenas de bytes cada uno, y el archivo es un búfer circular de tamaño fijo, así que una máquina activa conserva aproximadamente los últimos varios millones de operaciones. En un escritorio con la asignación predeterminada de 32 MB son unos pocos días. En un servidor de archivos dimensionado a 1 GB son semanas.

Dónde reside realmente el journal

El journal no es un archivo normal. Es un flujo de datos alternativo llamado $J, adjunto al archivo de metadatos \$Extend\$UsnJrnl en la raíz del volumen. Dos consecuencias que pillan a quien no lo ha hecho antes:

  • Un arrastrar y soltar en Explorer, un xcopy o un robocopy copiará silenciosamente el flujo predeterminado vacío y acabarás con un archivo de cero bytes. Usa una herramienta que entienda ADS: FTK Imager, X-Ways, icat de The Sleuth Kit, RawCopy.exe, PowerForensics. El post complementario sobre extraer $J de una imagen de disco recorre cada uno.
  • Hay un flujo hermano llamado $Max en el mismo archivo de metadatos. Solo contiene metadatos de tamaño y configuración. Si extraes $Max por error, no obtienes nada útil. Elige siempre $J.

El archivo en sí es sparse. NTFS reserva el tamaño máximo configurado en disco pero deja la región inicial no usada a ceros. Cuando extraes $J de una imagen, los primeros cientos de megabytes suelen ser todo ceros antes de llegar al primer registro real. Cualquier parser razonable (usnrs, el USN-Journal-Parser de PoorBillionaire, MFTECmd, el parser WebAssembly de esta página) salta los ceros iniciales automáticamente.

Estructura del registro (USN_RECORD_V2)

Cada registro es una estructura USN_RECORD_V2. Microsoft documenta el layout en winioctl.h; el resumen campo por campo:

CampoBytesSignificado
RecordLength4Tamaño total del registro incluyendo el nombre de archivo
Major / Minor version2 + 22.0 para todo lo que verás en la práctica
FileReferenceNumber8Entrada MFT más secuencia del archivo
ParentFileReferenceNumber8Entrada MFT más secuencia del directorio padre
USN8Posición de este registro en el journal
Timestamp8Windows FILETIME (ticks de 100 ns desde 1601-01-01)
Reason4Máscara de bits que describe qué cambió
SourceInfo4Pistas (p. ej. DATA_MANAGEMENT para cambios provocados por el SO)
SecurityId4Índice en $Secure:$SII
FileAttributes4Atributos NTFS estándar
FilenameLength / Offset2 + 2Dónde vive el nombre UTF-16 dentro del registro
FilenamenUTF-16 LE, sin terminador nulo

La máscara Reason es el campo que más rinde. Es aditiva, no exclusiva: un único registro puede llevar FileCreate | DataExtend | Close juntos. Un ciclo de vida típico de un archivo se ve como FileCreate | DataExtendDataExtend | CloseBasicInfoChange | CloseFileDelete | Close. Reconstruir esa secuencia a posteriori es cómo sabes lo que realmente le pasó a un archivo. El post sobre Reason codes es la referencia de campo para cada bit.

Dos campos que el lector nuevo suele subestimar:

  • FileReferenceNumber es tu clave de join. Agrupa los registros por este y tendrás la historia completa del archivo a través de renombrados, cambios de atributo y eliminación. Ten en cuenta que los 16 bits superiores son un número de secuencia: un archivo borrado y un archivo nuevo asignado a la misma entrada MFT compartirán la entrada pero diferirán en la secuencia.
  • ParentFileReferenceNumber es la única información de ruta en el registro. El nombre de archivo es solo la hoja. Necesitas un $MFT parseado para recorrer la cadena de padres hasta una ruta completa. Adquiere ambos, siempre.

Por qué se gana su sitio en DFIR

Dos razones que aparecen en casi cada caso.

Primero, el journal sobrevive a la eliminación. Una vez que un archivo desaparece del espacio de nombres y su entrada MFT ha sido reutilizada, el registro de su creación, crecimiento, renombrado y eliminación sigue en $J hasta que el búfer circular pase por encima. Lo mismo para archivos vaciados de la papelera de reciclaje: los archivos $I de la papelera se borran, pero el journal recuerda. En una caza del "¿existió alguna vez este archivo?", el journal sobrevivirá a la entrada MFT y a los metadatos de la papelera la mayoría de las veces.

Segundo, es barato y autodescriptivo. Un $J de 100 MB típicamente contiene de cinco a diez millones de registros que abarcan días o semanas. Cada registro lleva su propia marca de tiempo, Reason, referencia de archivo y referencia padre. Puedes pasarlo por un parser en segundos y pivotar sobre cualquier campo. No hay otro artefacto Windows a ese precio.

Lo que no te dirá es quién. No hay usuario, ni proceso, ni línea de comandos en ningún registro. Para asociar un actor, correlaciona por marca de tiempo con el evento 4663 (Object Access, requiere SACLs que casi con seguridad no configuraste) de Security.evtx, el evento 11 de Sysmon (File Create) o los árboles de procesos del evento 1 de Sysmon. El journal responde al qué y al cuándo. Otros logs responden al quién.

Dónde encaja en la pila de artefactos

El modelo mental que uso, ordenado de "estado actual" a "historia pasada":

  • $MFT — el índice del estado actual. Cada archivo y directorio del volumen, incluyendo algunas entradas recientemente borradas. Cuatro timestamps por atributo, sin historia.
  • $UsnJrnl:$J — historia por operación, de días a semanas. De esto estás leyendo.
  • $LogFile — el log de transacciones de recuperación de crash de NTFS. De minutos a horas de imágenes antes/después en bruto. El post USN journal vs $MFT vs $LogFile desglosa cuándo recurrir a cuál.

Junta el journal con Prefetch, AmCache, Shimcache, archivos LNK y jump lists y normalmente puedes reconstruir qué se ejecutó, qué se abrió y qué tocó el disco al minuto.

Qué sigue

Si nunca has abierto un journal, la forma más rápida de construir intuición es coger uno de una máquina de pruebas y cargarlo en el parser arriba de esta página junto al $MFT correspondiente. Filtra por FileCreate y lee el archivo tal como Windows lo escribió. Los posts que siguen en esta serie profundizan en los flags Reason, el procedimiento de extracción y los patrones que el journal expone para ransomware, exfiltración, timestomping y recuperación de archivos borrados.

Lecturas adicionales

  • Microsoft Learn — Change Journals y la referencia USN_RECORD_V2.
  • usnrs de Airbus CERT — el parser open source de USN_RECORD_V2 más limpio que hay, que también alimenta el módulo WebAssembly de este sitio.
  • Brian Carrier, File System Forensic Analysis — el tratamiento de longitud de libro sobre las tripas de NTFS que todavía no ha sido reemplazado.