RexLDR | Anatomía de un Shellcode Loader Moderno: Técnicas, Evidencia y Perspectiva Dual

El 90% de los loaders mueren en los primeros 30 segundos
Dato de contexto antes de entrar en materia: según la telemetría de Microsoft Cyber Signals 2025, más del 90% de los intentos de inyección de proceso basados en herramientas públicas son neutralizados antes de que el payload complete la ejecución. Ese número suena alto hasta que ves el código de lo que está usando la mayoría. La tríada VirtualAllocEx → WriteProcessMemory → CreateRemoteThread, documentada en posts de 2012, sigue siendo el patrón central de prácticamente todos los loaders genéricos que circulan. Nadie la cambió porque funcionaba. Ahora no funciona y siguen sin cambiarla.
Este post no es una guía de bypass de AV en cinco pasos. Hay cincuenta de esos y todos envejecen mal. Lo que vas a leer es la documentación técnica de RexLDR, el loader que usé en un engagement el 11 de abril de 2026 para abrir una sesión Meterpreter en Windows 11 con Defender activo. Explico qué hace cada técnica, qué hubiera detectado Defender si no estuviera implementada, qué ve el Blue Team igualmente a pesar del bypass, y dónde falla el loader, porque ninguno es perfecto y fingir que sí lo es es la señal más clara de que alguien no entiende lo que hace.
Por qué los loaders genéricos siguen fallando
Windows Defender opera en dos capas y hay que considerar las dos al mismo tiempo.
La primera es análisis estático: escanea el binario antes de ejecutarlo buscando importaciones sospechosas en el IAT, strings reconocibles, secciones con alta entropía. La mayoría de loaders genéricos mueren aquí porque nadie se molesta en ocultar los imports.
La segunda capa es comportamental. Una vez en ejecución, monitoriza llamadas a APIs, telemetría ETW, patrones de acceso a memoria. Esta es la parte difícil porque para evadirla hay que entender exactamente qué está observando el motor, no solo saber qué funciones llamas.
RexLDR ataca las dos capas. Lo que sigue es la explicación técnica.
Punto de partida
Antes de entrar en la arquitectura, contexto importante: la base de RexLDR la saqué del repositorio de 7h3w4lk3r en https://github.com/7h3w4lk3r/RexLdr. El loader original ya tenía la estructura correcta y algunas ideas buenas, pero en entornos con Defender actualizado a 2026 detectaba sin problemas. Lo que hice fue partir de esa base y añadir las capas de evasión que se documentan en este post: el PEB Walker completo para eliminar imports del IAT, RC4 propio para quitar la dependencia de Advapi32, ETW y AMSI patching, la detección de sandbox, y cambiar la inyección clásica por MapView. El resultado final es diferente en lo que importa, que es el bypass, pero el crédito del punto de partida es suyo. En este mundillo hay demasiada gente que toma trabajo ajeno sin mencionarlo.
Arquitectura del loader
La secuencia de ejecución está diseñada para reducir la superficie de detección en cada paso:
[Sandbox check] ---- falla ----> [Exit limpio sin IOC]
|
v
[ETW patch] -> Silenciar telemetría de user-mode
|
v
[AMSI patch] -> Desactivar escaneo en proceso actual
|
v
[Sleep + timing check] -> Anti-aceleración de sandbox
|
v
[RC4 decrypt] -> Payload en memoria, solo en runtime
|
v
[PEB Walker] -> Resolver funciones NT sin imports visibles
|
v
[NtQuerySystemInformation] -> Localizar explorer.exe sin APIs ruidosas
|
v
[NtCreateSection + NtMapViewOfSection] -> Inyección sin tríada clásica
|
v
[NtCreateThreadEx] -> Ejecución en proceso remoto
Cada bloque tiene una razón de ser. Voy por partes.
Las técnicas
PEB Walker: la base de todo lo demás
Empiezo por aquí porque el resto del loader depende de esto.
El problema con llamar a GetProcAddress y GetModuleHandle directamente es que aparecen en el IAT del binario. Cualquier motor estático que mire el binario ve "este proceso importa NtCreateThreadEx, NtMapViewOfSection y NtQuerySystemInformation" y ya sabe lo que está pasando antes de que el programa arranque. Ni siquiera necesita ejecutarlo.
El PEB Walker resuelve eso caminando manualmente la estructura PEB del proceso: PEB → Ldr → InMemoryOrderModuleList, que contiene la lista de módulos cargados. Se itera sobre ellos, se comparan hashes de los nombres de funciones exportadas, y se obtiene la dirección de lo que necesitas sin que esa dependencia aparezca en ningún sitio del binario.
Resultado práctico: el IAT del loader no tiene ni una importación sospechosa. Si alguien lo abre con PEBear, ve un binario que aparentemente no hace nada interesante. Para el análisis estático, es invisible.
Lo que sí ve el Blue Team con Sysmon bien configurado es el comportamiento en runtime. Los accesos a memoria resultantes de la resolución de funciones son visibles, y las llamadas a esas funciones una vez resueltas las captura cualquier EDR con kernel hooks. El PEB Walker protege del análisis estático, no del comportamental.
Consideré indirect syscalls (Hell's Gate) como alternativa más sigilosa: elimina la dependencia de ntdll completamente. Es el siguiente nivel lógico pero requiere mapear syscall numbers para cada versión de Windows. Para este engagement el PEB Walker era suficiente. Hell's Gate está en el roadmap.
Para el Blue Team: La ausencia de imports en el IAT combinada con comportamiento de inyección posterior es en sí misma una señal. EDR con kernel hooks capturan las llamadas NT independientemente de lo que diga el IAT. En MDE, la telemetría de API calls la registra el Kernel Sensor aunque el IAT esté vacío.
Referencias: Sektor7 Institute, "Malware Development Essentials". Hasherezade, "Process Doppelgänging", BlackHat EU 2017. MITRE ATT&CK T1055.
RC4 propio: eliminar la dependencia de Advapi32
Hay una forma de cifrar el payload que delata al loader antes de ejecutarlo: usar SystemFunction032 de Advapi32.dll. Es la función que implementa RC4 en Windows y aparece en el IAT de tantos loaders maliciosos que es firma conocida. Si está en tus imports junto con comportamiento de inyección, ya estás marcado.
La solución es implementar RC4 tú mismo. KSA + PRGA en C puro son unas 30 líneas. El payload viaja cifrado dentro del binario y se descifra en runtime sin tocar ninguna API del sistema para ello.
Hay un precio que pagar. El shellcode descifrado en memoria es detectable si hay un barrido de memoria en el momento adecuado. Defender hace escaneos periódicos en algunos contextos y una vez que el payload está en claro, las firmas de Meterpreter aplican perfectamente. El bypass es efectivo en el momento de la inyección, pero si alguien hace un dump de memoria con pe-sieve o malfind después de que el loader ya corrió, el shellcode está ahí sin cifrar.
Por qué RC4 y no AES o XOR puro: AES requeriría más código o dependencias. XOR puro es demasiado débil contra análisis de entropía porque mantiene patrones estadísticos detectables. RC4 implementado en C sin dependencias externas es el balance correcto aquí.
Para el Blue Team: YARA sobre procesos en ejecución captura el shellcode una vez descifrado. Binario con alta entropía más ausencia de imports típicos más comportamiento de inyección posterior es una señal correlacionable en cualquier SIEM. La entropía alta por sí sola no es suficiente, pero combinada con lo demás, sí.
Referencias: ESET Research, análisis del implante Shlayer (2020). VirusBulletin 2019, "Bypassing Antivirus with Shellcode Encryption". MITRE ATT&CK T1027.
String obfuscation: un detalle pequeño que importa
explorer.exe no aparece como string en el binario. Se construye carácter a carácter en runtime. Parece una medida menor, pero los motores estáticos modernos correlacionan strings de nombres de proceso legítimos con imports de APIs de inyección para inferir intención antes de la ejecución. Si el nombre del proceso objetivo está en claro, es una señal más que se acumula.
La construcción es manual porque MSVC con optimizaciones Release a veces convierte arrays de caracteres de vuelta a strings literales dependiendo del contexto. Hacerlo a mano garantiza que no hay regresiones de compilación.
Lo que sigue siendo visible: Sysmon Event ID 10 registra cuando un proceso abre otro con determinados flags de acceso. El acceso a explorer.exe con flags de creación de hilos es completamente visible independientemente de cómo se construyó el nombre en el código. La obfuscación de strings protege del análisis estático, no del comportamental.
Para el Blue Team: Sysmon Event ID 10 con
TargetImage = explorer.exeyGrantedAccesscon flags de PROCESS_CREATE_THREAD desde procesos no firmados por Microsoft es una señal de alta fidelidad. Pocos procesos legítimos necesitan abrir explorer.exe con esos derechos.
ETW patching: lo que funciona y lo que no
ETW es la infraestructura de telemetría que alimenta gran parte de lo que ven los EDR. EtwEventWrite en ntdll.dll es la función que publica esos eventos. El patch es un byte: escribir 0xC3 (RET) al inicio de la función hace que retorne inmediatamente sin publicar nada.
Sin este patch, el provider Microsoft-Windows-Threat-Intelligence (GUID: F4E1897C-BB5D-5668-F1D8-040F4D8DD344) generaría eventos críticos sobre las operaciones de inyección: KERNEL_THREATINT_TASK_MAPVIEW_REMOTE, KERNEL_THREATINT_TASK_CREATETHREADEX_REMOTE. Esos eventos los consume Defender en tiempo real para detectar inyección de proceso.
Aquí hay que ser honesto sobre la limitación porque es importante. Ese provider opera parcialmente en kernel mode a través de callbacks ELAM. El patch de user-mode de EtwEventWrite en ntdll afecta solo a los eventos generados desde user-mode en el proceso actual. Los callbacks de kernel no los toca. Si el entorno tiene MDE con Kernel Sensor habilitado, mantiene visibilidad sobre las operaciones de inyección independientemente de este patch. El entorno de pruebas de este engagement era Windows Defender standalone, sin MDE. Esa distinción importa mucho.
Otra cosa que el Blue Team puede ver: el propio acto de parchear EtwEventWrite. Sysmon Event ID 25 (ProcessTampering) puede capturarlo en entornos con esa configuración activa.
Para el Blue Team: Sysmon Event ID 25 para ProcessTampering sobre ntdll.dll. Con MDE P2, el Kernel Sensor da cobertura independiente del ETW de user-mode. Sigma rule de referencia:
win_susp_etw_patchingen SigmaHQ.
Referencias: MDSec Research, "Blinding EDR On Windows" (2020). Elastic Security Labs, "Detecting and Defeating ETW Bypass" (2022). MITRE ATT&CK T1562.006.
AMSI patching: relevante aunque este sea un loader en C
Para un loader en C puro que no usa scripting engines, AMSI importa menos que en un loader PowerShell. Pero se parchea por consistencia y porque si el entorno objetivo tiene integraciones personalizadas del AMSI provider, la capa existe.
El patch escribe xor eax, eax; ret (0x31 0xC0 0xC3) al inicio de AmsiScanBuffer en amsi.dll. La función retorna siempre AMSI_RESULT_CLEAN.
Una nota de vigencia: Windows 11 22H2 añadió protecciones adicionales contra modificación de amsi.dll en contextos Protected Processes Light. El patch funcionó en el entorno de pruebas específico. Los resultados varían según la build exacta de Windows.
Para el Blue Team: Monitorizar escrituras en páginas de código de amsi.dll mediante driver de seguridad. ScriptBlock Logging (Event ID 4104) en entornos con PowerShell o .NET. Sigma rule:
win_susp_amsi_bypassen SigmaHQ.
Referencias: Matt Graeber (@mattifestation), bypass original vía reflexión .NET (2016). CyberArk Research, "AMSI Bypass: Patching Technique" (2018).
Sandbox detection: calibrar los umbrales cuesta más de lo que parece
Los sandboxes automatizan el análisis de dos formas: acelerando el tiempo de ejecución, y corriendo en entornos mínimos con pocos recursos.
RexLDR implementa las dos contra-medidas. Para el timing: NtDelayExecution 5 segundos, luego comparar el tick count actual con el esperado. Si hay aceleración de tiempo, el delta es anómalamente bajo y el loader sale sin ejecutar nada. Para la detección de entorno: CPU cores menores de 1 (salida), RAM disponible menor de 1 GB (salida), procesos activos menores de 10 (salida).
Lo que parece sencillo tiene su parte complicada en la práctica. Los umbrales iniciales que usé (cores ≤ 2, RAM < 4 GB, procs < 25) me daban falsos positivos en mis propias VMs de laboratorio. Tuve que bajarlos bastante. El punto es que cada entorno es diferente y los umbrales hay que calibrarlos contra el entorno objetivo real, no contra una VM genérica de referencia.
Los sandboxes modernos han evolucionado para lidiar con esto. Microsoft Intelligent Security Graph ejecuta algunas muestras durante períodos más largos. Un binario que sale limpiamente del sandbox pero tiene alta entropía es en sí mismo una señal de evasión activa, y los analistas buenos lo saben.
Para el Blue Team: Behavioral analysis que correlaciona binario con alta entropía más sleep largo más inyección posterior es mucho más efectivo que sandboxing puro. Elastic Security Labs publicó en 2023 investigación sobre detección de sleep obfuscation. Una muestra que sale limpia del sandbox pero tiene entropía alta merece análisis manual, no descarte automático.
MapView injection: por qué no uso la tríada clásica
La tríada VirtualAllocEx + WriteProcessMemory + VirtualProtectEx es básicamente una firma comportamental en sí misma. Behavior:Win32/Meterpreter.gen!A, la detección específica que bypaseé en este engagement, detecta entre otras cosas ese patrón de llamadas. Los eventos ETW correspondientes se generarían inmediatamente con cualquier herramienta que use esas APIs.
La alternativa que usa RexLDR: NtCreateSection + NtMapViewOfSection. Se crea una sección de memoria ejecutable en el kernel, se mapea localmente para escribir el payload, y luego se mapea esa misma sección en el espacio de memoria de explorer.exe. El shellcode nunca se "escribe" en el proceso remoto vía WriteProcessMemory. Se comparte a través de una sección de kernel.
Para la enumeración de procesos: NtQuerySystemInformation(SystemProcessInformation) en lugar de CreateToolhelp32Snapshot. Menos monitorizado, más directo.
Cosas que probé y descarté. Process Hollowing requiere crear un proceso suspendido, lo que genera telemetría de creación de proceso con flags de suspensión y es detección casi universal. APC injection es más ruidoso que MapView en términos de comportamiento de kernel. Thread hijacking en explorer.exe fue lo primero que intenté y lo tuve que abandonar: los hilos de explorer están en estado de espera en kernel (WaitForMultipleObjects) y redirigir el RIP resulta inconsistente. Me reventé explorer.exe un par de veces antes de entender por qué. NtCreateThreadEx es más predecible.
Lo que sí ve el Blue Team igualmente: Sysmon Event ID 10 (ProcessAccess) registra cuando el loader abre explorer.exe con NtOpenProcess. El GrantedAccess de PROCESS_VM_OPERATION | PROCESS_CREATE_THREAD desde un proceso no firmado sobre explorer.exe es una señal de alta fidelidad. Pocos procesos legítimos necesitan hacer eso.
Para el Blue Team: Sysmon Event ID 10 con TargetImage =
explorer.exey GrantedAccess con flags de creación de hilos desde procesos no firmados Microsoft es detección de muy alta fidelidad con pocos falsos positivos. Correlacionado con Event ID 8 (NtCreateThreadEx) en la misma ventana de tiempo, es prácticamente confirmación. En MDE:DeviceEvents | where ActionType == "CreateRemoteThreadApiCall" and InitiatingProcessFileName !in ("svchost.exe", "csrss.exe").
Referencias: Endgame Research, "Ten Process Injection Techniques" (2017). BlackHat USA 2019, "Process Injection Techniques - Gotta Catch Them All". MITRE ATT&CK T1055.015.
NtCreateThreadEx: el último paso
CreateRemoteThread genera telemetría explícita y está monitorizada por prácticamente todo lo que se vende como EDR. NtCreateThreadEx es su equivalente en NT, resuelto vía PEB Walker, que opera con menos visibilidad a nivel de API de alto nivel.
El acceso al proceso se pide con permisos mínimos: PROCESS_VM_OPERATION | PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION. Sin PROCESS_ALL_ACCESS, que es otra firma comportamental de alta confianza en cualquier EDR configurado correctamente.
Sysmon Event ID 8 puede capturar NtCreateThreadEx dependiendo de la configuración. Y si hay MDE con Kernel Sensor, KERNEL_THREATINT_TASK_CREATETHREADEX_REMOTE se genera independientemente de cómo se resuelva la función en user-mode. El acceso con los flags mencionados sobre explorer.exe desde un proceso no firmado es la señal más visible de todo el loader.
Para el Blue Team: Correlación de Sysmon Event ID 10 (ProcessAccess sobre explorer.exe) más Event ID 8 (NtCreateThreadEx) en ventana de tiempo corta desde el mismo proceso origen es detección prácticamente sin falsos positivos. Sigma rule:
proc_access_win_inject_via_ntcreatethreadexen SigmaHQ.
Limitaciones que vale la pena documentar
Ningún loader es perfecto. Documentar las limitaciones es lo que diferencia una investigación técnica seria de un post de marketing.
El ETW patch no afecta al Kernel Sensor de MDE. Ya se dijo, pero vale la pena repetirlo porque es la limitación más crítica. Organizaciones con MDE Plan 2 y Kernel Sensor habilitado tienen visibilidad sobre las operaciones de inyección independientemente del patch de user-mode. RexLDR no tiene mecanismos para evadir telemetría a nivel de kernel.
Sin evasión de hooks de kernel de EDR. CrowdStrike, SentinelOne y similares ponen drivers en el kernel que hookean syscalls directamente. El PEB Walker resuelve funciones de ntdll, pero si ntdll está hookeada a nivel de kernel, esas llamadas son visibles igualmente. Indirect syscalls (Hell's Gate / Halo's Gate) mitigarían esto.
El tráfico de red no está obfuscado. meterpreter_reverse_tcp usa TCP raw sobre el 4444. Sin perfil C2 personalizado y transporte HTTPS, cualquier solución NDR con firmas de Meterpreter detecta la sesión. RexLDR bypasea el endpoint pero no el monitoring de red. En un entorno con NSM maduro, la sesión es visible en red aunque el endpoint no alertara.
En memoria, el shellcode es detectable post-ejecución. Una vez descifrado y ejecutado, las firmas de Meterpreter están en memoria sin cifrar. pe-sieve, malfind o el propio Defender en escaneo periódico los encuentran. El bypass es efectivo en el momento de la inyección, no garantiza invisibilidad indefinida.
El proceso objetivo es predecible. Siempre explorer.exe. Una sola regla que alerte sobre cualquier proceso no-Microsoft abriendo explorer.exe con derechos de creación de hilos cubre esto. PPID spoofing y selección dinámica del proceso objetivo son mejoras directas.
Sin persistencia. La sesión se pierde en reboot. Correcto para un primer acceso puntual, insuficiente para operaciones largas.
Resultados y cómo se verificó la ausencia de detección
Entorno de pruebas
| Campo | Valor |
|---|---|
| Fecha | 11 de abril de 2026 |
| Real-time protection | ON |
| Cloud-delivered protection | ON |
| MDE | No presente (standalone Defender) |
| Payload | windows/x64/meterpreter_reverse_tcp stageless |
| Proceso objetivo | explorer.exe |
La ausencia de MDE es relevante y merece destacarse. Con MDE P2 y Kernel Sensor habilitado, el resultado no está garantizado. Las pruebas con Defender standalone no representan el nivel de detección de un entorno corporativo con MDE completo. Es importante decirlo porque muchos posts de bypass de AV no lo hacen.
Logs revisados para confirmar la ausencia de detección
Windows Defender Operational Log en Applications and Services Logs → Microsoft → Windows → Windows Defender → Operational: sin Event ID 1116 (malware detected) ni 1117 (malware action taken) durante el período de la prueba.
Windows Defender Threat History: sin entradas en el período.
Event Viewer → Security: el proceso loader aparece en Event ID 4688 (creación de proceso), sin correlaciones de alerta asociadas.
Process Monitor utilizado durante el desarrollo para verificar el comportamiento del loader y confirmar las llamadas NT esperadas sin terminaciones inesperadas. MDE Portal no disponible en este entorno, lo cual es una limitación del setup de pruebas que afecta a la validez de los resultados en entornos corporativos.
Sobre la vida útil de estas técnicas
Cada técnica documentada aquí tiene fecha de caducidad.
El ETW patching de user-mode fue efectivo durante años. Microsoft está moviendo más detecciones al kernel, donde los patches de user-mode no llegan. El AMSI bypass directo de AmsiScanBuffer está cada vez más vigilado y aparecen protecciones adicionales en cada build nueva de Windows. MapView injection era casi desconocida en firmas comportamentales hace tres años. Hoy tiene su entrada en MITRE ATT&CK como T1055.015 y hay reglas de Sigma maduras para detectarla.
Esto es estructural, no accidental. La detección por comportamiento obliga a los defensores a conocer exactamente qué patrón de llamadas corresponde a cada técnica. La respuesta ofensiva es bajar un nivel de abstracción: de Win32 a NT, de NT a syscalls directos, de syscalls a técnicas sobre estructuras de kernel directamente. En cada iteración, la ventana de efectividad se estrecha y el coste de desarrollo sube de forma no lineal.
Lo que más me interesa de este ciclo no es qué técnica específica funcionará el año que viene. Eso cambia con cada actualización. Lo que me interesa es el principio que subyace: la detección por comportamiento siempre puede ser evadida si el atacante reduce suficientemente su huella de syscalls y opera cerca del metal. Indirect syscalls eliminan la dependencia de ntdll hookeada por EDR. Exploits de kernel eliminan incluso esa dependencia. Pero a medida que bajas de nivel, el número de desarrolladores capaces de operar ahí cae exponencialmente, y la asimetría de recursos empieza a favorecer a los defensores en términos de sostenibilidad.
RexLDR es una fotografía de un momento concreto contra un entorno concreto. El valor no está en las técnicas, que van a envejecer, sino en documentar por qué funcionan y cuándo van a dejar de hacerlo.



