Rust, Хакинг на Rust

Курс — Хакинг на Rust. #12 Низкоуровневое программирование. Работа с указателями и сырыми данными (unsafe)

Здравствуйте, дорогие друзья.

Rust славится своей системой безопасности памяти, которая предотвращает целый класс ошибок, таких как use-after-free или гонки данных. Однако для реализации некоторых низкоуровневых операций, взаимодействия с аппаратным обеспечением или интеграции с кодом на других языках (например, C) приходится выходить за рамки этих ограничений. Здесь на сцену выходит ключевое слово unsafe — инструмент, который даёт разработчику больше контроля, но требует повышенной осторожности. В этом разделе мы разберём, как использовать unsafe для работы с указателями, сырыми данными и внешними библиотеками, а также обсудим связанные риски.

4.1 Сырые указатели: *const T и *mut T

Сырые указатели (raw pointers) в Rust похожи на указатели в C/C++. Они не подчиняются правилам владения и заимствования, что делает их мощным, но опасным инструментом.

Основные характеристики:

  • Небезопасны: Разыменование сырых указателей требует блока unsafe.
  • Могут быть нулевыми: В отличие от ссылок (&T, &mut T), сырые указатели могут указывать на null.
  • Не гарантируют валидность: Указатель может ссылаться на освобождённую память или неверный адрес.

Пример создания и разыменования:

Когда использовать:

  • Взаимодействие с аппаратным обеспечением (например, запись в регистры памяти).
  • Оптимизация критических участков кода.
  • Интеграция с C-библиотеками (см. раздел 4.3).

4.2 Управление памятью: стек vs. куча

В Rust память автоматически освобождается при выходе переменной из области видимости (стек) или через систему владения (куча). Однако в unsafe коде можно вручную управлять памятью, что полезно для эксплойтов или обработки бинарных данных.

Работа с кучей через Box<T> и сырые указатели:

Опасности:

  • Утечки памяти: Если забыть вызвать dealloc, данные останутся в куче.
  • Повреждение данных: Запись за пределы выделенного блока (аналог buffer overflow в C).

4.3 FFI: Интеграция с C-библиотеками

Rust позволяет вызывать функции из C-библиотек через Foreign Function Interface (FFI). Это особенно полезно для использования существующих инструментов (например, OpenSSL) или работы с системными вызовами.

Пример вызова функции strlen из libc:

Правила работы с FFI:

  1. Используйте extern "C" для указания C ABI (Application Binary Interface).
  2. Преобразуйте Rust-типы (например, &str) в сырые указатели.
  3. Проверяйте валидность передаваемых данных (например, нулевые указатели).

4.4 Риски и этические аспекты

Хотя unsafe расширяет возможности Rust, его неосторожное использование может привести к уязвимостям:

  • Use-after-free: Доступ к памяти после её освобождения.
  • Data races: Одновременная запись в разделяемые данные из нескольких потоков.
  • Переполнение буфера: Запись за пределы выделенного блока памяти.

Рекомендации:

  • Минимизируйте использование unsafe, инкапсулируя его в безопасные функции.
  • Проверяйте код с помощью инструментов статического анализа (например, miri).
  • Документируйте, почему использование unsafe оправдано в конкретном случае.

4.5 Практический пример: Чтение памяти процесса

Для демонстрации возможностей unsafe напишем простой сниппет, который считывает байты из произвольного адреса памяти (только для образовательных целей!):

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

Заключение

Работа с unsafe и сырыми данными — это мощный инструмент в арсенале хакера на Rust. Она позволяет обходить ограничения языка для реализации эксплойтов, анализа бинарников или оптимизации критичного кода. Однако с этой силой приходит ответственность: всегда проверяйте код на уязвимости и используйте unsafe только тогда, когда это действительно необходимо. В следующих главах мы рассмотрим, как эти низкоуровневые техники применяются для создания эксплойтов и анализа уязвимостей.

Задачи для самостоятельного решения:

  1. Напишите функцию, которая копирует данные из одного блока памяти в другой, используя сырые указатели.
  2. Создайте FFI-обёртку для функции malloc из libc и проверьте её работу.
  3. Реализуйте простой шелл-код (в учебных целях), используя unsafe для манипуляции памятью.
Хакинг на языке программирования Rust

На этом все. Всем хорошего дня!

Цикл статей по курсу — «Хакинг на Rust».