¿Por qué la variable local no se sobrescribe debido al desbordamiento del búfer?

1

Aquí está el programa my C ++ en el que ocurrió el desbordamiento del búfer, aún la variable local no está dañada:

#include <iostream>
#include <cstring>

using namespace std;

int main() {
    // your code goes here
    int i = 20;
    char var[4];

    strcpy(var, "biiiiiiiyee");
    cout<<" var is : "<< var << endl;
    cout<<" i : " << i << endl;
    return 0;
}

La salida del programa es:

 var is : biiiiiiiyee
 i : 20

El supuesto es que sobrescribir el final del búfer var debería corromper la variable i .

En el programa anterior, estoy sobrescribiendo el búfer, pero la variable local no se corrigió. ¿Por qué?

    
pregunta Pro Account 27.09.2016 - 16:39
fuente

1 respuesta

2

Alineación. Se permite al compilador colocar variables locales en la pila de manera que la alineación con las palabras de la CPU sea mejor. En otras palabras, no hay nada que obligue al compilador a colocar variables locales una tras otra en la pila. En realidad, la colocación de variables locales una tras otra estaría ahorrando un par de bytes en el tamaño del programa para una penalización considerablemente grande en el tiempo de procesamiento.

Permítame usar C simple en lugar de C ++ para explicar porque es mucho más fácil colocarlo en GDB. Programa convertido a C simple, llamemos pp.c :

#include <stdio.h>
#include <string.h>

int main() {
    int i = 20;
    char var[4];

    strcpy(var, "12345678901");
    printf(" var is : %s\n", var);
    printf(" i : %d\n", i);
    return 0;
}

Tenga en cuenta que la cadena que usé es de la misma longitud:

"12345678901"
"biiiiiiiyee"

Vamos a compilarlo sin optimizaciones, por si acaso:

gcc -O0 -g -o pp pp.c

Y ahora veamos el programa en ejecución:

$ gdb -q pp
Reading symbols from pp...done.
(gdb) list
1   #include <stdio.h>
2   #include <string.h>
3   
4   int main() {
5       int i = 20;
6       char var[4];
7   
8       strcpy(var, "12345678901");
9       printf(" var is : %s\n", var);
10      printf(" i : %d\n", i);
(gdb) break 8
Breakpoint 1 at 0x4004fa: file pp.c, line 8.
(gdb) break 9
Breakpoint 2 at 0x400510: file pp.c, line 9.
(gdb) run
Starting program: /home/grochmal/tmp/pp 

Breakpoint 1, main () at pp.c:8
8       strcpy(var, "12345678901");

OK, justo antes de que ocurra el desbordamiento, veamos dónde está la pila y dónde están las variables en la pila:

(gdb) p $rsp
$1 = (void *) 0x7fffffffe860
(gdb) x/16x 0x7fffffffe860
0x7fffffffe860: 0xffffe950  0x00007fff  0x00000000  0x00000014
0x7fffffffe870: 0x00400550  0x00000000  0xf7a5c291  0x00007fff
0x7fffffffe880: 0xf7dd0798  0x00007fff  0xffffe958  0x00007fff
0x7fffffffe890: 0xf7b9cc48  0x00000001  0x004004f6  0x00000000
(gdb) p &var
$2 = (char (*)[4]) 0x7fffffffe860

Muy bien, var está realmente en la parte superior de la pila ( 0x7fffffffe860 ) pero i no es 4 bytes más tarde. i está en 0x7fffffffe86c , 12 bytes más tarde ( i es 20, es decir, 0x00000014 ). Veamos que pasa después:

(gdb) cont
Continuing.

Breakpoint 2, main () at pp.c:9
9       printf(" var is : %s\n", var);
(gdb) x/16x 0x7fffffffe860
0x7fffffffe860: 0x34333231  0x38373635  0x00313039  0x00000014
0x7fffffffe870: 0x00400550  0x00000000  0xf7a5c291  0x00007fff
0x7fffffffe880: 0xf7dd0798  0x00007fff  0xffffe958  0x00007fff
0x7fffffffe890: 0xf7b9cc48  0x00000001  0x004004f6  0x00000000

¡El búfer se desborda! Pero no lo suficiente, necesitamos 1 byte adicional (recuerde que 0x00 es el terminador nulo) para llegar al lugar en la memoria donde se encuentra i . Si cambiamos nuestro strcpy a:

strcpy(var, "AAAAAAAAAAAA\x39\x00");

Tienes que sobrescribir i :

$ gcc -O0 -g -o pp pp.c
$ ./pp
 var is : AAAAAAAAAAAA9
 i : 1337

No es fácil adivinar la alineación, depende de la CPU y el compilador. La mayoría de los sombreros (blanco / gris / negro) lo hacen por prueba y error, o compilando el programa en el mismo entorno en el que se ejecuta y luego mirándolo en un depurador (como hicimos anteriormente).

    
respondido por el grochmal 27.09.2016 - 18:02
fuente

Lea otras preguntas en las etiquetas