La API de Windows (conocida también como Win32 API) proporciona la interfaz nativa mediante la cual las aplicaciones interactúan con el nucleo y los subsistemas del sistema operativo. Operaciones como la representación gráfica, el acceso al sistema de archivos, la modificación del registro o la gestión y administración de memoria virtual dependen de este conjunto de funciones.
Es fruto de estudio entre desarrolladores de malware, investigadores y blue team debido a su uso y abuso durante la ejecución de tareas maliciosas. Es debido a esto que su comprensión y entendimiento es fundamental durante la creación de malware personalizado o análisis de soluciones maliciosas. Es decir, entender como funciona la API de Windows e identificar sus diferentes usos jugará en nuestro favor a la hora de adentrarnos en el mundo del desarrollo de malware.
Arquitectura: Funciones C y COM
En sus orígenes, la API de Windows se diseñó exclusivamente bajo el paradigma de funciones estructuradas en C. Este lenguaje se seleccionó por el hecho de representar el menor común denominador, dando pie a que binarios compilados en otros lenguajes consumieran los servicios del sistema operativo a bajo nivel.
La expansión del sistema operativo generó dos limitaciones principales:
- La proliferación descontrolada de miles de funciones.
- La ausencia de consistencia en las nomenclaturas y la falta de agrupaciones lógicas (como los namespaces en C++).
Para solventar estas restricciones estructurales, Microsoft introdujo el Component Object Model (COM). Desarrollado inicialmente para permitir la comunicación e intercambio de datos entre aplicaciones de Microsoft Office (tecnología conocida como Object Linking and Embedding u OLE), COM sustituyó al antiguo mecanismo Dynamic Data Exchange (DDE).
COM se fundamenta en dos principios arquitectónicos:
- Comunicación mediante interfaces: Los clientes interactúan con los objetos (servidores COM) a través de contratos definidos e invariables denominados interfaces. Los métodos se organizan bajo un mecanismo de despacho por Virtual Table (
vtable), replicando el comportamiento de las funciones virtuales en C++. - Carga dinámica: La implementación del componente reside en librerías de enlace dinámico (
.dll) o ejecutables independientes (.exe) denominados servidores COM, los cuales se cargan en tiempo de ejecución en lugar de vincularse de forma estática al cliente.
Tipos de datos
Para estandarizar el desarrollo y asegurar la portabilidad del código entre plataformas de 32 y 64 bits, Windows define sus propios datos primitivos basados en directivas typedef.
| Tipo de Dato | Definición Técnica | Tamaño (x86) | Tamaño (x64) | Uso |
|---|---|---|---|---|
DWORD |
unsigned long |
4 bytes | 4 bytes | Int de 32 bits invariable. Almacena flags de protección, tamaños e identificadores. |
SIZE_T |
size_t |
4 bytes | 8 bytes | Representa el tamaño máximo de un objeto direccionable en la arquitectura actual. |
PVOID / LPVOID |
void* |
4 bytes | 8 bytes | Puntero genérico no tipado. Define direcciones base de memoria. |
HANDLE |
void* |
4 bytes | 8 bytes | Token que indexa un objeto del kernel (procesos, hilos, archivos). |
HMODULE |
void* |
4 bytes | 8 bytes | Handle específico de un módulo. Almacena la dirección base de un PE (.exe o .dll) en memoria. |
ULONG_PTR |
Entero sin signo | 4 bytes | 8 bytes | Entero del mismo tamaño que un puntero. Se emplea para realizar aritmética de punteros segura. |
Cadenas de caracteres (Strings)
El SDK de Windows clasifica las cadenas según su codificación (ANSI frente a Unicode/Wide) y sus permisos de acceso mediante prefijos específicos:
PCSTR/LPCSTR: Puntero a una cadena de caracteres ANSI de 8 bits, constante y terminada en nulo (const char*). El prefijo "LP" (Long Pointer) es un residuo heredado de la arquitectura segmentada de 16 bits.PSTR/LPSTR: Puntero a una cadena de caracteres ANSI de 8 bits modificable (char*).PCWSTR/LPCWSTR: Puntero a una cadena de caracteres Unicode (UTF-16) de 16 bits, constante y terminada en nulo (const wchar_t*). Requieren el prefijoLen su inicialización.PWSTR/LPWSTR: Puntero a una cadena de caracteres Unicode de 16 bits modificable (wchar_t*).
ANSI ("A) vs. Unicode ("W")
La mayoría de las funciones expuestas por el subsistema Win32 cuentan con dos implementaciones paralelas en las librerías del sistema (v.g., kernel32.dll), diferenciadas por un sufijo:
- Versión "A" (ANSI): Procesa parámetros de entrada basados en caracteres de 8 bits (
LPCSTR). - Versión "W" (Wide / Unicode): Procesa parámetros de entrada basados en caracteres de 16 bits (
LPCWSTR).
Internamente, las versiones "A" actúan como wrappers que convierten las cadenas ANSI a Unicode antes de delegar la operación en los componentes del sistema operativo. El impacto en el consumo de memoria difiere según la variante:
char strAnsi[] = "orionoffensive"; // Ocupa 15 bytes (14 caracteres + 1 byte nulo 0x00)
wchar_t strWide[] = L"orionoffensive"; // Ocupa 30 bytes (Cada carácter consume 2 bytes + 2 bytes nulos)Input y Output
En la documentación técnica del SDK de Windows (y en las funciones del código que normalmente se muestra en Orion Offensive) los parámetros de las funciones incluyen macros anotativas entre corchetes para explicitar su comportamiento:
[in](Input): El parámetro transmite datos hacia el interior de la función. Se pasa por valor o por puntero constante.[out](Output): El parámetro devuelve información al llamador. Se pasa obligatoriamente por referencia mediante el uso de punteros.[in, optional]: Parámetro de entrada que admite un puntero nulo (NULL) si la función no requiere dicha estructura para operar.
Estas macros (IN, OUT) no tienen valor para el compilador de C; son directivas informativas destinadas al usuario.
Implementación práctica: MessageBoxW
Para analizar la interacción con las APIs en un escenario práctico, vamos a usar la función MessageBoxW. Esta API se encarga de crear, parametrizar y desplegar un cuadro de diálogo en pantalla.
Según su documentación en el SDK de Windows establece el retorno de un tipo int:
int MessageBoxW(
[in, optional] HWND hWnd,
[in, optional] LPCWSTR lpText,
[in, optional] LPCWSTR lpCaption,
[in] UINT uType
);Los parámetros que acepta MessageBoxW son:
hWnd: Un handle hacia la ventana propietaria del cuadro de diálogo. Al seroptional, pasamosNULLpara indicar que la alerta no depende de ninguna ventana previa.lpText: Un puntero constante a una cadena de caracteres Unicode (LPCWSTR) que contiene el mensaje que se mostrará dentro del cuadro.lpCaption: Un puntero constante a una cadena de caracteres Unicode (LPCWSTR) que define el título de la ventana de diálogo.uType: Un entero sin signo (UINT) de 32 bits que determina los botones y el icono que se renderizarán mediante constantes predefinidas (v.g.,MB_OK,MB_ICONINFORMATION).
Código
El siguiente programa inicializa y muestra la ventana utilizando la codificación Unicode correspondiente:
#include <windows.h>
#include <stdio.h>
int main() {
// Definición de cadenas de caracteres en formato Unicode
LPCWSTR titulo = L"Título de la ventana";
LPCWSTR mensaje = L"Mensaje de la ventana.";
printf("[*] Invocando a MessageBoxW desde user-space.\n");
// Invocación de la API
int resultado = MessageBoxW(
NULL,
mensaje,
titulo,
MB_OK | MB_ICONINFO
);
if (resultado == 0) {
printf("[-] Error en MessageBoxW. Codigo: %lu\n", GetLastError());
return -1;
}
printf("[+] MessageBoxW ejecutado. Código de respuesta del usuario: %d\n", resultado);
return 0;
}

