diff --git a/idevice/Cargo.toml b/idevice/Cargo.toml index 4180e6e..7151cbe 100644 --- a/idevice/Cargo.toml +++ b/idevice/Cargo.toml @@ -38,6 +38,7 @@ sha2 = { version = "0.10", optional = true } [features] core_device_proxy = ["dep:serde_json", "dep:json", "dep:byteorder"] debug_proxy = [] +dvt = ["dep:byteorder"] heartbeat = [] installation_proxy = [] misagent = [] @@ -57,6 +58,7 @@ xpc = [ full = [ "core_device_proxy", "debug_proxy", + "dvt", "heartbeat", "installation_proxy", "misagent", diff --git a/idevice/src/dvt/message_aux.rs b/idevice/src/dvt/message_aux.rs new file mode 100644 index 0000000..7cd640c --- /dev/null +++ b/idevice/src/dvt/message_aux.rs @@ -0,0 +1,290 @@ +// Jackson Coxson + +use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; +use plist::Value; +use std::io::{Cursor, Read, Write}; + +const MESSAGE_AUX_MAGIC: u64 = 0x1f0; +const DTX_MESSAGE_MAGIC: u32 = 0x1F3D5B79; +const EMPTY_DICTIONARY: u32 = 0xa; + +#[derive(Debug, Clone)] +pub enum AuxValue { + Object(Vec), + Int(u32), + Long(u64), + Bytes(Vec), + PlistObject(Value), +} + +#[derive(Debug, Clone)] +pub struct AuxItem { + aux_type: u32, + value: AuxValue, +} + +#[derive(Debug, Clone)] +pub struct MessageAux { + magic: u64, + aux: Vec, +} + +#[derive(Debug, Clone)] +pub struct DtxMessageHeader { + magic: u32, + cb: u32, + fragment_id: u16, + fragment_count: u16, + length: u32, + identifier: u32, + conversation_index: u32, + channel_code: i32, + expects_reply: u32, +} + +#[derive(Debug, Clone)] +pub struct DtxMessagePayloadHeader { + flags: u32, + auxiliary_length: u32, + total_length: u64, +} + +impl MessageAux { + pub fn new() -> Self { + MessageAux { + magic: MESSAGE_AUX_MAGIC, + aux: Vec::new(), + } + } + + pub fn append_int(&mut self, value: u32) -> &mut Self { + self.aux.push(AuxItem { + aux_type: 3, + value: AuxValue::Int(value), + }); + self + } + + pub fn append_long(&mut self, value: u64) -> &mut Self { + self.aux.push(AuxItem { + aux_type: 6, + value: AuxValue::Long(value), + }); + self + } + + pub fn append_obj(&mut self, value: Value) -> &mut Self { + // Serialize the plist value to binary format + let mut buf = Vec::new(); + value.to_writer_binary(&mut buf).unwrap(); + + self.aux.push(AuxItem { + aux_type: 2, + value: AuxValue::Object(buf), + }); + self + } + + pub fn serialize(&self) -> Vec { + let mut result = Vec::new(); + + // Write magic number + result.write_u64::(self.magic).unwrap(); + + // Calculate and write the total size of aux data + let mut aux_data = Vec::new(); + for item in &self.aux { + // Write empty dictionary marker + aux_data + .write_u32::(EMPTY_DICTIONARY) + .unwrap(); + + // Write type + aux_data.write_u32::(item.aux_type).unwrap(); + + // Write value based on type + match &item.value { + AuxValue::Object(data) => { + aux_data + .write_u32::(data.len() as u32) + .unwrap(); + aux_data.write_all(data).unwrap(); + } + AuxValue::Int(value) => { + aux_data.write_u32::(*value).unwrap(); + } + AuxValue::Long(value) => { + aux_data.write_u64::(*value).unwrap(); + } + AuxValue::Bytes(data) => { + aux_data.write_all(data).unwrap(); + } + AuxValue::PlistObject(obj) => { + let mut buf = Vec::new(); + obj.to_writer_binary(&mut buf).unwrap(); + aux_data.write_all(&buf).unwrap(); + } + } + } + + // Write the length of aux data + result + .write_u64::(aux_data.len() as u64) + .unwrap(); + + // Write aux data + result.write_all(&aux_data).unwrap(); + + result + } + + pub fn deserialize(mut data: &[u8]) -> Result { + let magic = data.read_u64::()?; + let aux_length = data.read_u64::()?; + + let mut aux_items = Vec::new(); + let mut aux_data = data.take(aux_length); + + while let Ok(empty_dict) = aux_data.read_u32::() { + if empty_dict != EMPTY_DICTIONARY { + // Handle non-standard format + continue; + } + + let aux_type = aux_data.read_u32::()?; + + let value = match aux_type { + 2 => { + // Object (Binary Plist) + let length = aux_data.read_u32::()?; + let mut buffer = vec![0u8; length as usize]; + aux_data.read_exact(&mut buffer)?; + + // You could optionally parse the binary plist here + let cursor = Cursor::new(buffer); + let plist_value: Value = Value::from_reader(cursor).expect("bad plist"); + AuxValue::PlistObject(plist_value) + } + 3 => { + // Int + let value = aux_data.read_u32::()?; + AuxValue::Int(value) + } + 6 => { + // Long + let value = aux_data.read_u64::()?; + AuxValue::Long(value) + } + _ => { + // Default: raw bytes (remaining) + let mut buffer = Vec::new(); + aux_data.read_to_end(&mut buffer)?; + AuxValue::Bytes(buffer) + } + }; + + aux_items.push(AuxItem { aux_type, value }); + } + + Ok(MessageAux { + magic, + aux: aux_items, + }) + } +} + +impl Default for MessageAux { + fn default() -> Self { + Self::new() + } +} + +impl DtxMessageHeader { + pub fn new() -> Self { + DtxMessageHeader { + magic: DTX_MESSAGE_MAGIC, + cb: 0, + fragment_id: 0, + fragment_count: 0, + length: 0, + identifier: 0, + conversation_index: 0, + channel_code: 0, + expects_reply: 0, + } + } + + pub fn serialize(&self) -> Vec { + let mut result = Vec::new(); + result.write_u32::(self.magic).unwrap(); + result.write_u32::(self.cb).unwrap(); + result.write_u16::(self.fragment_id).unwrap(); + result + .write_u16::(self.fragment_count) + .unwrap(); + result.write_u32::(self.length).unwrap(); + result.write_u32::(self.identifier).unwrap(); + result + .write_u32::(self.conversation_index) + .unwrap(); + result.write_i32::(self.channel_code).unwrap(); + result + .write_u32::(self.expects_reply) + .unwrap(); + result + } + + pub fn deserialize(mut data: &[u8]) -> Result { + Ok(DtxMessageHeader { + magic: data.read_u32::()?, + cb: data.read_u32::()?, + fragment_id: data.read_u16::()?, + fragment_count: data.read_u16::()?, + length: data.read_u32::()?, + identifier: data.read_u32::()?, + conversation_index: data.read_u32::()?, + channel_code: data.read_i32::()?, + expects_reply: data.read_u32::()?, + }) + } +} + +impl Default for DtxMessageHeader { + fn default() -> Self { + Self::new() + } +} + +impl DtxMessagePayloadHeader { + pub fn new() -> Self { + DtxMessagePayloadHeader { + flags: 0, + auxiliary_length: 0, + total_length: 0, + } + } + + pub fn serialize(&self) -> Vec { + let mut result = Vec::new(); + result.write_u32::(self.flags).unwrap(); + result + .write_u32::(self.auxiliary_length) + .unwrap(); + result.write_u64::(self.total_length).unwrap(); + result + } + + pub fn deserialize(mut data: &[u8]) -> Result { + Ok(DtxMessagePayloadHeader { + flags: data.read_u32::()?, + auxiliary_length: data.read_u32::()?, + total_length: data.read_u64::()?, + }) + } +} + +impl Default for DtxMessagePayloadHeader { + fn default() -> Self { + Self::new() + } +} diff --git a/idevice/src/dvt/mod.rs b/idevice/src/dvt/mod.rs new file mode 100644 index 0000000..2513cb7 --- /dev/null +++ b/idevice/src/dvt/mod.rs @@ -0,0 +1,3 @@ +// Jackson Coxson + +pub mod message_aux; diff --git a/idevice/src/lib.rs b/idevice/src/lib.rs index 27e6328..2a84f02 100644 --- a/idevice/src/lib.rs +++ b/idevice/src/lib.rs @@ -4,6 +4,8 @@ pub mod core_device_proxy; #[cfg(feature = "debug_proxy")] pub mod debug_proxy; +#[cfg(feature = "dvt")] +pub mod dvt; #[cfg(feature = "heartbeat")] pub mod heartbeat; #[cfg(feature = "xpc")]