De la Shellcode Artesanal al C2 Builder: Anatomía de un Dropper y su Metamorfosis en Framework
Análisis de la creación de un dropper de shellcode en C puro y Assembly (WinExec/ExitProcess), el desarrollo de un stub criptográfico en NASM, la evolución hacia un constructor de malware con panel C2 en C#, y un compendio extenso de técnicas de inyección, evasión y persistencia en Windows.
/índice_de_técnicas +
- Fase 1: Shellcode Artesanal
- Resolución Dinámica de la API
- El Arte del Despliegue
- Stub Criptográfico en NASM
- BugBuilder — Automatización y C2
- Bitácora de Desarrollo
- Galería de la UI
- Conclusión: Del Byte al Framework
- Classic Code Injection
- Classic DLL Injection
- Find Process by Name
- Windows Shellcoding 1
- PEB y TEB
- PE File Structure
- APC Injection (Early Bird)
- Thread Hijacking
- SetWindowsHookEx
- Windows Fibers
- API Hooking
- NtCreateThreadEx
- Memory Sections
- ZwQueueApcThread
- KernelCallbackTable
- RWX Memory Hunting
- FindWindow Injection
- Download and Inject
- EnumDesktopsA / EnumChildWindows
- XOR Encryption Evasion
- Hiding Function Calls
- API Hashing
- Persistence Techniques
01. Fase 1: El Arte de la Shellcode Artesanal (hace 3 años)
La filosofía del dropper manual
El punto de partida no fue un builder con interfaz gráfica, sino un dropper puro en C. El objetivo era claro: escribir una shellcode capaz de lanzar un ejecutable (calc.exe) y finalizar limpiamente el proceso llamando a WinExec y ExitProcess. No se trataba de evadir AV, sino de entender la anatomía de una shellcode: cómo se posiciona en memoria, cómo resuelve las direcciones de la API de Windows, y cómo se pasa el control al flujo de ejecución modificado.
El stub: De C a Assembly
Para generar los opcodes, primero escribimos la lógica en C estándar. Este archivo (stub_shellcode.c) sirve como molde conceptual para entender qué queremos que haga el código a bajo nivel.
A partir de este código, la tarea de ingeniería inversa consistió en traducir esto a Assembly NASM. Creamos stub_shellcode.asm, donde definimos manualmente la pila, pusheamos la cadena "calc.exe" en formato little-endian y configuramos los registros para la llamada a la API.
Para pasar "calc.exe" a la pila en x86, es necesario invertir el orden de los bytes. La shellcode no usa APIs de alto nivel para strings; todo son bytes crudos empujados a mano. Esto es fundamental para que la shellcode sea independiente de la posición y no dependa de secciones de datos.
02. Resolución Dinámica de la API (GetProcAddress)
Para que nuestra shellcode funcione en diferentes versiones de Windows (o con diferentes parches), no podemos hardcodear las direcciones de WinExec o ExitProcess. Aquí entra en juego la técnica de resolución dinámica en tiempo de ejecución.
Obtención de la dirección base de kernel32.dll
Utilizamos GetModuleHandle("kernel32.dll") para obtener la dirección base en memoria de la DLL. Kernel32 es la piedra angular de la API de Windows y siempre está cargada en el espacio de memoria de los procesos de usuario. A diferencia del PEB (Process Environment Block) walking en shellcode pura, aquí aprovechamos que el dropper es un ejecutable compilado y podemos usar la API de Windows antes de saltar al código malicioso.
La herejía del formateo en el arreglo
Una vez que tenemos las direcciones winexec_addr y exitprocess_addr, debemos parcharlas dentro de nuestro arreglo de shellcode. Aquí nos enfrentamos a un detalle técnico crucial: el procesador x86 es little-endian. Si la función GetProcAddress nos devuelve una dirección como 0xAF5477FF, debemos guardarla en el arreglo de shellcode como 0xFF, 0x77, 0x54, 0xAF.
Para ello, aplicamos máscaras de bits y desplazamientos para extraer cada byte de la dirección y colocarlo en la posición correcta del arreglo. Esta técnica es la que permite que el call ebx de nuestra shellcode aterrice en la dirección correcta de WinExec. Es un error común en principiantes olvidar este paso y terminar con una shellcode que salta a direcciones de memoria corruptas, provocando un crash inmediato.
03. El Arte del Despliegue: Punteros a Función
El archivo run.c contiene el mecanismo de lanzamiento. Declaramos un puntero a función (int(*func)()) y le asignamos la dirección de memoria de nuestro arreglo shellcode.
Al hacer el casting func = (int(*)())shellcode; y posteriormente llamar a func(), estamos obligando al procesador a saltar a nuestra región de datos y ejecutar esos bytes como si fueran código. Esto es la esencia de un dropper: no hay inyección en otro proceso, solo ejecución directa.
Sin embargo, esta técnica tiene una limitación importante: la región de memoria donde reside el arreglo debe tener permisos de ejecución. En sistemas modernos con DEP (Data Execution Prevention) habilitado, esto requeriría un paso previo con VirtualProtect para marcar la página de memoria como ejecutable. En su momento, la PoC se probó en un entorno controlado sin DEP para validar la lógica de la shellcode antes de añadir esa capa de evasión.
Esta técnica es un "dropper". No estamos secuestrando el registro EIP mediante un buffer overflow. Simplemente estamos cargando un arreglo de bytes ejecutables en memoria y redirigiendo el flujo de ejecución hacia él. Es una técnica más ruidosa pero más controlada. En un exploit real, la shellcode se inyecta en otro proceso o se ejecuta tras corromper la pila.
04. Ofuscación Avanzada: El Stub Criptográfico en NASM
Mientras el builder en C# resolvía el problema de la generación de payloads, surgió una cuestión más fundamental: ¿cómo proteger la shellcode en reposo? A finales de 2025, me planteé crear un stub descifrador en Assembly puro. La idea era simple: la shellcode real viaja cifrada (con un esquema de claves dinámicas), y un pequeño stub en NASM se encarga de descifrarla en tiempo de ejecución antes de saltar a ella. El objetivo era doble: evadir firmas estáticas y añadir una capa de ofuscación que complicara el análisis forense.
El payload de prueba (Linux x86)
Para esta prueba de concepto, utilicé un payload mínimo que ejecuta una syscall de salida en Linux (exit(10+22)). La shellcode en bruto es la siguiente:
La lógica de descifrado (Bloques XOR con mutación)
El stub en NASM utiliza un algoritmo de descifrado por bloques. En lugar de un simple XOR estático, implementé una mezcla de operaciones para cada bloque de 4 bytes: A ^ B + (C - 1) - (D + 1). Los datos cifrados se almacenan en la sección .data y el stub reserva un buffer en .bss.
La lógica es particularmente interesante porque no depende de constantes fijas globales; cada bloque tiene su propio conjunto de claves (A, B, C, D), lo que permite generar un stub personalizado para cada payload, dificultando la creación de firmas estáticas por parte de los AV. La elección de las operaciones (XOR, decremento, incremento, resta) no es arbitraria: se busca una mezcla de operaciones aritméticas y lógicas que no sea trivial de simplificar por un motor de análisis estático.
Este stub está escrito para la convención de syscalls de Linux x86 (int 0x80). La diferencia clave con el mundo Windows es el uso de interrupciones de software vs. llamadas a la API kernel32. Sin embargo, la técnica de descifrado XOR por bloques es completamente portable a Windows si se ajusta la shellcode objetivo y se reemplaza la syscall de salida por una llamada a ExitProcess.
05. Fase 2: BugBuilder — Automatización y C2 (hace 2 años)
La shellcode manual era poderosa pero impráctica para operaciones a gran escala. La evolución lógica fue BugBuilder, un proyecto en C# (.NET Framework) que traslada el concepto de dropper a un entorno de construcción visual con panel de control.
El motor de compilación interno: Roslyn y CodeDOM
Uno de los mayores desafíos técnicos fue generar un ejecutable sin tener un compilador externo instalado. La solución residió en incrustar el compilador de C# dentro del builder. Para ello, se evaluaron dos enfoques:
- CodeDOM (Code Document Object Model): Permite generar código fuente y compilarlo en tiempo de ejecución usando el proveedor CSharpCodeProvider. Es la opción más portable y la que se utilizó en la PoC inicial.
- Roslyn (Microsoft.CodeAnalysis.CSharp): El compilador moderno de C# como servicio, que ofrece un control más granular sobre la sintaxis y los árboles de expresión. Ideal para payloads más complejos que requieren características modernas del lenguaje.
El código fuente de la carga útil se mantenía como un recurso embebido en el ensamblado, y se reemplazaban marcadores de posición (placeholders) con las configuraciones del usuario (IP, puerto, métodos de ofuscación) antes de la compilación. Esto permitía generar un binario único para cada campaña sin necesidad de herramientas externas.
Arquitectura del proyecto
BugBuilder se divide en dos componentes principales:
- El Builder (Cliente): Una interfaz Windows Forms donde el operador configura el payload. Se selecciona el icono, el nombre del archivo, y se activan opciones de persistencia (registros, startup folder, copias). Se integran módulos de ofuscación y encoding de tráfico.
- El Servidor C2: Un listener TCP asíncrono que recibe conexiones de los clientes infectados. Los datos se transmiten en formato JSON, facilitando el parseo de la información del bot (IP, país, SO, usuario, ping).
El protocolo de comunicación TCP
A diferencia del dropper original que solo ejecutaba calc.exe, BugBuilder establece un canal de persistencia. El cliente malicioso se conecta al servidor, serializa un objeto ModelInfo a JSON y lo envía. La elección de JSON sobre formatos binarios fue pragmática: facilita la depuración, es legible por humanos durante el desarrollo, y es trivial de parsear en ambos extremos con Newtonsoft.Json.
El Panel de Control (C2)
El operador utiliza una ventana con pestañas:
- Builder: Configuración del payload. Incluye parámetros de red (Host/IP, Puerto) y técnicas de evasión como la compresión del tráfico o el cifrado (XOR, AES, RSA).
- Infected: Un grid en tiempo real que muestra los bots conectados, ordenados por IP, País, Usuario, Sistema Operativo y latencia (Ping).
La inclusión de opciones como LZMA, GZip, Huffman, o diferentes esquemas de cifrado demuestran una transición desde una PoC académica a una herramienta orientada a operaciones que enfrenta entornos defendidos por firewalls e IDS. La variabilidad del payload es clave para evadir firmas estáticas. Este mismo principio de variabilidad se aplica en el triage automatizado con analystty, donde la detección de TTPs debe adaptarse a payloads ofuscados.
06. Bitácora de Desarrollo: De la Idea al GridView
La construcción del builder no fue un proceso lineal. Rescatando los registros de chat originales, se puede trazar la evolución del pensamiento técnico:
La semilla de la idea (Enero 2024)
El problema inicial era claro: generar un .exe sin un compilador externo. La solución consistió en incrustar todo lo necesario dentro del ensamblado del builder, utilizando el compilador de C# en memoria y manteniendo el código fuente de la carga útil como strings de recursos.
El siguiente paso fue diseñar la UI. La meta era una interfaz modular donde el usuario pudiera personalizar cada aspecto del binario de salida, desde el ícono hasta los métodos de ofuscación del tráfico. La arquitectura de compilación interna (CodeDOM) fue clave para esta etapa.
La conexión con el C2
Una vez generado el .exe, debía reportar a casa. La decisión técnica fue utilizar un formato de serialización ligero (JSON) sobre TCP. Se implementó un modelo de datos (ModelInfo) para mapear la información de la víctima (IP, país, hostname, SO, ping) y visualizarla en un GridView.
La necesidad de una flag para evaluar el pipeline de procesamiento demuestra una comprensión temprana de los protocolos de comunicación maliciosa avanzados. No es suficiente enviar datos; el binario y el servidor deben negociar si el tráfico está codificado, comprimido o cifrado, tal como se ve en la UI final con las opciones de Encode/Compress/Encrypt. Esta negociación es lo que permite que el C2 sea flexible y resistente a cambios en las defensas del target.
La trampa del "todo en strings"
En septiembre de 2024, reflexioné sobre la arquitectura del builder. La primera iteración almacenaba todo el código fuente del payload como strings literales en los recursos, con las herramientas de compilación incrustadas. Este enfoque, aunque funcional, era frágil: cualquier cambio en la lógica del payload requería modificar cadenas largas y propensas a errores de sintaxis. La lección fue que el builder debía ser más modular, con plantillas parametrizables en lugar de código fuente hardcodeado.
07. Galería de la UI: Anatomía de BugBuilder
Las siguientes imágenes documentan el estado final de la PoC. Representan la materialización de los conceptos discutidos en la bitácora: un generador de payloads con su propio panel de comando y control.
Interfaz de Construcción (Builder)
Panel de Control Vacío (Infected)
Conexión Exitosa (C2 Activo)
08. Conclusión: Del Byte al Framework
La progresión de winshellcode- a BugBuilder representa un arco de aprendizaje fundamental en el desarrollo de software de seguridad ofensiva. Se pasó de entender la microarquitectura de una pila de llamadas a abstraer esa lógica en una fábrica de binarios.
Tabla comparativa de la evolución
| Característica | winshellcode- (Fase 1) | Stub Cripto (Fase 2) | BugBuilder (Fase 3) |
|---|---|---|---|
| Lenguaje | C + Assembly (NASM) | Assembly (NASM) | C# (.NET Framework) |
| Payload | Shellcode fija (calc.exe) | Shellcode variable descifrada | Payload configurable con persistencia |
| Ofuscación | Opcodes en bruto | XOR multibloque con llaves | Compresión, Cifrado (AES/RSA), Iconos camuflaje |
| Comunicación | Ninguna (Ejecuta y termina) | Ninguna | C2 TCP bidireccional con JSON |
| Resolución API | Manual (GetProcAddress + parcheo little-endian) | Syscalls directas (int 0x80) | Automática (.NET P/Invoke o Referencias) |
| Generación de binario | Compilación externa (GCC) | Ensamblador externo (NASM) | Compilación interna (CodeDOM / Roslyn) |
El primer proyecto nos enseñó el valor de la precisión manual: saber exactamente qué bytes ocupa una instrucción y cómo la pila afecta al flujo. El segundo proyecto añadió la capa de protección de la carga útil, entendiendo que la shellcode en reposo es la parte más vulnerable del ciclo de vida de un payload. Finalmente, BugBuilder operacionalizó ese conocimiento, convirtiendo un arte técnico en una herramienta modular lista para entornos hostiles, donde la capacidad de regenerar binarios con firmas diferentes es la diferencia entre el éxito y la detección.
09. Classic Code Injection into the Process
Consiste en introducir código malicioso en un proceso legítimo en ejecución, lo que permite al atacante secuestrar el proceso y utilizarlo para sus propios fines. En términos generales, la técnica consiste en:
- Identificar el proceso de destino: Puede ser cualquier programa común (notepad.exe) o algún proceso más crítico en el sistema.
- Inyectar el código: El código malicioso es inyectado en el espacio de memoria del proceso de destino. Para lograr esto existen diversos métodos: escribir directamente en la memoria del proceso o cargar una DLL maliciosa.
- Ejecución del código inyectado: Podemos crear un nuevo hilo dentro del proceso que comience a ejecutar el código, o manipular la memoria del proceso para sobreescribir las instrucciones con un salto al código inyectado.
Mecanismos defensivos y sus evasiones:
- DEP (Data Execution Prevention): Evita que se ejecute código desde ubicaciones de memoria no confiables.
- ASLR (Address Space Layout Randomization): Aleatoriza el diseño de la memoria de un proceso, dificultando predecir dónde se cargará el código.
- CFG (Control Flow Guard): Rastrea el flujo de datos a través de un programa para detectar intentos de inyección.
Para explicar esta técnica, analizamos el concepto general: "encontrar un espacio de la memoria de un proceso (en este caso, el mismo proceso) y escribir sobre él".
Ahora pivotamos a un proceso distinto (ej. calc.exe) usando OpenProcess, VirtualAllocEx, WriteProcessMemory y CreateRemoteThread. La memoria se asigna directamente en el espacio de direcciones del proceso objetivo con permisos de ejecución.
10. Classic DLL Injection into the Process
Consiste en cargar una DLL maliciosa en un proceso legítimo. El flujo es: localizar la DLL, crear un hilo en el proceso destino, asignar memoria para almacenar la ruta de la DLL, cargar la DLL mediante LoadLibraryA, y ejecutar su punto de entrada (DllMain).
En teoría, para realizar DLL hijacking solo debemos aprovechar la jerarquía de búsqueda de las DLL. Sin embargo, las pruebas en Windows 10 moderno no funcionan debido a protecciones como Known DLLs y mitigaciones de búsqueda de DLL.
11. Find Process ID by Name and Inject to It
Lógica necesaria para encontrar procesos por nombre e inyectarlos usando CreateToolhelp32Snapshot y Process32First/Next.
12. Windows Shellcoding 1
Patrón fundamental de ejecución de shellcode mediante punteros a función. Este es exactamente el mecanismo que usamos en el dropper original:
Se incluye un script auxiliar en Python para revertir strings y convertirlos a hexadecimal en formato little-endian, necesario para construir la shellcode.
13. Windows Shellcoding 2 — PEB y TEB
Cada vez que ejecutamos cualquier archivo .exe, lo primero que se crea en el sistema operativo son las estructuras PEB (Process Environment Block) y TEB (Thread Environment Block). El PEB contiene información necesaria para el funcionamiento del proceso, mientras que cada hilo tiene su propio TEB.
Cuando escribimos en ASM, podemos recorrer la estructura PEB → LDR y encontrar la dirección de kernel32.dll para cargarla en nuestro shellcode sin usar GetModuleHandle:
14. Windows Shellcoding 3 — PE File Structure
Un archivo PE es el formato nativo de Win32, análogo a los COFF de Unix. Su estructura incluye:
- DOS Header: Almacena información necesaria para cargar el archivo PE.
- DOS Stub: Área mayormente llena de ceros después de los primeros 64 bytes.
- PE Header (NT Header): Contiene la firma "PE\0\0" (bytes 50 45 00 00).
- File Header (COFF Header): Características básicas del archivo.
- Optional Header: Contiene AddressOfEntryPoint, ImageBase, SectionAlignment, SizeOfImage y los directorios de datos (tabla de exportación, importación, recursos, excepciones, certificados, reubicación, TLS, IAT, CLR runtime header, etc.).
- Section Table: Arreglo de estructuras IMAGE_SECTION_HEADER que define secciones como .text.
15. APC Injection Technique (Early Bird)
La técnica Early Bird aprovecha QueueUserAPC para poner en cola un APC (Asynchronous Procedure Call) en un hilo específico. La descripción de alto nivel es:
- Crear un proceso legítimo con el flag CREATE_SUSPENDED.
- Asignar memoria para la carga útil en el espacio del nuevo proceso.
- Declarar la rutina APC que apunta al código shellcode.
- Reanudar el hilo principal para que ejecute el APC antes de comenzar su ejecución normal.
APC Injection via NtTestAlert
NtTestAlert es una syscall no documentada que comprueba si hay APC pendientes en el hilo actual y los ejecuta. Antes de que cualquier hilo comience a ejecutarse, su dirección de inicio Win32 llama a NtTestAlert para ejecutar cualquier APC pendiente. Podemos aprovechar esto para desencadenar la ejecución de nuestra carga útil sin necesidad de un hilo remoto en otro proceso.
APC Injection via Alertable Threads
Esta variante encuentra todos los subprocesos del proceso destino y pone en cola un APC a todos ellos, maximizando la probabilidad de ejecución.
16. Code Injection via Thread Hijacking
El secuestro de un proceso se logra suspendiendo un proceso existente y luego desasignando o vaciando su memoria, que luego se puede reemplazar con código malicioso. Los pasos clave son:
- Crear un identificador para un proceso víctima existente.
- Suspender el proceso con SuspendThread.
- Obtener el contexto del hilo con GetThreadContext (incluye el registro de instrucciones RIP).
- Modificar ct.Rip = (DWORD_PTR)rb para que apunte a nuestra shellcode.
- Establecer el contexto modificado con SetThreadContext.
- Reanudar el hilo con ResumeThread.
17. Classic DLL Injection via SetWindowsHookEx
SetWindowsHookEx permite instalar hooks de sistema global. Al configurar un hook de teclado global (WH_KEYBOARD) y especificar una función callback que reside en nuestra DLL maliciosa, obligamos al sistema a cargar la DLL en el espacio de direcciones de los procesos que reciben mensajes de teclado. Los sistemas modernos bloquean esta técnica usando CFG (Control Flow Guard) y CIG (Code Integrity Guard).
18. Code Injection via Windows Fibers
Windows Fibers es una API que permite crear hilos de ejecución ligeros. Son más eficientes que los hilos del sistema pero menos seguros al no estar protegidos con las características de los hilos del sistema. La técnica consiste en:
- Convertir el hilo actual en un Fiber con ConvertThreadToFiber(NULL).
- Asignar memoria para el código malicioso con VirtualAlloc.
- Crear una nueva Fiber con CreateFiber apuntando al código.
- Cambiar el contexto de ejecución con SwitchToFiber.
19. Windows API Hooking
El API hooking es una técnica mediante la cual interceptamos y modificamos el comportamiento de las llamadas a la API. Consiste en reemplazar los primeros 5 bytes de la función original con una instrucción JMP relativo (\xE9) que redirige a nuestra función personalizada. Debemos almacenar los bytes originales para poder restaurar la función cuando sea necesario.
Un segundo método utiliza push + ret (\x68 + dirección + \xC3) para redirigir la ejecución a nuestra función, en lugar del salto relativo.
20. DLL Injection via Undocumented NtCreateThreadEx
Esta técnica utiliza la función no documentada NtCreateThreadEx de ntdll.dll, que permite indicar el tamaño de la pila y otras flags de creación dándole más flexibilidad que CreateRemoteThread. Al no estar documentada y no exportarse de la biblioteca de importación de kernel32.dll, es más sigilosa que las técnicas tradicionales.
21. Code Injection via Memory Sections (NtCreateSection)
Una sección es un bloque de memoria que se comparte entre procesos, creado mediante la API NtCreateSection. La técnica implica:
- Crear una sección de memoria compartida con NtCreateSection.
- Mapear la sección en el proceso local con NtMapViewOfSection.
- Mapear la misma sección en el proceso objetivo (solo lectura).
- Copiar el payload en la sección local (se refleja en la remota).
- Crear un hilo remoto con RtlCreateUserThread.
- Desmapear con ZwUnmapViewOfSection.
22. Code Injection via ZwQueueApcThread
Similar a la técnica APC estándar pero usando ZwQueueApcThread en lugar de QueueUserAPC. Combina la creación de secciones de memoria compartida con ZwCreateSection y el encolado de APC usando la función nativa de ntdll.dll. Se utiliza ZwSetInformationThread con THREADINFOCLASS 1 para forzar que el hilo se vuelva alertable.
23. Process Injection via KernelCallbackTable
KernelCallbackTable es una tabla de punteros a funciones que se utiliza para registrar controladores de eventos del kernel. Al inyectar código en esta tabla, un atacante puede reemplazar un controlador de eventos existente por uno propio. Cuando se produce el evento, se llamará al controlador malicioso, permitiendo ejecutar código arbitrario en el contexto del proceso objetivo.
24. Process Injection via RWX Memory Hunting
Esta técnica busca regiones de memoria con permisos RWX (Read-Write-Execute) en el espacio de memoria del proceso objetivo usando VirtualQueryEx. Una vez encontrada una región RWX, escribe directamente la shellcode allí y crea un hilo remoto. Esto evita la necesidad de llamar a VirtualAllocEx, reduciendo la huella de llamadas sospechosas.
25. Process Injection via FindWindow
Utiliza FindWindow para localizar el identificador de ventana (HWND) del proceso objetivo. Luego de obtener el HWND, se usa GetWindowThreadProcessId para obtener el PID y proceder con la inyección estándar.
VM Evasion via FindWindow
Variante de evasión de máquinas virtuales que busca ventanas específicas de VirtualBox (VBoxTrayToolWndClass, VBoxTrayToolWnd) para detectar si se está ejecutando en un entorno virtualizado y condicionar la ejecución del payload.
26. Download and Inject Logic
Técnica para descargar carga útil o DLL maliciosa desde una URL usando HTTP (WinInet). El beneficio es que se puede usar detrás de redes que filtran todo tráfico excepto HTTP, e incluso puede funcionar a través de un proxy preconfigurado.
El flujo es: abrir sesión HTTP con InternetOpen, descargar el archivo con InternetOpenUrl e InternetReadFile, guardarlo localmente con CreateFile/WriteFile, y luego inyectarlo usando la técnica clásica de DLL injection.
27. Run Shellcode via EnumDesktopsA / EnumChildWindows
Estas técnicas aprovechan funciones de enumeración de la API de Windows que aceptan un callback como parámetro. Al pasar la dirección de nuestra shellcode como función de callback, el sistema la ejecutará en el contexto del proceso actual:
- EnumDesktopsA: Enumera escritorios. El callback se pasa como DESKTOPENUMPROC.
- EnumChildWindows: Enumera ventanas secundarias. El callback se pasa como WNDENUMPROC.
28. AV Engines Evasion: XOR Encryption
La técnica de evasión usando XOR para cifrar/descifrar aplica una operación XOR byte a byte entre la shellcode y una clave secreta. El proceso es:
- Cifrado: Se toma la shellcode (arreglo de opcodes), se selecciona una clave aleatoria, y se aplica XOR bit a bit a cada byte.
- Descifrado en tiempo de ejecución: Se aplica la misma clave XOR al arreglo cifrado antes de copiarlo a memoria ejecutable.
Se incluye un script en Python para automatizar el proceso: cifrar la shellcode, reemplazar el placeholder en la plantilla C++, y compilar con MinGW.
29. Hiding Function Calls with XOR Encryption
Esta técnica aplica XOR al nombre de funciones API como VirtualAlloc para que no aparezcan como strings legibles en el binario. En tiempo de ejecución, se descifra el nombre, se obtiene la dirección con GetProcAddress, y se asigna a un puntero de función para su uso.
30. API Hashing for AV Evasion
El hashing de funciones API implica convertir el nombre de la función en un valor hash único y usar ese hash en lugar del nombre real. El procedimiento es:
- Cargar la DLL con LoadLibrary.
- Recorrer la tabla de exportación de la DLL.
- Calcular el hash de cada nombre de función exportada.
- Comparar con el hash pre-calculado de la función deseada.
- Si coincide, obtener la dirección de la función y usarla.
Esto evita que strings como "VirtualAlloc" o "CreateThread" aparezcan en el binario, eludiendo firmas estáticas.
31. Persistence Techniques
Registry Run Keys
Modificar las claves de ejecución del registro para que el código malicioso se ejecute automáticamente al iniciar el sistema o al iniciar sesión el usuario:
- HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
Screensaver Hijack
Reemplazar el salvapantallas legítimo por uno malicioso modificando las claves de registro:
- HKEY_CURRENT_USER\Control Panel\Desktop\ScreenSaveActive
- ScreenSaveTimeout
- SCRNSAVE.EXE
COM DLL Hijack
Sustituir una DLL legítima de un objeto COM por una maliciosa. La clave de registro para un objeto COM específico se encuentra en:
- Software\Classes\CLSID\{...}\InprocServer32
Windows Services
Los servicios de Windows se ejecutan en segundo plano sin interfaz gráfica. La persistencia se logra creando un nuevo servicio con inicio automático o modificando un servicio existente para que apunte a un binario malicioso.
AppInit_DLLs
La clave AppInit_DLLs permite especificar una lista de DLLs que se cargarán automáticamente en el espacio de direcciones de casi todas las aplicaciones cuando se inician. Requiere altos privilegios.
Netsh Helper DLL
Netsh puede cargar DLLs auxiliares. Al registrar una DLL maliciosa como helper de Netsh, esta se cargará cada vez que se ejecute la herramienta. La DLL debe exportar la función InitHelperDll.
Winlogon
Modificar las claves de Winlogon para ejecutar código malicioso al iniciar sesión:
- HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\Shell
- ...\Winlogon\Userinit
Port Monitors
Los monitores de puerto del servicio de cola de impresión (spoolsv.exe) pueden ser reemplazados por una DLL maliciosa que se cargará en el contexto de este servicio del sistema. La clave de registro es:
- \System\CurrentControlSet\Control\Print\Monitors\[nombre]
/¿Querés ver estas técnicas aplicadas en un caso real?
Las técnicas de inyección, API Hashing y persistencia documentadas aquí son las mismas que enfrenté al analizar el dropper multi-etapa con analystty y al deconstruir campañas reales de NetSupport RAT y Lumma Stealer.