Implement openstdiosocket

This commit is contained in:
Jackson Coxson
2025-08-24 17:52:38 -06:00
parent bb432d8807
commit 3d3a8eb55f
4 changed files with 106 additions and 42 deletions

View File

@@ -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<R: ReadWrite> AppServiceClient<R> {
});
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<R: ReadWrite> AppServiceClient<R> {
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<String>,
@@ -186,6 +196,7 @@ impl<R: ReadWrite> AppServiceClient<R> {
start_suspended: bool,
environment: Option<plist::Dictionary>,
platform_options: Option<plist::Dictionary>,
stdio_uuid: Option<uuid::Uuid>,
) -> Result<LaunchResponse, IdeviceError> {
let bundle_id = bundle_id.into();
@@ -196,20 +207,34 @@ impl<R: ReadWrite> AppServiceClient<R> {
}
},
"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<R: ReadWrite> AppServiceClient<R> {
) -> 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(),
),
)
.await?;
@@ -279,16 +302,14 @@ impl<R: ReadWrite> AppServiceClient<R> {
) -> Result<SignalResponse, IdeviceError> {
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(),
),
)
.await?;
@@ -314,9 +335,8 @@ impl<R: ReadWrite> AppServiceClient<R> {
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,
@@ -326,7 +346,6 @@ impl<R: ReadWrite> AppServiceClient<R> {
})
.into_dictionary()
.unwrap(),
),
)
.await?;

View File

@@ -45,7 +45,7 @@ impl<R: ReadWrite> DiagnostisServiceClient<R> {
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

View File

@@ -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<R: ReadWrite> CoreDeviceServiceClient<R> {
Ok(Self { inner: client })
}
pub async fn invoke_with_plist(
&mut self,
feature: impl Into<String>,
input: plist::Dictionary,
) -> Result<plist::Value, IdeviceError> {
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<String>,
input: Option<plist::Dictionary>,
input: Option<crate::xpc::Dictionary>,
) -> Result<plist::Value, IdeviceError> {
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<R: ReadWrite> CoreDeviceServiceClient<R> {
"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()),

View File

@@ -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<dyn ReadWrite>) -> Result<Self, IdeviceError> {
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<dyn ReadWrite>,
}
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<uuid::Uuid, IdeviceError> {
let mut buf = [0u8; 16];
self.inner.read_exact(&mut buf).await?;
let res = uuid::Uuid::from_bytes(buf);
Ok(res)
}
}