Попытка разбить стек - PullRequest
       11

Попытка разбить стек

26 голосов
/ 29 января 2009

Я пытаюсь сделать пример из Smashing the Stack для удовольствия и прибыли в C, но я застрял в какой-то момент, Ниже приведен код (у меня есть 64-битная машина с 64-битной Ubuntu):

int main()
{
    int x;

    x = 0;
    func(1,2,3);
    x = 1;
    printf("x is : %d\n", x);
}

void func(int a, int b, int c)
{
    char buffer[1];
    int *ret;

    ret = buffer + 17;
    (*ret) += 7;
}

Приведенный выше код работает нормально, и при возврате строка x=1 не выполняется, но я не могу понять логику ret = buffer + 17;, не должно ли это быть ret = buffer + 16;, т. Е. 8 байтов для буфера и 8 для сохраненный базовый указатель в стеке.

Во-вторых, я понимаю, что char buffer[1] занимает 8 байтов (из-за 64-битной арки) и если я увеличу этот буфер до buffer[2], все тот же код должен работать нормально, НО этого не происходит, и он начинает выдавать ошибку сегмента.

С уважением, Ньюмэн

Ответы [ 5 ]

13 голосов
/ 29 января 2009

'char' на каждой архитектуре, которую я использовал, имеет ширину 8 бит, независимо от того, является ли это 8-битным микро, 16-битным микро, 32-битным ПК или 64-битным новым ПК. Int, с другой стороны, имеет тенденцию быть размером слова.

Порядок размещения локальных элементов в стеке может зависеть от реализации. Я предполагаю, что ваш компилятор помещает "int * ret" в стек перед "char buffer 1 ". Таким образом, чтобы добраться до адреса возврата, мы должны пройти через «char char 1 » (1 байт), «int * ret» (8 байт) и сохраненный базовый указатель (8 байт) для всего 17 байт.

Вот описание стекового фрейма на 64-битной x86: http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-035-computer-language-engineering-spring-2010/projects/x86-64

9 голосов
/ 29 января 2009

Пошаговая разборка в gdb (disassemble, stepi, nexti) и просмотр регистров на каждом шаге (информационные регистры).

Вот как можно выполнить разборку:

gdb ./myprogram
break main
run
display/4i $eip
stepi
stepi
...
info registers
...

Вы также должны знать (вы, вероятно, уже сделали это, учитывая, что часть его работала), что во многих дистрибутивах защита стека включена по умолчанию в gcc. Вы можете вручную отключить его с помощью -fno-stack-protector.

3 голосов
/ 29 января 2009

С этим огромным количеством стэков ваш лучший друг - gdb . Поскольку вы уже segfaulting, вы уже пишете память, которой вы не должны быть (хороший знак). Более эффективный способ сделать это правильно - изменить адрес возврата на другой, который является действительным адресом (например, на адрес func или на какой-либо полученный шелл-код). Хороший ресурс, который я бы порекомендовал, - Руководство по кодировщику оболочки , но, поскольку вы работаете с 64-битной архитектурой, для начала работы требуется много работы.

0 голосов
/ 04 февраля 2010

Подумайте о том, чтобы взглянуть на технику заимствованного кода стелса , если вы заинтересованы в использовании переполнения буфера x64.

0 голосов
/ 29 января 2009

Помимо (или, что еще лучше, помимо) запуска отладчика, вы также можете использовать конструкцию printf "% p" для вывода адресов ваших переменных, например ::

printf("buf: %p\n", buffer); //&buffer[0] works too; &buffer works for an array
printf("ret: %p\n", &ret):
printf("a:   %p\n", &a);

Печать адресов разных вещей может дать отличное представление о том, как ваш компилятор / реализация размещает вещи в фоновом режиме. И вы можете сделать это прямо из кода C тоже!

...