mirror of
https://github.com/EFForg/rayhunter.git
synced 2026-05-18 22:04:46 -07:00
Parse log bodies
This commit is contained in:
174
src/diag.rs
174
src/diag.rs
@@ -1,7 +1,7 @@
|
|||||||
//! Diag protocol serialization/deserialization
|
//! Diag protocol serialization/deserialization
|
||||||
|
|
||||||
use chrono::{DateTime, FixedOffset};
|
use chrono::{DateTime, FixedOffset};
|
||||||
use deku::prelude::*;
|
use deku::{prelude::*, bitvec::{BitSlice, Msb0}};
|
||||||
|
|
||||||
#[derive(Debug, Clone, DekuWrite)]
|
#[derive(Debug, Clone, DekuWrite)]
|
||||||
pub struct RequestContainer {
|
pub struct RequestContainer {
|
||||||
@@ -58,7 +58,7 @@ pub struct HdlcEncapsulatedMessage {
|
|||||||
pub data: Vec<u8>,
|
pub data: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, DekuRead)]
|
#[derive(Debug, Clone, PartialEq, DekuRead)]
|
||||||
#[deku(type = "u8")]
|
#[deku(type = "u8")]
|
||||||
pub enum Message {
|
pub enum Message {
|
||||||
#[deku(id = "16")]
|
#[deku(id = "16")]
|
||||||
@@ -68,8 +68,9 @@ pub enum Message {
|
|||||||
inner_length: u16,
|
inner_length: u16,
|
||||||
log_type: u16,
|
log_type: u16,
|
||||||
timestamp: Timestamp,
|
timestamp: Timestamp,
|
||||||
#[deku(count = "inner_length - 12")]
|
//#[deku(count = "inner_length - 12")]
|
||||||
payload: Vec<u8>,
|
#[deku(ctx = "*log_type, *inner_length - 12")]
|
||||||
|
body: LogBody,
|
||||||
},
|
},
|
||||||
|
|
||||||
// kinda unpleasant deku hackery here. deku expects an enum's variant to be
|
// kinda unpleasant deku hackery here. deku expects an enum's variant to be
|
||||||
@@ -86,7 +87,111 @@ pub enum Message {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, DekuRead)]
|
#[derive(Debug, Clone, PartialEq, DekuRead)]
|
||||||
|
#[deku(ctx = "log_type: u16, hdr_len: u16", id = "log_type")]
|
||||||
|
pub enum LogBody {
|
||||||
|
#[deku(id = "0x412f")]
|
||||||
|
WcdmaSignallingMessage {
|
||||||
|
channel_type: u8,
|
||||||
|
radio_bearer: u8,
|
||||||
|
length: u16,
|
||||||
|
#[deku(count = "length")]
|
||||||
|
msg: Vec<u8>,
|
||||||
|
},
|
||||||
|
#[deku(id = "0x512f")]
|
||||||
|
GsmRrSignallingMessage {
|
||||||
|
channel_type: u8,
|
||||||
|
message_type: u8,
|
||||||
|
length: u8,
|
||||||
|
#[deku(count = "length")]
|
||||||
|
msg: Vec<u8>,
|
||||||
|
},
|
||||||
|
#[deku(id = "0x5226")]
|
||||||
|
GprsMacSignallingMessage {
|
||||||
|
channel_type: u8,
|
||||||
|
message_type: u8,
|
||||||
|
length: u8,
|
||||||
|
#[deku(count = "length")]
|
||||||
|
msg: Vec<u8>,
|
||||||
|
},
|
||||||
|
#[deku(id = "0xb0c0")]
|
||||||
|
LteRrcOtaMessage {
|
||||||
|
ext_header_version: u8,
|
||||||
|
rrc_rel: u8,
|
||||||
|
rrc_version: u8,
|
||||||
|
#[deku(skip, cond = "*ext_header_version < 25")] // handle post-NR releases
|
||||||
|
nc_rrc_rel: Option<u16>,
|
||||||
|
bearer_id: u8,
|
||||||
|
phy_cell_id: u16,
|
||||||
|
|
||||||
|
// extended header. some of these fields need manual parsing based on
|
||||||
|
// header version
|
||||||
|
#[deku(reader = "read_lte_rrc_ota_message_log_freq(deku::rest, *ext_header_version)")]
|
||||||
|
freq: u32,
|
||||||
|
sfn: u16,
|
||||||
|
channel_type: u8,
|
||||||
|
#[deku(reader = "read_lte_rrc_ota_message_log_msg(deku::rest)")]
|
||||||
|
msg: Vec<u8>,
|
||||||
|
},
|
||||||
|
#[deku(id_pat = "0xb0e2 | 0xb0e3 | 0xb0ec | 0xb0ed")]
|
||||||
|
Nas4GMessage {
|
||||||
|
ext_header_version: u8,
|
||||||
|
rrc_rel: u8,
|
||||||
|
rrc_version_minor: u8,
|
||||||
|
rrc_version_major: u8,
|
||||||
|
#[deku(count = "hdr_len - 4")] // is this right??
|
||||||
|
msg: Vec<u8>,
|
||||||
|
},
|
||||||
|
#[deku(id = "0x11eb")]
|
||||||
|
IpTraffic {
|
||||||
|
#[deku(count = "hdr_len - 8")] // is this right???
|
||||||
|
msg: Vec<u8>,
|
||||||
|
},
|
||||||
|
#[deku(id = "0x713a")]
|
||||||
|
UmtsNasOtaMessage {
|
||||||
|
is_uplink: u8,
|
||||||
|
length: u32,
|
||||||
|
#[deku(count = "length")]
|
||||||
|
msg: Vec<u8>,
|
||||||
|
},
|
||||||
|
#[deku(id = "0xb821")]
|
||||||
|
NrRrcOtaMessage {
|
||||||
|
#[deku(count = "hdr_len")]
|
||||||
|
msg: Vec<u8>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_lte_rrc_ota_message_log_freq(rest: &BitSlice<u8, Msb0>, ext_header_version: u8) -> Result<(&BitSlice<u8, Msb0>, u32), DekuError> {
|
||||||
|
if ext_header_version < 8 {
|
||||||
|
let (rest, freq) = u16::read(rest, ())?;
|
||||||
|
Ok((rest, freq as u32))
|
||||||
|
} else {
|
||||||
|
let (rest, freq) = u32::read(rest, ())?;
|
||||||
|
Ok((rest, freq))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_lte_rrc_ota_message_log_msg(rest: &BitSlice<u8, Msb0>) -> Result<(&BitSlice<u8, Msb0>, Vec<u8>), DekuError> {
|
||||||
|
let (mut rest, length) = u16::read(rest, ())?;
|
||||||
|
if length != rest.len() as u16 / 8 {
|
||||||
|
let (new_rest, _) = u16::read(rest, ())?;
|
||||||
|
rest = new_rest;
|
||||||
|
}
|
||||||
|
let (new_rest, length) = u16::read(rest, ())?;
|
||||||
|
rest = new_rest;
|
||||||
|
if length != rest.len() as u16 / 8 {
|
||||||
|
return Err(DekuError::Incomplete(NeedSize::new(length as usize * 8)));
|
||||||
|
}
|
||||||
|
let mut result = Vec::new();
|
||||||
|
for _ in 0..length {
|
||||||
|
let (new_rest, byte) = u8::read(rest, ())?;
|
||||||
|
rest = new_rest;
|
||||||
|
result.push(byte);
|
||||||
|
}
|
||||||
|
Ok((rest, result))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, DekuRead)]
|
||||||
#[deku(endian = "little")]
|
#[deku(endian = "little")]
|
||||||
pub struct Timestamp {
|
pub struct Timestamp {
|
||||||
pub ts: u64,
|
pub ts: u64,
|
||||||
@@ -106,14 +211,14 @@ impl Timestamp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, DekuRead)]
|
#[derive(Debug, Clone, PartialEq, DekuRead)]
|
||||||
#[deku(ctx = "opcode: u32, subopcode: u32", id = "opcode")]
|
#[deku(ctx = "opcode: u32, subopcode: u32", id = "opcode")]
|
||||||
pub enum ResponsePayload {
|
pub enum ResponsePayload {
|
||||||
#[deku(id = "115")]
|
#[deku(id = "115")]
|
||||||
LogConfig(#[deku(ctx = "subopcode")] LogConfigResponse),
|
LogConfig(#[deku(ctx = "subopcode")] LogConfigResponse),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, DekuRead)]
|
#[derive(Debug, Clone, PartialEq, DekuRead)]
|
||||||
#[deku(ctx = "subopcode: u32", id = "subopcode")]
|
#[deku(ctx = "subopcode: u32", id = "subopcode")]
|
||||||
pub enum LogConfigResponse {
|
pub enum LogConfigResponse {
|
||||||
#[deku(id = "1")]
|
#[deku(id = "1")]
|
||||||
@@ -152,9 +257,10 @@ pub fn build_log_mask_request(log_type: u32, log_mask_bitsize: u32, accepted_log
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use crate::diag_device::LOG_CODES_FOR_RAW_PACKET_LOGGING;
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
// Just about all of these test cases from manually parsing diag packets w/ QCSuper
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_request_serialization() {
|
fn test_request_serialization() {
|
||||||
let req = Request::LogConfig(LogConfigRequest::RetrieveIdRanges);
|
let req = Request::LogConfig(LogConfigRequest::RetrieveIdRanges);
|
||||||
@@ -177,17 +283,17 @@ mod test {
|
|||||||
fn test_build_log_mask_request() {
|
fn test_build_log_mask_request() {
|
||||||
let log_type = 11;
|
let log_type = 11;
|
||||||
let bitsize = 513;
|
let bitsize = 513;
|
||||||
let req = build_log_mask_request(log_type, bitsize, &LOG_CODES_FOR_RAW_PACKET_LOGGING);
|
let req = build_log_mask_request(log_type, bitsize, &crate::diag_device::LOG_CODES_FOR_RAW_PACKET_LOGGING);
|
||||||
assert_eq!(req, Request::LogConfig(LogConfigRequest::SetMask {
|
assert_eq!(req, Request::LogConfig(LogConfigRequest::SetMask {
|
||||||
log_type: log_type,
|
log_type: log_type,
|
||||||
log_mask_bitsize: bitsize,
|
log_mask_bitsize: bitsize,
|
||||||
log_mask: vec![
|
log_mask: vec![
|
||||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0,
|
||||||
0x1, 0x0, 0x0, 0x0, 0xc, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
0x0, 0x0, 0xc, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
0x0, 0x0, 0x0, 0x0, 0x0,
|
0x0,
|
||||||
],
|
],
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@@ -218,18 +324,32 @@ mod test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_message_parsing() {
|
fn test_logs() {
|
||||||
let msg_bytes = vec![16, 0, 26, 0, 26, 0, 167, 24, 38, 161, 72, 107, 146, 30, 2, 1, 1, 1, 0, 0, 0, 0, 0, 140, 10, 0, 0, 220, 5, 0];
|
env_logger::init();
|
||||||
match Message::from_bytes((msg_bytes.as_slice(), 0)) {
|
let data = vec![
|
||||||
Ok((_, Message::Log { pending_msgs, outer_length, inner_length, log_type, timestamp, payload })) => {
|
16, 0, 38, 0, 38, 0, 192, 176, 26, 165, 245, 135, 118, 35, 2, 1, 20,
|
||||||
assert_eq!(pending_msgs, 0);
|
14, 48, 0, 160, 0, 2, 8, 0, 0, 217, 15, 5, 0, 0, 0, 0, 7, 0, 64, 1,
|
||||||
assert_eq!(outer_length, 26);
|
238, 173, 213, 77, 208
|
||||||
assert_eq!(inner_length, 26);
|
];
|
||||||
assert_eq!(log_type, 6311);
|
let msg = Message::from_bytes((&data, 0)).unwrap().1;
|
||||||
assert_eq!(timestamp.to_datetime().date_naive(), chrono::NaiveDate::from_ymd_opt(2023, 12, 4).unwrap());
|
assert_eq!(msg, Message::Log {
|
||||||
assert_eq!(payload, vec![1, 1, 0, 0, 0, 0, 0, 140, 10, 0, 0, 220, 5, 0]);
|
pending_msgs: 0,
|
||||||
|
outer_length: 38,
|
||||||
|
inner_length: 38,
|
||||||
|
log_type: 0xb0c0,
|
||||||
|
timestamp: Timestamp { ts: 72659535985485082 },
|
||||||
|
body: LogBody::LteRrcOtaMessage {
|
||||||
|
ext_header_version: 20,
|
||||||
|
rrc_rel: 14,
|
||||||
|
rrc_version: 48,
|
||||||
|
nc_rrc_rel: None,
|
||||||
|
bearer_id: 0,
|
||||||
|
phy_cell_id: 160,
|
||||||
|
freq: 2050,
|
||||||
|
sfn: 4057,
|
||||||
|
channel_type: 5,
|
||||||
|
msg: vec![0x40, 0x1, 0xee, 0xad, 0xd5, 0x4d, 0xd0],
|
||||||
},
|
},
|
||||||
_ => panic!("failed to parse message"),
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,12 +99,13 @@ impl DiagDevice {
|
|||||||
Ok(data) => match Message::from_bytes((&data, 0)) {
|
Ok(data) => match Message::from_bytes((&data, 0)) {
|
||||||
Ok(((_, leftover_bytes), res)) => {
|
Ok(((_, leftover_bytes), res)) => {
|
||||||
if leftover_bytes > 0 {
|
if leftover_bytes > 0 {
|
||||||
println!("warning: {} leftover bytes when Response", leftover_bytes);
|
println!("warning: {} leftover bytes when parsing Message", leftover_bytes);
|
||||||
}
|
}
|
||||||
result.push(res);
|
result.push(res);
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("error parsing response: {:?}", e);
|
println!("error parsing response: {:?}", e);
|
||||||
|
println!("{:?}", data);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
|
|||||||
@@ -5,12 +5,13 @@ mod log_codes;
|
|||||||
|
|
||||||
use crate::diag_device::DiagDevice;
|
use crate::diag_device::DiagDevice;
|
||||||
|
|
||||||
fn main() -> std::io::Result<()> {
|
fn main() -> diag_device::DiagResult<()> {
|
||||||
let mut dev = DiagDevice::new().unwrap();
|
env_logger::init();
|
||||||
dev.config_logs().unwrap();
|
let mut dev = DiagDevice::new()?;
|
||||||
|
dev.config_logs()?;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
for msg in dev.read_response().unwrap() {
|
for msg in dev.read_response()? {
|
||||||
println!("msg: {:?}", msg);
|
println!("msg: {:?}", msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user