Las librerías de enlace dinámico (por sus siglas DLL) componen una parte importante de la arquitectura del sistema operativo Windows. Su propósito principal es promover la reutilización de código y la modularidad mediante la encapsulación de funciones, datos y recursos en dado un archivo independiente.
A diferencia de los ejecutables convencionales (.exe),los DLLs no están diseñadas para ejecutarse como procesos autónomos. En su lugar, el sistema operativo mapea el archivo DLL dentro del espacio de memoria virtual de un proceso host existente, permitiéndole al proceso aprovechar el código exportado.
Desde la óptica de la seguridad ofensiva,los DLLs constituyen el vector de inyección y persistencia más habitual. Las técnicas de DLL Injection, DLL Sideloading y API Hooking, las cuales operan sobre la premisa de forzar a un proceso legítimo a cargar una librería maliciosa dentro de su espacio de confianza.
DllMain como punto de entrada
Todo archivo DLL bien formado (aunque no es estrictamente obligatorio a nivel de compilación) implementa un punto de entrada principal denominado DllMain. Esta función actúa como el inicializador de la librería.
BOOL WINAPI DllMain(
HINSTANCE hinstDLL, // Handle del módulo (Dirección base del DLL en memoria)
DWORD fdwReason, // Motivo de la llamada
LPVOID lpvReserved // Parámetro reservado (indica si la carga fue estática o dinámica)
);El loader del sistema operativo invoca automáticamente DllMain cuando la librería cambia de estado dentro del proceso. El comportamiento interno del código inyectado se define evaluando el parámetro fdwReason:
DLL_PROCESS_ATTACH: Ocurre en el instante en el que el DLL es mapeado en el espacio de direcciones del proceso. Es el lugar estándar donde el payload inicializa sus variables globales, establece hooks o ejecuta su rutina principal.DLL_THREAD_ATTACH: Ocurre cuando el proceso host crea un nuevo hilo (después de que el DLL haya sido cargado).DLL_THREAD_DETACH: Ocurre cuando un hilo del proceso termina su ejecución limpiamente.DLL_PROCESS_DETACH: Ocurre cuando el DLL se descarga del espacio de memoria (por la finalización del proceso o por una llamada aFreeLibrary). Se utiliza para revertir modificaciones (Unhooking) o liberar recursos.
Bloqueo del loader: Al ejecutarse
DllMain, el hilo actual mantiene un bloqueo sobre el cargador del sistema operativo. Esto significa que, invocar funciones comoCreateThreado dependencias que fuercen la carga de otras DLLs dentro deDLL_PROCESS_ATTACH, puede causar un deadlock que congele el proceso. Es por esto que nuestro código deberá evadir esto ejecutando su lógica principal en un hilo separado o difiriendo la ejecución mediante APCs (Asynchronous Procedure Calls).
Function Exports
Con el objetivo de que el proceso pueda invocar las rutinas del DLL, la librería debe exponer explícitamente qué funciones están disponibles hacia fuera. Esto se registra en el Export Directory del formato PE.
Al desarrollar una DLL en C o C++, el compilador utiliza identificadores específicos (__declspec(dllexport)) para indicarle al linker que la función debe ser agregada a la tabla de exportación (EAT).
// Se define una función exportada
extern "C" __declspec(dllexport) void FuncionExportada() {
// Código a ejecutar
MessageBoxW(NULL, L"DLL Inyectada", L"Alerta", MB_OK);
}extern "C": Indica al compilador (especialmente en C++) para que no aplique Name Mangling al compilar la función. Esto garantiza que la función se registre en el binario con el nombre exacto "FuncionExportada", facilitando su posterior localización.__declspec(dllexport): Etiqueta la función como un recurso exportable.
Tipos de enlaces DLL
Un proceso puede aprovechar y enlazar una DLL en su espacio de memoria mediante dos mecanismos distintos.
En tiempo de carga
- El enlace estático o implícito ocurre cuando el programa define en su código fuente las cabeceras (
.h) y se enlaza contra la librería estática de importación (.lib) correspondiente durante la compilación. - El compilador escribe una dependencia estática en la Import Address Table (IAT) del ejecutable. Cuando el programa se inicia, el OS Loader examina la IAT y carga automáticamente todos los DLLs requeridas (ej.
kernel32.dll,user32.dll) antes de transferir el control al punto de entrada del programa (main). - Si el sistema operativo no encuentra el DLL en el disco, la aplicación no se iniciará y mostrará un error del sistema.
En tiempo de ejecución
El enlace dinámico o explícito permite a un proceso cargar una DLL a demanda mientras la aplicación ya se está ejecutando. Esta es la técnica base que emplea el software ofensivo (ej. para resolver funciones nativas o evadir el análisis de la IAT por parte de EDRs).
Se requieren dos APIs para realizar una vinculación en tiempo de ejecución:
- (o
LoadLibraryEx): Solicita al kernel que mapee el DLL en la memoria del proceso. Si es exitosa, devuelve unHMODULE(la dirección base de el DLL). - : Dada la dirección base del módulo obtenida en el paso anterior y el nombre de la función exportada en formato ASCII, esta API recorre el Export Directory de el DLL y devuelve la dirección de memoria exacta donde reside el código de la función solicitada.
// Ejemplo de carga en tiempo de ejecución
// Definición del prototipo de la función que esperamos encontrar en el DLL
typedef void (WINAPI* fnEjecutarPayload)();
int main() {
// Cargamos el DLL en memoria durante la ejecución
HMODULE hModulo = LoadLibraryA("Payload.dll");
if (hModulo != NULL) {
// Resolvemos la dirección de la función exportada
fnEjecutarPayload pFuncion = (fnEjecutarPayload)GetProcAddress(hModulo, "EjecutarPayload");
if (pFuncion != NULL) {
// Invocamos la función
pFuncion();
}
// Descargamos el DLL del proceso (Desencadena DLL_PROCESS_DETACH)
FreeLibrary(hModulo);
}
return 0;
}Rundll32.exe como ejecutor de DLLs
Dado que un DLL no puede ejecutarse por sí solo, Windows proporciona una herramienta legítima preinstalada denominada rundll32.exe. Su función es actuar como un proceso host contenedor que carga el DLL especificada e invoca una de sus funciones exportadas.
Es comúnmente abusada en tácticas de evasión de defensas (técnica conocida como Living off the Land o LOLBin), ya que permite ejecutar código malicioso en una DLL bajo el paraguas de un ejecutable firmado por Microsoft. En la actualidad, el uso de LOLBin como formas de ejecución está altamente detectado en entornos maduros a nivel de seguridad.
Otro método de ejecutar DLLs dentro del ámbito ofensivo sería mediante la carga reflectiva, también conocida como Reflective DLL loading, de la cuál hablaremos más adelante.



