Stack Smashing попытка дать segfault
Я пытаюсь сделать пример из Smashing the Stack for Fun and Profit на 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]
, все равно тот же код должен работать нормально, НО этого не происходит, и он начинает выдавать ошибку seg.
С уважением,
Ньюман
Ответы
Ответ 1
'char' для каждой используемой архитектуры составляет 8 бит, независимо от того, является ли это 8-битным микро, 16-битным микро, 32-битным ПК или 64-битным новым ПК. Int, с другой стороны, имеет тенденцию быть размером слова.
Порядок, который локальные жители помещают в стек, может быть специфичным для реализации. Я предполагаю, что ваш компилятор помещает "int * ret" в стек до "char buffer 1". Итак, чтобы перейти к обратному адресу, нам нужно пройти через char buffer 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
Ответ 2
Проделайте разборку в gdb (дизассемблируйте, stepi, nexti) и посмотрите на регистры на каждом этапе (информационные регистры).
Здесь вы можете выполнить разборку:
gdb ./myprogram
break main
run
display/4i $eip
stepi
stepi
...
info registers
...
Вы также должны знать (возможно, вы уже знаете, что у вас есть часть его работы), что во многих дистрибутивах защитник стека включен по умолчанию в gcc. Вы можете отключить его вручную с помощью -fno-stack-protector
.
Ответ 3
С большим количеством этого разбитого материала, ваш лучший друг gdb. Поскольку вы уже сталкиваетесь, вы уже пишете память, которую вы не должны быть (хороший знак). Более эффективный способ сделать это правильно - это изменить обратный адрес в другом месте, где действительный адрес (например, на адрес func
или на какой-либо шелл-код у вас есть). Отличный ресурс, который я бы рекомендовал, - это Handcoder Handbook, но поскольку вы находитесь в 64-битной архитектуре, многие примеры требуют бит работы, чтобы идти.
Ответ 4
Помимо (или еще лучше) выполнения отладчика, вы также можете использовать конструкцию 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-кода тоже!
Ответ 5
Посмотрите на скрытую заимствованную технику куска кода, если вас интересует эксплуатация переполнения буфера x64.