Подтвердить что ты не робот

Переполнение буфера появилось до того, как оно ожидается

Я пытаюсь взять контроль над переполнением стека. Во-первых, вот пример кода 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? Где моя ошибка?

4b9b3361

Ответ 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.