Согласно -таблице, x это код символа T. А теперь сделаем так: C:\Secureco\ChapterStacOverrun.exe Адрес foo = 00401000 Адрес bar = 00401045 Мой стек выглядит так: 00000000 00000000 7000 001FF 004010A 0041ECE Теперь стек выглядит так: 44434241 48474645 CBA 50FED 00535251 0041ECE Так-так, уже лучше! Изменяя входные данные, мы получаем возможность корректировать адрес команды, которую программа будет исполнять следующей. Подумать только, мы контролируем работу программы при помощи вводимых ГЛАВА 5 Враг №1: переполнение буфера 115 данных! Ясно, что, подсунув ей символы с кодами x, x, x вместо подстроки QRC, мы заставим ее выполнить функцию bar. Но как передать эти коды в качестве аргументов командной строки? (Коду x вообще соответствует непечатаемый символ.) Как любой нормальный хакер, я напишу сценарий HackOverrun.pl на Perl, который накормит приложение нужным зельем передаст в командной строке необходимые аргументы: $arg = ""."\x\x\x"; $cmd = "StackOverrun ".$arg; system($cmd); Запустив сценарий, получаем желаемый результат: C:\Secureco\Chapterperl HackOverrun.pl Адрес foo = 00401000 Адрес bar = 00401045 Мой стек выглядит так: 7FBDB 7FE 7000 001FF 004010A 0041ECA ? @ Теперь стек выглядит так: 44434241 48474645 CBA 50FED 00401045 0041ECA Черт! Меня взломали! Просто, правда? Такое доступно даже начинающему программисту. В реальной атаке вместо первых 16 символов мы вставили бы вредоносный ассемблерный код и установили бы адрес возврата на начало буфера. В следующий раз, когда вы будете работать с вводом данных пользователем, вспомните, как просто вас могут сделать. Имейте в виду: при использовании другого компилятора или в локализованной (не U.S. English) версии ОС смещения могут отличаться. Именно поэтому многие читатели первого издания этой книги жаловались, что примеры не всегда работали. Это одна из причин, по которой я жульничал и выводил на экран адреса двух моих функций. Чтобы заставить пример работать, надо повторить все проделанное выше и подставить в Perl-сценарий свой адрес функции bar. Кроме того, если вы скомпилируете программу в Visual C++. NET с установленным по умолчанию параметром,GC, она вообще откажется работать. (В этом-то и идея флага. GC предотвратить переполнение буфера! ) Отключите GC в параметрах проекта или скомпилируйте программу из командной строки. Часть II Методы безопасного кодирования А теперь посмотрим, как эксплуатируется ошибка занижения размера буфера на единицу (off-by-one error). Звучит непонятно, но при ближайшем рассмотрении несложно.,* OffByOne.c *. #include stdio.h #include string.h void foo(const char* in) char buf; strncpy(buf, in, sizeof(buf)); buf,sizeof(buf). = '\0'; Оп! ля! ля! На один больше! printf("%s\n", buf); } void bar(const char* in), printf("Черт! Меня взломали! \n"); int main(int argc, char* argv), if(argc ! = 2) printf("Использование: %s,string.\n", argv); return ! 1; printf("Адрес foo %p, Адрес bar %p\n", foo, bar); foo(argv); return 0; } Наш горе-программист попал пальцем в небо использовал функцию strncpy для копирования буфера и sizeof для определения его размера. Ошибка в том, что в буфер записывается на один байт больше, чем требуется. Чтобы увидеть это, скомпилируйте программу в режиме Release с отладочной информацией. В параметрах проекта в разделе CC++ выберите значение параметра Debug Information Format такое же, как и для отладочной версии, и отключите оптимизацию, поскольку она конфликтует с отладочной информацией. Если вы пользуетесь Visual Studio. NET, отключите параметры командной строки,GC и. RTC, иначе пример не будет работать. Затем перейдите в раздел Linker (компоновка) и там тоже включите генерацию отладочной информации. Введем строку из большого числа символов A в качестве аргументов командной строки, установим точку останова на вызов функции foo и приступим к более детальному анализу кода. Во-первых, откройте окно Registers и запомните значение регистра EBP оно очень важно для нас. Продолжите поэтапное выполнение программы и войдите ГЛАВА 5 Враг №1: переполнение буфера 117 в функцию foo. Откройте окно Memory и найдите там адрес переменной buf. Вызов strncpy заполнит буфер символами A, а значение, располагающееся сразу за переменной buf, сохраненный указатель из регистра EBP. Выполните следующую строку программы, где происходит запись завершающего символа null и обратите внимание, как сохраненный указатель EBP поменял свое значение с xFF на xFF (на моей машине установлена Visual C++ 6.0, а у вас значения могут отличаться).