diff --git a/Cargo.lock b/Cargo.lock index 5373abc..48fe7b8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1086,7 +1086,7 @@ dependencies = [ [[package]] name = "idevice" -version = "0.1.36" +version = "0.1.37" dependencies = [ "base64", "byteorder", diff --git a/idevice/Cargo.toml b/idevice/Cargo.toml index c7e0b14..ee7f8db 100644 --- a/idevice/Cargo.toml +++ b/idevice/Cargo.toml @@ -2,7 +2,7 @@ name = "idevice" description = "A Rust library to interact with services on iOS devices." authors = ["Jackson Coxson"] -version = "0.1.36" +version = "0.1.37" edition = "2021" license = "MIT" documentation = "https://docs.rs/idevice" @@ -62,6 +62,7 @@ core_device = ["xpc", "dep:uuid"] core_device_proxy = ["dep:serde_json", "dep:json", "dep:byteorder"] crashreportcopymobile = ["afc"] debug_proxy = [] +diagnostics_relay = [] dvt = ["dep:byteorder", "dep:ns-keyed-archive"] heartbeat = ["tokio/macros", "tokio/time"] house_arrest = ["afc"] @@ -88,6 +89,7 @@ full = [ "core_device_proxy", "crashreportcopymobile", "debug_proxy", + "diagnostics_relay", "dvt", "heartbeat", "house_arrest", diff --git a/idevice/src/services/diagnostics_relay.rs b/idevice/src/services/diagnostics_relay.rs new file mode 100644 index 0000000..bb5a545 --- /dev/null +++ b/idevice/src/services/diagnostics_relay.rs @@ -0,0 +1,113 @@ +//! Diagnostics Relay + +use crate::{lockdown::LockdownClient, obf, Idevice, IdeviceError, IdeviceService}; + +/// Client for interacting with the Diagnostics Relay +pub struct DiagnosticsRelayClient { + /// The underlying device connection with established service + pub idevice: Idevice, +} + +impl IdeviceService for DiagnosticsRelayClient { + /// Returns the service name as registered with lockdownd + fn service_name() -> std::borrow::Cow<'static, str> { + obf!("com.apple.mobile.diagnostics_relay") + } + + /// Establishes a connection to the service + /// + /// # Arguments + /// * `provider` - Device connection provider + /// + /// # Returns + /// A connected `DiagnosticsRelayClient` instance + /// + /// # Errors + /// Returns `IdeviceError` if any step of the connection process fails + /// + /// # Process + /// 1. Connects to lockdownd service + /// 2. Starts a lockdown session + /// 3. Requests the service port + /// 4. Establishes connection to the port + /// 5. Optionally starts TLS if required by service + async fn connect( + provider: &dyn crate::provider::IdeviceProvider, + ) -> Result { + let mut lockdown = LockdownClient::connect(provider).await?; + lockdown + .start_session(&provider.get_pairing_file().await?) + .await?; + + let (port, ssl) = lockdown.start_service(Self::service_name()).await?; + + let mut idevice = provider.connect(port).await?; + if ssl { + idevice + .start_session(&provider.get_pairing_file().await?) + .await?; + } + + Ok(Self { idevice }) + } +} + +impl DiagnosticsRelayClient { + /// Creates a new client from an existing device connection + /// + /// # Arguments + /// * `idevice` - Pre-established device connection + pub fn new(idevice: Idevice) -> Self { + Self { idevice } + } + + /// Requests data from the IO registry + /// + /// # Arguments + /// * `current_plane` - The plane to request the tree as + /// * `entry_name` - The entry to get + /// * `entry_class` - The class to filter by + /// + /// # Returns + /// A plist of the tree on success + pub async fn ioregistry( + &mut self, + current_plane: Option>, + entry_name: Option>, + entry_class: Option>, + ) -> Result, IdeviceError> { + let mut req = plist::Dictionary::new(); + if let Some(plane) = current_plane { + let plane = plane.into(); + req.insert("CurrentPlane".into(), plane.into()); + } + if let Some(name) = entry_name { + let name = name.into(); + req.insert("EntryName".into(), name.into()); + } + if let Some(class) = entry_class { + let class = class.into(); + req.insert("EntryClass".into(), class.into()); + } + req.insert("Request".into(), "IORegistry".into()); + self.idevice + .send_plist(plist::Value::Dictionary(req)) + .await?; + let mut res = self.idevice.read_plist().await?; + + match res.get("Status").and_then(|x| x.as_string()) { + Some("Success") => {} + _ => { + return Err(IdeviceError::UnexpectedResponse); + } + } + + let res = res + .remove("Diagnostics") + .and_then(|x| x.into_dictionary()) + .and_then(|mut x| x.remove("IORegistry")) + .and_then(|x| x.into_dictionary()); + + Ok(res) + } +} diff --git a/idevice/src/services/mod.rs b/idevice/src/services/mod.rs index dea53bf..17bdcb2 100644 --- a/idevice/src/services/mod.rs +++ b/idevice/src/services/mod.rs @@ -10,6 +10,8 @@ pub mod core_device_proxy; pub mod crashreportcopymobile; #[cfg(feature = "debug_proxy")] pub mod debug_proxy; +#[cfg(feature = "diagnostics_relay")] +pub mod diagnostics_relay; #[cfg(feature = "dvt")] pub mod dvt; #[cfg(feature = "heartbeat")] diff --git a/tools/Cargo.toml b/tools/Cargo.toml index 8526f46..85a225f 100644 --- a/tools/Cargo.toml +++ b/tools/Cargo.toml @@ -93,6 +93,10 @@ path = "src/lockdown.rs" name = "restore_service" path = "src/restore_service.rs" +[[bin]] +name = "ioreg" +path = "src/ioreg.rs" + [dependencies] idevice = { path = "../idevice", features = ["full"] } tokio = { version = "1.43", features = ["io-util", "macros", "time", "full"] }