La lista de marcas temporales en un .pf es lo que la mayoría de analistas lee. La lista de archivos que el ejecutable tocó es lo que sobrevuelan. Ese orden está al revés, y he construido más de una investigación a partir de patrones encontrados en la lista de carga que las marcas temporales solas nunca habrían sacado a la luz.
Este post va de leer el array de recursos. Qué captura. Qué no. Y las tres o cuatro formas que te dicen que algo está raro en cuanto ordenas la columna adecuada de la manera correcta.
Qué contiene en realidad la lista de carga
El servicio Prefetch observa los primeros diez segundos de vida de un proceso. Cada archivo cuya ruta resuelve el kernel durante esa ventana acaba en el array de métricas de archivos, con la ruta real almacenada como cadena UTF-16LE en Section C. Cuando llegas a leer la salida parseada, esas rutas resueltas se ven como \DEVICE\HARDDISKVOLUME2\Windows\System32\kernel32.dll y \DEVICE\HARDDISKVOLUME2\Users\bob\Documents\contract.docx.
Esto es más amplio que "DLLs cargadas". Incluye:
- Todas las DLLs cargadas por el proceso del binario, incluidas las cargadas por delay-load, siempre que la carga ocurra dentro de la ventana de traza.
- Archivos abiertos (en el sentido de
CreateFile) por el proceso. Lecturas en particular, porque Windows rastrea fallas de página; algunas escrituras también, según la versión. - Archivos de configuración, archivos de datos, documentos, archivos de hives de registro si el proceso los abre directamente vía el sistema de archivos en lugar de la API de registro.
- DLLs y archivos tocados por procesos hijos que el binario lanzó en sus primeros diez segundos, en algunas builds de Windows.
El límite anda en unas 1024 entradas por archivo .pf. En binarios muy parlanchines se llega al tope y el resto se descarta. En la mayoría de binarios obtienes el conjunto completo.
La ventana de traza es fija en aproximadamente diez segundos. Un binario que se queda quieto treinta segundos y luego carga su payload real tendrá una lista engañosamente pequeña, porque el trabajo pesado ocurrió fuera de la ventana.
Qué no contiene
Las escrituras se registran parcialmente pero no de forma fiable. Los archivos que el proceso creó y nunca volvió a abrir podrían no aparecer. Si necesitas un registro completo de archivos escritos, necesitas Sysmon Event ID 11 en EVTX o el USN journal.
La línea de comandos no está en la lista de carga. Los argumentos pasados al ejecutable no están en ninguna parte del archivo Prefetch. Si el binario leyó sus argumentos y luego abrió archivos basándose en ellos, las rutas resultantes aparecen. Los argumentos en sí, no.
Los sockets de red no están en la lista de carga. Prefetch es un artefacto del sistema de archivos.
Los hijos lanzados fuera de la ventana de diez segundos no están en la lista de carga del padre, aunque tienen sus propios archivos .pf (sujeto a las salvedades habituales de SSD/servidor).
Patrón uno: secuestro del orden de búsqueda de DLL
El patrón clásico de detección. Un binario de confianza como winword.exe o mstsc.exe debería cargar sus DLLs desde \System32\ (y un puñado de directorios bien conocidos). Un .pf cuya lista de carga muestre al binario de confianza cargando version.dll o cryptbase.dll desde \AppData\Local\Temp\ o \Users\Public\ es un secuestro del orden de búsqueda hasta que se demuestre lo contrario.
Las formas a buscar:
\DEVICE\HARDDISKVOLUME2\WINDOWS\SYSTEM32\KERNEL32.DLL <- normal
\DEVICE\HARDDISKVOLUME2\USERS\PUBLIC\VERSION.DLL <- no normal
A falta de una línea base buena, la regla es: las DLLs de sistema que cargan desde cualquier sitio que no sea \Windows\System32\ (o \SysWOW64\ para procesos de 32 bits, o \Program Files\<vendor>\ para las DLLs propias redistribuidas del vendor) merecen un vistazo más cercano. Algunas serán side-by-side legítimos. La mayoría no.
Ordena la lista de carga por prefijo de ruta, agrupa todo fuera de \Windows\ y \Program Files\ y mira lo que queda. En un host limpio, lo que queda serán mayormente los archivos de datos del propio binario y documentos de usuario. En un host comprometido, las DLLs depositadas serán evidentes.
Patrón dos: rutas de configuración de malware
El malware de commodity tiende a almacenar su configuración en una ubicación fija relativa a sí mismo o a su directorio de instalación. Nombres como config.bin, settings.dat, tox.lic, client.json y key.dat aparecen en listas de carga con desalentadora regularidad. La ruta suele estar bajo \AppData\Roaming\<alguna carpeta>\, \ProgramData\<alguna carpeta>\ o directamente junto al binario.
Esto te da dos cosas. Primero, la existencia de un archivo de configuración en una ruta específica en un momento específico, aunque el archivo haya sido borrado desde entonces. Segundo, un nombre por el que buscar en el resto del host: si \AppData\Roaming\nzhxxk\config.bin está en una lista de carga .pf, puedes pivotar a la MFT, el USN journal y el registro para averiguar cuándo se escribió, modificó y borró la configuración.
He construido líneas de tiempo donde la lista de carga Prefetch era lo único que me decía que jamás existió un archivo de configuración. El archivo había desaparecido, el directorio había desaparecido, y la única evidencia que sobrevivía era la cadena de ruta horneada en una Section C de .pf.
Patrón tres: aperturas de documentos
Si la pregunta es "¿abrió este usuario este documento?", Prefetch puede corroborarlo. Un .pf para winword.exe o acrord32.exe cuya lista de carga contenga \Users\<usuario>\Documents\<docname> o \Users\<usuario>\Downloads\<docname> dice que la aplicación abrió ese archivo durante una de sus ejecuciones rastreadas.
Las advertencias: la lista de carga no te dice qué ejecución abrió qué archivo. Si el .pf muestra ocho tiempos de ejecución y la lista de carga muestra tres documentos, no puedes mapear directamente "documento X se abrió durante la ejecución Y". La lista es acumulativa a lo largo de la ventana de traza de las ejecuciones más recientes, con entradas antiguas siendo sobrescritas a medida que el binario se vuelve a trazar.
Para aperturas de documentos por instancia, combina la evidencia Prefetch con archivos LNK, jump lists y las propias listas MRU de Office en el registro. La entrada Prefetch corrobora que la ruta del archivo fue resuelta por el proceso; los artefactos por usuario te dicen cuándo y desde dónde la invocó el usuario.
Patrón cuatro: divulgación de configuración por binarios legítimos
Este aparece en casos de insider threat y propiedad intelectual más que en los de malware, pero merece la pena destacarlo.
Cuando un proceso toca un archivo, esa ruta entra en la lista de carga, sea el archivo sensible o no. Si un script ejecutó findstr.exe sobre un directorio de RR. HH., el .pf para findstr.exe registrará las rutas resueltas de los archivos que efectivamente abrió. Lo mismo para tar.exe, 7z.exe, xcopy.exe y cualquier otra herramienta de archivado o copia.
Lo que esto significa en la práctica: cuando sospeches exfiltración de datos involucrando una utilidad conocida, saca el .pf de esa utilidad y mira su lista de carga. Los archivos que la utilidad tocó estarán ahí. No conseguirás los argumentos de línea de comandos, pero conseguirás una enumeración parcial de los archivos implicados.
El patrón también sirve para detectar reconocimiento. Un .pf para whoami.exe o net.exe es normal en la mayoría de hosts; un .pf para nltest.exe con rutas \Windows\debug\ en la lista de carga es reconocimiento hasta que se demuestre lo contrario. La lista de carga te dice qué consultó realmente la herramienta.
Patrón cinco: arqueología del directorio temp
Los directorios \AppData\Local\Temp\ y \Windows\Temp\ acumulan ejecutables y DLLs que los atacantes dejan atrás. Muchos se borran, a veces segundos después de ser usados. Los .pf de los binarios que los leyeron, sin embargo, sobreviven.
Una lista de carga Prefetch para rundll32.exe que muestre \AppData\Local\Temp\evil.dll te dice que esa DLL fue cargada por rundll32.exe, que la DLL estaba en esa ruta en ese momento, y (puesto que el .pf de rundll32.exe se actualiza en cada ejecución) que alguna ejecución reciente de rundll32 hizo exactamente eso. Si el archivo ya no está ahí, tienes evidencia de su existencia y uso previos.
Combina con las marcas temporales de resolución de ruta de la lista de carga (campos de inicio/duración de Section A) y a veces puedes ordenar las cargas dentro de la ventana de traza. La granularidad no es magnífica, pero basta para distinguir "cargado inmediatamente al inicio" de "cargado cerca del final de la ventana de traza".
Trabajar la lista de carga con eficiencia
Un workflow que sale rentable:
- Parsea el
.pfa un CSV plano con columnas: ejecutable, ruta del ejecutable, contador de ejecuciones, última ejecución, ruta cargada. Una fila por par (ejecutable, ruta cargada). - Filtra las rutas que empiecen por
\Windows\,\Program Files\,\Program Files (x86)\. Es mayoritariamente ruido en un host donde aún no has identificado anomalías. - Ordena lo que queda por ejecutable y luego por ruta. Mira qué tocó cada binario fuera de los directorios estándar.
- Para los binarios que tocaron directorios escribibles por el usuario (
\AppData\,\ProgramData\,\Users\Public\,\Temp\), comprueba si los archivos siguen ahí. Empareja con la MFT. - Para hits interesantes, saca las entradas correspondientes de Sysmon EID 11 (file create) o EID 7 (image load) en EVTX para corroborar.
El paso 2 es lo que hace manejable la lista de carga. Un volcado de lista de carga en bruto para un host activo puede ser de cientos de miles de líneas. Filtrar a rutas no-sistema lo reduce a algo que puedes leer de verdad.
Lo que la lista de carga no es
La lista de carga no es un log de comportamiento del proceso. Es una instantánea de rutas que Windows resolvió durante una ventana de traza corta. Trátala como una visión de alta recall y baja precisión de "archivos que este proceso tocó". Algunas entradas serán incidentales. Algunas serán evidencia. La destreza está en saber cuál es cuál.
La buena noticia es que los patrones anteriores se sostienen para la mayoría de binarios y actores de amenaza que he visto en la práctica. En cuanto pases una tarde mirando listas de carga sobre unas docenas de archivos Prefetch de una intrusión real, empezarás a detectar las anomalías antes de poder explicar por qué.
Lecturas adicionales
- Yogesh Khatri, Some practical analysis of Prefetch and other artifacts: ejemplos de listas de carga que vale la pena leer.
- Eric Zimmerman, PECmd: su salida CSV es la forma más limpia de meter la lista de carga en una hoja de cálculo para el workflow de filtrado anterior.
- MITRE ATT&CK, T1574.001 DLL Search Order Hijacking: la técnica que el patrón uno detecta.