From 2e4de4a2dff154cf0d79d5c82ee16b1bb44f622a Mon Sep 17 00:00:00 2001 From: Will Greenberg Date: Tue, 24 Jun 2025 12:15:04 -0700 Subject: [PATCH] lib: Use pycrate-rs NAS parser --- Cargo.lock | 14 +++++++ lib/Cargo.toml | 1 + lib/src/analysis/imsi_requested.rs | 55 ++++++++++++++----------- lib/src/analysis/information_element.rs | 21 ++++++---- 4 files changed, 58 insertions(+), 33 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index db2eff5..b8e7573 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2220,6 +2220,19 @@ dependencies = [ "syn 2.0.101", ] +[[package]] +name = "pycrate-rs" +version = "0.1.0" +source = "git+https://github.com/wgreenberg/pycrate-rs#9e72e40bee9c3c09205ad871cf681628b443de7c" +dependencies = [ + "clap", + "deku", + "env_logger 0.11.8", + "log", + "serde", + "thiserror 2.0.12", +] + [[package]] name = "qoi" version = "0.4.1" @@ -2378,6 +2391,7 @@ dependencies = [ "log", "nix", "pcap-file-tokio", + "pycrate-rs", "serde", "telcom-parser", "thiserror 1.0.69", diff --git a/lib/Cargo.toml b/lib/Cargo.toml index d0bc5af..a50eee2 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -24,6 +24,7 @@ libc = "0.2.150" log = "0.4.20" nix = { version = "0.29.0", features = ["feature"] } pcap-file-tokio = "0.1.0" +pycrate-rs = { git = "https://github.com/wgreenberg/pycrate-rs" } thiserror = "1.0.50" telcom-parser = { path = "../telcom-parser" } tokio = { version = "1.44.2", default-features = false } diff --git a/lib/src/analysis/imsi_requested.rs b/lib/src/analysis/imsi_requested.rs index 6d9a4dc..4ba24ad 100644 --- a/lib/src/analysis/imsi_requested.rs +++ b/lib/src/analysis/imsi_requested.rs @@ -1,5 +1,10 @@ +use std::any::Any; use std::borrow::Cow; +use pycrate_rs::nas::emm::EMMMessage; +use pycrate_rs::nas::generated::emm::emm_identity_request::{EMMIdentityRequest, IDTypeV}; +use pycrate_rs::nas::NASMessage; + use super::analyzer::{Analyzer, Event, EventType, Severity}; use super::information_element::{InformationElement, LteInformationElement}; @@ -41,30 +46,32 @@ impl Analyzer for ImsiRequestedAnalyzer { }; // NAS identity request, ID type IMSI - if payload == &[0x07, 0x55, 0x01] { - if self.packet_num < PACKET_THRESHHOLD { - return Some(Event { - event_type: EventType::QualitativeWarning { - severity: Severity::Medium, - }, - message: format!( - "NAS IMSI identity request detected, however it was within \ - the first {} packets of this analysis. If you just \ - turned your device on, this is likely a \ - false-positive.", - PACKET_THRESHHOLD - ), - }); - } else { - return Some(Event { - event_type: EventType::QualitativeWarning { - severity: Severity::High, - }, - message: format!( - "NAS IMSI identity request detected (packet {})", - self.packet_num - ), - }); + if let NASMessage::EMMMessage(EMMMessage::EMMIdentityRequest(req)) = payload { + if req.id_type.inner == IDTypeV::IMSI { + if self.packet_num < PACKET_THRESHHOLD { + return Some(Event { + event_type: EventType::QualitativeWarning { + severity: Severity::Medium, + }, + message: format!( + "NAS IMSI identity request detected, however it was within \ + the first {} packets of this analysis. If you just \ + turned your device on, this is likely a \ + false-positive.", + PACKET_THRESHHOLD + ), + }); + } else { + return Some(Event { + event_type: EventType::QualitativeWarning { + severity: Severity::High, + }, + message: format!( + "NAS IMSI identity request detected (packet {})", + self.packet_num + ), + }); + } } } None diff --git a/lib/src/analysis/information_element.rs b/lib/src/analysis/information_element.rs index 9286755..1b60efe 100644 --- a/lib/src/analysis/information_element.rs +++ b/lib/src/analysis/information_element.rs @@ -5,17 +5,20 @@ use crate::gsmtap::{GsmtapMessage, GsmtapType, LteNasSubtype, LteRrcSubtype}; use telcom_parser::{decode, lte_rrc}; +use pycrate_rs::nas::NASMessage; use thiserror::Error; #[derive(Error, Debug)] pub enum InformationElementError { - #[error("Failed decoding")] - DecodingError(#[from] telcom_parser::ParsingError), + #[error("Failed decoding RRC message")] + RRCDecodingError(#[from] telcom_parser::ParsingError), + #[error("Failed decoding NAS message")] + NASDecodingError(#[from] pycrate_rs::nas::ParseError), #[error("Unsupported LTE RRC subtype {0:?}")] UnsupportedGsmtapType(GsmtapType), } -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone)] pub enum InformationElement { GSM, UMTS, @@ -25,7 +28,7 @@ pub enum InformationElement { FiveG, } -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone)] pub enum LteInformationElement { DlCcch(lte_rrc::DL_CCCH_Message), // This element of the enum is substantially larger than the others, @@ -44,8 +47,7 @@ pub enum LteInformationElement { SbcchSlBch(lte_rrc::SBCCH_SL_BCH_Message), SbcchSlBchV2x(lte_rrc::SBCCH_SL_BCH_Message_V2X_r14), - // FIXME: actually parse NAS messages - NAS(Vec), + NAS(NASMessage), // FIXME: unclear which message these "NB" types map to //DlCcchNb(), //DlDcchNb(), @@ -89,9 +91,10 @@ impl TryFrom<&GsmtapMessage> for InformationElement { }; Ok(InformationElement::LTE(Box::new(lte))) } - GsmtapType::LteNas(LteNasSubtype::Plain) => Ok(InformationElement::LTE(Box::new( - LteInformationElement::NAS(gsmtap_msg.payload.clone()), - ))), + GsmtapType::LteNas(LteNasSubtype::Plain) => { + let msg = NASMessage::parse(&gsmtap_msg.payload)?; + Ok(InformationElement::LTE(Box::new(LteInformationElement::NAS(msg)))) + } _ => Err(InformationElementError::UnsupportedGsmtapType( gsmtap_msg.header.gsmtap_type, )),