Si la pila crece hacia abajo, las funciones que se llaman más adelante obtienen marcos de pila en direcciones de memoria inferiores. Además, la dirección de retorno se inserta en la pila antes de que se reserve el espacio para las variables locales, por lo que la dirección de retorno obtiene una dirección más alta que las variables locales. Pero las matrices y los buffers aún se indexan hacia arriba en la memoria, por lo que escribir más allá del final de la matriz pegará muy bien en la siguiente dirección de la pila.
Ejemplo, con arte ASCII obligatorio:
Considere la función trivial que toma información de una fuente no confiable y la copia en un búfer local:
void foo(char *s)
{
char buf[8];
strcpy(buf, s);
return;
}
La pila se parece un poco a esto:
<---- stack grows to the left
memory addresses increase to the right -->
0x8000 0x8010
+--------+----------+---------++------------
+ buf[8] | ret addr | char *s || .......
+--------+----------+---------++--------------
<--------- foo() -----------> <---- caller --
La pila se llena de derecha a izquierda, comenzando con los argumentos de la función, luego la dirección de retorno y luego los locales de la función. Es fácil ver que un simple desbordamiento de buf
hacia el aumento de direcciones llegará a la dirección de retorno muy bien.
Entonces, ¿qué pasa si la pila está invertida y crece hacia arriba? Luego, el desbordamiento de un búfer se ejecutará de la misma manera que crece la pila, hacia la parte vacía de la pila.
Suena bien, pero no ayuda si foo()
llama a otra función para hacer la copia. Lo que no es inusual, solo lo hice con strcpy
. Ahora la pila se ve así:
stack grows to the right -->
memory addresses increase to the right -->
0x8000 0x8010
------------++---------+----------+---------++-----------+-------------+
.... || char *s | ret addr | buf[8] || ret addr | locals ... |
------------++---------+----------+---------++-----------+-------------+
caller ---> <-------- foo() -------------> <---- strcpy() ---------->
Ahora, sobrepasar (a la derecha) el búfer en el marco de pila de foo()
sobrescribirá muy bien la dirección de retorno de strcpy()
, no foo()
. Sin embargo, no importa, seguimos saltando a una ubicación establecida por los datos controlados por el atacante y desbordados.