v0.1.0: logo, i18n pt/en, CI com caches, testes e documentação
- Logo oficial (sabia.png) e conjunto de ícones do app desktop. - Interface em português e inglês: tradutor por observação do DOM, seletor de idioma e checagem de cobertura de tradução no CI. - CI/CD com caches (rust-cache, .deb do Tauri em cache) e pipeline de release multiplataforma (CLI em 5 alvos + app desktop via tauri-action), templates de issue/PR. - Suíte de testes de integração do sabia-core (scan e2e, handshake/crack 4-way e PMKID, pcap, trilha de auditoria) — 46 testes verdes. - Documentação completa pt/en: README, ROADMAP, SECURITY, CONTRIBUTING, CHANGELOG e docs/ (cli, interface, arquitetura), com enquadramento de uso autorizado. - Correções de clippy (-D warnings) e formatação em toda a árvore. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
df27974359
commit
791538c9d9
112 files changed
+15620
-494
No files matched your search
@@ -0,0 +1,164 @@
|
||||
//! Construção e envio de quadros de gerenciamento (desautenticação e
|
||||
//! desassociação) para testes autorizados de robustez de redes Wi-Fi.
|
||||
//!
|
||||
//! A construção do quadro é independente de plataforma e testável; o envio
|
||||
//! usa `AF_PACKET` e só existe no Linux. O chamador (engine) impõe limites de
|
||||
//! quantidade e intervalo e registra a ação na trilha de auditoria.
|
||||
|
||||
use crate::error::Result;
|
||||
#[cfg(target_os = "linux")]
|
||||
use crate::error::SabiaError;
|
||||
|
||||
pub const BROADCAST: [u8; 6] = [0xff; 6];
|
||||
|
||||
/// Cabeçalho radiotap mínimo (8 bytes, sem campos). É aceito por mac80211 e
|
||||
/// deixa o driver escolher a taxa; suficiente para quadros de gerenciamento.
|
||||
fn radiotap_min() -> [u8; 8] {
|
||||
[0, 0, 8, 0, 0, 0, 0, 0]
|
||||
}
|
||||
|
||||
fn mgmt_frame(subtype: u8, bssid: [u8; 6], destination: [u8; 6], reason: u16) -> Vec<u8> {
|
||||
let fc: u16 = (subtype as u16) << 4;
|
||||
let mut f = Vec::with_capacity(8 + 24 + 2);
|
||||
f.extend_from_slice(&radiotap_min());
|
||||
f.extend_from_slice(&fc.to_le_bytes());
|
||||
f.extend_from_slice(&0u16.to_le_bytes());
|
||||
f.extend_from_slice(&destination); // addr1: destino
|
||||
f.extend_from_slice(&bssid); // addr2: BSSID/transmissor
|
||||
f.extend_from_slice(&bssid); // addr3: BSSID
|
||||
f.extend_from_slice(&[0u8; 2]); // número de sequência
|
||||
f.extend_from_slice(&reason.to_le_bytes());
|
||||
f
|
||||
}
|
||||
|
||||
/// Quadro de desautenticação (subtipo 12). `destination` pode ser uma
|
||||
/// estação específica ou [`BROADCAST`].
|
||||
pub fn deauth_frame(bssid: [u8; 6], destination: [u8; 6], reason: u16) -> Vec<u8> {
|
||||
mgmt_frame(12, bssid, destination, reason)
|
||||
}
|
||||
|
||||
/// Quadro de desassociação (subtipo 10).
|
||||
pub fn disassoc_frame(bssid: [u8; 6], destination: [u8; 6], reason: u16) -> Vec<u8> {
|
||||
mgmt_frame(10, bssid, destination, reason)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Reason {
|
||||
Unspecified = 1,
|
||||
PreviousAuthInvalid = 2,
|
||||
DeauthLeaving = 3,
|
||||
Inactivity = 4,
|
||||
Class2FrameFromNonAuth = 7,
|
||||
}
|
||||
|
||||
impl Reason {
|
||||
pub fn from_u16(value: u16) -> u16 {
|
||||
if value == 0 {
|
||||
1
|
||||
} else {
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub struct RawInjector {
|
||||
fd: std::os::fd::RawFd,
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
impl RawInjector {
|
||||
pub fn open(iface: &str) -> Result<Self> {
|
||||
use std::ffi::CString;
|
||||
use std::io;
|
||||
use std::mem;
|
||||
|
||||
let fd = unsafe { libc::socket(libc::AF_PACKET, libc::SOCK_RAW, 0x0003) };
|
||||
if fd < 0 {
|
||||
let err = io::Error::last_os_error();
|
||||
return Err(if err.raw_os_error() == Some(libc::EPERM) {
|
||||
SabiaError::PermissionDenied
|
||||
} else {
|
||||
SabiaError::Io(err)
|
||||
});
|
||||
}
|
||||
let cname =
|
||||
CString::new(iface).map_err(|_| SabiaError::InterfaceNotFound(iface.to_string()))?;
|
||||
let ifindex = unsafe { libc::if_nametoindex(cname.as_ptr()) };
|
||||
if ifindex == 0 {
|
||||
unsafe { libc::close(fd) };
|
||||
return Err(SabiaError::InterfaceNotFound(iface.to_string()));
|
||||
}
|
||||
let mut sll: libc::sockaddr_ll = unsafe { mem::zeroed() };
|
||||
sll.sll_family = libc::AF_PACKET as u16;
|
||||
sll.sll_protocol = 0x0003u16.to_be();
|
||||
sll.sll_ifindex = ifindex as i32;
|
||||
let ret = unsafe {
|
||||
libc::bind(
|
||||
fd,
|
||||
&sll as *const libc::sockaddr_ll as *const libc::sockaddr,
|
||||
mem::size_of::<libc::sockaddr_ll>() as u32,
|
||||
)
|
||||
};
|
||||
if ret < 0 {
|
||||
let err = io::Error::last_os_error();
|
||||
unsafe { libc::close(fd) };
|
||||
return Err(SabiaError::Io(err));
|
||||
}
|
||||
Ok(Self { fd })
|
||||
}
|
||||
|
||||
pub fn send(&self, frame: &[u8]) -> Result<usize> {
|
||||
let n = unsafe {
|
||||
libc::send(
|
||||
self.fd,
|
||||
frame.as_ptr() as *const libc::c_void,
|
||||
frame.len(),
|
||||
0,
|
||||
)
|
||||
};
|
||||
if n < 0 {
|
||||
return Err(SabiaError::Io(std::io::Error::last_os_error()));
|
||||
}
|
||||
Ok(n as usize)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
impl Drop for RawInjector {
|
||||
fn drop(&mut self) {
|
||||
unsafe { libc::close(self.fd) };
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn bssid() -> [u8; 6] {
|
||||
[0x00, 0x11, 0x22, 0x33, 0x44, 0x55]
|
||||
}
|
||||
|
||||
fn station() -> [u8; 6] {
|
||||
[0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb]
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn builds_deauth_frame() {
|
||||
let f = deauth_frame(bssid(), station(), 7);
|
||||
assert_eq!(&f[0..4], &[0, 0, 8, 0]);
|
||||
assert_eq!(u16::from_le_bytes([f[8], f[9]]), 0x00c0);
|
||||
assert_eq!(&f[12..18], &station());
|
||||
assert_eq!(&f[18..24], &bssid());
|
||||
assert_eq!(&f[24..30], &bssid());
|
||||
assert_eq!(u16::from_le_bytes([f[32], f[33]]), 7);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn builds_disassoc_broadcast_frame() {
|
||||
let f = disassoc_frame(bssid(), BROADCAST, 1);
|
||||
assert_eq!(u16::from_le_bytes([f[8], f[9]]), 0x00a0);
|
||||
assert_eq!(&f[12..18], &BROADCAST);
|
||||
assert_eq!(f.len(), 8 + 24 + 2);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user