¿Por qué se ejecutan los desbordamientos de búfer en la dirección en que están?

13

Sigo el video de The Security Tube aquí .

Hace una revisión general de los desbordamientos de búfer, y menciona cómo se ejecuta la memoria de mayor a menor en la pila (al menos con su implementación, supongo). Entonces pasamos la dirección de memoria de una función que no se llama en el programa, a un búfer de 3 palabras. Desbordamos ese búfer con una cadena de 12 caracteres y luego la dirección de memoria hacia atrás . Así que se ve algo como esto:

printf "123456789abc\x32\x07\x45\xb4" | ./demo

La dirección real era (b4450732).

¿Por qué es que mostramos la dirección de la memoria hacia atrás, pero al final de la pila? Si la memoria se lee de alta a baja, ¿no debería ser al revés, pero al comienzo de la cadena pasamos al programa? Obviamente, este no es el caso, ya que él lo mostró funcionando. Sin embargo; Me parece que la pila no se desbordaría y los valores x\cba987654321 se ejecutarían.

    
pregunta theCowardlyFrench 01.03.2015 - 03:01
fuente

2 respuestas

16

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.Losvaloresreturnaddress,gyhseránvaloresde8bytes.gpuedeseriguala0x10,perocuandoloempujasenlapilaseverácomo0x0000000000000010.

Despuésdequelosparámetrosseinsertanenlapila,seejecutalainstruccióncall.Estoempujaráladirecciónderetornoalapilaysaltaráalafunciónparasuejecución.Debidoaquelapilacrecehaciaunespaciodedireccionesbajocadavezqueseempujaalgoenlapila,seacercaacero.Enlaimagendearribatambiénverálasvariableslocalesxx,yyyzz.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.

    
respondido por el RoraΖ 02.03.2015 - 15:46
fuente
15

Hay dos cosas que están sucediendo aquí:

  1. En x86 y x86-64 (y la mayoría del otro hardware), la pila crece desde la parte superior de la memoria hacia abajo. Debido a esto, los datos utilizados por una función (p. Ej., El búfer que está desbordando) se producen en un número de dirección inferior al de los datos utilizados para llamar a la función (p. Ej., La dirección a la que se debe volver después de que se realiza la función, que tratando de desbordar en).

  2. En x86 y x86-64 (y en una variedad de otro hardware), los valores de varios bytes como las direcciones se almacenan en orden little-endian , es decir. "hacia atrás" desde el punto de vista de una persona que lo lee.

respondido por el Mark 01.03.2015 - 03:10
fuente

Lea otras preguntas en las etiquetas