mirror of
https://github.com/jkcoxson/idevice.git
synced 2026-03-02 14:36:16 +01:00
Implement openstdiosocket
This commit is contained in:
@@ -3,7 +3,7 @@
|
|||||||
use log::warn;
|
use log::warn;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
use crate::{IdeviceError, ReadWrite, RsdService, obf};
|
use crate::{IdeviceError, ReadWrite, RsdService, obf, xpc::XPCObject};
|
||||||
|
|
||||||
use super::CoreDeviceServiceClient;
|
use super::CoreDeviceServiceClient;
|
||||||
|
|
||||||
@@ -152,7 +152,7 @@ impl<R: ReadWrite> AppServiceClient<R> {
|
|||||||
});
|
});
|
||||||
let res = self
|
let res = self
|
||||||
.inner
|
.inner
|
||||||
.invoke("com.apple.coredevice.feature.listapps", Some(options))
|
.invoke_with_plist("com.apple.coredevice.feature.listapps", options)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let res = match res.as_array() {
|
let res = match res.as_array() {
|
||||||
@@ -178,6 +178,16 @@ impl<R: ReadWrite> AppServiceClient<R> {
|
|||||||
Ok(desd)
|
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(
|
pub async fn launch_application(
|
||||||
&mut self,
|
&mut self,
|
||||||
bundle_id: impl Into<String>,
|
bundle_id: impl Into<String>,
|
||||||
@@ -186,6 +196,7 @@ impl<R: ReadWrite> AppServiceClient<R> {
|
|||||||
start_suspended: bool,
|
start_suspended: bool,
|
||||||
environment: Option<plist::Dictionary>,
|
environment: Option<plist::Dictionary>,
|
||||||
platform_options: Option<plist::Dictionary>,
|
platform_options: Option<plist::Dictionary>,
|
||||||
|
stdio_uuid: Option<uuid::Uuid>,
|
||||||
) -> Result<LaunchResponse, IdeviceError> {
|
) -> Result<LaunchResponse, IdeviceError> {
|
||||||
let bundle_id = bundle_id.into();
|
let bundle_id = bundle_id.into();
|
||||||
|
|
||||||
@@ -196,20 +207,34 @@ impl<R: ReadWrite> AppServiceClient<R> {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"arguments": arguments, // Now this will work directly
|
"arguments": arguments,
|
||||||
"environmentVariables": environment.unwrap_or_default(),
|
"environmentVariables": environment.unwrap_or_default(),
|
||||||
"standardIOUsesPseudoterminals": true,
|
"standardIOUsesPseudoterminals": true,
|
||||||
"startStopped": start_suspended,
|
"startStopped": start_suspended,
|
||||||
"terminateExisting": kill_existing,
|
"terminateExisting": kill_existing,
|
||||||
"user": {
|
"user": {
|
||||||
"shortName": "mobile"
|
"active": true,
|
||||||
},
|
},
|
||||||
"platformSpecificOptions": plist::Value::Data(crate::util::plist_to_xml_bytes(&platform_options.unwrap_or_default())),
|
"platformSpecificOptions": plist::Value::Data(crate::util::plist_to_xml_bytes(&platform_options.unwrap_or_default())),
|
||||||
},
|
},
|
||||||
"standardIOIdentifiers": {}
|
});
|
||||||
})
|
|
||||||
.into_dictionary()
|
let req: XPCObject = req.into();
|
||||||
.unwrap();
|
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
|
let res = self
|
||||||
.inner
|
.inner
|
||||||
@@ -259,13 +284,11 @@ impl<R: ReadWrite> AppServiceClient<R> {
|
|||||||
) -> Result<(), IdeviceError> {
|
) -> Result<(), IdeviceError> {
|
||||||
let bundle_id = bundle_id.into();
|
let bundle_id = bundle_id.into();
|
||||||
self.inner
|
self.inner
|
||||||
.invoke(
|
.invoke_with_plist(
|
||||||
"com.apple.coredevice.feature.uninstallapp",
|
"com.apple.coredevice.feature.uninstallapp",
|
||||||
Some(
|
crate::plist!({"bundleIdentifier": bundle_id})
|
||||||
crate::plist!({"bundleIdentifier": bundle_id})
|
.into_dictionary()
|
||||||
.into_dictionary()
|
.unwrap(),
|
||||||
.unwrap(),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
@@ -279,16 +302,14 @@ impl<R: ReadWrite> AppServiceClient<R> {
|
|||||||
) -> Result<SignalResponse, IdeviceError> {
|
) -> Result<SignalResponse, IdeviceError> {
|
||||||
let res = self
|
let res = self
|
||||||
.inner
|
.inner
|
||||||
.invoke(
|
.invoke_with_plist(
|
||||||
"com.apple.coredevice.feature.sendsignaltoprocess",
|
"com.apple.coredevice.feature.sendsignaltoprocess",
|
||||||
Some(
|
crate::plist!({
|
||||||
crate::plist!({
|
"process": { "processIdentifier": pid as i64},
|
||||||
"process": { "processIdentifier": pid as i64},
|
"signal": signal as i64,
|
||||||
"signal": signal as i64,
|
})
|
||||||
})
|
.into_dictionary()
|
||||||
.into_dictionary()
|
.unwrap(),
|
||||||
.unwrap(),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
@@ -314,19 +335,17 @@ impl<R: ReadWrite> AppServiceClient<R> {
|
|||||||
let bundle_id = bundle_id.into();
|
let bundle_id = bundle_id.into();
|
||||||
let res = self
|
let res = self
|
||||||
.inner
|
.inner
|
||||||
.invoke(
|
.invoke_with_plist(
|
||||||
"com.apple.coredevice.feature.fetchappicons",
|
"com.apple.coredevice.feature.fetchappicons",
|
||||||
Some(
|
crate::plist!({
|
||||||
crate::plist!({
|
"width": width,
|
||||||
"width": width,
|
"height": height,
|
||||||
"height": height,
|
"scale": scale,
|
||||||
"scale": scale,
|
"allowPlaceholder": allow_placeholder,
|
||||||
"allowPlaceholder": allow_placeholder,
|
"bundleIdentifier": bundle_id
|
||||||
"bundleIdentifier": bundle_id
|
})
|
||||||
})
|
.into_dictionary()
|
||||||
.into_dictionary()
|
.unwrap(),
|
||||||
.unwrap(),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ impl<R: ReadWrite> DiagnostisServiceClient<R> {
|
|||||||
|
|
||||||
let res = self
|
let res = self
|
||||||
.inner
|
.inner
|
||||||
.invoke("com.apple.coredevice.feature.capturesysdiagnose", Some(req))
|
.invoke_with_plist("com.apple.coredevice.feature.capturesysdiagnose", req)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
if let Some(len) = res
|
if let Some(len) = res
|
||||||
|
|||||||
@@ -10,8 +10,10 @@ use crate::{
|
|||||||
|
|
||||||
mod app_service;
|
mod app_service;
|
||||||
mod diagnosticsservice;
|
mod diagnosticsservice;
|
||||||
|
mod openstdiosocket;
|
||||||
pub use app_service::*;
|
pub use app_service::*;
|
||||||
pub use diagnosticsservice::*;
|
pub use diagnosticsservice::*;
|
||||||
|
pub use openstdiosocket::*;
|
||||||
|
|
||||||
const CORE_SERVICE_VERSION: &str = "443.18";
|
const CORE_SERVICE_VERSION: &str = "443.18";
|
||||||
|
|
||||||
@@ -26,13 +28,26 @@ impl<R: ReadWrite> CoreDeviceServiceClient<R> {
|
|||||||
Ok(Self { inner: client })
|
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(
|
pub async fn invoke(
|
||||||
&mut self,
|
&mut self,
|
||||||
feature: impl Into<String>,
|
feature: impl Into<String>,
|
||||||
input: Option<plist::Dictionary>,
|
input: Option<crate::xpc::Dictionary>,
|
||||||
) -> Result<plist::Value, IdeviceError> {
|
) -> Result<plist::Value, IdeviceError> {
|
||||||
let feature = feature.into();
|
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();
|
let mut req = xpc::Dictionary::new();
|
||||||
req.insert(
|
req.insert(
|
||||||
@@ -52,10 +67,7 @@ impl<R: ReadWrite> CoreDeviceServiceClient<R> {
|
|||||||
"CoreDevice.featureIdentifier".into(),
|
"CoreDevice.featureIdentifier".into(),
|
||||||
XPCObject::String(feature),
|
XPCObject::String(feature),
|
||||||
);
|
);
|
||||||
req.insert(
|
req.insert("CoreDevice.input".into(), input);
|
||||||
"CoreDevice.input".into(),
|
|
||||||
plist::Value::Dictionary(input).into(),
|
|
||||||
);
|
|
||||||
req.insert(
|
req.insert(
|
||||||
"CoreDevice.invocationIdentifier".into(),
|
"CoreDevice.invocationIdentifier".into(),
|
||||||
XPCObject::String(uuid::Uuid::new_v4().to_string()),
|
XPCObject::String(uuid::Uuid::new_v4().to_string()),
|
||||||
|
|||||||
33
idevice/src/services/core_device/openstdiosocket.rs
Normal file
33
idevice/src/services/core_device/openstdiosocket.rs
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user