Introducción a la API de Windows para el Desarrollo de Malware — Parte 5
Qué tal amigos, en la entrega anterior estuvimos viendo el formato PE, la estructura que Windows lee antes de ejecutar cualquier archivo…
Introducción a la API de Windows para el Desarrollo de Malware — Parte 5

Qué tal amigos, en la entrega anterior estuvimos viendo el formato PE, la estructura que Windows lee antes de ejecutar cualquier archivo. Hoy vamos a bajar un nivel más y vamos a hablar del lenguaje que realmente entiende el procesador: el ensamblador x64.
Si alguna vez abriste un binario en un debugger como x64dbg y viste instrucciones como mov rax, rcx o push rbp y cerraste la ventana inmediatamente — esta entrega es para ti.
¿Qué es el ensamblador?
El ensamblador (o assembly) es el nivel de abstracción más bajo antes del código máquina puro. Cada instrucción en ensamblador corresponde directamente a una instrucción que ejecuta el procesador.
Cuando tú escribes esto en C++:
int suma = a + b;
El compilador lo traduce a algo parecido a esto en ensamblador:
mov eax, [a] ; Carga el valor de 'a' en el registro EAX
add eax, [b] ; Suma el valor de 'b' al registro EAX
mov [suma], eax ; Guarda el resultado en 'suma'
¿Por qué importa esto en el desarrollo ofensivo? Porque el malware más sofisticado no usa funciones de alto nivel que puedan ser interceptadas. En cambio, habla directamente con el procesador y el kernel usando ensamblador, evitando los hooks que colocan los EDR y los antivirus sobre la WinAPI.
Los registros en x64
Los registros son pequeños espacios de almacenamiento dentro del procesador, extremadamente rápidos. En arquitectura x64 los principales son de 64 bits y tienen el prefijo R:
Registro Uso principal RAX Acumulador. Guarda el valor de retorno de las funciones RBX Registro de propósito general RCX Primer argumento de una función (en la convención x64 de Windows) RDX Segundo argumento de una función R8 Tercer argumento R9 Cuarto argumento RSP Stack Pointer — apunta al tope del stack RBP Base Pointer — referencia la base del stack frame actual RIP Instruction Pointer — apunta a la próxima instrucción a ejecutar
Esto es crítico: cuando en C++ llamas a una función como VirtualAlloc(NULL, 1024, ...), el compilador está cargando NULL en RCX, 1024 en RDX, y así sucesivamente antes de ejecutar el call. El procesador no sabe nada de nombres de parámetros, solo de registros.
Instrucciones básicas
Veamos las instrucciones que verás constantemente al analizar o escribir código ofensivo:
mov — mover datos
mov rax, 10 ; Carga el valor 10 en RAX
mov rbx, rax ; Copia el valor de RAX a RBX
Es la instrucción más común. Mueve un valor a un registro o a una dirección de memoria.
push y pop — el stack
push rax ; Guarda RAX en el stack y decrementa RSP
pop rbx ; Recupera el valor del tope del stack y lo pone en RBX
El stack es una región de memoria LIFO (último en entrar, primero en salir). Se usa para guardar valores temporalmente y para pasar argumentos en funciones.
call y ret — llamadas a funciones
call VirtualAlloc ; Llama a la función VirtualAlloc
ret ; Regresa al punto donde fue llamada la función actual
call guarda la dirección de retorno en el stack y salta a la función. ret la recupera y regresa.
add y sub — aritmética
add rax, 5 ; RAX = RAX + 5
sub rcx, 1 ; RCX = RCX - 1
cmp y jmp — comparaciones y saltos
cmp rax, 0 ; Compara RAX con 0 (modifica flags internos)
je etiqueta ; Salta a 'etiqueta' si son iguales (Jump if Equal)
jne etiqueta ; Salta si NO son iguales (Jump if Not Equal)
jmp etiqueta ; Salto incondicional
Estas instrucciones son los if y los while del ensamblador.
Ensamblador inline con C++
En C++ podemos mezclar ensamblador directamente en nuestro código usando bloques __asm. Veamos un ejemplo sencillo donde usamos ensamblador para sumar dos números:
#include <iostream>
int main() {
int a = 10;
int b = 20;
int resultado = 0;
__asm {
mov eax, a ; Carga 'a' en EAX
add eax, b ; Suma 'b' a EAX
mov resultado, eax ; Guarda el resultado
}
std::cout << "Resultado: " << resultado << std::endl; // 30
return 0;
}
Sencillo, pero ilustra perfectamente cómo el procesador manipula datos. Nota que usamos EAX (32 bits) porque estamos operando con enteros de 32 bits — si fueran valores de 64 bits usaríamos RAX.
¿Qué tiene que ver esto con el malware?
Todo. Cuando un EDR quiere monitorear si un proceso llama a NtAllocateVirtualMemory (la función del kernel detrás de VirtualAlloc), coloca un hook: modifica los primeros bytes de esa función para interceptar la llamada.
El malware avanzado evita ese hook haciendo la llamada directamente al kernel mediante una syscall, sin pasar por la función hooked. Y una syscall se ve así:
mov r10, rcx ; Convención de syscall en Windows
mov eax, 0x18 ; Número de syscall de NtAllocateVirtualMemory
syscall ; Llamada directa al kernel
Eso es ensamblador puro. Sin WinAPI, sin hooks, directo al kernel.
No vamos a profundizar en esto aquí, pero ya puedes ver por qué dominar ensamblador es la diferencia entre hacer herramientas que un AV detecta en segundos y herramientas que pasan desapercibidas.
¿Qué sigue?
En la próxima entrega vamos a hablar de qué es un loader, para qué sirve y por qué es una de las piezas más importantes en el desarrollo ofensivo moderno.
Si no quieres esperar y quieres aprender syscalls directas, técnicas de evasión de EDR y cómo construir tus propias herramientas ofensivas desde cero, todo eso está cubierto en profundidad en el **Curso de Desarrollo Ofensivo en Windows con C++ (CDOWCP)**.
Happy Hacking!
메타데이터
- post_id
- 09fb9e2e2d5e
- slug
- introducción-a-la-api-de-windows-para-el-desarrollo-de-malware-parte-5-09fb9e2e2d5e
- url
- https://medium.com/@dalthon/introducci%C3%B3n-a-la-api-de-windows-para-el-desarrollo-de-malware-parte-5-09fb9e2e2d5e
- canonical_url
- https://medium.com/@dalthon/introducci%C3%B3n-a-la-api-de-windows-para-el-desarrollo-de-malware-parte-5-09fb9e2e2d5e
- author_url
- https://medium.com/@dalthon
- status
- ok
- fetched_at
- 2026-06-09 15:37:30