Grunt's personal blog

this is my personal blog for my hacking stuff, my degree stuff, etc

View on GitHub

The Stack

¿Qué es la Pila?

La pila es una región especial de memoria utilizada durante la ejecución de programas para almacenar variables locales y metadatos relacionados con el control de flujo. Es una estructura dinámica que crece y decrece según las necesidades del programa.

En esta sección, aprenderás cómo funciona la pila, cómo se asigna y libera memoria, y cómo se utiliza para gestionar las llamadas a funciones.


Estructura de la Pila

La pila se organiza como una secuencia de bloques de memoria llamados marcos de pila. Cada vez que se llama a una función, se asigna un nuevo marco de pila para almacenar sus variables locales y otros datos necesarios.

Cuando la función termina, su marco de pila se libera automáticamente, devolviendo la memoria al sistema.


Ejemplo de ejecución:

Introduce tu nombre: 
>> Juan

Introduce tu edad: 
>> 30

Visualización de la pila:

            .        espacio de pila no asignado         . /|\
            |                      ...                   |  |
            +-------- 0x7fffffffed80 (RSP) ---------+    |  |
  buffer -> | 4A | 75 | 61 | 6E | 00 | 00 | 00 | 00 |    |  |
            +---------------------------------------+    |  |
            | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 |    |  |
            +---------------------------------------+    |  |
            | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 |    |  |
            +---------------------------------------+    |  |
 padding -> | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 |    |  |
            +---------------------------------------+    |
     age -> | 1E | 00 | 00 | 00 | 00 | 00 | 00 | 00 |    |
            +-------- 0x7fffffffedb0 (RBP) ---------+    |
 old RBP -> | D0 | ED | FF | FF | FF | 7F | 00 | 00 |    |
            +---------------------------------------+    |
  return -> | 4C | 18 | 40 | 00 | 00 | 00 | 00 | 00 |    |
   addr     +---------------------------------------+    |
            |                      ...                   |
            .          marco de pila previo              .

Explorando la Pila con un Depurador

Para comprender cómo se asignan y gestionan los marcos de pila, utilizaremos un depurador. Coloca un punto de interrupción en una función de interés y observa cómo se comporta la pila durante su ejecución.

break nombre_de_funcion

Prólogo de Función

El prólogo de una función es un conjunto de instrucciones que se ejecutan al inicio de la misma. Su propósito es preparar un nuevo marco de pila para la función.

nombre_de_funcion:
    push    rbp
    mov     rbp, rsp
    sub     rsp, 0x30 
    ...

Estas instrucciones realizan las siguientes tareas:

  1. Guardan el puntero base anterior (rbp) en la pila.
  2. Establecen un nuevo puntero base (rbp) para el marco de pila actual.
  3. Reservan espacio en la pila para las variables locales de la función.

Puntero de Pila (RSP)

El registro rsp apunta a la parte superior de la pila. Cada vez que se realiza una operación de push, el puntero de pila se ajusta para reflejar la nueva asignación de memoria.

push    rbp

Internamente, esta instrucción:

  1. Resta 8 bytes del puntero de pila (rsp).
  2. Almacena el valor de rbp en la nueva ubicación de la pila.

Puntero Base (RBP)

El registro rbp actúa como un marcador para el inicio del marco de pila actual. Durante el prólogo de la función, el valor anterior de rbp se guarda en la pila, y luego se actualiza para apuntar al nuevo marco.

mov     rbp, rsp

Esto permite que el programa acceda fácilmente a las variables locales y a los parámetros de la función.


Asignación de Memoria en la Pila

La asignación de memoria en la pila es rápida y eficiente. Para reservar espacio, simplemente se resta un valor del puntero de pila (rsp).

sub     rsp, 0x30

Esto garantiza que la memoria por encima del puntero de pila esté disponible para su uso.


Epílogo de Función

El epílogo de una función es el conjunto de instrucciones que se ejecutan al final de la misma. Su propósito es liberar el marco de pila actual y devolver el control al llamador.

leave   
retn

La instrucción leave realiza las siguientes operaciones:

  1. Restaura el puntero de pila (rsp) al valor del puntero base (rbp).
  2. Recupera el valor anterior de rbp desde la pila.

Finalmente, la instrucción retn devuelve la ejecución al llamador.