Skip to content

Las operaciones redo/undo del $LogFile de NTFS, explicadas

La cabecera de un registro del diario NTFS byte a byte, los 38 opcodes redo/undo, cuáles importan en forense y cómo se ven creación, borrado y renombrado.

Publicado el 9 min de lectura

TL;DR. Un registro del diario de NTFS es una cabecera LFS (LSN, LSN anterior, id. de transacción…) seguida de una cabecera NTFS: opcode redo, opcode undo, dónde están los bytes redo y undo, a qué atributo abierto se refieren y en qué punto de él. Existen treinta y ocho opcodes (0x00–0x25), pero unos pocos concentran casi todo el valor forense: InitializeFileRecordSegment, DeallocateFileRecordSegment, CreateAttribute / DeleteAttribute, UpdateResidentValue y las cuatro operaciones Add/DeleteIndexEntry*. Todo lo demás es contabilidad interna que, aun así, necesitas para resolver los destinos y agrupar los registros.

Si $LogFile te parece opaco es porque una sola acción del usuario —«renombrar este archivo»— se convierte en varios registros, y cada uno solo tiene sentido con el contexto adecuado: qué atributo está abierto con qué índice, qué tamaño de clúster usa el volumen, cómo era el registro FILE antes. Este artículo es la tabla de descifrado. La visión de conjunto está en Análisis forense del $LogFile de NTFS: la guía completa.

Dos cabeceras por registro

Todo registro del diario empieza con la cabecera de registro LFS, seguida de los datos del cliente. En el caso del cliente NTFS, esos datos empiezan con su propia cabecera. Desplazamientos tomados del fslog.c de ntfs3 de Linux:

Cabecera de registro LFS (0x30 bytes)

DesplazamientoTamañoCampo
0x008LSN del registro
0x088LSN anterior del cliente (misma transacción)
0x108LSN undo siguiente del cliente
0x184Longitud de los datos del cliente
0x1C4Id. del cliente
0x204Tipo de registro: 1 = registro de cliente, 2 = reinicio del cliente
0x244Id. de transacción
0x282Flags (0x1 = el registro continúa en la página siguiente)

Cabecera del registro NTFS (inicio de los datos del cliente)

DesplazamientoTamañoCampoUso
0x002Operación redoQué hacer
0x022Operación undoCómo revertirlo
0x04 / 0x062 + 2Desplazamiento / longitud redoDónde están los bytes redo
0x08 / 0x0A2 + 2Desplazamiento / longitud undoDónde están los bytes undo
0x0C2Atributo destinoÍndice en la tabla de atributos abiertos
0x0E2LCN que siguenNúmero de números de clúster en 0x20
0x102Desplazamiento de registroDesplazamiento del atributo dentro del registro FILE (o de la entrada dentro de un búfer de índice)
0x122Desplazamiento de atributoDesplazamiento del cambio dentro de ese atributo
0x142Desplazamiento de bloque de clústerEn unidades de 512 bytes, dentro del clúster destino
0x188VCN destinoClúster virtual en el atributo destino
0x208 × nLCN destinoClústeres físicos

Los registros de tipo 2 son checkpoints (áreas de reinicio del cliente), no cambios. Los trata el artículo $LogFile: el área de reinicio y los LSN, explicados.

Encontrar la entrada de la MFT que toca un registro

Los registros no dicen «entrada 322 de la MFT». Dicen «atributo n.º N de la tabla de atributos abiertos, VCN v, desplazamiento de bloque de clúster c». Cuando el atributo destino es $MFT:$DATA, la entrada es:

entry = (VCN × cluster_size + cluster_block_offset × 512) / mft_record_size

Hacen falta dos parámetros del volumen. Un analizador puede tomarlos del sector de arranque o, como hace el analizador de $LogFile en el navegador, calibrarlos a partir del propio diario: los registros InitializeFileRecordSegment contienen una imagen completa de un registro FILE cuya cabecera indica su propio número de registro, así que gana la combinación de tamaño de clúster y tamaño de registro que ubica correctamente la mayoría de ellos.

La tabla de atributos abiertos se reconstruye a partir de los registros OpenNonresidentAttribute y de los OpenAttributeTableDump y AttributeNamesDump escritos en cada checkpoint. La estructura de sus entradas varía según la versión del cliente NTFS (0x28 frente a 0x2C bytes), y ese es uno de los puntos en los que los analizadores pueden discrepar.

Las 38 operaciones, agrupadas

Los nombres siguientes son los del enum NTFS_LOG_OPERATION de ntfs3, que también usa el analizador.

CódigoOperaciónGrupoValor forense
0x00Noop—Parte undo de muchos registros que solo tienen redo
0x01CompensationLogRecordTransacciónSe escribe durante una reversión
0x02InitializeFileRecordSegmentRegistro FILEAlto: imagen completa del registro FILE de una entrada nueva o reutilizada
0x03DeallocateFileRecordSegmentRegistro FILEAlto: entrada liberada (borrado)
0x04WriteEndOfFileRecordSegmentRegistro FILEBajo
0x05CreateAttributeAtributoAlto: atributo nuevo, p. ej. $FILE_NAME o $DATA residente
0x06DeleteAttributeAtributoAlto: atributo eliminado, p. ej. el $FILE_NAME antiguo en un renombrado
0x07UpdateResidentValueAtributoAlto: horas de $STANDARD_INFORMATION, valores residentes pequeños
0x08UpdateNonresidentValueAtributoMedio: búferes de índice, escrituras en $UsnJrnl, otros flujos
0x09UpdateMappingPairsAtributoMedio: data runs (crecimiento del archivo)
0x0ADeleteDirtyClustersAtributoBajo
0x0BSetNewAttributeSizesAtributoMedio: cambios de tamaño
0x0C / 0x0DAdd / DeleteIndexEntryRootÍndiceAlto: entradas de carpeta en $INDEX_ROOT
0x0E / 0x0FAdd / DeleteIndexEntryAllocationÍndiceAlto: entradas de carpeta en $INDEX_ALLOCATION
0x10WriteEndOfIndexBufferÍndiceBajo
0x11 / 0x12SetIndexEntryVcnRoot / AllocationÍndiceBajo (fontanería del árbol B)
0x13 / 0x14UpdateFileNameRoot / AllocationÍndiceMedio: información de $FILE_NAME duplicada en $I30
0x15 / 0x16Set / ClearBitsInNonresidentBitMapBitmapBajo por sí solo: asignación de clústeres o de entradas de la MFT
0x17HotFix—Raro
0x18EndTopLevelActionTransacciónEstructura
0x19–0x1BPrepare / Commit / ForgetTransactionTransacciónLímites de las transacciones
0x1COpenNonresidentAttributeTablaNecesario para resolver los destinos
0x1D–0x20Volcados OpenAttributeTable / AttributeNames / DirtyPageTable / TransactionTableCheckpointNecesarios para resolver los destinos
0x21 / 0x22UpdateRecordDataRoot / AllocationÍndiceMedio: índices distintos de $I30 ($ObjId, $Quota, $Secure)
0x23 / 0x24UpdateRelativeDataInIndex / 2ÍndiceBajo
0x25ZeroEndOfFileRecordRegistro FILEBajo

El undo suele ser el inverso del redo: AddIndexEntryRoot se deshace con DeleteIndexEntryRoot, CreateAttribute con DeleteAttribute, SetBits… con ClearBits…, y UpdateResidentValue con otro UpdateResidentValue que lleva los bytes antiguos. Las operaciones cuya reversión no necesita nada, como inicializar un registro FILE nuevo, suelen tener Noop como undo.

Cómo se ven las acciones habituales

Estos son los patrones que busca un analizador. Las secuencias reales contienen más registros (bitmaps, tamaños, seguridad, escrituras USN) y el orden exacto puede variar según la versión de Windows.

Creación de un archivo

  • InitializeFileRecordSegment sobre la nueva entrada. Redo = imagen completa del registro FILE: cabecera (número de secuencia, flags), $STANDARD_INFORMATION, $FILE_NAME (nombre, referencia del padre, cuatro horas) y quizá un $DATA pequeño.
  • AddIndexEntryRoot o AddIndexEntryAllocation en el índice $I30 de la carpeta padre. La entrada de índice incluye una copia de $FILE_NAME.

Borrado

  • DeleteIndexEntryRoot / DeleteIndexEntryAllocation, que quitan el nombre de la carpeta padre (la entrada eliminada está en los bytes undo, a veces en los redo).
  • DeallocateFileRecordSegment sobre la entrada, con ClearBitsInNonresidentBitMap sobre el bitmap de la MFT.

Renombrado o movimiento

  • dfir.ru documenta un renombrado como DeleteIndexEntryRoot, DeleteAttribute ($FILE_NAME antiguo), CreateAttribute ($FILE_NAME nuevo), AddIndexEntryRoot (How the $LogFile works?). Un movimiento es lo mismo con otra carpeta padre en la entrada añadida. En carpetas grandes aparecen las variantes …Allocation en lugar de …Root.

Cambio de marca de tiempo

  • UpdateResidentValue sobre el atributo $STANDARD_INFORMATION (el primer atributo de un registro FILE, en el desplazamiento 0x38 en los registros de NTFS 3.1). El redo contiene los bytes nuevos; el undo, los antiguos. La actualización puede ser parcial —dfir.ru señala que puede empezar en la marca M—, así que un analizador debe usar el desplazamiento de atributo para saber cuál de las cuatro horas está leyendo. Detectar timestomping con el $LogFile de NTFS se basa en esto.

Contenido de archivos pequeños

  • Un atributo $DATA residente creado con CreateAttribute o incluido en la imagen del registro FILE puede llevar los primeros bytes de un archivo pequeño. Las ediciones posteriores son otra historia: la documentación de LogFileParser indica que, en Windows moderno, el contenido de los cambios en datos residentes no suele guardarse, solo el hecho de que hubo un cambio. Consulta $LogFile: recuperar evidencias de archivos borrados.

Agrupar registros en acciones

Dos enfoques:

  1. Por transacción. Usar el id. de transacción y la cadena de LSN anteriores, delimitada por registros ForgetTransaction o de confirmación. Es el más fiel, pero más difícil de hacer bien a través de los checkpoints.
  2. Por entrada de la MFT y proximidad. Tomar los registros que tocan la misma entrada dentro de una ventana de LSN. Es más sencillo y resiste mejor las páginas dañadas, pero dos acciones sin relación sobre el mismo archivo, muy seguidas, pueden fusionarse.

El analizador en el navegador usa por ahora el segundo enfoque (una ventana de ±256 registros por entrada de la MFT) y expone cada registro en bruto para que puedas comprobar la agrupación. La agrupación por transacciones está en su hoja de ruta; conviene usar LogFileParser y NTFS Log Tracker para confirmar.

Leer los bytes uno mismo

Cuando un evento importa, abre el registro en bruto: comprueba los opcodes redo/undo, el atributo destino (por ejemplo #322:$STANDARD_INFORMATION o $MFT:$DATA), el desplazamiento de registro, el desplazamiento de atributo y el hexadecimal. Un undo de 32 bytes en el desplazamiento de atributo 0 de $STANDARD_INFORMATION contiene cuatro valores FILETIME en little-endian: creación, modificación, cambio MFT y acceso.

Preguntas frecuentes

¿Qué son las operaciones redo y undo del $LogFile?

Cada registro del diario de NTFS describe un cambio de metadatos dos veces: la operación y los bytes redo indican cómo volver a aplicar el cambio, y la operación y los bytes undo, cómo revertirlo. La recuperación reaplica redo en las transacciones confirmadas y aplica undo en las no confirmadas.

¿Qué operaciones del $LogFile muestran la creación o el borrado de un archivo?

Una creación suele mostrar InitializeFileRecordSegment sobre el nuevo registro FILE más AddIndexEntryRoot o AddIndexEntryAllocation en la carpeta padre. Un borrado muestra DeleteIndexEntryRoot o DeleteIndexEntryAllocation seguido de DeallocateFileRecordSegment.

Para saber más

Artículos relacionados

Artículos relacionados