Читаем Защита от хакеров корпоративных сетей полностью

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

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

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

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

Перейти на страницу:
Нет соединения с сервером, попробуйте зайти чуть позже