Читаем Атака на Internet полностью

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

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

Возьмем, например, программу-дрозофилу:

main(int argc, char *argv[] )

{

process_data(argv[1]);

}

#define FIXED 16

process_data (char *data)

{

char buf[FIXED];

strcpy (buf, data);

return;

}

Для нее стек после вызова process_data() будет выглядеть примерно так, как это показано на рис. 9.1.

Рис. 9.1. Состояние стека после вызова уязвимой функции

Теперь уже не надо быть суперхакером, чтобы заметить, что адрес возврата находится не только в одном сегменте с локальными переменными, но и имеет больший адрес. Тогда, передав в качестве данных строку, имеющую заведомо больший размер, чем у отведенного под ее обработку буфера, мы сможем затереть все, что лежит в памяти выше, чем этот буфер, так как функция strcpy() будет копировать данные до тех пор, пока не встретит нуль-символ \0. В нашем примере достаточно передать как входной параметр строку длиной более 15 байт для выхода за границу буфера плюс еще несколько байт для изменения собственно адреса возврата.

Не случайно в приведенных выше рассуждениях ни разу не встретилось упоминания о конкретной операционной системе. Действительно, технология переполнения локального буфера весьма универсальна и будет работать практически в любой ОС (об ограничениях чуть ниже), поэтому читатель может скомпилировать программу-дрозофилу в его любимой ОС и посмотреть на результат, подав на вход, скажем, строку из 30 единиц (этого должно быть достаточно для любой ОС и любого компилятора). UNIX-системы при этом выведут что-то типа «Segmentation fault, core dumped». Информация от Windows NT (рис. 9.2) для хакера более наглядна – по ней сразу понятно, что произошло именно переполнение буфера с возможностью подмены адреса возврата, так как адрес, на котором «споткнулась» программа, был не чем иным, как 0x31313131. Это соответствует шестнадцатеричному коду для строки из четырех единиц. Если ввести строку, состоящую из неодинаковых символов, например 01234567890abcdefghijklmnopqst, то по выведенному адресу станет ясно, в каком месте строки должен стоять будущий адрес возврата.

Рис. 9.2. Информация о сбое программы в Windows NT

Перейти на страницу:

Похожие книги