2010-02-25 20 views
29

Mam zadanie domowe z prośbą o wywołanie funkcji bez wyraźnie nazywając ją przy użyciu przepełnienie bufora. Kod jest w zasadzie następujący:Jak mogę wywołać przepełnienie bufora?

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

void g() 
{ 
    printf("now inside g()!\n"); 
} 


void f() 
{ 
    printf("now inside f()!\n"); 
    // can only modify this section 
    // cant call g(), maybe use g (pointer to function) 
} 

int main (int argc, char *argv[]) 
{ 
    f(); 
    return 0; 
} 

Chociaż nie jestem pewien, jak postępować. Zastanawiałem się nad zmianą adresu zwrotnego dla licznika programu, aby przejść bezpośrednio do adresu g(), ale nie jestem pewien, jak uzyskać do niego dostęp. W każdym razie wskazówki będą świetne.

+9

4 upvotes na pytanie domowej! PO nawet nie wymyślił pytania ... wow, niektórzy ludzie są pod wrażeniem. – Lazarus

+0

@Lazarus, uaktualniłem twój komentarz. O o!:-) –

+15

@Lazarus fakt, że jest to zadanie domowe, nie ma nic wspólnego z tym, że uważam to za interesujące. Ulepszyłem także to podejście, ponieważ chcę zachęcić do ciekawszych zadań domowych zamiast prostego "Zamknąłem bufor pliku, a teraz, gdy próbuję czytać z pliku, to nie działa. Dlaczego?" (Innymi słowy, podniosłem pytania, na które nie znam odpowiedzi, ale chcę) – Yacoby

Odpowiedz

14

Podstawowym założeniem jest to, aby zmienić adres powrotu z funkcji tak, że gdy funkcja zwraca jest nadal wykonywać w nowej hakera adres. Wykonane przez Nils w jednej z odpowiedzi, można zadeklarować kawałek pamięci (zwykle tablicy) i przelewać je w taki sposób, że adres zwrotny jest nadpisane jak również.

Proponuję Ci nie ślepo wziąć dowolny z programów podanych tutaj bez faktycznego zrozumienia jak one działają. Ten artykuł jest bardzo dobrze napisany, a znajdziesz to bardzo przydatne:

A step-by-step on the buffer overflow vulnerablity

3

Spróbuj tego:

void f() 
{ 
    void *x[1]; 
    printf("now inside f()!\n"); 
    // can only modify this section 
    // cant call g(), maybe use g (pointer to function) 
    x[-1]=&g; 
} 

czy to jedno:

void f() 
{ 
    void *x[1]; 
    printf("now inside f()!\n"); 
    // can only modify this section 
    // cant call g(), maybe use g (pointer to function) 
    x[1]=&g; 
} 
+3

Wyjaśnienie byłoby świetnie, bo to praca domowa. –

+2

x jest zmienną lokalną, więc znajduje się na stosie. Ponieważ x jest tablicą o rozmiarze 1, tylko x [0] jest poprawne. Pisząc adres g wx [-1] lub x [1], istnieje szansa, że ​​nadpisamy adres zwrotny. Zależy to od organizacji stosu, którego wersja działa. –

11

To kompilator zależne, więc żadna odpowiedź może być udzielona.

Poniższy kod będzie robić to, co chcesz na gcc 4.4.1. Skompilować z optymalizacje wyłączona

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

void g() 
{ 
    printf("now inside g()!\n"); 
} 


void f() 
{ 
    int i; 
    void * buffer[1]; 
    printf("now inside f()!\n"); 

    // can only modify this section 
    // cant call g(), maybe use g (pointer to function) 

    // place the address of g all over the stack: 
    for (i=0; i<10; i++) 
    buffer[i] = (void*) g; 

    // and goodbye.. 
} 

int main (int argc, char *argv[]) 
{ 
    f(); 
    return 0; 
} 

Output (ważne!):

[email protected]:~$ gcc overflow.c 
[email protected]:~$ ./a.out 
now inside f()! 
now inside g()! 
now inside g()! 
now inside g()! 
now inside g()! 
now inside g()! 
now inside g()! 
Segmentation fault 
+0

Używam gcc 4.4.1 i nie wiem, jak wyłączyć optymalizację: wypróbowałem gcc -O0 -o buff buff.c (to jest zero) , a także gcc -O1 -fno-defer-pop - fno-thread-jumps -fno-branch-probability -fno-cprop-registers -fno-guess-branch-prawdopodobieństwo -fno-omit-frame-pointer -o buff buff.c nic nie zadziałało. – sa125

+1

Ustaw wyjście aplikacji wewnątrz funkcji 'g()', aby uniknąć błędu segmentacji =) – Kieveli

+0

sa125, może gcc próbuje zoptymalizować do innej architektury procesora. O ile mi wiadomo, domyślnie jest to procesor systemu, z którego korzystasz. To może zmienić wygląd ramki stosu f() i może uniemożliwić przepełnienie. –

7

Ponieważ jest to praca domowa, chciałbym echo codeaddict's suggestion zrozumienia jak przepełnienie bufora faktycznie działa.

Nauczyłem się tej techniki, czytając znakomity (jeśli nieco przestarzały) artykuł/samouczek na temat wykorzystania luk w przepełnieniu bufora: Smashing The Stack For Fun And Profit.

+2

+1 za powiązanie z tym artykułem. –

3

Chociaż to rozwiązanie nie wykorzystuje technikę przepełnienia nadpisać adres powrotu z funkcji na stosie, to nadal powoduje g() aby uzyskać nazywane od f() w drodze powrotnej do main() tylko przez modyfikację f() i niestawienie g() bezpośrednio.

Function epilogue -jak zespół rolki dodaje f() zmodyfikować wartość adresu powrotu w stos tak, że f() powróci przez g().

#include <stdio.h> 

void g() 
{ 
    printf("now inside g()!\n"); 
} 

void f() 
{ 
    printf("now inside f()!\n"); 
    // can only modify this section 
    // cant call g(), maybe use g (pointer to function) 

    /* x86 function epilogue-like inline assembly */ 
    /* Causes f() to return to g() on its way back to main() */ 
    asm(
     "mov %%ebp,%%esp;" 
     "pop %%ebp;" 
     "push %0;" 
     "ret" 
     : /* no output registers */ 
     : "r" (&g) 
     : "%ebp", "%esp" 
     ); 
} 

int main (int argc, char *argv[]) 
{ 
    f(); 
    return 0; 
}

Zrozumienie, jak ten kod działa może prowadzić do lepszego zrozumienia jak stos ramka funkcją jest ustawienie dla danej architektury, która tworzy podstawę technik przepełnienia bufora.