Защитись и замети! Коваленко Дмитрий aka Ingrem Спецвыпуск Xakep, номер #045, стр. 045-068-2 return 0; } int main() { char big[255]; puts("Enter string:"); gets(big); overflow_proc(big); puts("No overflow ;-)"); return 0; } Этот исходник – своего рода классика. Он упоминается практически во всех статьях про переполнения буфера, и чаще всего именно на нем юные хакеры оттачивают свое умение писать эксплоиты ;-). Отключим автоматический контроль переполнения стека и откомпилируем исходник. Запустим overflow.exe. Теперь, если по запросу "Enter string:" ввести строку длиннее 10 символов, приложение вылетит с ошибкой: Аварийное завершение происходит по очень простой причине: в процедуре overflow_proc строка копируется из big в buff с помощью стандартной функции strcpy. Проблема в том, что strcpy не проверяет, помещается ли строка в buff, и затирает стековый фрейм. Как этого избежать? Тут есть два пути. Во-первых, прежде чем копировать строку в буфер, можно просто проверить ее длину. Если она больше размера буфера, то не копировать ее вообще. Проверку можно организовать, например, так: Листинг int overflow_proc(char* big) { char buff[10]; if(strlen(big)>10) { strcpy(buff, big); } else { // а-а-ааа! спасите, взламывают! puts("Overflow attack detected!"); }; return 0; } Второй путь – использовать безопасные функции. В C++ многие стандартные функции не проверяют длину строки. Это касается, например, strcpy, gets, sprintf. Поэтому вместо них лучше использовать безопасные аналоги: strncpy, fgets, snprintf. При использовании безопасной строковой функции программист должен в параметрах передать максимальный размер строки: Листинг int overflow_proc(char* big) { char buff[10]; strncpy(buff, big, 9); return 0; } Если строка в big будет больше 9 байт, в buff скопируются только первые 9 байт из big. Остальная часть строки ("хвост") будет обрезана без каких-либо предупреждений. Кстати, о безопасных функциях. В нашем примере, если хакер введет больше 255 символов, опять возникнет переполнение, но уже в буфере big. Так что лучше в функции main заменить gets на безопасную fgets: Листинг int main() { char big[255]; FILE *stream = fopen("CON:", "r"); puts("Enter string:"); fgets(big, 254, stream); overflow_proc(big); return 0; } Ну, вот вроде бы и все, теперь наше приложение может себя защитить. Правда несложно? Едем дальше. А если не строка?.. Действительно, а что если в буфер заносится не строка с завершающим нулем, а просто цепочка байт? Данные произвольной структуры, среди которых вполне может оказаться пара-тройка нулей? Совет тут один – проверяй длину данных самостоятельно. Есть два общих правила, срабатывающих в 99% случаев. Первое. Многие функции, с помощью которых производится чтение в буфер, позволяют задавать количество читаемых байт. Следи, чтобы это количество не превысило размер буфера! Например, ты считываешь данные из файла с помощью API _lread в буфер величиной 255 байт. Всегда проверяй, чтобы последний параметр _lread был не больше этого значения. Второе. Если длину данных нельзя задать явно – ее можно узнать с помощью какой-то специальной функции! Например, ты читаешь из сокета с помощью API recv. Чтобы узнать количество байт, используй API ioctlsocket. Другой пример: ты преобразовываешь обычную строку в UNICODE. Чтобы узнать, какой буфер для этого понадобится, вызови API MultiByteToWideChar с нулевым последним параметром. Примеров можно привести много. Думай головой и не ленись заглядывать в MSDN. |