← Back to list

Introducción a la API de Windows Para el desarrollo de malware, parte 1.

La Windows API son el conjunto de funciones nativas que el sistema operativo Windows ofrece para interactuar directamente con sus…

Dalton · 2025-09-04 03:56 · 3 claps · 6.6 min read
#pentesting #malware-development #programming #red-team-tools #red-teaming
Open on Medium ↗
Wiki topics: SAF · Safety & Alignment 💻 · Programming 🔒 · Cybersecurity

Introducción a la API de Windows Para el desarrollo de malware, parte 1.

La Windows API son el conjunto de funciones nativas que el sistema operativo Windows ofrece para interactuar directamente con sus componentes: desde la gestión de memoria y procesos, hasta el acceso al sistema de archivos, red y GUI. Comprender este ecosistema no solo es esencial para el desarrollo de aplicaciones legítimas, sino también para entender las técnicas y mecanismos empleados por el malware moderno.

En el ámbito de la ciberseguridad, el estudio de la WinAPI es especialmente relevante para comprende las técnicas utilizadas por el software malicioso y, a su vez, diseñar defensas más eficaces. Lejos de ser una herramienta exclusiva de los atacantes, el conocimiento profundo de estas funciones es indispensable para analistas, investigares y profesionales que participan en operaciones de Red Teaming.

Windows distingue el acceso al hadware mediante dos modos distintos: modo usuario y modo kernel. Las API o llamadas del sistema interactúan entre cada modo y envían información al sistema para su procesamiento.

Cuando hablamos del desarrollo de malware generalmente hablamos de lenguajes de bajo nivel, en este caso usaremos C++ y para aprovechar realmete el poder de C++ en Windows se debe ir más allá de la biblioteca estándar e interactuar directamente con la API de Windows, un amplio conjunto de funciones nativas proporcionadas por el mismo sistema operativo para interactura con procesos, memoria, archivos, ventanas y el propio Kernel.

Pero a demas de la API de Windows tambien usaremos:

VirtualAlloc: Para reservar, confirmar y modificar el estado de regiones de memoria dentro de su propio espacio de direcciones virtuales.

VirtualFree: Para liberar memoria signada en otro proceso.

CreateProces y TerminateProcess: Para crear, suspender y finalizar procesos.

CreateFile, ReadFile y WriteFile: Para la interaccion con el sistema de archivos.

HANDLE, DWORD, LPVOID Y LPCSTR: Para trabajar con identificadores y tipos de datos nativos.

Envio de mensajes con MessageBox:

#include <windows.h>   //Cabecera necesaria para interactar con la API

int main(){
    MessageBoxA(                  //Función que mostrara una ventana emergente 
       NULL,                      //Si el parametro es NULL el cuatdo de mensaje no tiene nunguna ventana
       "Hola Mundo",              // Mensaje a mostrar 
       "Uso de la API",           //Titulo de la ventana  
       MB_OK | MB_ICONINFORMATION //El cuadro de mensaje tiene un boton ok y un icono que consta de una letra minúscula i en un círculo aparece en el cuadro de mensaje.
    );

    return 0;
}

Con este ejemplo podemos ver como lanzar una ventana emergente del sistema operativo.

Otro ejemplo que podemos ver es la asignación de memoria con VirtualAlloc.

#include <windows.h>
#include <iostream>

int main(){

    LPVOID mem = VirtualAlloc(
        NULL,
        1024,
        MEM_COMMIT | MEM_RESERVE,
        PAGE_READWRITE
    );

if (mem = NULL) {
    std::cerr << "Fallo en la asignación de memoria" << GetLastError() << std::endl;
    return 1;
}

strcpy_s((char*)mem, 1024, "Asignación de memoria via WinAPI");

std::cout <<"Contenido de memoria: " << (char*)mem << std::endl;

VirtualFree(mem, 0, MEM_RELEASE);

return 0;

En este ejemplo podemos ver la asignación de memoria haciendo uso de la API de Windows, pero vamos a desglosarlo.

LPVOID: Es un puntero genérico a void, si falla la variable (mem) sera NULL.

LPVOID mem = VirtualAlloc(
    NULL,                      // Deja que Windows eliga automáticamente donde reservar la memoria
    1024,                      // Tamaño para reservar y comprometer en bytes 1024 reservará almenos 4KB
    MEM_COMMIT | MEM_RESERVE,  // MEM_COMMIT Reserva un rango de direcciones virtuales, MEM_COMMIT Asigna espacio físico en ese rango
    PAGE_READWRITE             // Establece la protección de la memoria, en este casi lectura y escrit
);

Como se menciono si el mem es null sera un fallo, esto es manejado con la condición if, la cual la salida sera un 1.

if (mem = NULL) {
    std::cerr << "Fallo en la asignación de memoria" << GetLastError() << std::endl;
    return 1;
}

Si mem no es NULL entonces será una asignación de memoria exitoso, en tal caso podremos escribir en el espacio reservado para memoria en mem, esto lo podemos hacer con el uso de la función strcpy_s para copiar en ese espacio reservado y ademas de esto podemos leer este contenido haciendo un cast de char a mem.

errno_t strcpy_s(
    char *dest,        // Buffer destino, casteando la variable mem
    rsize_t destsz,    // Tamaño del buffer destino (en bytes)
    const char *src    // Cadena fuente
);

En este caso usamos esta función ya que estamos copiando una cadena, la lectura de este dato en memoria es tan fácil como hacer un cast a mem.

std::cout <<"Contenido de memoria: " << (char*)mem << std::endl;

Para poder finalizar el programa debemos liberar este espacio en memoria, si no la liberamos esto puede dar pie a un memory leak, para poder liberar la memoria usaremos VirtualFree.

BOOL VirtualFree(
    LPVOID lpAddress,  // Dirección base de la memoria mem en este caso
    SIZE_T dwSize,     // Tamaño a liberar (en bytes) Cuando usas MEM_RELEASE, este valor debe ser 0
    DWORD  dwFreeType  // Tipo de liberación MEM_RELEASE: Libera todo el bloque y la reserva de direccione
);

Creación de un nueo proceso con CreateProcessA.

En este ejemplo crearemos un nuevo proceso notepad.exe y esperaremos a que termine.

#include <windows.h>
#include <iostream>

int main() {
    STARTUPINFOA si = { sizeof(si) };  
    PROCESS_INFORMATION pi;           


    LPCSTR appName = "C:\\Windows\\System32\\notepad.exe";


    BOOL success = CreateProcessA(
        appName,       
        NULL,          
        NULL,         
        NULL,          
        FALSE,        
        0,             
        NULL,          
        NULL,          
        &si,           
        &pi           
    );

    if (!success) {
        std::cerr << "Fallo al crear proceso " << GetLastError() << std::endl;
        return 1;
    }

    std::cout << "Proceso creado PID: " << pi.dwProcessId << std::endl;


    WaitForSingleObject(pi.hProcess, INFINITE);


    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);

    return 0;
}

Procedamos a desglosar el código:

STARTUPINFOA si = { sizeof(si) };  // Inicializa la estructura STARTUPINFO el cual contiene información sobre cómo se debe iniciar el proceso
PROCESS_INFORMATION pi;           // Estructura que recibirá información del proceso creado

STARTUPINFOA es una estructura que se usa con CreateProcessA para controrar cómo se incia un proceso.

typedef struct _STARTUPINFOA {
    DWORD  cb;                  // Tamaño de la estructura en bytes
    LPSTR  lpReserved;          // Reservado, debe ser NULL
    LPSTR  lpDesktop;           // Escritorio de la ventana (p.ej., "winsta0\\default")
    LPSTR  lpTitle;             // Título de la ventana de la consola
    DWORD  dwX;                 // Posición horizontal inicial de la ventana
    DWORD  dwY;                 // Posición vertical inicial de la ventana
    DWORD  dwXSize;             // Ancho inicial de la ventana
    DWORD  dwYSize;             // Alto inicial de la ventana
    DWORD  dwXCountChars;       // Columnas de caracteres
    DWORD  dwYCountChars;       // Filas de caracteres
    DWORD  dwFillAttribute;     // Atributo de relleno
    DWORD  dwFlags;             // Flags de inicio (p.ej., STARTF_USESHOWWINDOW)
    WORD   wShowWindow;         // Cómo mostrar la ventana (p.ej., SW_SHOW)
    WORD   cbReserved2;         // Reservado
    LPBYTE lpReserved2;         // Reservado
    HANDLE hStdInput;           // Handle de entrada estándar
    HANDLE hStdOutput;          // Handle de salida estándar
    HANDLE hStdError;           // Handle de error estándar
} STARTUPINFOA, *LPSTARTUPINFOA;

cb: Tamaño de la estructura.

dwFlags: Flags para indicar qué campos se usan.

wShowWindow: Controla cómo se muestra la ventana.

hsIntput/Output/Error: Punteros a handles para redirigr entrada/salia/errores

ldDesktop: Escritorio donde se creará la ventana.

lpTitle: Titulo de la ventana

PROCESS_INFORMATION es una estructura que se utiliza con funciones como CreateProcess.

typedef struct _PROCESS_INFORMATION {
    HANDLE hProcess;      // Handle del proceso
    HANDLE hThread;       // Handle del hilo principal
    DWORD  dwProcessId;   // ID del proceso
    DWORD  dwThreadId;    // ID del hilo principal
} PROCESS_INFORMATION, *PPROCESS_INFORMATION, *LPPROCESS_INFORMATION;

hProcess: Handle del proceso recién creado.

hThread: Handle al hilo principal del proceso.

dwProcessId: ID único del proceso asignado por Windows.

dwThreadId: ID del hilo principal.

Debemos señalar la ruta del ejecutable:

LPCSTR appName = "C:\\Windows\\System32\\notepad.exe";

LPCSTR: Puntero a cadena ANSI

BOOL CreateProcessA(
    LPCSTR                lpApplicationName,        // Ruta del ejecutable
    LPSTR                 lpCommandLine,            // Línea de comandos (puede ser NULL)
    LPSECURITY_ATTRIBUTES lpProcessAttributes,      // Seguridad del proceso
    LPSECURITY_ATTRIBUTES lpThreadAttributes,       // Seguridad del hilo
    BOOL                  bInheritHandles,          // Hereda handles de este proceso
    DWORD                 dwCreationFlags,          // Flags de creación (p.ej., CREATE_NEW_CONSOLE)
    LPVOID                lpEnvironment,            // Variables de entorno (NULL = hereda del proceso padre)
    LPCSTR                lpCurrentDirectory,       // Directorio de trabajo (NULL = directorio actual)
    LPSTARTUPINFOA        lpStartupInfo,            // Información de inicio (ventana, I/O)
    LPPROCESS_INFORMATION lpProcessInformation      // Recibe info del proceso/hilo
);

Esta estructura nos dara dos valores de retorno TRUE y FALSE en dado caso de que sea FALSE se usara GetLastError() para saber el error.

if (!success) {
        std::cerr << "Fallo al crear proceso " << GetLastError() << std::endl;
        return 1;
    }

Des podemos saber el PID del proceso, para esto podemos usar dwProcessId el cual es un DWORD que contiene un ID único del proceso recién creado.

std::cout << "Proceso creado PID: " << pi.dwProcessId << std::endl;

Este PID es un número que Windows asigna automáticamente a cada procso en ejecución, sirve para identifcar y manipular el proceo desde tu programa u otras herramientas del sistema.

En la parte final del programa podemos encontrar:

WaitForSingleObject(pi.hProcess, INFINITE);


    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);

WaitForSingleObject espera a que el proceso al que apunta pi.hProcess termine INFINITE significa que espera indefinidamente hasta que el proceso finalice.

DWORD WaitForSingleObject(
    HANDLE hHandle,       // Handle al objeto (proceso, hilo, mutex, etc.)
    DWORD dwMilliseconds  // Tiempo máximo a esperar (INFINITE = indefinido)
);

Finalmente CloseHandle:

Libera el handle de Windows para el recurso, no termina el proceso ni el hilo; solo libera el descriptor en el programa. Si no cierras los handles, el programa puede tener fugas de recursos.

BOOL CloseHandle(
    HANDLE hObject  // Handle al recurso que quieres cerrar
);

hObject: Cualquier handle válido devuelto por funciones de Windows, ya sea CreateProcess, CreateFile, CreateThread.

Retorna un True si se cerro exitosamente y FALSE para un error, aqui seguimos usando GetLastError() para obtener información.

Estos son uno de los usos de WinAP, evidentemete se puden usar para realizar cosas maliciosas, pero en esta ocasión lo dejaremos aquí, en la próximas entregas andaremos en otras funciones.

Happy Hacking!

¿Quieres llevar esto al siguiente nivel?

En este artículo vimos la base de la ejecución de código en memoria. Pero en entornos reales esto es solo el inicio.

En mi entrenamiento profundizo en:

  • desarrollo de loaders evasivos
  • ejecución de shellcode sin detección
  • bypass de antivirus y EDR
  • uso avanzado de WinAPI en escenarios reales

👉 Puedes ver el contenido completo aquí: 👉 https://academy.spartan-cybersec.com/l/pdp/curso-de-desarrollo-ofensivo-en-windows-con-c-plus-cdowcp


메타데이터
post_id
051bcdbd09fd
slug
introducción-a-la-api-de-windows-para-el-desarrollo-de-malware-parte-1-051bcdbd09fd
url
https://medium.com/@dalthon/introducci%C3%B3n-a-la-api-de-windows-para-el-desarrollo-de-malware-parte-1-051bcdbd09fd
canonical_url
https://medium.com/@dalthon/introducci%C3%B3n-a-la-api-de-windows-para-el-desarrollo-de-malware-parte-1-051bcdbd09fd
author_url
https://medium.com/@dalthon
status
ok
fetched_at
2026-06-13 07:35:29