Curso intensivo en arquitectura de computadoras
En las arquitecturas Intel x86 y x64 hay algo que se llama la pila. Aquí es esencialmente donde se almacena todo para determinar la ruta de ejecución. Los parámetros de las funciones, las variables locales y las direcciones de retorno se almacenan en la pila. Los registros de la CPU mantienen un registro de dónde se está ejecutando el programa en la pila. Puede insertar valores y direcciones de memoria en la pila hasta el tamaño de bits de la arquitectura.
¿Qué quiero decir con eso? Si está en un sistema de 32 bits, entonces cada valor que presione en la pila tendrá un tamaño de unsigned int
32 bits (4 bytes). Si está en un sistema de 64 bits, tendrá un tamaño de 64 bits (8 bytes). A continuación se muestra un ejemplo [1] de lo que la pila Parece que para una función:
uint32_t function(int a, int b, int c, int d, int e, int f, int g, int h);
Elejemplodearribaesparax64.LosregistrosdelaCPURDI,RSI,RDX,RCX,R8yR9almacenanlosprimeros6parámetros,yelrestoseinsertanenlapila.returnaddress
,g
yh
seránvaloresde8bytes.g
podríaseriguala0x10
,perocuandoloempujasenlapilaseverácomo0x0000000000000010
.
Despuésdequelosparámetrosseinsertanenlapila,seejecutalainstruccióncall
.Estoempujaráladirecciónderetornoalapilaysaltaráalafunciónparasuejecución.Debidoaquelapilacrecehaciaunespaciodedireccionesbajocadavezqueseempujaalgoenlapila,semuevemáscercadecero.Enlaimagendearribatambiénverálasvariableslocalesxx
,yy
yzz
.Cadaunodeestostambiénseestámoviendohaciaabajodelapilahacialamemoriainferior.Porsupuesto,tengaencuentaqueelprogramasiempreestámanipulandoel arriba de la pila .
Desbordamiento de pila
Digamos que creas una variable local que es un búfer con un máximo de 12 bytes. Algo así, unsigned char buffer[12];
. La pila hace espacio para 12 bytes de datos. Digamos que llenamos este búfer con "012345678912"
que se verá así en la pila 1 :
High
...
32313938
37363534
33323130
...
Low
Porque el comienzo del búfer siempre será hacia la memoria inferior [2] . Entonces, cuando comienzas a escribir en un búfer, siempre estás escribiendo de Bajo a Alto. Si no asigna suficiente espacio y escribe más datos de los que ha asignado, tiene un desbordamiento de búfer.
Endianess
Ahora desea sobrescribir la dirección de retorno en la pila. Lo coloca al final de su cadena de manera que cuando la copia escribe en una memoria superior, sobrescriba su dirección de retorno con la dirección que desea que aparezca allí 2 . Intel lanza otra bola curva hacia ti. Los sistemas x86 y x64 son Little Endian. Lo que significa que el byte menos significativo está en la dirección más pequeña.
Por lo tanto, querrá escribir el byte menos significativo de la dirección ( 0x32
) en la memoria inferior. Así que escribirá la dirección en la memoria hacia atrás porque está escribiendo de memoria baja a alta. Al ver la dirección de mayor a menor (como se muestra en el ejemplo anterior), la dirección de la memoria se verá correcta. Sin embargo, cuando se ve de bajo a alto, será hacia atrás. El aspecto importante a recordar es que la arquitectura requiere que el LSB se escriba en la memoria inferior.
1 - Estoy usando un sistema de 32 bits aquí porque es más fácil dibujar un ancho de 4 bytes.
2 - EBP guardado, estoy ignorando esto Porque no es importante para la discusión. Es muy importante recordar, pero ignorar su existencia por ahora.