Вопрос с защитой от разрушения стека и переполнением буфера

Я занимаюсь исследованием переполнения буфера, и мне было интересно, как работает защита от разрушения стека.

у меня есть этот код:

int main( )
{ 
    char Buf[16];
    printf(“Digite o seu nome: ”);
    gets(Buf);
    printf(“%s”,Buf);
return 0;
} 

Я компилирую его с помощью gcc

а затем я вставил кучу символов, чтобы заполнить буфер

Сначала я поставил 16 символов

$ ./Пример1

Цифровое имя: AAAAAAAAAAAAAAAAAA

Оля АААААААААААААААА

Это нормально, потому что буфер имеет правильный размер

Далее я пробую 24 символа

$ ./Пример1

Цифровое имя: AAAAAAAAAAAAAAAAAAAAAAAAAA

Оля АААААААААААААААААААААААААА

Почему он до сих пор работает?

разве это не должно привести к завершению программы!?

Он завершает программу только тогда, когда я помещаю 25 или более символов

./Пример1

Цифровое имя: AAAAAAAAAAAAAAAAAAAAAAAAAAA

Оля АААААААААААААААААААААААААА

* обнаружен сбой стека *: ./Exemplo1 завершен

Почему? что после буфера не является обратным адресом? То, о чем я читал и что, я думаю, понимаю, заключается в том, что оно должно иметь канареечное значение, но оно должно завершить программу, если это значение изменилось, и с 24 символами, записанными в буфер, не должно ли это по-прежнему давать мне обнаружение разрушения стека даже если адрес возврата не изменился, но изменилось канареечное значение.

Спасибо.


person Renato    schedule 19.07.2011    source источник
comment
Это весь ваш код? Откуда Оля?   -  person diedthreetimes    schedule 19.07.2011
comment
извините за то, что я редактировал код и не понял =/ но я просто изменил printf(%s\n, Buf); to printf(\n\nOla %s\n, Buf);   -  person Renato    schedule 20.07.2011


Ответы (4)


Из моего краткого чтения сгенерированной сборки, вот что, по моему мнению, происходит (gcc 4.4.5 на 64-битной Ubuntu).

Канарейка — это двойное слово (8 байт). Стек, включая канарейку, увеличивается с шагом 16 байт. Канарейка размещается прямо в конце кадра стека. Чтобы удовлетворить все три требования одновременно, gcc может потребоваться вставить дополнение между вашими автоматическими переменными и канарейкой.

Поскольку в вашем коде Buf имеет длину 16 байтов, gcc помещает 8 байтов заполнения между Buf и канарейкой (необходимо для того, чтобы сделать размер кадра стека кратным 16 байтам). Это объясняет, почему вы можете ввести до 24 символов, не вызывая обнаружения разрушения стека.

Если я изменю Buf на 8 байтов, больше не будет необходимости в каких-либо дополнениях, а ввод 9 символов вызовет защиту:

#include <stdio.h>

int main( )
{ 
    char Buf[8];
    printf("Digite o seu nome: ");
    gets(Buf);
    printf("%s",Buf);
    return 0;
 }

aix@aix:~$ ./a.out 
Digite o seu nome: AAAAAAAA
AAAAAAAA

aix@aix:~$ ./a.out 
Digite o seu nome: AAAAAAAAA
*** stack smashing detected ***: ./a.out terminated

Очевидно, это зависит от компилятора, аппаратной платформы, флагов компилятора и т.д.

Также само собой разумеется, что не следует ожидать, что этот механизм будет пуленепробиваемым.

Если вы хотите поэкспериментировать дальше, попробуйте скомпилировать свой код с разными размерами буфера и с/без -fno-stack-protector. Если вы используете -S для генерации ассемблерного кода, вы сможете увидеть, как сгенерированный код изменится при настройке параметров.

person NPE    schedule 19.07.2011
comment
Да, изменение размера буфера на 8 работает, я не знал, что стек будет увеличиваться с шагом 16 байт для всего, я думал, что это только для буфера, а остальное будет распределяться по мере необходимости ... Всегда ли это 16 байт для x64 процессоры? Кстати большое спасибо =D - person Renato; 19.07.2011
comment
Также это означает, что адрес возврата будет начинаться после 32 байтов после начала буфера? - person Renato; 19.07.2011
comment
@Renato: Что касается вашего первого вопроса, то опция -mpreferred-stack-boundary gcc обеспечивает некоторую гибкость. Что касается второго вопроса, я рекомендую вам сгенерировать и прочитать сборку. - person NPE; 19.07.2011

Компилятор не дает никаких гарантий относительно того, как данные будут организованы в стеке. Буфер buf[] может размещаться рядом с важными данными, такими как канарейка, старый указатель стека и адрес возврата; или между ними может быть дополнительное пространство. В этом случае похоже, что там, вероятно, есть какие-то отступы.

person Community    schedule 19.07.2011

Фактический размер буфера может быть больше, чем указанный вами размер, или в стеке помимо буфера может быть больше. Выходя за границу буфера, вы либо записываете в дополнительную память в буфере, либо перезаписываете другое содержимое стека. Это нехорошо, так как некоторые из ваших локальных переменных могут испортиться, но адрес возврата еще не будет перезаписан.

Только если вы пишете за пределами выделенного кадра локального стека и перезаписываете адрес возврата функции, которая в данный момент выполняется, могут произойти плохие вещи. Если вам повезет, ваша программа просто рухнет. Но что именно произойдет, трудно предугадать.

person Rudy Velthuis    schedule 19.07.2011

В настоящее время большинство компиляторов включают файлы cookie стека или другую форму защиты стека. Вероятно, это ProPolice в действии /Buffer_overflow_protection#GCC_Stack-Smashing_Protector_.28ProPolice.29

person DipSwitch    schedule 19.07.2011