mirror of
https://github.com/EFForg/rayhunter.git
synced 2026-06-12 07:43:30 -07:00
Use QMDL instead of our custom debug file format
This might be a bit premature, but since we don't seem to be experiencing any more parsing errors when reading MessagesContainers, let's switch to outputting a standard QMDL (Qualcomm Mobile Diagnostic Log) file. This file format is more widely used by tools like scat, and the only data we lose out on is the container metadata and non-UserData messages (which we weren't using anyway).
This commit is contained in:
@@ -8,15 +8,14 @@ use log::debug;
|
||||
fn main() -> DiagResult<()> {
|
||||
env_logger::init();
|
||||
|
||||
let mut dev = DiagDevice::new()?;
|
||||
dev.enable_debug_mode("/data/wavehunter/wavehunter-debug")?;
|
||||
let mut dev = DiagDevice::new("./wavehunter.qmdl")?;
|
||||
dev.config_logs()?;
|
||||
|
||||
println!("The orca is hunting for stingrays...");
|
||||
|
||||
let mut gsmtap_parser = GsmtapParser::new();
|
||||
// We are going to want to add a timestamp to this pcap file eventually
|
||||
let mut pcap_file = PcapFile::new("/data/wavehunter/wavehunter.pcap").unwrap();
|
||||
let mut pcap_file = PcapFile::new("./wavehunter.pcap").unwrap();
|
||||
pcap_file.write_iface_header().unwrap();
|
||||
|
||||
loop {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use wavehunter::debug_file::DebugFileReader;
|
||||
use wavehunter::qmdl::QmdlFileReader;
|
||||
use wavehunter::diag_reader::DiagReader;
|
||||
use wavehunter::diag_device::{DiagResult, DiagDeviceError};
|
||||
use wavehunter::gsmtap_parser::GsmtapParser;
|
||||
@@ -11,17 +11,17 @@ fn main() -> DiagResult<()> {
|
||||
|
||||
let args: Vec<String> = std::env::args().collect();
|
||||
if args.len() != 2 {
|
||||
error!("Usage: {} /path/to/debug/file", args[0]);
|
||||
error!("Usage: {} /path/to/qmdl/file", args[0]);
|
||||
std::process::exit(1);
|
||||
}
|
||||
let mut debug_reader = DebugFileReader::new(&args[1])?;
|
||||
let mut qmdl_reader = QmdlFileReader::new(&args[1])?;
|
||||
|
||||
let mut gsmtap_parser = GsmtapParser::new();
|
||||
let mut pcap_file = PcapFile::new("./wavehunter.pcap").unwrap();
|
||||
pcap_file.write_iface_header().unwrap();
|
||||
|
||||
loop {
|
||||
match debug_reader.read_response() {
|
||||
match qmdl_reader.read_response() {
|
||||
Ok(msgs) => {
|
||||
for msg in msgs {
|
||||
debug!("msg: {:?}", msg);
|
||||
@@ -32,10 +32,10 @@ fn main() -> DiagResult<()> {
|
||||
}
|
||||
},
|
||||
Err(DiagDeviceError::IO(err)) if err.kind() == std::io::ErrorKind::UnexpectedEof => {
|
||||
println!("Reached end of debug file, exiting...");
|
||||
println!("Reached end of QMDL file, exiting...");
|
||||
std::process::exit(0);
|
||||
},
|
||||
Err(err) => panic!("Error reading debug file {}", err),
|
||||
Err(err) => panic!("Error reading QMDL file {}", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
use crate::diag_reader::DiagReader;
|
||||
use crate::diag_device::DiagResult;
|
||||
use crate::diag::*;
|
||||
|
||||
use deku::prelude::*;
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
use log::warn;
|
||||
|
||||
#[derive(Debug, DekuRead, DekuWrite)]
|
||||
#[deku(endian = "little")]
|
||||
pub struct DebugFileBlock<'a> {
|
||||
pub size: u32,
|
||||
#[deku(count = "size")]
|
||||
pub data: &'a [u8],
|
||||
}
|
||||
|
||||
pub struct DebugFileReader {
|
||||
file: File,
|
||||
}
|
||||
|
||||
impl DebugFileReader {
|
||||
pub fn new<P>(path: P) -> DiagResult<Self> where P: AsRef<std::path::Path> {
|
||||
let file = std::fs::File::options()
|
||||
.read(true)
|
||||
.open(path)?;
|
||||
Ok(DebugFileReader { file })
|
||||
}
|
||||
}
|
||||
|
||||
impl DiagReader for DebugFileReader {
|
||||
fn get_next_messages_container(&mut self) -> DiagResult<MessagesContainer> {
|
||||
let mut bytes_read_buf = [0; 4];
|
||||
self.file.read_exact(&mut bytes_read_buf)?;
|
||||
let bytes_read = u32::from_le_bytes(bytes_read_buf) as usize;
|
||||
let mut data = vec![0; bytes_read as usize];
|
||||
self.file.read_exact(&mut data)?;
|
||||
let ((leftover_bytes, _), container) = MessagesContainer::from_bytes((&data, 0))?;
|
||||
if leftover_bytes.len() > 0 {
|
||||
warn!("warning: {} leftover bytes when parsing MessagesContainer", leftover_bytes.len());
|
||||
}
|
||||
Ok(container)
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,12 @@
|
||||
use chrono::{DateTime, FixedOffset};
|
||||
use deku::prelude::*;
|
||||
|
||||
pub const MESSAGE_TERMINATOR: u8 = 0x7e;
|
||||
pub const MESSAGE_ESCAPE_CHAR: u8 = 0x7d;
|
||||
|
||||
pub const ESCAPED_MESSAGE_TERMINATOR: u8 = 0x5e;
|
||||
pub const ESCAPED_MESSAGE_ESCAPE_CHAR: u8 = 0x5d;
|
||||
|
||||
#[derive(Debug, Clone, DekuWrite)]
|
||||
pub struct RequestContainer {
|
||||
pub data_type: DataType,
|
||||
|
||||
+11
-28
@@ -1,11 +1,11 @@
|
||||
use crate::hdlc::{hdlc_encapsulate, HdlcError};
|
||||
use crate::diag::{Message, ResponsePayload, Request, LogConfigRequest, LogConfigResponse, build_log_mask_request, RequestContainer, DataType, MessagesContainer};
|
||||
use crate::diag_reader::{DiagReader, CRC_CCITT};
|
||||
use crate::debug_file::DebugFileBlock;
|
||||
use crate::qmdl::QmdlFileWriter;
|
||||
use crate::log_codes;
|
||||
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Write};
|
||||
use std::io::Read;
|
||||
use std::os::fd::AsRawFd;
|
||||
use thiserror::Error;
|
||||
use log::{info, warn, error};
|
||||
@@ -59,7 +59,7 @@ const DIAG_IOCTL_SWITCH_LOGGING: u32 = 7;
|
||||
|
||||
pub struct DiagDevice {
|
||||
file: File,
|
||||
debug_file: Option<File>,
|
||||
pub qmdl_file: QmdlFileWriter,
|
||||
read_buf: Vec<u8>,
|
||||
use_mdm: i32,
|
||||
}
|
||||
@@ -70,53 +70,36 @@ impl DiagReader for DiagDevice {
|
||||
while bytes_read == 0 {
|
||||
bytes_read = self.file.read(&mut self.read_buf)?;
|
||||
}
|
||||
if let Some(debug_file) = self.debug_file.as_mut() {
|
||||
let debug_block = DebugFileBlock {
|
||||
size: bytes_read as u32,
|
||||
data: &self.read_buf[0..bytes_read],
|
||||
};
|
||||
let debug_block_bytes = debug_block.to_bytes()?;
|
||||
debug_file.write_all(&debug_block_bytes)?;
|
||||
}
|
||||
let ((leftover_bytes, _), container) = MessagesContainer::from_bytes((&self.read_buf[0..bytes_read], 0))?;
|
||||
if leftover_bytes.len() > 0 {
|
||||
warn!("warning: {} leftover bytes when parsing MessagesContainer", leftover_bytes.len());
|
||||
}
|
||||
self.qmdl_file.write_container(&container)?;
|
||||
Ok(container)
|
||||
}
|
||||
}
|
||||
|
||||
impl DiagDevice {
|
||||
pub fn new() -> DiagResult<Self> {
|
||||
let file = std::fs::File::options()
|
||||
pub fn new<P>(qmdl_path: P) -> DiagResult<Self> where P: AsRef<std::path::Path> {
|
||||
let diag_file = std::fs::File::options()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.open("/dev/diag")?;
|
||||
let fd = file.as_raw_fd();
|
||||
let fd = diag_file.as_raw_fd();
|
||||
|
||||
let qmdl_file = QmdlFileWriter::new(qmdl_path)?;
|
||||
|
||||
enable_frame_readwrite(fd, MEMORY_DEVICE_MODE)?;
|
||||
let use_mdm = determine_use_mdm(fd)?;
|
||||
|
||||
Ok(DiagDevice {
|
||||
read_buf: vec![0; BUFFER_LEN],
|
||||
file,
|
||||
debug_file: None,
|
||||
file: diag_file,
|
||||
qmdl_file,
|
||||
use_mdm,
|
||||
})
|
||||
}
|
||||
|
||||
// Creates a file at the given path where all binary output from /dev/diag
|
||||
// will be recorded.
|
||||
pub fn enable_debug_mode<P>(&mut self, path: P) -> DiagResult<()> where P: AsRef<std::path::Path> {
|
||||
let debug_file = std::fs::File::options()
|
||||
.create(true)
|
||||
.write(true)
|
||||
.open(path)?;
|
||||
info!("enabling debug mode, writing debug output to {:?}", debug_file);
|
||||
self.debug_file = Some(debug_file);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn write_request(&mut self, req: &Request) -> DiagResult<()> {
|
||||
let buf = RequestContainer {
|
||||
data_type: DataType::UserSpace,
|
||||
|
||||
+2
-1
@@ -1,3 +1,4 @@
|
||||
use crate::diag;
|
||||
use crate::{diag::*, hdlc::hdlc_decapsulate};
|
||||
use crate::diag_device::DiagResult;
|
||||
|
||||
@@ -36,7 +37,7 @@ pub trait DiagReader {
|
||||
fn parse_response_container(&self, container: MessagesContainer) -> DiagResult<Vec<Message>> {
|
||||
let mut result = Vec::new();
|
||||
for msg in container.messages {
|
||||
for sub_msg in msg.data.split_inclusive(|&b| b == 0x7e) {
|
||||
for sub_msg in msg.data.split_inclusive(|&b| b == diag::MESSAGE_TERMINATOR) {
|
||||
match hdlc_decapsulate(&sub_msg, &CRC_CCITT) {
|
||||
Ok(data) => match Message::from_bytes((&data, 0)) {
|
||||
Ok(((leftover_bytes, _), res)) => {
|
||||
|
||||
+13
-11
@@ -7,6 +7,8 @@ use crc::Crc;
|
||||
use bytes::Buf;
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::diag::{MESSAGE_ESCAPE_CHAR, MESSAGE_TERMINATOR, ESCAPED_MESSAGE_ESCAPE_CHAR, ESCAPED_MESSAGE_TERMINATOR};
|
||||
|
||||
#[derive(Debug, Error, PartialEq)]
|
||||
pub enum HdlcError {
|
||||
#[error("Invalid checksum (expected {0}, got {1})")]
|
||||
@@ -22,25 +24,25 @@ pub enum HdlcError {
|
||||
}
|
||||
|
||||
pub fn hdlc_encapsulate(data: &[u8], crc: &Crc<u16>) -> Vec<u8> {
|
||||
let mut result: Vec<u8> = vec![];
|
||||
let mut result: Vec<u8> = Vec::with_capacity(data.len());
|
||||
|
||||
for &b in data {
|
||||
match b {
|
||||
0x7e => result.extend([0x7d, 0x5e]),
|
||||
0x7d => result.extend([0x7d, 0x5d]),
|
||||
MESSAGE_TERMINATOR => result.extend([MESSAGE_ESCAPE_CHAR, ESCAPED_MESSAGE_TERMINATOR]),
|
||||
MESSAGE_ESCAPE_CHAR => result.extend([MESSAGE_ESCAPE_CHAR, ESCAPED_MESSAGE_ESCAPE_CHAR]),
|
||||
_ => result.push(b),
|
||||
}
|
||||
}
|
||||
|
||||
for b in crc.checksum(&data).to_le_bytes() {
|
||||
match b {
|
||||
0x7e => result.extend([0x7d, 0x5e]),
|
||||
0x7d => result.extend([0x7d, 0x5d]),
|
||||
MESSAGE_TERMINATOR => result.extend([MESSAGE_ESCAPE_CHAR, ESCAPED_MESSAGE_TERMINATOR]),
|
||||
MESSAGE_ESCAPE_CHAR => result.extend([MESSAGE_ESCAPE_CHAR, ESCAPED_MESSAGE_ESCAPE_CHAR]),
|
||||
_ => result.push(b),
|
||||
}
|
||||
}
|
||||
|
||||
result.push(0x7e);
|
||||
result.push(MESSAGE_TERMINATOR);
|
||||
result
|
||||
}
|
||||
|
||||
@@ -49,21 +51,21 @@ pub fn hdlc_decapsulate(data: &[u8], crc: &Crc<u16>) -> Result<Vec<u8>, HdlcErro
|
||||
return Err(HdlcError::TooShort);
|
||||
}
|
||||
|
||||
if data[data.len() - 1] != 0x7e {
|
||||
if data[data.len() - 1] != MESSAGE_TERMINATOR {
|
||||
return Err(HdlcError::NoTrailingCharacter(data[data.len() - 1]));
|
||||
}
|
||||
|
||||
let mut unescaped = Vec::new();
|
||||
let mut unescaped = Vec::with_capacity(data.len());
|
||||
let mut escaping = false;
|
||||
for &b in &data[..data.len() - 1] {
|
||||
if escaping {
|
||||
match b {
|
||||
0x5e => unescaped.push(0x7e),
|
||||
0x5d => unescaped.push(0x7d),
|
||||
ESCAPED_MESSAGE_TERMINATOR => unescaped.push(MESSAGE_TERMINATOR),
|
||||
ESCAPED_MESSAGE_ESCAPE_CHAR => unescaped.push(MESSAGE_ESCAPE_CHAR),
|
||||
_ => return Err(HdlcError::InvalidEscapeSequence(b)),
|
||||
}
|
||||
escaping = false;
|
||||
} else if b == 0x7d {
|
||||
} else if b == MESSAGE_ESCAPE_CHAR {
|
||||
escaping = true
|
||||
} else {
|
||||
unescaped.push(b);
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ pub mod hdlc;
|
||||
pub mod diag;
|
||||
pub mod diag_device;
|
||||
pub mod diag_reader;
|
||||
pub mod debug_file;
|
||||
pub mod qmdl;
|
||||
pub mod log_codes;
|
||||
pub mod gsmtap;
|
||||
pub mod gsmtap_parser;
|
||||
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
//! QMDL files are Qualcomm Mobile Diagnostic Logs. Their format is very simple,
|
||||
//! just a series of of concatenated HDLC encapsulated diag::Message structs.
|
||||
|
||||
use crate::diag_reader::DiagReader;
|
||||
use crate::diag_device::DiagResult;
|
||||
use crate::diag::{MessagesContainer, MESSAGE_TERMINATOR, HdlcEncapsulatedMessage, DataType};
|
||||
|
||||
use std::fs::File;
|
||||
use std::io::{Write, BufReader, BufRead};
|
||||
|
||||
pub struct QmdlFileWriter {
|
||||
file: File,
|
||||
pub total_written: usize,
|
||||
}
|
||||
|
||||
impl QmdlFileWriter {
|
||||
pub fn new<P>(path: P) -> DiagResult<Self> where P: AsRef<std::path::Path> {
|
||||
let file = std::fs::File::options()
|
||||
.create(true)
|
||||
.append(true)
|
||||
.open(path)?;
|
||||
Ok(QmdlFileWriter {
|
||||
file,
|
||||
total_written: 0,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn write_container(&mut self, container: &MessagesContainer) -> DiagResult<()> {
|
||||
for msg in &container.messages {
|
||||
self.file.write_all(&msg.data)?;
|
||||
self.total_written += msg.data.len();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct QmdlFileReader {
|
||||
file: BufReader<File>,
|
||||
buf: Vec<u8>
|
||||
}
|
||||
|
||||
impl QmdlFileReader {
|
||||
pub fn new<P>(path: P) -> DiagResult<Self> where P: AsRef<std::path::Path> {
|
||||
let file = std::fs::File::options()
|
||||
.read(true)
|
||||
.open(path)?;
|
||||
Ok(QmdlFileReader {
|
||||
file: BufReader::new(file),
|
||||
buf: Vec::new(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl DiagReader for QmdlFileReader {
|
||||
fn get_next_messages_container(&mut self) -> DiagResult<MessagesContainer> {
|
||||
let bytes_read = self.file.read_until(MESSAGE_TERMINATOR, &mut self.buf)?;
|
||||
|
||||
// Since QMDL is just a flat list of messages, we can't actually
|
||||
// reproduce the container structure they came from in the original
|
||||
// read. So we'll just pretend that all containers had exactly one
|
||||
// message. As far as I know, the number of messages per container
|
||||
// doesn't actually affect anything, so this should be fine.
|
||||
Ok(MessagesContainer {
|
||||
data_type: DataType::UserSpace,
|
||||
num_messages: 1,
|
||||
messages: vec![
|
||||
HdlcEncapsulatedMessage {
|
||||
len: 1,
|
||||
data: self.buf[0..bytes_read].to_vec(),
|
||||
},
|
||||
]
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user