Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
БІКС 2015_1 / lec_2 BIKS.doc
Скачиваний:
76
Добавлен:
12.02.2016
Размер:
465.92 Кб
Скачать

5.4.2. Переповнення буфера у статичній або динамічній пам'яті

Буфер не завжди розміщують у стеку. Програмісти, які знають про небезпеку «зривання стека», можуть спробувати виправити ситуацію, оголосивши буфер у статичній або динамічній пам'яті. Для наведеного вище фрагмента коду функції test_function() рядок коду

char buff[5];

у першому варіанті достатньо замінити рядком

static char buff [5];

а у другому ~ рядком

buff = (char *j malloc (5);

що справді вилучить буфери зі стека.

Але проблеми на цьому не вичерпуються [60]. Оскільки буфер оголошено не у стеку, порушників позбавляють можливості підмінити адресу повернення із функції. Щоправда, в них тоді з'являється інша можливість: здійснивши перепов­нення буфера, модифікувати дані, які знаходяться в адресному просторі програми, що виконується. Поряд з уразливим до переповнення буфером можуть знаходи­тися покажчики на функції та дані структур для функцій long jmp (), модифіку­вавши які, також можна викликати виконання власного коду. Крім того, поряд із буфером у статичній або динамічній пам'яті можуть знаходитися змінні, після мо­дифікування яких з'являється можливість викликати виконання функцій поруш­ника, навіть не передаючи керування, а саме: імена файлів, паролі та ідентифі­катори процесів (PID), користувачів (UID), груп (GID) тощо.

Отже, переповнення буфера небезпечне завжди, де б це не відбувалося. Відтак усувати проблему потрібно не переміщенням буфера, а скасуванням можливості переповнення, що досягається перевірками довжини рядків і розмірів масивів, які копіюються або до яких здійснюється звернення.

5.4.3. Помилка переповнення в один байт

Розглянемо специфічну помилку переповнення буфера, менш помітну, ніж роз­глянуті вище, але теж здатну викликати неприємності. Причина наявності по­милки переповнення в один байт полягає в особливостях форматів подання ряд­ків символів. У більшості форматів рядок займає у пам'яті на 1 байт більше, ніж потрібно для розміщення всіх його символів. У деяких мовах програмування ря­док завершується символом NULL, який не враховується для визначення його довжини. Старі функції MS-DOS передбачали роботу з рядками, які завершува­лися символом $. А в мові Паскаль нульовий байт було відведено для значення

довжини рядка.

Повернімося до прикладу функції testf unction () (див. рис. 5.4). У функ­ції оголошено буфер buff довжиною у 5 байт, куди можна помістити рядок із максимальною довжиною у 4 символи. Як було показано вище, для запобігання переповненню буфера доцільно передбачити перевірку довжини рядка. І якщо програміст припуститься помилки переповнення в один байт (тобто дозволить максимальну довжину рядка у 5 байт), то помилка переповнення буфера виника­тиме тоді і лише тоді, коли довжина рядка дорівнює 5.

Оскільки логіка роботи функції під час аналізу вихідного тексту виглядатиме абсолютно правильною, таку помилку помітити важко, якщо не шукати її спеці­ально. Звісно, ця помилка не дає змоги передати керування, позаяк змінюється (точніше, обнуляється) лише 1 байт, розташований безпосередньо за виділеним буфером. У наведеному прикладі це змінна Ь. Але цілком імовірно, що вона мати­ме суттєве значення для логіки функціонування програми. Наприклад, значення цієї змінної може встановлювати рівень привілеїв користувача у системі, причо­му її нульове значення відповідає рівню суперкористувача.

Ще одна особливість полягає в тому, що різні компілятори можуть змінювати порядок розміщення локальних змінних у стеку. Тоді замість b у цій позиції опи­ниться змінна а. На практиці це виглядає так: інколи після введення певного ряд­ка програма діє некоректно, хоча після оброблення іншим компілятором на тих самих вхідних даних діє цілком коректно або ж демонструє зовсім іншу помилку.