diff --git a/idevice/src/services/core_device/app_service.rs b/idevice/src/services/core_device/app_service.rs index eedd185..5b177d9 100644 --- a/idevice/src/services/core_device/app_service.rs +++ b/idevice/src/services/core_device/app_service.rs @@ -3,7 +3,7 @@ use log::warn; use serde::Deserialize; -use crate::{IdeviceError, ReadWrite, RsdService, obf}; +use crate::{IdeviceError, ReadWrite, RsdService, obf, xpc::XPCObject}; use super::CoreDeviceServiceClient; @@ -152,7 +152,7 @@ impl AppServiceClient { }); let res = self .inner - .invoke("com.apple.coredevice.feature.listapps", Some(options)) + .invoke_with_plist("com.apple.coredevice.feature.listapps", options) .await?; let res = match res.as_array() { @@ -178,6 +178,16 @@ impl AppServiceClient { Ok(desd) } + /// Launches an application by a bundle ID. + /// + /// # Notes + /// * `start_suspended` - If set to true, you will need to attach a debugger using + /// `DebugServer` to continue. + /// + /// * `stdio_uuid` - Create a new ``OpenStdioSocketClient``, read the UUID, and pass it to this + /// function. Note that if the process already has another stdio UUID, this parameter is ignored by + /// iOS. Either make sure the proccess isn't running, or pass ``kill_existing: true`` + #[allow(clippy::too_many_arguments)] // still didn't ask pub async fn launch_application( &mut self, bundle_id: impl Into, @@ -186,6 +196,7 @@ impl AppServiceClient { start_suspended: bool, environment: Option, platform_options: Option, + stdio_uuid: Option, ) -> Result { let bundle_id = bundle_id.into(); @@ -196,20 +207,34 @@ impl AppServiceClient { } }, "options": { - "arguments": arguments, // Now this will work directly + "arguments": arguments, "environmentVariables": environment.unwrap_or_default(), "standardIOUsesPseudoterminals": true, "startStopped": start_suspended, "terminateExisting": kill_existing, "user": { - "shortName": "mobile" + "active": true, }, "platformSpecificOptions": plist::Value::Data(crate::util::plist_to_xml_bytes(&platform_options.unwrap_or_default())), }, - "standardIOIdentifiers": {} - }) - .into_dictionary() - .unwrap(); + }); + + let req: XPCObject = req.into(); + let mut req = req.to_dictionary().unwrap(); + req.insert( + "standardIOIdentifiers".into(), + match stdio_uuid { + Some(u) => { + let u = XPCObject::Uuid(u); + let mut d = crate::xpc::Dictionary::new(); + d.insert("standardInput".into(), u.clone()); + d.insert("standardOutput".into(), u.clone()); + d.insert("standardError".into(), u.clone()); + d.into() + } + None => crate::xpc::Dictionary::new().into(), + }, + ); let res = self .inner @@ -259,13 +284,11 @@ impl AppServiceClient { ) -> Result<(), IdeviceError> { let bundle_id = bundle_id.into(); self.inner - .invoke( + .invoke_with_plist( "com.apple.coredevice.feature.uninstallapp", - Some( - crate::plist!({"bundleIdentifier": bundle_id}) - .into_dictionary() - .unwrap(), - ), + crate::plist!({"bundleIdentifier": bundle_id}) + .into_dictionary() + .unwrap(), ) .await?; @@ -279,16 +302,14 @@ impl AppServiceClient { ) -> Result { let res = self .inner - .invoke( + .invoke_with_plist( "com.apple.coredevice.feature.sendsignaltoprocess", - Some( - crate::plist!({ - "process": { "processIdentifier": pid as i64}, - "signal": signal as i64, - }) - .into_dictionary() - .unwrap(), - ), + crate::plist!({ + "process": { "processIdentifier": pid as i64}, + "signal": signal as i64, + }) + .into_dictionary() + .unwrap(), ) .await?; @@ -314,19 +335,17 @@ impl AppServiceClient { let bundle_id = bundle_id.into(); let res = self .inner - .invoke( + .invoke_with_plist( "com.apple.coredevice.feature.fetchappicons", - Some( - crate::plist!({ - "width": width, - "height": height, - "scale": scale, - "allowPlaceholder": allow_placeholder, - "bundleIdentifier": bundle_id - }) - .into_dictionary() - .unwrap(), - ), + crate::plist!({ + "width": width, + "height": height, + "scale": scale, + "allowPlaceholder": allow_placeholder, + "bundleIdentifier": bundle_id + }) + .into_dictionary() + .unwrap(), ) .await?; diff --git a/idevice/src/services/core_device/diagnosticsservice.rs b/idevice/src/services/core_device/diagnosticsservice.rs index 084a390..803f1fb 100644 --- a/idevice/src/services/core_device/diagnosticsservice.rs +++ b/idevice/src/services/core_device/diagnosticsservice.rs @@ -45,7 +45,7 @@ impl DiagnostisServiceClient { let res = self .inner - .invoke("com.apple.coredevice.feature.capturesysdiagnose", Some(req)) + .invoke_with_plist("com.apple.coredevice.feature.capturesysdiagnose", req) .await?; if let Some(len) = res diff --git a/idevice/src/services/core_device/mod.rs b/idevice/src/services/core_device/mod.rs index e6ef7f7..1f405ce 100644 --- a/idevice/src/services/core_device/mod.rs +++ b/idevice/src/services/core_device/mod.rs @@ -10,8 +10,10 @@ use crate::{ mod app_service; mod diagnosticsservice; +mod openstdiosocket; pub use app_service::*; pub use diagnosticsservice::*; +pub use openstdiosocket::*; const CORE_SERVICE_VERSION: &str = "443.18"; @@ -26,13 +28,26 @@ impl CoreDeviceServiceClient { Ok(Self { inner: client }) } + pub async fn invoke_with_plist( + &mut self, + feature: impl Into, + input: plist::Dictionary, + ) -> Result { + let input: XPCObject = plist::Value::Dictionary(input).into(); + let input = input.to_dictionary().unwrap(); + self.invoke(feature, Some(input)).await + } + pub async fn invoke( &mut self, feature: impl Into, - input: Option, + input: Option, ) -> Result { let feature = feature.into(); - let input = input.unwrap_or_default(); + let input: crate::xpc::XPCObject = match input { + Some(i) => i.into(), + None => crate::xpc::Dictionary::new().into(), + }; let mut req = xpc::Dictionary::new(); req.insert( @@ -52,10 +67,7 @@ impl CoreDeviceServiceClient { "CoreDevice.featureIdentifier".into(), XPCObject::String(feature), ); - req.insert( - "CoreDevice.input".into(), - plist::Value::Dictionary(input).into(), - ); + req.insert("CoreDevice.input".into(), input); req.insert( "CoreDevice.invocationIdentifier".into(), XPCObject::String(uuid::Uuid::new_v4().to_string()), diff --git a/idevice/src/services/core_device/openstdiosocket.rs b/idevice/src/services/core_device/openstdiosocket.rs new file mode 100644 index 0000000..e5630ce --- /dev/null +++ b/idevice/src/services/core_device/openstdiosocket.rs @@ -0,0 +1,33 @@ +// Jackson Coxson + +use tokio::io::AsyncReadExt; + +use crate::{IdeviceError, ReadWrite, RsdService, obf}; + +impl RsdService for OpenStdioSocketClient { + fn rsd_service_name() -> std::borrow::Cow<'static, str> { + obf!("com.apple.coredevice.openstdiosocket") + } + + async fn from_stream(stream: Box) -> Result { + Ok(Self { inner: stream }) + } +} + +/// Call ``read_uuid`` to get the UUID. Pass that to app service launch to connect to the stream of +/// the launched app. Inner is exposed to read and write to, using Tokio's AsyncReadExt/AsyncWriteExt +pub struct OpenStdioSocketClient { + pub inner: Box, +} + +impl OpenStdioSocketClient { + /// iOS assigns a UUID to a newly opened stream. That UUID is then passed to the launch + /// parameters of app service to start a stream. + pub async fn read_uuid(&mut self) -> Result { + let mut buf = [0u8; 16]; + self.inner.read_exact(&mut buf).await?; + + let res = uuid::Uuid::from_bytes(buf); + Ok(res) + } +}