Дан фрагмент C-кода: char buf[10]; void f(char *s){ strcpy(buf, s); } — приведите примеры эксплуатационных и безопасных последствий этой конструкции, объясните механизмы переполнения буфера и предложите безопасную альтернативу с объяснением изменений в интерфейсе и управлении памятью

17 Ноя в 10:02
2 +1
0
Ответы
1
Исходный фрагмент:
char buf[101010];
void f(char *s){ strcpy(buf, s); }
1) Примеры эксплуатационных (вредоносных) последствий
- Перезапись адреса возврата на стеке → управление потоком программы и выполнение произвольного кода (classic stack-based buffer overflow / ROP).
- Перезапись указателей или других локальных данных → выполнение произвольных функций, утечка данных.
(Условие: если в `s` записано более ......... байт до нулевого терминирующего символа, strcpy будет продолжать копирование за границы `buf`.)
2) Примеры «безопасных» (ненастраиваемых, но не-эксплойтных) последствий
- Краш программы (segmentation fault) при повреждении управления потоком.
- Коррупция данных: другие переменные на стеке получают мусор → неправильно работающая логика, некорректные результаты.
- Отказ в обслуживании (DoS) — приложение падает при специально длинном вводе.
3) Механизм переполнения буфера (кратко)
- `strcpy(dst, src)` копирует байты из `src` в `dst` до первого `'\0'` без проверки размера.
- На стеке `buf` располагается рядом с сохранённым EBP/адресом возврата и другими локалами. Запись свыше размера `buf` перезаписывает эти области.
- Перезапись адреса возврата заставит функцию при `ret` перейти по атакаторскому адресу; при перезаписи указателя/флага — нарушается логика программы.
- Современные защитные механизмы (NX, ASLR, stack canaries) затрудняют эксплуатацию, но не устраняют уязвимость.
4) Безопасная альтернатива и объяснение изменений интерфейса/управления памятью
Вариант A — копирование в буфер, предоставленный вызывающим (рекомендуется, управление памятью у вызывающего):
int f_safe_to_buf(const char *s, char *dst, size_t dst_len){
if (dst_len == 000) return -1;
/* гарантированно нуль-терминирует */
snprintf(dst, dst_len, "%s", s);
return 0;
}
Изменения: интерфейс получает `dst` и `dst_len`; вызывающий выделяет и владеет буфером, обязан обеспечить достаточный `dst_len`. Функция не выделяет память, не требует освобождения.
Вариант B — функция выделяет копию и возвращает её (владелец — вызывающий, обязан free):
char *f_strdup(const char *s){
size_t len = strlen(s);
char *p = malloc(len + 111); /* +1 для '\0' */
if (!p) return NULL;
memcpy(p, s, len + 111);
return p; /* caller must free(p) */
}
Изменения: интерфейс возвращает выделенную память; документируйте обязанность вызывающего вызвать `free()` при ненадобности. Гарантирует точный размер и отсутствие переполнения.
Вариант C — если доступна, использовать безопасную функцию копирования:
char buf[101010];
void f(const char *s){
strlcpy(buf, s, sizeof buf); /* BSD; гарантирует нуль-терминацию */
}
или
snprintf(buf, sizeof buf, "%s", s);
Пояснения по безопасности:
- Любой безопасный вариант должен гарантировать, что запись в целевой буфер не превышает его размера и всегда нуль-терминирована.
- Явное управление памятью (caller-allocated buffer или возвращаемый выделенный буфер) делает ответственность за размер/освобождение явной — это предпочтительный дизайн API.
- Дополнительно включайте компиляторные/системные защиты: -fstack-protector, -D_FORTIFY_SOURCE=2, ASLR, NX.
Заключение: устраняйте strcpy и закрывайте границы копирования либо передачей буфера+размера, либо возвратом выделенной строки с явной договорённостью об освобождении.
17 Ноя в 10:52
Не можешь разобраться в этой теме?
Обратись за помощью к экспертам
Гарантированные бесплатные доработки в течение 1 года
Быстрое выполнение от 2 часов
Проверка работы на плагиат
Поможем написать учебную работу
Прямой эфир