Reversing C++ — Funciones, Stack, Shadow Space (Windows x64)¶
Apuntes estructurados basados en el análisis del calling convention de Windows x64 y el comportamiento de funciones en C/C++ visto al decompilar.
1. Declaración vs Definición de Funciones¶
Declaración (prototipo)¶
Indica al compilador que existe una función con cierto tipo de retorno y tipos de parámetros.
Características:
- No necesita nombres de parámetros.
- Permite llamar a la función antes de su definición.
- Va al inicio del archivo o en un header .h.
Definición¶
Contiene el código de la función.
Debe coincidir en:
- tipo de retorno
- nombre
- tipos de parámetros
2. main NO es el entry point real¶
El ejecutable arranca en funciones del CRT (runtime de C), no en main.
El entry point:
- Inicializa stack, heap, TLS.
- Prepara argumentos, entorno, seguridad.
- Finalmente hace call main.
En IDA:
Ctrl + E → entry point real.
3. El Stack en x64¶
- El stack es LIFO.
RSPapunta al “tope de la pila”.- Cada slot típico ocupa 8 bytes.
call¶
- Escribe el return address en el stack (push implícito).
- Salta a la función.
ret¶
- Lee el return address desde
[RSP]. - Salta de vuelta.
- Ajusta
RSP.
Dirección de crecimiento¶
- El stack crece hacia abajo (hacia direcciones más bajas).
- Cada reserva o
pushreduceRSP.
4. Calling Convention de Windows x64¶
Orden de argumentos¶
RCXRDXR8R9- Del 5.º en adelante → stack
Valor de retorno¶
- Se devuelve en
RAX(EAXpara enteros de 32 bits).
5. Shadow Space (Home Space)¶
En Windows x64, toda función llamada debe tener en el caller una reserva obligatoria de 32 bytes justo debajo del return address.
Ese espacio sirve para que la función callee pueda, si quiere, guardar ahí los 4 argumentos pasados por registro.
Layout típico al entrar en una función:
[RSP] → return address
[RSP+8] → shadow slot 1 (arg1)
[RSP+16] → shadow slot 2 (arg2)
[RSP+24] → shadow slot 3 (arg3)
[RSP+32] → shadow slot 4 (arg4)
[RSP+40] → 5º argumento (si existe)
[RSP+48] → 6º argumento (si existe)
...
El compilador: - Puede copiar los registros ahí. - O dejarlos vacíos si usa los registros directamente.
6. Ejemplo: mayor(int a, int b)¶
Caller¶
Callee¶
Puede empezar con:
Luego compara:
EAX contiene el valor de retorno.
7. Ejemplo con 6 argumentos¶
a→ RCXb→ RDXc→ R8d→ R9e,f→ stack
En el callee todos quedan en orden en el stack gracias al shadow space.
8. Stack frame en IDA¶
IDA permite ver:
- Layout estático del stack (shadow, arguments, locals).
- Cambios de RSP con “stack pointer tracking”.
- Decompilado reconstruyendo parámetros a partir de
[rsp+offset].
Ejemplo típico mostrado por IDA:
+----------------------+
| local_20 |
| local_18 |
| local_10 |
| local_8 |
+----------------------+
| arg_20 (6º) |
| arg_18 (5º) |
| shadow slot 4 |
| shadow slot 3 |
| shadow slot 2 |
| shadow slot 1 |
+----------------------+
| return address |
9. Resumen mental para reversing x64¶
- 4 argumentos → RCX, RDX, R8, R9.
- Shadow space = 32 bytes entre return address y argumentos extra.
- Función puede copiar esos args al stack o no.
- 5º y 6º argumento siempre en stack.
- RAX/EAX = valor de retorno.
- El stack crece hacia direcciones bajas.
- IDA permite ver todo el frame bien representado.