2016-02-11 2 views
4

Я пытаюсь понять концепцию переполнения буфера, и у меня возникли проблемы с вычислением количества данных, которые нужно заполнить в стеке, чтобы он переполнялся правильно. Предположим, мне предоставлен некоторый код (это не мой код, и да, это класс, но это не градиентное задание): Цель состоит в том, чтобы получить бар, который будет исполнен.Форсирование переполнения буфера для понимания

#include <stdio.h> 
#include <string.h> 

void foo(char *s) { 
    char buf[4]; 
    strcpy(buf, s); 
    printf("You entered: [%s]", buf); 
    fflush(stdout); 
} 

void bar() { 
    printf("\n\nWhat? I was not supposed to be called!\n\n"); 
    fflush(stdout); 
} 

int main(int argc, char *argv[]) { 
    if (argc != 2) { 
    printf("Usage: %s some_string", argv[0]); 
    return 2; 
    } 
    foo(argv[1]); 
    return 0;  
} 

Когда я разобрать строку я получаю начальный адрес бара:

(gdb) disas bar 
Dump of assembler code for function bar: 
    0x000000000040062d <+0>: push %rbp 

мне сказали, что там должно быть 28 байт данных чучела в буфер, а последние 4 байта, необходимо \x2d\x06\x04\x00. Где вы получаете 24 байта, чтобы узнать, сколько случайных данных набиты.

В общем, и что меня больше всего интересует, как обобщить и понять это для любой проблемы.

Как вы вычисляете количество байтов, необходимых для заполнения кадра стека, а затем перезаписываете обратный адрес функции?

Примечание: Это написано в C и скомпилирован с GCC 4.4.7

+0

Это упражнение было бы безопаснее, если бы вы сделали это на Java, C# или на каком-либо другом управляемом языке вместо C. Принуждение стека к переполнению на C не было чем-то, что я сделал бы намеренно. –

+0

@RobertHarvey Я узнаю о кибербезопасности, поэтому мне нужно это понять. Я понимаю, что это намеренно не сделано. – bdeo

+0

@RobertHarvey иногда нам нужно быть храбрыми программистами :) –

ответ

1

Из одного фрагмента кода C вы можете получить разные результаты.

void foo(char *s) { 
    char buf[4]; 
    strcpy(buf, s); 
    printf("You entered: [%s]", buf); 
    fflush(stdout); 
} 

Компилятор может выводить

ADD SP, 4 // space for buf 
PUSH SP-12 // address of string 's' 
PUSH SP-8 // address of SP (4 + space for s) 
call strcpy 
push literal_You_entered 
push SP-8 
call printf 
... 

Или он может хотеть иметь кадр стека.

PUSH Frame 
MOVE SP, Frame 
ADD SP, 4 // space for buf 
.... 

Или, возможно, потребуется сохранить регистры

PUSH Frame 
MOVE SP, Frame 
PUSH Reg1 
PUSH Reg2 
ADD SP, 4 // space for buf 

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

Попробуйте волшебный буфер, например '\x01\x02\x03\x04\.....\xfe\xff'.

Ошибка сегментации, которую вы получаете, должна показывать вам, где находятся важные байты. Нарушение доступа в 0x08090a0b будет описывать, какие байты были возвратом.

Переписывание указателя кадра (FP) означает, что вы можете изменить поведение в вызывающей функции, что также может помочь вам.

-1

Как вы вычислить количество байтов, необходимых для заполнения стека, а затем перезаписать адрес возврата функции?

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

+0

Вы заявили очевидное. Я пытаюсь выяснить, как вычислить количество байтов между переменной и обратным адресом. Почему в этом случае это 24 байта? – bdeo

+0

Это не то, что вы вычисляете, это просто то, что вы узнаете. Это так, как в конкретной структуре фрейма стека. – Barmar

+0

Если у вас была копия объявления структуры для фрейма стека, вы можете использовать 'offsetof' для его вычисления. – Barmar

Смежные вопросы