Курс — Хакинг на Rust. #14 Низкоуровневое программирование. FFI и интеграция с C-библиотеками
Здравствуйте, дорогие друзья!
Rust славится своей безопасностью, но в хакерских сценариях часто требуется взаимодействие с низкоуровневыми системами или существующими C-библиотеками. Для этого язык предоставляет Foreign Function Interface (FFI) — механизм вызова функций из других языков, особенно C. Этот раздел научит вас интегрировать C-код в Rust-проекты, обрабатывать указатели, строки и структуры, а также избегать типичных ошибок.
4.1. Основы FFI: вызов функций из C
FFI в Rust реализуется через ключевое слово extern
. Для вызова функции из C-библиотеки:
- Объявите функцию в блоке
extern "C"
с сигнатурой, соответствующей C-коду. - Укажите имя библиотеки через атрибут
#[link]
.
Пример: вызов функции getpid
из libc
1 2 3 4 5 6 7 8 9 10 11 12 |
use std::os::raw::c_int; #[link(name = "c")] extern "C" { fn getpid() -> c_int; } fn main() { unsafe { println!("PID: {}", getpid()); } } |
#[link(name = "c")]
подключает стандартную C-библиотеку.unsafe
обязателен: компилятор Rust не может гарантировать безопасность внешнего кода.
4.2. Работа с типами данных
C и Rust используют разные представления типов. Для корректной интеграции:
- Используйте типы из модуля
std::os::raw
(например,c_int
,c_char
). - Для структур применяйте
#[repr(C)]
, чтобы выравнивание совпадало с C.
Пример: работа с struct timeval
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
#[repr(C)] struct timeval { tv_sec: libc::time_t, tv_usec: libc::suseconds_t, } extern "C" { fn gettimeofday(tv: *mut timeval, tz: *mut libc::c_void) -> libc::c_int; } fn main() { let mut tv = timeval { tv_sec: 0, tv_usec: 0 }; unsafe { gettimeofday(&mut tv as *mut _, std::ptr::null_mut()); println!("Секунды: {}", tv.tv_sec); } } |
4.3. Строки и буферы
В C строки представлены как *const c_char
. Для конвертации используйте CString
и CStr
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
use std::ffi::{CString, CStr}; use std::os::raw::c_char; extern "C" { fn strdup(s: *const c_char) -> *mut c_char; } fn main() { let s = CString::new("Hello, C!").unwrap(); let c_str = unsafe { let ptr = strdup(s.as_ptr()); CStr::from_ptr(ptr).to_string_lossy().into_owned() }; println!("{}", c_str); // Не забудьте освободить память! unsafe { libc::free(ptr as *mut _) }; } |
4.4. Обработка ошибок
C-функции часто возвращают коды ошибок. Оберните их в Result
для удобства:
1 2 3 4 5 6 7 8 |
fn call_c_function() -> Result<(), &'static str> { let result = unsafe { some_c_function() }; if result == 0 { Ok(()) } else { Err("Ошибка в C-функции") } } |
4.5. Использование bindgen для автоматизации
Утилита bindgen
генерирует Rust-обертки на основе C-заголовков. Пример build.rs
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
extern crate bindgen; use std::path::PathBuf; fn main() { let bindings = bindgen::Builder::default() .header("wrapper.h") .generate() .expect("Не удалось сгенерировать обертки"); bindings .write_to_file(PathBuf::from("src/bindings.rs")) .expect("Не удалось записать файл"); } |
В wrapper.h
укажите заголовки C-библиотеки. Сгенерированный код будет содержать все функции и структуры.
4.6. Опасности FFI
- Утечки памяти : Если C-функция выделяет память, её нужно освобождать через соответствующий вызов (например,
libc::free
). - Несовместимость типов : Ошибка в определении структуры может привести к неопределенному поведению.
- Гонки данных : При многопоточном доступе к C-коду Rust не гарантирует безопасность.
Пример уязвимости :
1 2 3 4 5 6 7 8 9 |
extern "C" { fn insecure_function(buffer: *mut u8, size: usize); } // ❌ Плохо: размер буфера не проверяется unsafe { let buffer = [0u8; 10]; insecure_function(buffer.as_mut_ptr(), 100); // Переполнение! } |
4.7. Практический пример: интеграция с OpenSSL
Создадим обертку для хэширования через OpenSSL:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
extern crate openssl; use openssl::hash::{Hasher, MessageDigest}; fn sha256_hash(data: &[u8]) -> Vec<u8> { let mut hasher = Hasher::new(MessageDigest::sha256()).unwrap(); hasher.update(data).unwrap(); hasher.finish().unwrap().to_vec() } fn main() { let hash = sha256_hash(b"secret"); println!("SHA-256: {:x?}", hash); } |
Здесь используется безопасная обертка openssl-sys
, но под капотом она вызывает C-функции через FFI.
Итоги раздела
FFI позволяет использовать мощь C-библиотек в Rust, но требует осторожности. Помните:
- Всегда проверяйте размеры буферов и корректность типов.
- Освобождайте ресурсы, выделенные в C.
- Используйте
bindgen
для автоматизации, но проверяйте сгенерированный код.
В следующих главах мы применим эти знания для создания сетевых снифферов и эксплойтов, интегрируя низкоуровневые функции в безопасный код Rust.

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