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

на главную - закладки

Жанры

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

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

Шрифт:

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

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

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

Наиболее часто используется так называемое соглашение о вызове функций языка 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.

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

Жена со скидкой, или Случайный брак

Ардова Алиса
Любовные романы:
любовно-фантастические романы
8.15
рейтинг книги
Жена со скидкой, или Случайный брак

Невеста вне отбора

Самсонова Наталья
Любовные романы:
любовно-фантастические романы
7.33
рейтинг книги
Невеста вне отбора

Жена моего брата

Рам Янка
1. Черкасовы-Ольховские
Любовные романы:
современные любовные романы
6.25
рейтинг книги
Жена моего брата

Адмирал южных морей

Каменистый Артем
4. Девятый
Фантастика:
фэнтези
8.96
рейтинг книги
Адмирал южных морей

Прорвемся, опера! Книга 3

Киров Никита
3. Опер
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Прорвемся, опера! Книга 3

Релокант. По следам Ушедшего

Ascold Flow
3. Релокант в другой мир
Фантастика:
фэнтези
попаданцы
рпг
5.00
рейтинг книги
Релокант. По следам Ушедшего

Неудержимый. Книга XVI

Боярский Андрей
16. Неудержимый
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Неудержимый. Книга XVI

Русь. Строительство империи 2

Гросов Виктор
2. Вежа. Русь
Фантастика:
попаданцы
альтернативная история
рпг
5.00
рейтинг книги
Русь. Строительство империи 2

Законы Рода. Том 4

Flow Ascold
4. Граф Берестьев
Фантастика:
юмористическое фэнтези
аниме
5.00
рейтинг книги
Законы Рода. Том 4

Полковник Империи

Ланцов Михаил Алексеевич
3. Безумный Макс
Фантастика:
альтернативная история
6.58
рейтинг книги
Полковник Империи

Сотник

Ланцов Михаил Алексеевич
4. Помещик
Фантастика:
альтернативная история
5.00
рейтинг книги
Сотник

Последняя Арена 7

Греков Сергей
7. Последняя Арена
Фантастика:
рпг
постапокалипсис
5.00
рейтинг книги
Последняя Арена 7

Дракон - не подарок

Суббота Светлана
2. Королевская академия Драко
Фантастика:
фэнтези
6.74
рейтинг книги
Дракон - не подарок

Плохой парень, Купидон и я

Уильямс Хасти
Любовные романы:
современные любовные романы
5.00
рейтинг книги
Плохой парень, Купидон и я