Чтение онлайн

на главную

Жанры

Защита от хакеров корпоративных сетей

авторов Коллектив

Шрифт:

Приведенные дампы стека позволят читателю хорошо понять принципы заполнения стека. Приобретенные знания пригодятся при обсуждении способов переполнения буфера.

Стековый фрейм и соглашения о вызове функций

Известно несколько вариантов соглашений о вызове функций в программе, каждый из которых отличается способом использования стека. Иногда очистка стека после завершения функции возлагается на вызывающую программу, а иногда на вызванную функцию. Тип вызова говорит компилятору, как генерировать программный код и каким образом использовать стековый фрейм.

Наиболее часто используется так называемое соглашение о вызове функций языка C (C declaration syntax). Параметры функции, объявленной в стиле языка С, записываются в стек в обратном порядке (справа налево, то есть первый параметр

записывается в стек последним). Для вызванной функции это удобно, потому что в подобном случае первый параметр выталкивается из стека в первую очередь. По завершении функции вызывающая программа очищает стек, зная число и тип ранее размещенных в стеке параметров. Описанное соглашение о вызове функций позволяет передать ей переменное число параметров. Этот вариант по умолчанию используется для генерации кода MS Visual C/C++ и широко распространен на многих платформах. Иногда его называют синтаксисом вызова cdecl (cdecl calling syntax). Функция printf является примером функции, которая использует синтаксис cdecl для обработки переменного числа параметров. По завершении функции printf вызвавшая ее программа очищает стек.

Другое широко используемое соглашение о вызове функций получило название синтаксиса стандартного вызова (standard call syntax). Аналогично синтаксису cdecl переданные функции параметры записываются в стек в обратном порядке. Разница состоит в том, что вызванная функция изменяет указатель стека до своего завершения. Иногда это удобно, поскольку в вызывающей программе не надо думать, как правильно изменить указатель стека. Кроме того, это позволяет локализовать программный код изменения стека в вызванной функции. Большинство функций WIN32 API написано с использованием синтаксиса стандартного вызова, иногда известного как stdcall.

Третий вариант соглашения о вызове функций получил название синтаксиса быстрого вызова (fast call syntax). Он схож на синтаксис стандартного вызова тем, что вызываемая функция перед своим завершением очищает стек, но отличается способом записи параметров в стек. Первые два параметра функции передаются через регистры, поэтому они в стек не записываются и вызываемая функция обращается к первым двум параметрам через регистры, в которые они были помещены. Синтаксис быстрого вызова часто используется в программном коде Delphi-программ и в пространстве ядра операционной системы NT (пространство ядра (kernel space) – блок виртуальной памяти, отведенный для использования программного ядра в привилегированном режиме).

Наконец, следует упомянуть о явном порядке (синтаксисе) вызова функций (naked syntax). На самом деле этот вариант соглашения о вызове функций не придерживается никаких правил вызова, поскольку при его использовании не предполагается генерации стандартных команд вызова функций. Явный порядок вызова функций заставляет программиста самостоятельно учитывать все нюансы обращения к функциям. Он редко используется, и если предполагается его использовать, то на это должны быть веские причины, например поддержка очень старого участка выполнимого программного кода.

Основы переполнения буфера

Буфер переполняется, когда в него пытаются записать слишком много данных. Предположим, что буфер – это стакан воды. Можно наполнять стакан, пока он не станет полным, но потом вода начнет переливаться через край. Буферы похожи на стакан воды, а язык С (и производные от него, подобно С++) предлагает множество способов записи в буфер лишних, невмещающихся данных.

В этом случае при записи в стек данных появляются проблемы. Ранее были приведены примеры размещения в стеке локальных переменных (см. 16-байтный буфер данных в программах на рис. 8.1 и 8.4). Эти примеры показывают, что буфер фиксированного размера может быть размещен в стеке где угодно. Что произойдет при записи в буфер важной информации большего размера, чем может вместить размещенный в стеке буфер? Подобно стакану с водой, буфер переполнится!

При записи 16 байт в область буфера программы, представленной на рис. 8.1,

он становится полным. При записи 17 байт один байт записывается в область стека, предназначенную для хранения переменной int2. Произошло искажение, или порча, данных (data corruption). При всех последующих обращениях к переменной int2 будет получено ее неверное значение. Продолжая в том же духе, при записи в буфер 28 байт будет затерто ранее сохраненное в стеке значение регистра EBP, а при записи 32 байт – значение регистра EIP. В результате при выполнении команды возврата из функции ret в регистр EIP будет записано значение из стека (затертое при записи в буфер) и, интерпретируя записанное значение как адрес следующей выполняемой команды, будет передано управление по содержимому регистра EIP. Если в регистр EIP будет помещен указатель на программу, то она будет выполнена.

Языку С может быть приписано следующее высказывание: «Мы предлагаем достаточно веревки, чтобы повеситься». Другими словами, язык С предлагает мощные средства управления компьютером, которыми программист должен разумно пользоваться, избегая потенциальных проблем. C – язык, в котором чрезмерно строго не контролируются типы обрабатываемых данных (loosely typed language), поэтому в нем не предусмотрено каких-либо мер безопасности при обработке разнотипных данных. Часто в написанных на языке C программах происходит переполнение буфера из-за допущенных ошибок при обработке строк. В таблице 8.1 приведены некоторые из небезопасных функций обработки строк языка C. Эта таблица ни в коем случае не претендует на полное освещение всех проблематичных функций, но дает хорошее представление относительно наиболее часто используемых.

Таблица 8.1.

Примеры проблематичных функций языка С

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

Простое неуправляемое переполнение: программа-пример

На рисунке 8.10 приведен простой пример неуправляемого переполнения. Он не годится для использования на практике, но полезен для изучения. Программа демонстрирует наиболее типичную ошибку программирования, которая отражается на работоспособности программы. В программе вызывается специально написанная для примера функция bof, которая записывает двадцатисимвольную строку в буфер, предназначенный для хранения 8 байт. В результате происходит переполнение буфера. Заметьте, что в главной функции main функция printf никогда не будет вызвана, поскольку в результате переполнения буфера при завершении функции bof управление будет передано по неверному адресу возврата. Приведенная на рис. 8.10 программа была скомпилирована как консольное приложение Windows в режиме построения окончательной версии Release.

Рис. 8.10. Простое неуправляемое переполнение в стеке

Дизассемблирование

Хорошо раскрывают суть предыдущей программы результаты ее дизассемблирования, приведенные на рис. 8.11. Обратите внимание на то, что в функции main стековые переменные не создаются, а переменная буфера buffer в функции bof используется без предварительной инициализации. Определение переменной без инициализации уже может стать источником проблем и потенциальной возможности переполнения буфера. Это целиком зависит от состояния стека в момент создания переменной и ее использования в дальнейшем. Для инициализации переменных рекомендуется использовать функции memset или bzero.

Поделиться:
Популярные книги

Последний Паладин

Саваровский Роман
1. Путь Паладина
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Последний Паладин

Идущий в тени 8

Амврелий Марк
8. Идущий в тени
Фантастика:
фэнтези
рпг
5.00
рейтинг книги
Идущий в тени 8

Все еще не Герой!. Том 2

Довыдовский Кирилл Сергеевич
2. Путешествие Героя
Фантастика:
боевая фантастика
юмористическое фэнтези
городское фэнтези
рпг
5.00
рейтинг книги
Все еще не Герой!. Том 2

Совпадений нет

Безрукова Елена
Любовные романы:
любовно-фантастические романы
5.50
рейтинг книги
Совпадений нет

Муж на сдачу

Зика Натаэль
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
Муж на сдачу

На границе империй. Том 7. Часть 4

INDIGO
Вселенная EVE Online
Фантастика:
боевая фантастика
космическая фантастика
5.00
рейтинг книги
На границе империй. Том 7. Часть 4

Довлатов. Сонный лекарь

Голд Джон
1. Не вывожу
Фантастика:
альтернативная история
аниме
5.00
рейтинг книги
Довлатов. Сонный лекарь

СД. Восемнадцатый том. Часть 1

Клеванский Кирилл Сергеевич
31. Сердце дракона
Фантастика:
фэнтези
героическая фантастика
боевая фантастика
6.93
рейтинг книги
СД. Восемнадцатый том. Часть 1

Темный Охотник

Розальев Андрей
1. КО: Темный охотник
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Темный Охотник

Назад в СССР: 1985 Книга 3

Гаусс Максим
3. Спасти ЧАЭС
Фантастика:
попаданцы
альтернативная история
5.50
рейтинг книги
Назад в СССР: 1985 Книга 3

Целитель. Книга вторая

Первухин Андрей Евгеньевич
2. Целитель
Фантастика:
фэнтези
попаданцы
5.00
рейтинг книги
Целитель. Книга вторая

Сумеречный стрелок 8

Карелин Сергей Витальевич
8. Сумеречный стрелок
Фантастика:
городское фэнтези
попаданцы
альтернативная история
аниме
5.00
рейтинг книги
Сумеречный стрелок 8

Огненный князь 3

Машуков Тимур
3. Багряный восход
Фантастика:
фэнтези
боевая фантастика
попаданцы
5.00
рейтинг книги
Огненный князь 3

Магия чистых душ

Шах Ольга
Любовные романы:
любовно-фантастические романы
5.40
рейтинг книги
Магия чистых душ