EDR Internals: Cómo Funcionan y Cómo se Evaden
Análisis técnico de la arquitectura interna de los EDR modernos. User-mode hooks, kernel callbacks, ETW consumers, minifilters, y las técnicas de evasión que usa el malware: unhooking, direct syscalls, BYOVD, callback removal. Perspectiva ofensiva y defensiva.
Cómo ve un EDR lo que ocurre en tu sistema
Un EDR (Endpoint Detection and Response) no es un antivirus con otro nombre. Es un sistema de monitorización y respuesta que opera en múltiples capas del sistema operativo simultáneamente. Entender su arquitectura interna es necesario tanto para los defensores (para saber qué cobertura tienen realmente) como para entender por qué ciertas técnicas de malware son efectivas.
Arquitectura de un EDR moderno
Las capas de monitorización
User Mode (Ring 3)
├── [Capa 1] Hooks en ntdll.dll
│ └── Interceptan: NtAllocateVirtualMemory, NtWriteVirtualMemory,
│ NtCreateThreadEx, NtMapViewOfSection, etc.
│
├── [Capa 2] ETW Consumers (user-mode)
│ └── Consumen: PowerShell, .NET, AMSI, DNS, WMI events
│
└── [Capa 3] AMSI Provider
└── Escanea: scripts PowerShell, VBScript, .NET assemblies
Kernel Mode (Ring 0)
├── [Capa 4] Kernel Callbacks
│ ├── PsSetCreateProcessNotifyRoutine (procesos)
│ ├── PsSetCreateThreadNotifyRoutine (threads)
│ ├── PsSetLoadImageNotifyRoutine (DLLs/images)
│ ├── ObRegisterCallbacks (handles/objects)
│ └── CmRegisterCallback (registro)
│
├── [Capa 5] ETW Consumers (kernel)
│ └── Microsoft-Windows-Threat-Intelligence (ETW-TI, PPL required)
│
├── [Capa 6] Minifilter Driver
│ └── Intercepta I/O de archivos (crear, escribir, eliminar)
│
└── [Capa 7] Network Filter
└── WFP (Windows Filtering Platform) para monitorizar red
Cloud
├── [Capa 8] Telemetría enviada al cloud
│ └── Análisis con ML, correlación entre endpoints, threat intel
│
└── [Capa 9] Respuesta remota
└── Aislar endpoint, matar proceso, rollback
User-mode hooks: la capa más visible y más atacada
Cuando un EDR se instala, modifica funciones clave de ntdll.dll en cada proceso. El mecanismo es un inline hook:
ntdll!NtAllocateVirtualMemory (ORIGINAL):
4C 8B D1 mov r10, rcx
B8 18 00 00 00 mov eax, 0x18 ; syscall number
0F 05 syscall
C3 ret
ntdll!NtAllocateVirtualMemory (HOOKEADA por EDR):
E9 XX XX XX XX jmp EDR_Hook_NtAllocVM ; salta al codigo del EDR
B8 18 00 00 00 mov eax, 0x18
0F 05 syscall
C3 ret
Los primeros bytes se reemplazan con un jmp al código del EDR. El EDR analiza los parámetros, decide si la llamada es legítima, y si lo es, ejecuta las instrucciones originales seguidas del syscall.
Funciones comúnmente hookeadas
| Función (ntdll) | Por qué la hookea el EDR |
|---|---|
NtAllocateVirtualMemory | Detectar asignación de memoria RWX (code injection) |
NtWriteVirtualMemory | Detectar escritura en memoria de otro proceso |
NtCreateThreadEx | Detectar creación de threads remotos |
NtProtectVirtualMemory | Detectar cambio de permisos (RW→RWX) |
NtMapViewOfSection | Detectar section mapping (PE loading) |
NtQueueApcThread | Detectar APC injection |
NtCreateFile | Monitorizar acceso a archivos sensibles |
NtOpenProcess | Detectar acceso cross-process |
NtDuplicateObject | Detectar duplicación de handles |
NtSetContextThread | Detectar modificación de contexto (hollowing) |
Kernel callbacks: la capa difícil de evadir
Los kernel callbacks se registran en el kernel del sistema operativo y notifican al EDR de eventos a nivel de sistema:
| Callback | Registrado con | Eventos |
|---|---|---|
| Process creation | PsSetCreateProcessNotifyRoutineEx | Todo nuevo proceso |
| Thread creation | PsSetCreateThreadNotifyRoutine | Todo nuevo thread |
| Image load | PsSetLoadImageNotifyRoutine | Toda DLL/EXE cargada |
| Handle operations | ObRegisterCallbacks | Apertura/duplicación de handles a procesos/threads |
| Registry | CmRegisterCallbackEx | Operaciones en el registro |
Estos callbacks se ejecutan en kernel mode. No se pueden bypassear con técnicas de user-mode (unhooking, syscalls directas). Solo se pueden evadir con acceso al kernel (BYOVD, kernel exploit).
Técnicas de evasión de EDR
Técnica 1: Unhooking de ntdll
Restaurar las funciones originales de ntdll.dll eliminando los hooks del EDR:
Método A: Leer ntdll.dll desde disco
// 1. Abrir ntdll.dll desde disco (copia limpia, sin hooks)
HANDLE hFile = CreateFile("C:\\Windows\\System32\\ntdll.dll", GENERIC_READ, ...);
// 2. Mapear en memoria
HANDLE hMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
LPVOID cleanNtdll = MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0);
// 3. Encontrar la seccion .text de la copia limpia
// 4. Copiar .text limpio sobre .text hookeado del ntdll cargado
LPVOID hookedNtdll = GetModuleHandle("ntdll.dll");
// ... copiar seccion .text de cleanNtdll a hookedNtdll
Método B: Leer desde KnownDlls
// KnownDlls es un directorio de objetos del kernel con copias limpias de DLLs del sistema
HANDLE hSection;
UNICODE_STRING name;
RtlInitUnicodeString(&name, L"\\KnownDlls\\ntdll.dll");
OBJECT_ATTRIBUTES objAttr;
InitializeObjectAttributes(&objAttr, &name, OBJ_CASE_INSENSITIVE, NULL, NULL);
NtOpenSection(&hSection, SECTION_MAP_READ, &objAttr);
// Mapear y copiar .text
Método C: Suspender threads del EDR y unhook
Suspender los threads del EDR temporalmente para que no detecte el unhooking:
// 1. Enumerar threads del proceso del EDR
// 2. SuspendThread() en cada uno
// 3. Unhook ntdll
// 4. Ejecutar payload
// 5. Re-hook ntdll (opcional)
// 6. ResumeThread() en cada uno
Técnica 2: Direct Syscalls
Eludir ntdll completamente ejecutando syscalls directamente:
; En vez de llamar a NtAllocateVirtualMemory en ntdll (hookeado):
; Ejecutar la syscall directamente
mov r10, rcx
mov eax, 0x18 ; Syscall number de NtAllocateVirtualMemory
syscall ; Ir directamente al kernel
ret
Problema: los syscall numbers cambian entre versiones de Windows. Herramientas que lo resuelven:
| Herramienta | Método |
|---|---|
| SysWhispers3 | Genera stubs con syscall numbers hardcodeados por versión |
| HellsGate | Lee los syscall numbers dinámicamente de ntdll en runtime |
| Halo's Gate | Como HellsGate pero funciona incluso si ntdll está hookeada (lee de funciones vecinas) |
| TartarusGate | Combinación de técnicas para máxima compatibilidad |
| RecycledGate | Reutiliza stubs de syscall de funciones no hookeadas |
Técnica 3: Indirect Syscalls
Variante de direct syscalls que es más difícil de detectar:
En vez de ejecutar 'syscall' en el codigo del malware (detectable por EDR como
codigo fuera de ntdll ejecutando syscall), el malware salta a la instruccion
'syscall' DENTRO de ntdll, pero DESPUES del hook.
Hook del EDR: jmp EDR_Hook ← saltar esto
Instrucciones: mov eax, 0x18
syscall ← saltar directamente aqui
ret
El EDR ve una syscall ejecutándose desde dentro de ntdll (normal), pero sin pasar por su hook (bypassed).
Técnica 4: BYOVD para eliminar kernel callbacks
Cubierto en detalle en el artículo de BYOVD. Desde kernel mode, el atacante puede desregistrar los callbacks del EDR, cegándolo a nivel de kernel.
Técnica 5: ETW patching
Patchear EtwEventWrite en ntdll para que no genere eventos ETW. Cubre solo eventos user-mode; los kernel-mode requieren kernel access.
Técnica 6: Thread stack spoofing
Modificar el call stack de un thread para que parezca legítimo durante la inspección del EDR:
Call stack real: Call stack spoofed:
malware.exe → inject_code svchost.exe → normal_function
(ocultar la llamada maliciosa)
Comparativa: qué evasión bypasea qué capa
| Técnica de evasión | User hooks | Kernel callbacks | ETW user | ETW kernel | Minifilter |
|---|---|---|---|---|---|
| Unhooking ntdll | Sí | No | No | No | No |
| Direct syscalls | Sí | No | Sí (parcial) | No | No |
| Indirect syscalls | Sí | No | No | No | No |
| ETW patching | No | No | Sí | No | No |
| BYOVD | Sí | Sí | Sí | Sí (si kernel) | Sí |
| Thread stack spoof | Parcial | No | No | No | No |
Conclusión: solo BYOVD (acceso al kernel) puede evadir todas las capas. Las técnicas de user-mode solo afectan hooks en ntdll y ETW user-mode. Un EDR con buena cobertura de kernel callbacks y ETW-TI sigue detectando actividad incluso si los hooks de user-mode son bypasseados.
Estado del arte en la defensa de EDR
Protección de hooks
Los EDR modernos implementan protección contra unhooking:
- Monitorizar VirtualProtect en ntdll: si alguien cambia los permisos de memory de ntdll, alertar
- Verificar periódicamente la integridad de hooks: comparar hooks actuales con estado esperado
- Hooks en kernel además de user-mode: incluso si el user-mode hook se elimina, el kernel callback sigue activo
Detección de syscalls directas
- Call stack analysis: verificar que las syscalls provienen de ntdll, no de código desconocido
- ETW-TI: eventos del kernel que se generan independientemente de hooks
- Kernel callbacks: siempre se invocan, independientemente de cómo se hizo la syscall
Protección PPL
Los EDR registran su proceso como PPL (Protected Process Light):
- No puede ser terminado por otros procesos (incluso admin)
- No puede ser inyectado
- Solo puede ser atacado desde kernel mode (BYOVD)
HVCI como última línea
Con HVCI activo, incluso los ataques BYOVD son limitados:
- El hypervisor valida la integridad del código del kernel
- Drivers vulnerables en la blocklist no pueden cargarse
- Modificaciones al código del kernel son detectadas
Para el defensor: verificar la cobertura de tu EDR
Preguntas que debes hacer a tu vendor de EDR
- ¿Usan hooks en user-mode, kernel callbacks, o ambos?
- ¿Consumen ETW-TI (requiere PPL)?
- ¿Detectan unhooking de ntdll?
- ¿Detectan direct/indirect syscalls (call stack validation)?
- ¿Protegen contra BYOVD (driver blocklist integrada)?
- ¿Su proceso corre como PPL?
- ¿Es compatible con HVCI?
Testing con herramientas abiertas
| Herramienta | Qué testea |
|---|---|
| Atomic Red Team | Técnicas ATT&CK genéricas |
| ScareCrow | Evasión de EDR con payloads de Cobalt Strike |
| SharpBlock | Testea bypass de ETW y AMSI |
| InlineWhispers | Direct syscalls para testing |
| NimBlackout | BYOVD testing (terminar procesos PPL) |
Mapeo MITRE ATT&CK
| Técnica | ID | Contexto EDR |
|---|---|---|
| Impair Defenses: Disable or Modify Tools | T1562.001 | Desactivar EDR (BYOVD, unhook) |
| Process Injection | T1055 | Evasión de hooks via injection techniques |
| Exploitation for Privilege Escalation | T1068 | BYOVD para kernel access |
| Indicator Removal | T1070 | Eliminar evidencia post-evasión |
| Execution Guardrails | T1480 | Malware que solo ejecuta si puede evadir EDR |
Fuentes y referencias
- Russinovich, M. et al. "Windows Internals Part 1." Microsoft Press, 2017.
- Yosifovich, P. "Windows Kernel Programming." 2023.
- Outflank. "Direct Syscalls vs Indirect Syscalls." 2022.
- MDSec. "Bypassing User-Mode Hooks and Direct Invocation of System Calls." 2020.
- Elastic Security Labs. "EDR Architecture and Evasion." 2023.
- jthuraisamy. "SysWhispers3." https://github.com/jthuraisamy/SysWhispers
- am0nsec & smelly_vx. "HellsGate." 2020.
- CrowdStrike. "EDR Evasion Techniques in Modern Attacks." 2024.
- MITRE ATT&CK. "Impair Defenses (T1562)." https://attack.mitre.org/techniques/T1562/
- Red Canary. "EDR Evasion in 2024." Red Canary, 2024.
Preguntas frecuentes
Libros recomendados
Artículos relacionados
ETW para Detección de Malware: Event Tracing for Windows en Profundidad
BYOVD: Bring Your Own Vulnerable Driver y Drivers Maliciosos en Windows
AMSI Bypass: Técnicas de Evasión y Contramedidas Defensivas
De Alerta EDR a Informe Ejecutivo: Caso End-to-End Completo
Analisis de DLLs y Handles en Memoria: dlllist, handles y Deteccion de Inyeccion
Analisis de Procesos en Memoria Windows: pslist, pstree y Deteccion de Anomalias
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.