2011-08-03 12 views
16

Natknąłem się na ten kod pokazujący wykorzystanie ciągu formatów podczas czytania artykułu this.Nie można zrozumieć kodu użytego w łańcuchu formatu

#include <stdio.h> 

int main(void) 
{ 
char secret[]="hack.se is lame"; 
char buffer[512]; 
char target[512]; 

printf("secret = %pn",&secret); 

fgets(buffer,512,stdin); 
snprintf(target,512,buffer); 
printf("%s",target); 
} 

Wykonanie go z następujących wejście

[[email protected]]$ ./a.out 
secret = 0xbffffc68 
AAAA%x %x %x %x %x %x %x //Input given 
AAAA4013fe20 0 0 0 41414141 33313034 30326566 
- [[email protected]]$ 

Co rozumiem do tej pory jest sekwencją %x „s będzie kontynuować drukowanie wartości w powyższych adresów prąd %esp (jestem zakładając, że stos jest rośnie w dół w kierunku niższego adresu).

Czego nie rozumiem, to dane wejściowe są przechowywane w tablicy buffer, która nie może być mniejsza niż 512 bajtów od bieżącego %esp. Tak więc, jak wyjście może zawierać 41414141 (reprezentacja heksadecymalna AAAA) tuż po 4 %x, tj. Tuż nad 4 adresami bieżącego %esp. Bardzo się starałem też wpatrywać w kod zespołu, ale myślę, że nie mogłem wykonać manipulacji łańcuchami na stosie.

+0

Nie rozumiem twojego polecenia wywoływania. Czy '~/research/paper' jest plikiem wykonywalnym? Zrozumiałbym "./myprog

+0

OK, widzę, co się dzieje. Wykonywane jest 'a.out', które drukuje' secret = 0xbffffc68', a następnie czeka na wprowadzenie danych przez użytkownika. Dla mnie to tylko powtórzyło wejście z powrotem do ekranu na ostatniej linii, a następnie zakończyło. '~/research/paper' jest po prostu znakiem zachęty powłoki. –

+0

@ kerrek SB: Przykro mi z tego powodu. usunąłem go, aby było bardziej zrozumiałe. – Terminal

Odpowiedz

4

Przy wjeździe do snprintf, stos ma następujący:

0xbfd257d0:  0xxxxxxxxx  0xxxxxxxxx  0xxxxxxxxx  0x080484d5 
0xbfd257e0:  0xbfd25800  0x00000200  0xbfd25a00  0x00000000 
0xbfd257f0:  0x00000000  0x00000000  0x00000000  0x00000000 
0xbfd25800:  0x00000000  0x00000040  0xb7f22f2c  0x00000000 
0xbfd25810:  0x00000000  0x00000000  0x00000000  0x00000000 

0xbfd25800 -> target (initially 0x00000000 0x00000040 ...) 
...  -> garbage 
0xbfd257e8 -> pointer to buffer 
0xbfd257e4 -> 512 
0xbfd257e0 -> pointer to target 
0xbfd257df -> return address 

target zostaje zastąpione w wyniku snprintf przed snprintf dostaje do wykorzystania jego słowa jako argumenty: Po raz pierwszy pisze „aaaa” (0x41414141) na 0xbfd25800, następnie "% x" odczytuje wartość przy 0xbfd257ec i zapisuje ją przy 0xbfd25804, ..., następnie "% x" odczytuje wartość przy 0xbfd25800 (0x41414141) i zapisuje ją przy 0xbfd25814, ...

+0

Definicja 'target' zostaje nadpisana w wyniku' snprintf', ale nie otrzymałem części podążanej po tym, tj. Zanim snprintf użyje swoich słów jako argumentów.Myślę, że cel jest nadpisywany w wyniku snprintf. A moje pytanie brzmi: jak cel został zapisany z wartością 1414141? Innymi słowy, w jaki sposób sekwencja% x% x ... w buforze jest zastąpiona przez 1414141? – Terminal

+0

@ Terminalny, gdzie 0xbfd25800 zawiera teraz zero, jest zastępowany przez część "AAAA" przez "snprintf" przed oceną sekwencji% x, ponieważ 'snprintf' zaczyna się od lewej części łańcucha i kopiuje wszystko, dopóki nie trafi% x . W ten sposób nie trzeba przydzielać tymczasowego bufora, aby sformatować cały ciąg i skopiować go na raz. –

+0

@karl bielefeldt: Dzięki mam to !! Wtedy przypuszczam, że wyjaśnienie podane w tym linku nie jest poprawne. Będzie świetnie, jeśli ktokolwiek z was rzuci trochę światła na wyjaśnienia podane tuż po kodzie w linku w oryginalnym poście. Wierzę, że źle zrozumiałem coś, szczególnie, że oni znajdują offset dla bufora. – Terminal

1

Pierwsza z wszystko, spójrzmy na stos po wywołaniu snprintf():

Reading symbols from /home/blackbear/a.out...done. 
(gdb) run 
Starting program: /home/blackbear/a.out 
secret = 0xbffff40c 
ABCDEF%x %x %x %x %x %x %x 

Breakpoint 1, main() at prova.c:13 
13  printf("%s",target); 
(gdb) x/20x $esp 
0xbfffeff0: 0xbffff00c 0x00000200 0xbffff20c 0x00155d7c 
0xbffff000: 0x00155d7c 0x000000f0 0x000000f0 0x44434241 
0xbffff010: 0x35314645 0x63376435 0x35353120 0x20633764 
0xbffff020: 0x66203066 0x34342030 0x32343334 0x33203134 
0xbffff030: 0x34313335 0x20353436 0x37333336 0x35333436 
(gdb) 

Rzeczywiście możemy zobaczyć przy 0xbffff00c napis już sformatowany, więc sprintf() napisał właśnie tutaj. Możemy również zobaczyć, przy 0xbfffeff0, ostatni argument dla snprintf(): adres celu, który w rzeczywistości jest 0xbffff00c. Więc mogę wywnioskować, że łańcuchy są zapisywane od końca do początku ich przydzielonego miejsca na stosie, jak możemy również zobaczyć dodanie strcpy():

[email protected]:~$ cat prova.c 
#include <stdio.h> 
#include <string.h> 

int main(void) 
{ 
    char secret[]="hack.se is lame"; 
    char buffer[512]; 
    char target[512]; 

    printf("secret = %p\n", &secret); 

    strcpy(target, "ABCDEF"); 
    fgets(buffer,512,stdin); 
    snprintf(target,512,buffer); 
    printf("%s",target); 
} 
[email protected]:~$ gcc prova.c -g 
prova.c: In function ‘main’: 
prova.c:14: warning: format not a string literal and no format arguments 
prova.c:14: warning: format not a string literal and no format arguments 
[email protected]:~$ gdb ./a.out -q 
Reading symbols from /home/blackbear/a.out...done. 
(gdb) break 13 
Breakpoint 1 at 0x8048580: file prova.c, line 13. 
(gdb) run 
Starting program: /home/blackbear/a.out 
secret = 0xbffff40c 

Breakpoint 1, main() at prova.c:13 
13  fgets(buffer,512,stdin); 
(gdb) x/10x $esp 
0xbfffeff0: 0xbffff00c 0x080486bd 0x00000007 0x00155d7c 
0xbffff000: 0x00155d7c 0x000000f0 0x000000f0 0x44434241 
0xbffff010: 0x00004645 0x00000004 
(gdb) 

to jest to! Podsumowując, znaleźliśmy tam ciąg, ponieważ łańcuchy są przechowywane na stosie w odwrotny sposób, a początek (lub koniec?) Celu jest bliski esp.

Powiązane problemy