Курс — Хакинг на Rust. #9 Типы данных и структуры: Примитивы, строки, коллекции
Здравствуйте, дорогие друзья.
Типы данных в Rust — это основа, на которой строится безопасность и предсказуемость кода. Для хакеров это означает:
- Защиту от эксплуатации (например, переполнения буфера).
- Контроль над представлением данных (например, сетевые пакеты).
- Эффективную работу с памятью (без утечек и повреждений).
Разберем ключевые типы и структуры, которые пригодятся в кибербезопасности.
3.1 Примитивные типы: целые, числа с плавающей точкой, булевы
Rust требует явного указания типов, что предотвращает ошибки:
Целые числа
u8
,i8
— 8-битные беззнаковые/знаковые.u16
,i16
, …,u128
,i128
.usize
/isize
— размером с указатель (зависит от архитектуры).
Пример :
1 2 |
let port: u16 = 8080; // Номер порта let checksum: u32 = 0x1234_5678; // Контрольная сумма пакета |
Защита от переполнения :
В debug-режиме Rust проверяет целочисленные переполнения. В release-режиме — оборачивает значения (как в C), но это можно изменить через атрибуты.
Числа с плавающей точкой
f32
,f64
(по умолчанию).
Пример :
1 |
let delay = 0.5; // Задержка между пакетами в секундах |
Символы (char
)
- Unicode-символ, занимает 4 байта.
Пример :
1 |
let null_byte: char = '\0'; // Нулевой байт для эксплойтов |
Булевы значения
true
/false
.
3.2 Строки: String
и &str
Строки в Rust — сложная тема, но именно их безопасность делает язык уникальным.
&str
(строковый срез)
- Неизменяемая ссылка на UTF-8 данные.
- Используется для литералов:
1 |
let exploit_name: &str = "CVE-2023-1234"; |
String
- Владеет данными, выделенными в куче.
- Изменяемый и расширяемый:
1 2 |
let mut payload = String::from("A"); payload.push_str("B"); // "AB" |
Пример уязвимости в C :
1 2 |
char buffer[10]; strcpy(buffer, "This string is too long!"); // Переполнение буфера |
В Rust :
1 2 |
let buffer = String::from("This string is too long!"); // buffer.truncate(10); // Безопасное усечение |
3.3 Коллекции: Vec
, HashMap
, VecDeque
Коллекции в Rust управляют памятью автоматически, но с гарантиями безопасности.
Вектор (Vec<T>
)
- Динамический массив, хранящий данные в куче.
- Используется для хранения перехваченных пакетов, брутфорс-списков и т.д.
Пример :
1 2 |
let mut ports = vec![22, 80, 443]; ports.push(8080); // [22, 80, 443, 8080] |
Хэш-карта (HashMap<K, V>
)
- Ассоциативный массив для хранения метаданных атаки:
1 2 3 4 |
use std::collections::HashMap; let mut exploits = HashMap::new(); exploits.insert("CVE-2023-1234", "Buffer overflow in kernel"); |
Очередь (VecDeque<T>
)
- Для обработки пакетов в порядке очереди:
1 2 3 4 5 |
use std::collections::VecDeque; let mut queue = VecDeque::new(); queue.push_back(b"Packet1".to_vec()); queue.push_back(b"Packet2".to_vec()); |
3.4 Структуры: моделирование данных
Структуры позволяют создавать сложные типы для представления объектов (пакеты, эксплойты, статусы).
Пример :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
struct IPPacket { src_ip: [u8; 4], dst_ip: [u8; 4], payload: Vec<u8>, } impl IPPacket { fn new(src: [u8; 4], dst: [u8; 4], data: &[u8]) -> Self { IPPacket { src_ip: src, dst_ip: dst, payload: data.to_vec(), } } } |
Использование :
1 |
let packet = IPPacket::new([192, 168, 1, 1], [10, 0, 0, 1], b"malicious_data"); |
3.5 Перечисления: структурирование состояний
Перечисления (enum
) идеальны для моделирования статусов атаки или типов пакетов.
Пример :
1 2 3 4 5 6 7 8 9 10 11 |
enum AttackStatus { InProgress, Success { shellcode: Vec<u8> }, Failed(String), } enum Protocol { TCP(u16), UDP(u16), ICMP, } |
Обработка :
1 2 3 4 5 6 7 |
fn handle_status(status: AttackStatus) { match status { AttackStatus::Success { shellcode } => execute(shellcode), AttackStatus::Failed(reason) => log_error(reason), _ => {} } } |
3.6 Обработка ошибок: Result
и Option
Rust не использует исключения. Вместо этого:
Result<T, E>
— для операций, которые могут завершиться ошибкой.Option<T>
— для необязательных значений.
Пример сетевого запроса :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
use std::net::TcpStream; fn connect(host: &str, port: u16) -> Result<TcpStream, String> { match TcpStream::connect((host, port)) { Ok(stream) => Ok(stream), Err(e) => Err(format!("Ошибка подключения: {}", e)), } } // Использование match connect("192.168.1.1", 22) { Ok(stream) => println!("Успех!"), Err(e) => eprintln!("Провал: {}", e), } |
3.7 Работа с сырыми данными
Для анализа бинарных протоколов используйте срезы байтов (&[u8]
):
Пример парсера заголовка TCP :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
struct TCPHeader { src_port: u16, dst_port: u16, seq_num: u32, } impl TCPHeader { fn from_bytes(data: &[u8]) -> Option<Self> { if data.len() < 8 { return None; } Some(TCPHeader { src_port: u16::from_be_bytes([data[0], data[1]]), dst_port: u16::from_be_bytes([data[2], data[3]]), seq_num: u32::from_be_bytes([data[4], data[5], data[6], data[7]]), }) } } |
3.8 Практическое задание: анализ пакета
Задача : Напишите функцию, которая проверяет, содержит ли ICMP-пакет тип «Echo Request».
Решение :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
struct ICMPPacket { icmp_type: u8, code: u8, checksum: u16, data: Vec<u8>, } impl ICMPPacket { fn is_echo_request(&self) -> bool { self.icmp_type == 8 && self.code == 0 } } // Пример использования let icmp = ICMPPacket { icmp_type: 8, code: 0, checksum: 0x1234, data: vec![], }; println!("{}", icmp.is_echo_request()); // true |
Итог
Типы данных и структуры в Rust — это инструменты для создания безопасных и предсказуемых хакерских инструментов:
- Примитивы предотвращают целочисленные переполнения.
- Строки и коллекции защищают от переполнения буфера.
- Структуры и перечисления структурируют хаотичные данные (протоколы, статусы).
В следующих главах мы углубимся в низкоуровневые операции и создание эксплойтов. А пока — попробуйте написать парсер для UDP-пакета, используя структуры и срезы байтов.

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