2013-12-24 3 views
2

Я написал небольшую программу, чтобы понять, как работают стеки и переполнение буфера.ret address points to nowhere (?)

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

int main(int argc, char *argv[]) { 

    char array[20]; 
    printf("-=-=-=-=-=-=-=-=-=-= The prize pool is 2000$ -=-=-=-=-=-=-=-=-=--=\n"); 

    printf("-=-=-=-=-=- Whatever you supply goes to array! -=-=-=-=-=-=-=\n"); 

    strcpy(array, argv[1]); 

    printf("Array now is %p \n\n", &array); 

} 

Я бегу программу подачи

(gdb) run `perl -e 'print "A"x20 . "\x95\x84\x04\x08"x4'` 
The program being debugged has been started already. 
Start it from the beginning? (y or n) y 

Starting program: /root/tests/c-tests/myownexamples/simpleover2 `perl -e 'print "A"x20 . "\x95\x84\x04\x08"x4'` 

Установив точку останова в последней Printf мы можем видеть, что:

Breakpoint 3, main (argc=134513813, argv=0x8048495 <main+65>) 
at simpleover2.c:19 
19 printf("Array now is %p \n\n", &array); 
(gdb) info frame 
Stack level 0, frame at 0xbffff4a0: 
eip = 0x8048489 in main (simpleover2.c:19); saved eip **0x8048495** 
source language c. 
Arglist at 0xbffff498, args: argc=134513813, argv=0x8048495 <main+65> 
Locals at 0xbffff498, Previous frame's sp is 0xbffff4a0 
Saved registers: 
ebp at 0xbffff498, eip at 0xbffff49c 
(gdb) disassemble main 
Dump of assembler code for function main: 
    0x08048454 <+0>: push ebp 
    0x08048455 <+1>: mov ebp,esp 
    0x08048457 <+3>: sub esp,0x1c 
    0x0804845a <+6>: mov DWORD PTR [esp],0x8048560 
    0x08048461 <+13>: call 0x8048384 <[email protected]> 
    0x08048466 <+18>: mov DWORD PTR [esp],0x80485a4 
    0x0804846d <+25>: call 0x8048384 <[email protected]> 
    0x08048472 <+30>: mov eax,DWORD PTR [ebp+0xc] 
    0x08048475 <+33>: add eax,0x4 
    0x08048478 <+36>: mov eax,DWORD PTR [eax] 
    0x0804847a <+38>: mov DWORD PTR [esp+0x4],eax 
    0x0804847e <+42>: lea eax,[ebp-0x14] 
    0x08048481 <+45>: mov DWORD PTR [esp],eax 
    0x08048484 <+48>: call 0x8048364 <[email protected]> 
    => 0x08048489 <+53>: mov eax,0x80485e2 
    0x0804848e <+58>: lea edx,[ebp-0x14] 
    0x08048491 <+61>: mov DWORD PTR [esp+0x4],edx 
    0x08048495 <+65>: mov DWORD PTR [esp],eax 
    0x08048498 <+68>: call 0x8048374 <[email protected]> 
    0x0804849d <+73>: leave 
    0x0804849e <+74>: ret  

Так что я перезаписать адрес RET, но с адресом, который находится в кадре стека ТЕКСТ.

Следующий шаг за шагом исполнения до отпуска и RET инструкции

(gdb) nexti 
0x0804849e 21 } 
(gdb) i r eip 
eip   0x804849e 0x804849e <main+74> 
(gdb) i r eip 
eip   0x804849e 0x804849e <main+74> 
(gdb) nexti 
0x08048495 in main (argc=1435550665, argv=0xc35de589) at simpleover2.c:19 
19 printf("Array now is %p \n\n", &array); 
(gdb) i r eip 
eip   0x8048495 0x8048495 <main+65> 

Мы видим, что исполнение INDEED идет там и после с nexti мы получаем:

(gdb) nexti 
0x08048498 19 printf("Array now is %p \n\n", &array); 
(gdb) i r eip 
eip   0x8048498 0x8048498 <main+68> 
(gdb) nexti 

Program received signal SIGSEGV, Segmentation fault. 
0xb7eeae97 in strchrnul() from /lib/tls/i686/cmov/libc.so.6 

Так почему же ошибку сегментации? Кроме того, похоже, это действительно вызывает Е() для 2-й раз, но он не будет отображаться ..

./simpleover2 `perl -e 'print "A"x20 . "\x95\x84\x04\x08"x4'` 
-=-=-=-=-=-=-=-=-=-= The prize pool is 2000$ -=-=-=-=-=-=-=-=-=--= 
-=-=-=-=-=- Whatever you supply goes to array! -=-=-=-=-=-=-= 
Array now is 0xbf8ed894 

Segmentation fault 

Опять же, почему оно не Е() показывают, так как RET указывает на это? и как и выполнение продолжается там с тех пор, как были выполнены инструкции по отпуску и ret, которые фактически POP весь стек стека?

+0

Непонятно, каков ваш вопрос. Вы печатаете адрес 'array'. Чего вы ожидали? –

+0

Я ожидаю, что 2 раза печатаются вместо 1, так как выполнение снова возвращается к 0x8048495 адресу – user3124171

ответ

1

Ваш перезаписанный обратный адрес изменен на основной + 65 после того, как LEAVE восстанавливает исходные регистры программы. Поэтому после «возврата» на главную + 65 вы вызываете printf с разными параметрами, чем вы думаете, поэтому printf может попытаться напечатать что-то, что не завершено нулем. Затем он может сбежать с конца выделенной страницы и вызвать непредвиденную ошибку страницы на нераспределенной странице -> segfault. Я не могу сказать, что вы используете ABI, поэтому я не знаю, какие регистры используются для передачи параметров printf.

0

Вы меняете содержимое array, а не его адрес (вы не можете изменить адрес на самом деле!). &array означает '-адрес array -', который, я уверен, не то, что вы намерены. Если вы хотите узнать адрес массива символов в стеке, просто обратитесь к array без оператора &.

+0

. Я намерен изменить путь выполнения, и это действительно так. Вопрос в том, почему он segfaults и выполняет ли инструкция leave/ret фрейм стека (popping) или нет. – user3124171

+0

@ user3124171 Это segfaults, потому что использование '& array' вызывает неопределенное поведение. Вы не можете получить адрес для этого указателя, его фиксированный. –

+0

@ user3124171 И BTW: Какой путь _execution_ изменить в вашем примере, пожалуйста? Это без каких-либо ветвей. –