Parte: 5 — Explotación de sistemas y binarios · Fuente: System V AMD64 ABI · Erickson, Hacking 2e ⏱️ Duración estimada: 120 min · Nivel: Fundamentos
Comprender a fondo cómo funciona el stack de un proceso: cómo crece, qué guarda un stack frame,
cómo call/ret usan la dirección de retorno y cómo las convenciones de llamada (System V en
Linux, Microsoft x64 en Windows) dictan dónde van los argumentos. Este conocimiento es el que
convierte un "buffer overflow" abstracto en una técnica concreta de control de flujo.
Al finalizar, el alumno podrá:
RBP guardado, variables locales.call empuja la dirección de retorno y ret la consume.RDI, RSI, RDX, RCX, R8, R9.| # | Tema | Por qué importa |
|---|---|---|
| 1 | El stack crece hacia abajo | Direcciones menores según se apilan datos |
| 2 | RSP y RBP | Tope del stack y base del frame actual |
| 3 | push / pop | Mecánica de apilar y desapilar |
| 4 | call / ret y la dirección de retorno | El objetivo número uno del atacante |
| 5 | Stack frame y prólogo/epílogo | Dónde viven locales y saved RBP |
| 6 | System V AMD64 (Linux) | Argumentos en registros; retorno en RAX |
| 7 | cdecl / stdcall (x86) | Argumentos por stack; quién limpia |
| 8 | Red zone y alineación a 16 bytes | Reglas que rompen exploits si se ignoran |
push
resta a RSP, pop suma.push/call lo modifica.[rbp-X]. Clave:
con -fomit-frame-pointer puede no usarse.call guarda en el stack para que ret sepa a dónde volver.
Clave: sobrescribirla es la esencia del stack overflow.RDI, RSI, RDX, RCX, R8, R9; el resto por stack;
retorno en RAX. Clave: estándar en Linux/macOS.RSP que una función hoja puede usar sin ajustar RSP. Clave: propia
de System V; no existe en Windows x64.sudo apt install -y gdb gcc
# pwndbg mejora enormemente la vista del stack (se instala en la clase 118)
Usa la misma VM de la clase 116. Compila siempre con -O0 -g mientras aprendes para no perder el frame.
Entorno propio.
frame.c:c
#include <stdio.h>
long triple(long x) { long r = x * 3; return r; }
int main(void) { printf("%ld\n", triple(5)); return 0; }
Compila con símbolos: gcc -O0 -g frame.c -o frame.
Depura y coloca un breakpoint dentro de triple:
bash
gdb -q ./frame
(gdb) break triple
(gdb) run
(gdb) info registers rsp rbp rip
call:gdb
(gdb) x/4gx $rbp # muestra saved RBP y, en rbp+8, la dirección de retorno
Confirma que la palabra en $rbp+8 apunta de vuelta a main.
Verifica el paso de argumentos: info registers rdi justo al entrar en triple debe valer 5.
Avanza con finish y observa el valor de retorno en RAX (debe ser 15).
Repite compilando en 32 bits (gcc -m32) y comprueba que ahora el argumento se lee de [esp+...]
en vez de un registro.
triple indicando offsets de r, saved RBP y ret address.call y ret sobre RSP y RIP.f(a,b,c,d)?-fomit-frame-pointer y describe cómo cambia el epílogo.p $rsp y p $rbp en dos frames anidados y calcula el tamaño de cada frame.Con GDB, encuentra e imprime la dirección de retorno de triple sin usar backtrace, solo con
aritmética sobre $rbp.
Criterio de aceptación: el valor impreso coincide con la dirección de la instrucción siguiente a
call triple en main (verificable con disassemble main).
| Síntoma / mensaje | Causa y cómo arreglar |
|---|---|
| No encuentras saved RBP | Compilaste con optimización u -fomit-frame-pointer; usa -O0 |
| La dirección de retorno "no cuadra" | Confundes crecimiento del stack; recuerda que crece hacia abajo |
| Segfault por desalineación en x64 | Falta alineación a 16 bytes en RSP antes de call |
| Argumentos "vacíos" en registros | En x86 van por stack, no por registros |
info registers no muestra R8-R15 |
Estás en un binario de 32 bits |
❓ ¿Por qué el stack crece hacia abajo? Es una convención histórica que permite que stack y heap crezcan uno hacia el otro maximizando el espacio disponible.
❓ ¿Qué diferencia hay entre cdecl y stdcall? Ambas pasan argumentos por stack en x86; en cdecl limpia el llamador, en stdcall limpia la función llamada.
❓ ¿La red zone puede romper mis exploits? Sí: si escribes bajo RSP asumiendo que está libre,
puedes corromper datos que la función hoja usa.
Clase 116 — Arquitectura x86/x64 y lenguaje ensamblador