Partial diagnostics relay implementation

This commit is contained in:
Jackson Coxson
2025-07-30 15:47:54 -06:00
parent 0938c143a5
commit 9f2de4d340
5 changed files with 123 additions and 2 deletions

2
Cargo.lock generated
View File

@@ -1086,7 +1086,7 @@ dependencies = [
[[package]] [[package]]
name = "idevice" name = "idevice"
version = "0.1.36" version = "0.1.37"
dependencies = [ dependencies = [
"base64", "base64",
"byteorder", "byteorder",

View File

@@ -2,7 +2,7 @@
name = "idevice" name = "idevice"
description = "A Rust library to interact with services on iOS devices." description = "A Rust library to interact with services on iOS devices."
authors = ["Jackson Coxson"] authors = ["Jackson Coxson"]
version = "0.1.36" version = "0.1.37"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
documentation = "https://docs.rs/idevice" documentation = "https://docs.rs/idevice"
@@ -62,6 +62,7 @@ core_device = ["xpc", "dep:uuid"]
core_device_proxy = ["dep:serde_json", "dep:json", "dep:byteorder"] core_device_proxy = ["dep:serde_json", "dep:json", "dep:byteorder"]
crashreportcopymobile = ["afc"] crashreportcopymobile = ["afc"]
debug_proxy = [] debug_proxy = []
diagnostics_relay = []
dvt = ["dep:byteorder", "dep:ns-keyed-archive"] dvt = ["dep:byteorder", "dep:ns-keyed-archive"]
heartbeat = ["tokio/macros", "tokio/time"] heartbeat = ["tokio/macros", "tokio/time"]
house_arrest = ["afc"] house_arrest = ["afc"]
@@ -88,6 +89,7 @@ full = [
"core_device_proxy", "core_device_proxy",
"crashreportcopymobile", "crashreportcopymobile",
"debug_proxy", "debug_proxy",
"diagnostics_relay",
"dvt", "dvt",
"heartbeat", "heartbeat",
"house_arrest", "house_arrest",

View File

@@ -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<Self, IdeviceError> {
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<impl Into<String>>,
entry_name: Option<impl Into<String>>,
entry_class: Option<impl Into<String>>,
) -> Result<Option<plist::Dictionary>, 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)
}
}

View File

@@ -10,6 +10,8 @@ pub mod core_device_proxy;
pub mod crashreportcopymobile; pub mod crashreportcopymobile;
#[cfg(feature = "debug_proxy")] #[cfg(feature = "debug_proxy")]
pub mod debug_proxy; pub mod debug_proxy;
#[cfg(feature = "diagnostics_relay")]
pub mod diagnostics_relay;
#[cfg(feature = "dvt")] #[cfg(feature = "dvt")]
pub mod dvt; pub mod dvt;
#[cfg(feature = "heartbeat")] #[cfg(feature = "heartbeat")]

View File

@@ -93,6 +93,10 @@ path = "src/lockdown.rs"
name = "restore_service" name = "restore_service"
path = "src/restore_service.rs" path = "src/restore_service.rs"
[[bin]]
name = "ioreg"
path = "src/ioreg.rs"
[dependencies] [dependencies]
idevice = { path = "../idevice", features = ["full"] } idevice = { path = "../idevice", features = ["full"] }
tokio = { version = "1.43", features = ["io-util", "macros", "time", "full"] } tokio = { version = "1.43", features = ["io-util", "macros", "time", "full"] }