Perché la variabile locale non viene sovrascritta a causa dell'overflow del buffer?

1

Ecco il mio programma C ++ in cui è avvenuto l'overflow del buffer, tuttavia la variabile locale non è corrotta:

#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;
}

L'output del programma è:

 var is : biiiiiiiyee
 i : 20

L'assunto è che la sovrascrittura oltre la fine del buffer var dovrebbe danneggiare la variabile i .

Nel programma di cui sopra sto sovrascrivendo il buffer, ancora la variabile locale non è stata corretta. Perché?

    
posta Pro Account 27.09.2016 - 16:39
fonte

1 risposta

2

Alignment. Il compilatore può posizionare variabili locali nello stack in modo che l'allineamento alle parole della CPU sia migliore. In altre parole, non c'è nulla che costringa il compilatore a posizionare le variabili locali una dopo l'altra sullo stack. Posizionare le variabili locali una dopo l'altra significa salvare un paio di byte nella dimensione del programma per una penalità considerevole nel tempo di elaborazione.

Consentitemi di usare la C normale anziché il C ++ per spiegare perché è molto più facile rilasciarlo in GDB. Programma convertito in semplice C, chiamiamo è 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;
}

Si noti che la stringa che ho usato è della stessa lunghezza:

"12345678901"
"biiiiiiiyee"

Compiliamolo senza ottimizzazioni, nel caso in cui:

gcc -O0 -g -o pp pp.c

E ora diamo un'occhiata al programma in esecuzione:

$ 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 siamo appena prima che si verifichi l'overflow, vediamo dove si trova lo stack e dove sono le variabili nello stack:

(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

Molto buono, var è in cima alla pila ( 0x7fffffffe860 ) ma i non è più 4 byte più tardi. i è a 0x7fffffffe86c , 12 byte dopo ( i è 20, cioè 0x00000014 ). Vediamo cosa succede dopo:

(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

Il buffer fa overflow! Ma non abbastanza, abbiamo bisogno di 1 byte extra (ricordiamo che 0x00 è il terminatore null) per raggiungere il posto in memoria dove si trova i . Se modifichiamo strcpy in:

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

Si arriva a sovrascrivere i :

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

Non è facile indovinare l'allineamento, dipende dalla CPU e dal compilatore. La maggior parte dei cappelli (bianchi / grigi / neri) lo fanno per tentativi ed errori, o compilando il programma nello stesso ambiente in cui viene eseguito e poi guardandolo in un debugger (come abbiamo fatto sopra).

    
risposta data 27.09.2016 - 18:02
fonte

Leggi altre domande sui tag