diff --git a/lib/src/analysis/analyzer.rs b/lib/src/analysis/analyzer.rs index 132630b..dce4d04 100644 --- a/lib/src/analysis/analyzer.rs +++ b/lib/src/analysis/analyzer.rs @@ -124,7 +124,11 @@ pub trait Analyzer { /// heuristic deems it relevant. Again, be mindful of any state your /// [Analyzer] updates per message, since it may be run over hundreds or /// thousands of them alongside many other [Analyzers](Analyzer). - fn analyze_information_element(&mut self, ie: &InformationElement) -> Option; + fn analyze_information_element( + &mut self, + ie: &InformationElement, + packet_num: usize, + ) -> Option; /// Returns a version number for this Analyzer. This should only ever /// increase in value, and do so whenever substantial changes are made to @@ -296,6 +300,7 @@ impl<'de> Deserialize<'de> for AnalysisRow { pub struct Harness { analyzers: Vec>, + packet_num: usize, } impl Default for Harness { @@ -308,6 +313,7 @@ impl Harness { pub fn new() -> Self { Self { analyzers: Vec::new(), + packet_num: 0, } } @@ -328,15 +334,15 @@ impl Harness { } if analyzer_config.nas_null_cipher { - harness.add_analyzer(Box::new(NasNullCipherAnalyzer::new())) + harness.add_analyzer(Box::new(NasNullCipherAnalyzer {})) } if analyzer_config.incomplete_sib { - harness.add_analyzer(Box::new(IncompleteSibAnalyzer::new())) + harness.add_analyzer(Box::new(IncompleteSibAnalyzer {})) } if analyzer_config.test_analyzer { - harness.add_analyzer(Box::new(TestAnalyzer::new())) + harness.add_analyzer(Box::new(TestAnalyzer {})) } harness @@ -425,9 +431,11 @@ impl Harness { } pub fn analyze_information_element(&mut self, ie: &InformationElement) -> Vec> { + self.packet_num += 1; + self.analyzers .iter_mut() - .map(|analyzer| analyzer.analyze_information_element(ie)) + .map(|analyzer| analyzer.analyze_information_element(ie, self.packet_num)) .collect() } diff --git a/lib/src/analysis/connection_redirect_downgrade.rs b/lib/src/analysis/connection_redirect_downgrade.rs index c75eb62..256495d 100644 --- a/lib/src/analysis/connection_redirect_downgrade.rs +++ b/lib/src/analysis/connection_redirect_downgrade.rs @@ -25,7 +25,11 @@ impl Analyzer for ConnectionRedirect2GDowngradeAnalyzer { 1 } - fn analyze_information_element(&mut self, ie: &InformationElement) -> Option { + fn analyze_information_element( + &mut self, + ie: &InformationElement, + packet_num: usize, + ) -> Option { if let InformationElement::LTE(lte_ie) = ie && let LteInformationElement::DlDcch(msg_cont) = &**lte_ie && let DL_DCCH_MessageType::C1(c1) = &msg_cont.message @@ -37,7 +41,7 @@ impl Analyzer for ConnectionRedirect2GDowngradeAnalyzer { match carrier_info { RedirectedCarrierInfo::Geran(_carrier_freqs_geran) => Some(Event { event_type: EventType::High, - message: "Detected 2G downgrade".to_owned(), + message: format!("Detected 2G downgrade (packet {})", packet_num), }), _ => Some(Event { event_type: EventType::Informational, diff --git a/lib/src/analysis/imsi_requested.rs b/lib/src/analysis/imsi_requested.rs index ba392f1..58f9c44 100644 --- a/lib/src/analysis/imsi_requested.rs +++ b/lib/src/analysis/imsi_requested.rs @@ -23,7 +23,6 @@ pub enum State { } pub struct ImsiRequestedAnalyzer { - packet_num: usize, state: State, timeout_counter: usize, flag: Option, @@ -38,20 +37,19 @@ impl Default for ImsiRequestedAnalyzer { impl ImsiRequestedAnalyzer { pub fn new() -> Self { Self { - packet_num: 0, state: State::Unattached, timeout_counter: 0, flag: None, } } - fn transition(&mut self, next_state: State) { + fn transition(&mut self, next_state: State, packet_num: usize) { match (&self.state, &next_state) { // Reset timeout on successful auth (_, State::AuthAccept) => { debug!( "reset timeout counter at {} due to auth accept (frame {})", - self.timeout_counter, self.packet_num + self.timeout_counter, packet_num ); self.timeout_counter = 0; } @@ -62,7 +60,7 @@ impl ImsiRequestedAnalyzer { event_type: EventType::High, message: format!( "Identity requested after auth request (frame {})", - self.packet_num + packet_num ), }); } @@ -73,7 +71,7 @@ impl ImsiRequestedAnalyzer { event_type: EventType::High, message: format!( "Identity requested without Attach Request (frame {})", - self.packet_num + packet_num ), }); } @@ -84,7 +82,7 @@ impl ImsiRequestedAnalyzer { event_type: EventType::High, message: format!( "Disconnected after Identity Request without Auth Accept (frame {})", - self.packet_num + packet_num ), }); } @@ -95,7 +93,7 @@ impl ImsiRequestedAnalyzer { event_type: EventType::Informational, message: format!( "Identity Request happened but its not suspicious yet. (frame {})", - self.packet_num + packet_num ) .to_string(), }); @@ -106,7 +104,7 @@ impl ImsiRequestedAnalyzer { _ => { debug!( "Transition from {:?} to {:?} at {}", - self.state, next_state, self.packet_num + self.state, next_state, packet_num ); } } @@ -129,29 +127,31 @@ impl Analyzer for ImsiRequestedAnalyzer { 3 } - fn analyze_information_element(&mut self, ie: &InformationElement) -> Option { - self.packet_num += 1; - + fn analyze_information_element( + &mut self, + ie: &InformationElement, + packet_num: usize, + ) -> Option { if let InformationElement::LTE(inner) = ie { match &**inner { LteInformationElement::NAS(payload) => match payload { NASMessage::EMMMessage(EMMMessage::EMMExtServiceRequest(_)) | NASMessage::EMMMessage(EMMMessage::EMMAttachRequest(_)) => { - self.transition(State::AttachRequest); + self.transition(State::AttachRequest, packet_num); } NASMessage::EMMMessage(EMMMessage::EMMIdentityRequest(_)) => { - self.transition(State::IdentityRequest); + self.transition(State::IdentityRequest, packet_num); } NASMessage::EMMMessage(EMMMessage::EMMAttachComplete(_)) | NASMessage::EMMMessage(EMMMessage::EMMAuthenticationResponse(_)) => { - self.transition(State::AuthAccept); + self.transition(State::AuthAccept, packet_num); } NASMessage::EMMMessage(EMMMessage::EMMServiceReject(_)) | NASMessage::EMMMessage(EMMMessage::EMMAttachReject(_)) | NASMessage::EMMMessage(EMMMessage::EMMDetachRequestMO(_)) | NASMessage::EMMMessage(EMMMessage::EMMDetachRequestMT(_)) | NASMessage::EMMMessage(EMMMessage::EMMTrackingAreaUpdateReject(_)) => { - self.transition(State::Disconnect); + self.transition(State::Disconnect, packet_num); } _ => {} }, @@ -161,7 +161,7 @@ impl Analyzer for ImsiRequestedAnalyzer { | UL_CCCH_MessageType::C1( UL_CCCH_MessageType_c1::RrcConnectionReestablishmentRequest(_), ) => { - self.transition(State::AttachRequest); + self.transition(State::AttachRequest, packet_num); } _ => {} }, @@ -171,7 +171,7 @@ impl Analyzer for ImsiRequestedAnalyzer { _, )) = rrc_payload.message { - self.transition(State::Disconnect) + self.transition(State::Disconnect, packet_num) } } _ => {} @@ -182,14 +182,14 @@ impl Analyzer for ImsiRequestedAnalyzer { self.timeout_counter += 1; debug!( "timeout: counter {}, packet: {}", - self.timeout_counter, self.packet_num + self.timeout_counter, packet_num ); if self.timeout_counter >= TIMEOUT_THRESHHOLD { self.flag = Some(Event { event_type: EventType::Informational {}, message: format!( "Identity request happened without auth request followup (frame {})", - self.packet_num + packet_num ) .to_string(), }); diff --git a/lib/src/analysis/incomplete_sib.rs b/lib/src/analysis/incomplete_sib.rs index bb1e2b6..5e2aace 100644 --- a/lib/src/analysis/incomplete_sib.rs +++ b/lib/src/analysis/incomplete_sib.rs @@ -5,21 +5,7 @@ use telcom_parser::lte_rrc::{BCCH_DL_SCH_MessageType, BCCH_DL_SCH_MessageType_c1 use super::analyzer::{Analyzer, Event, EventType}; use super::information_element::{InformationElement, LteInformationElement}; -pub struct IncompleteSibAnalyzer { - packet_num: usize, -} - -impl Default for IncompleteSibAnalyzer { - fn default() -> Self { - Self::new() - } -} - -impl IncompleteSibAnalyzer { - pub fn new() -> Self { - Self { packet_num: 0 } - } -} +pub struct IncompleteSibAnalyzer {} impl Analyzer for IncompleteSibAnalyzer { fn get_name(&self) -> Cow<'_, str> { @@ -34,9 +20,11 @@ impl Analyzer for IncompleteSibAnalyzer { 1 } - fn analyze_information_element(&mut self, ie: &InformationElement) -> Option { - self.packet_num += 1; - + fn analyze_information_element( + &mut self, + ie: &InformationElement, + packet_num: usize, + ) -> Option { if let InformationElement::LTE(lte_ie) = ie && let LteInformationElement::BcchDlSch(sch_msg) = &**lte_ie && let BCCH_DL_SCH_MessageType::C1(c1) = &sch_msg.message @@ -47,7 +35,7 @@ impl Analyzer for IncompleteSibAnalyzer { event_type: EventType::Medium, message: format!( "SIB1 scheduling info list was malformed (packet {})", - self.packet_num + packet_num ), }); } diff --git a/lib/src/analysis/nas_null_cipher.rs b/lib/src/analysis/nas_null_cipher.rs index 95ab347..af67cdd 100644 --- a/lib/src/analysis/nas_null_cipher.rs +++ b/lib/src/analysis/nas_null_cipher.rs @@ -7,21 +7,7 @@ use pycrate_rs::nas::generated::emm::emm_security_mode_command::NASSecAlgoCiphAl use super::analyzer::{Analyzer, Event, EventType}; use super::information_element::{InformationElement, LteInformationElement}; -pub struct NasNullCipherAnalyzer { - packet_num: usize, -} - -impl Default for NasNullCipherAnalyzer { - fn default() -> Self { - Self::new() - } -} - -impl NasNullCipherAnalyzer { - pub fn new() -> Self { - Self { packet_num: 0 } - } -} +pub struct NasNullCipherAnalyzer {} impl Analyzer for NasNullCipherAnalyzer { fn get_name(&self) -> Cow<'_, str> { @@ -38,8 +24,11 @@ impl Analyzer for NasNullCipherAnalyzer { 1 } - fn analyze_information_element(&mut self, ie: &InformationElement) -> Option { - self.packet_num += 1; + fn analyze_information_element( + &mut self, + ie: &InformationElement, + packet_num: usize, + ) -> Option { let payload = match ie { InformationElement::LTE(inner) => match &**inner { LteInformationElement::NAS(payload) => payload, @@ -55,7 +44,7 @@ impl Analyzer for NasNullCipherAnalyzer { event_type: EventType::High, message: format!( "NAS Security mode command requested null cipher(packet {})", - self.packet_num + packet_num ), }); } diff --git a/lib/src/analysis/null_cipher.rs b/lib/src/analysis/null_cipher.rs index 2ffa42f..49e8bfd 100644 --- a/lib/src/analysis/null_cipher.rs +++ b/lib/src/analysis/null_cipher.rs @@ -131,7 +131,11 @@ impl Analyzer for NullCipherAnalyzer { 1 } - fn analyze_information_element(&mut self, ie: &InformationElement) -> Option { + fn analyze_information_element( + &mut self, + ie: &InformationElement, + packet_num: usize, + ) -> Option { let dcch_msg = match ie { InformationElement::LTE(lte_ie) => match &**lte_ie { LteInformationElement::DlDcch(dcch_msg) => dcch_msg, @@ -154,7 +158,7 @@ impl Analyzer for NullCipherAnalyzer { if null_cipher_detected { return Some(Event { event_type: EventType::High, - message: "Cell suggested use of null cipher".to_string(), + message: format!("Cell suggested use of null cipher (packet {})", packet_num), }); } None diff --git a/lib/src/analysis/priority_2g_downgrade.rs b/lib/src/analysis/priority_2g_downgrade.rs index 7623f79..fbad9f8 100644 --- a/lib/src/analysis/priority_2g_downgrade.rs +++ b/lib/src/analysis/priority_2g_downgrade.rs @@ -49,6 +49,7 @@ impl Analyzer for LteSib6And7DowngradeAnalyzer { fn analyze_information_element( &mut self, ie: &InformationElement, + packet_num: usize, ) -> Option { let sibs = &self.unpack_system_information(ie)?.0; for sib in sibs { @@ -62,9 +63,10 @@ impl Analyzer for LteSib6And7DowngradeAnalyzer { { return Some(Event { event_type: EventType::High, - message: - "LTE cell advertised a 3G cell for priority 0 reselection" - .to_string(), + message: format!( + "LTE cell advertised a 3G cell for priority 0 reselection (packet {})", + packet_num + ), }); } } @@ -77,9 +79,10 @@ impl Analyzer for LteSib6And7DowngradeAnalyzer { { return Some(Event { event_type: EventType::High, - message: - "LTE cell advertised a 3G cell for priority 0 reselection" - .to_string(), + message: format!( + "LTE cell advertised a 3G cell for priority 0 reselection (packet {})", + packet_num + ), }); } } diff --git a/lib/src/analysis/test_analyzer.rs b/lib/src/analysis/test_analyzer.rs index 2679679..1531be7 100644 --- a/lib/src/analysis/test_analyzer.rs +++ b/lib/src/analysis/test_analyzer.rs @@ -6,21 +6,7 @@ use super::analyzer::{Analyzer, Event, EventType}; use super::information_element::{InformationElement, LteInformationElement}; use deku::bitvec::*; -pub struct TestAnalyzer { - packet_num: usize, -} - -impl Default for TestAnalyzer { - fn default() -> Self { - Self::new() - } -} - -impl TestAnalyzer { - pub fn new() -> Self { - Self { packet_num: 0 } - } -} +pub struct TestAnalyzer {} impl Analyzer for TestAnalyzer { fn get_name(&self) -> Cow<'_, str> { @@ -37,9 +23,11 @@ impl Analyzer for TestAnalyzer { 1 } - fn analyze_information_element(&mut self, ie: &InformationElement) -> Option { - self.packet_num += 1; - + fn analyze_information_element( + &mut self, + ie: &InformationElement, + packet_num: usize, + ) -> Option { if let InformationElement::LTE(lte_ie) = ie && let LteInformationElement::BcchDlSch(sch_msg) = &**lte_ie && let BCCH_DL_SCH_MessageType::C1(c1) = &sch_msg.message @@ -66,7 +54,7 @@ impl Analyzer for TestAnalyzer { event_type: EventType::Low, message: format!( "SIB1 received (packet {}) CID: {}, PLMN: {}-{}", - self.packet_num, cid, mcc_string, mnc_string + packet_num, cid, mcc_string, mnc_string ), }); }