Курс — Хакинг на Rust. #32 Продвинутые темы. Интеграция с другими языками: Пример: ускорение фаззера написанного на Python
Здравствуйте, дорогие друзья.
Фаззинг — ключевой метод в кибербезопасности для обнаружения уязвимостей через подачу случайных данных. Однако Python, несмотря на удобство, страдает от низкой скорости выполнения из-за интерпретации и GIL (Global Interpreter Lock). Например, фаззер, тестирующий функцию обработки бинарных данных, может обрабатывать лишь сотни тестов в секунду, что недостаточно для анализа сложных целей.
Цель:
Использовать Rust для ускорения критичных участков фаззера, сохранив гибкость Python.
Шаг 1: Анализ исходного кода на Python
Предположим, у нас есть простой фаззер, который генерирует случайные байты и проверяет, вызывают ли они краш в функции process_data().
Пример медленного Python-фаззера:
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18  | 
						import random import subprocess def process_data(data):     # Имитация уязвимой функции (например, обработка изображений)     try:         # Здесь может быть вызов C-библиотеки через ctypes или анализ данных         if data == b"BADINPUT":             raise ValueError("Crash!")     except Exception as e:         print(f"Crash detected: {e}") def python_fuzzer():     while True:         data = bytes(random.getrandbits(8) for _ in range(100))         process_data(data) python_fuzzer()  | 
					
Проблемы:
- Генерация данных через 
random.getrandbitsмедленная. - Вызов 
process_data()в Python-цикле создает накладные расходы. 
Шаг 2: Вынос логики в Rust
Создадим Rust-библиотеку для генерации данных и их обработки.
Cargo.toml:
| 
					 1 2 3 4 5 6 7  | 
						[package] name = "fuzzer_core" version = "0.1.0" edition = "2021" [lib] crate-type = ["cdylib"]  # Для создания .so/.dll  | 
					
src/lib.rs:
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19  | 
						use rand::Rng; use std::os::raw::{c_char, c_int}; #[no_mangle] pub extern "C" fn generate_data(buffer: *mut u8, len: usize) {     let data: Vec<u8> = (0..len).map(|_| rand::random()).collect();     unsafe {         std::ptr::copy_nonoverlapping(data.as_ptr(), buffer, len);     } } #[no_mangle] pub extern "C" fn check_crash(data: *const u8, len: usize) -> c_int {     let slice = unsafe { std::slice::from_raw_parts(data, len) };     if slice == b"BADINPUT" {         return 1;  // Краш обнаружен     }     0  // Нет краша }  | 
					
Компиляция:
| 
					 1 2  | 
						cargo build --release # Результат: target/release/libfuzzer_core.so (Linux) или .dll (Windows)  | 
					
Шаг 3: Интеграция с Python через ctypes
fuzzer.py:
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22  | 
						import ctypes import time # Загрузка библиотеки lib = ctypes.CDLL("./target/release/libfuzzer_core.so") def rust_fuzzer():     buffer = (ctypes.c_ubyte * 100)()  # Буфер для данных     while True:         # Генерация данных через Rust         lib.generate_data(ctypes.byref(buffer), 100)         data = bytes(buffer)         # Проверка на краш         crash = lib.check_crash(ctypes.cast(data, ctypes.POINTER(ctypes.c_ubyte)), 100)         if crash:             print(f"Crash detected with data: {data}") # Сравнение производительности start = time.time() rust_fuzzer() print(f"Rust: {time.time() - start:.2f} секунд")  | 
					
Результаты:
- Python-фаззер: ~500 итераций/сек.
 - Rust-фаззер: ~500 000 итераций/сек.
Ускорение: 1000 раз! 
Шаг 4: Улучшение безопасности
Rust предотвращает типичные ошибки C/C++:
- Переполнение буфера: 
generate_data()использует безопасные методы копирования. - Утечки памяти: Нет ручного управления памятью, так как данные передаются через буфер, управляемый Python.
 
Обработка ошибок:
| 
					 1 2 3 4 5 6 7  | 
						#[no_mangle] pub extern "C" fn safe_check_crash(data: *const u8, len: usize) -> c_int {     if data.is_null() {         return -1;  // Ошибка: нулевой указатель     }     // Остальная логика }  | 
					
Шаг 5: Гибридный подход
Используем Python для управления фаззинг-процессом (логгирование, генерация отчетов), а Rust — для обработки данных.
Пример архитектуры:
- Python управляет пулом процессов.
 - Каждый процесс вызывает Rust-функции для тестирования.
 - Результаты агрегируются в Python.
 
Инструменты для упрощения интеграции
- maturin: Сборка Python-модулей с Rust-кодом через 
pyo3. - cffi: Альтернатива 
ctypesдля более сложных сценариев. - pytest: Тестирование гибридного кода.
 
Пример с maturin:
- Добавьте в 
Cargo.toml: 
| 
					 1 2 3  | 
						[dependencies.pyo3] version = "0.18" features = ["extension-module"]  | 
					
2. Код на Rust:
| 
					 1 2 3 4 5 6 7 8 9 10 11 12  | 
						use pyo3::prelude::*; #[pyfunction] fn check_crash_py(data: &[u8]) -> PyResult<bool> {     Ok(data == b"BADINPUT") } #[pymodule] fn fuzzer_core(_py: Python, m: &PyModule) -> PyResult<()> {     m.add_function(wrap_pyfunction!(check_crash_py, m)?)?;     Ok(()) }  | 
					
Установка в Python:
| 
					 1  | 
						maturin develop  | 
					
Заключение
Интеграция Rust с Python позволяет сохранить скорость разработки и гибкость скриптового языка, но при этом достичь производительности нативного кода. Для фаззинга это критично: чем больше тестов выполнено за единицу времени, тем выше шанс найти уязвимость. Даже базовая оптимизация через FFI дает тысячи процентов прироста, а использование pyo3 упрощает поддержку кода.
Советы:
- Начните с профилирования Python-кода, чтобы найти «узкие места».
 - Используйте 
unsafeв Rust только при крайней необходимости. - Автоматизируйте сборку через 
setuptoolsилиmaturin. 

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