2014-04-12 8 views
6

Robię ćwiczenia polegające na rozbijaniu stosów podczas zajęć, i już ukończyłem zadanie, ale jest jeden aspekt, którego nie rozumiem.Dlaczego istnieją 8 bajtów między końcem bufora a zapisanym wskaźnikiem ramki?

Oto program docelowy:

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

int bar(char *arg, char *out)         
{                
    strcpy(out, arg);            
    return 0;              
}                

void foo(char *argv[])           
{                
    char buf[256];            
    bar(argv[1], buf);           
}                

int main(int argc, char *argv[])        
{                
    if (argc != 2)            
    {               
     fprintf(stderr, "target1: argc != 2\n");     
     exit(EXIT_FAILURE);          
    }               
    foo(argv);             
    return 0;              
}                

Oto polecenia używane do go skompilować, w sprawie x86 maszynę wirtualną z systemem Ubuntu 12.04, z ASLR niepełnosprawnych.

gcc -ggdb -m32 -g -std=c99 -D_GNU_SOURCE -fno-stack-protector -m32 target1.c -o target1 
execstack -s target1 

Kiedy patrzę na wspomnienie tego programu na stosie, widzę, że buf ma adres 0xbffffc40. Ponadto zapisany wskaźnik ramki jest przechowywany pod numerem 0xbffffd48, a adres powrotu jest przechowywany pod numerem 0xbffffd4c.

Te konkretne adresy nie są istotne, ale zauważam, że nawet jeśli buf ma tylko długość 256, odległość 0xbffffd48 - 0xbffffc40 = 264. Symbolicznie to obliczenie to $fp - buf.

Dlaczego istnieje 8 dodatkowych bajtów między końcem buf a zapisanym wskaźnikiem ramki na stosie?

Oto niektóre demontaż funkcji foo. Sprawdziłem już to, ale nie widziałem żadnego oczywistego użycia tego regionu pamięci, chyba że było to implicite (tj. Efekt uboczny jakiejś instrukcji).

0x080484ab <+0>:  push %ebp      
    0x080484ac <+1>:  mov %esp,%ebp    
    0x080484ae <+3>:  sub $0x118,%esp    
    0x080484b4 <+9>:  mov 0x8(%ebp),%eax   
    0x080484b7 <+12>: add $0x4,%eax    
    0x080484ba <+15>: mov (%eax),%eax    
    0x080484bc <+17>: lea -0x108(%ebp),%edx  
    0x080484c2 <+23>: mov %edx,0x4(%esp)   
    0x080484c6 <+27>: mov %eax,(%esp)    
    0x080484c9 <+30>: call 0x804848c <bar>   
    0x080484ce <+35>: leave       
    0x080484cf <+36>: ret        
+0

Ponieważ ramki są zazwyczaj wyrównane do 16 (lub może 8, zapomniałem o szczegółach) bajtów. –

+1

@BasileStarynkevitch, Adres '0xbffffc40' jest już wyrównany do 16 bajtów, a 256 jest z pewnością wielokrotnością 16. – merlin2011

+0

Czy ten obszar jest używany do przechowywania rejestrów? –

Odpowiedz

2

Basile Starynkevitch dostaje nagrodę za wspomnieć alignment.

Okazuje się, że domyślnie gcc 4.7.2 wyrównuje granicę ramki do granicy 4 słów. Na emulowanym 32-bitowym sprzęcie wynosi 16 bajtów. Ponieważ zapisany wskaźnik ramki i zapisany wskaźnik instrukcji zajmują razem tylko 8 bajtów, kompilator umieścił kolejne 8 bajtów po końcu buf, aby wyrównać górną część ramki stosu do granicy 16 bajtów.

Użycie następującej dodatkowej flagi kompilatora powoduje zniknięcie 8 bajtów, ponieważ 8 bajtów wystarczy, aby wyrównać do granicy 2 słów.

-mpreferred-stack-boundary=2 
Powiązane problemy