Skip to content

Commit d463a53

Browse files
authored
feat: support launch app at ios17- (#23)
* feat: support launch app at ios17- * cargo fmt * clippy
1 parent c2ef847 commit d463a53

2 files changed

Lines changed: 119 additions & 13 deletions

File tree

idevice/src/services/dvt/mod.rs

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Jackson Coxson
22

3-
use crate::{IdeviceError, ReadWrite, RsdService, obf};
3+
use crate::provider::IdeviceProvider;
4+
use crate::services::lockdown::LockdownClient;
5+
use crate::{Idevice, IdeviceError, ReadWrite, RsdService, obf};
46

57
#[cfg(feature = "location_simulation")]
68
pub mod location_simulation;
@@ -17,3 +19,65 @@ impl RsdService for remote_server::RemoteServerClient<Box<dyn ReadWrite>> {
1719
Ok(Self::new(stream))
1820
}
1921
}
22+
23+
// iOS version support notes:
24+
// - com.apple.instruments.dtservicehub (RSD/XPC over HTTP2) is used on iOS 17+.
25+
// - com.apple.instruments.remoteserver is available on pre-iOS 17 (and many older versions).
26+
// - com.apple.instruments.remoteserver.DVTSecureSocketProxy is used by some iOS 14 builds.
27+
//
28+
// This impl enables Lockdown-based connection to Instruments Remote Server for iOS < 17
29+
// by reusing the same RemoteServerClient but sourcing the transport from StartService.
30+
impl crate::IdeviceService for remote_server::RemoteServerClient<Box<dyn ReadWrite>> {
31+
fn service_name() -> std::borrow::Cow<'static, str> {
32+
// Primary name for Instruments Remote Server
33+
obf!("com.apple.instruments.remoteserver")
34+
}
35+
36+
#[allow(async_fn_in_trait)]
37+
async fn connect(provider: &dyn IdeviceProvider) -> Result<Self, IdeviceError> {
38+
// Establish Lockdown session
39+
let mut lockdown = LockdownClient::connect(provider).await?;
40+
lockdown
41+
.start_session(&provider.get_pairing_file().await?)
42+
.await?;
43+
44+
// Try main Instruments service first, then DVTSecureSocketProxy (seen on iOS 14)
45+
let try_names = [
46+
obf!("com.apple.instruments.remoteserver"),
47+
obf!("com.apple.instruments.remoteserver.DVTSecureSocketProxy"),
48+
];
49+
50+
let mut last_err: Option<IdeviceError> = None;
51+
for name in try_names {
52+
match lockdown.start_service(name).await {
53+
Ok((port, ssl)) => {
54+
let mut idevice = provider.connect(port).await?;
55+
if ssl {
56+
idevice
57+
.start_session(&provider.get_pairing_file().await?)
58+
.await?;
59+
}
60+
// Convert to transport and build client
61+
let socket = idevice
62+
.get_socket()
63+
.ok_or(IdeviceError::NoEstablishedConnection)?;
64+
return Ok(remote_server::RemoteServerClient::new(socket));
65+
}
66+
Err(e) => {
67+
last_err = Some(e);
68+
}
69+
}
70+
}
71+
72+
Err(last_err.unwrap_or(IdeviceError::ServiceNotFound))
73+
}
74+
75+
#[allow(async_fn_in_trait)]
76+
async fn from_stream(idevice: Idevice) -> Result<Self, IdeviceError> {
77+
// Not used in our overridden connect path, but implemented for completeness
78+
let socket = idevice
79+
.get_socket()
80+
.ok_or(IdeviceError::NoEstablishedConnection)?;
81+
Ok(remote_server::RemoteServerClient::new(socket))
82+
}
83+
}

tools/src/process_control.rs

Lines changed: 54 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Jackson Coxson
22

33
use clap::{Arg, Command};
4+
use idevice::services::lockdown::LockdownClient;
45
use idevice::{IdeviceService, RsdService, core_device_proxy::CoreDeviceProxy, rsd::RsdHandshake};
56

67
mod common;
@@ -71,29 +72,70 @@ async fn main() {
7172
}
7273
};
7374

74-
let proxy = CoreDeviceProxy::connect(&*provider)
75+
let mut rs_client_opt: Option<
76+
idevice::dvt::remote_server::RemoteServerClient<Box<dyn idevice::ReadWrite>>,
77+
> = None;
78+
79+
if let Ok(proxy) = CoreDeviceProxy::connect(&*provider).await {
80+
let rsd_port = proxy.handshake.server_rsd_port;
81+
let adapter = proxy.create_software_tunnel().expect("no software tunnel");
82+
let mut adapter = adapter.to_async_handle();
83+
let stream = adapter.connect(rsd_port).await.expect("no RSD connect");
84+
85+
// Make the connection to RemoteXPC (iOS 17+)
86+
let mut handshake = RsdHandshake::new(stream).await.unwrap();
87+
let mut rs_client = idevice::dvt::remote_server::RemoteServerClient::connect_rsd(
88+
&mut adapter,
89+
&mut handshake,
90+
)
7591
.await
76-
.expect("no core proxy");
77-
let rsd_port = proxy.handshake.server_rsd_port;
92+
.expect("no connect");
93+
rs_client.read_message(0).await.expect("no read??");
94+
rs_client_opt = Some(rs_client);
95+
}
7896

79-
let adapter = proxy.create_software_tunnel().expect("no software tunnel");
80-
let mut adapter = adapter.to_async_handle();
81-
let stream = adapter.connect(rsd_port).await.expect("no RSD connect");
97+
let mut rs_client = if let Some(c) = rs_client_opt {
98+
c
99+
} else {
100+
// Read iOS version to decide whether we can fallback to remoteserver
101+
let mut lockdown = LockdownClient::connect(&*provider)
102+
.await
103+
.expect("lockdown connect failed");
104+
lockdown
105+
.start_session(&provider.get_pairing_file().await.expect("pairing file"))
106+
.await
107+
.expect("lockdown start_session failed");
108+
let pv = lockdown
109+
.get_value(Some("ProductVersion"), None)
110+
.await
111+
.ok()
112+
.and_then(|v| v.as_string().map(|s| s.to_string()))
113+
.unwrap_or_default();
114+
let major: u32 = pv
115+
.split('.')
116+
.next()
117+
.and_then(|s| s.parse().ok())
118+
.unwrap_or(0);
82119

83-
// Make the connection to RemoteXPC
84-
let mut handshake = RsdHandshake::new(stream).await.unwrap();
120+
if major >= 17 {
121+
// iOS 17+ with no CoreDeviceProxy: do not attempt remoteserver (would return InvalidService)
122+
panic!("iOS {pv} detected and CoreDeviceProxy unavailable. RemoteXPC tunnel required.");
123+
}
85124

86-
let mut rs_client =
87-
idevice::dvt::remote_server::RemoteServerClient::connect_rsd(&mut adapter, &mut handshake)
125+
// iOS 16 and earlier: fallback to Lockdown remoteserver (or DVTSecureSocketProxy)
126+
idevice::dvt::remote_server::RemoteServerClient::connect(&*provider)
88127
.await
89-
.expect("no connect");
128+
.expect("failed to connect to Instruments Remote Server over Lockdown (iOS16-). Ensure Developer Disk Image is mounted.")
129+
};
130+
131+
// Note: On both transports, protocol requires reading the initial message on root channel (0)
90132
rs_client.read_message(0).await.expect("no read??");
91133
let mut pc_client = idevice::dvt::process_control::ProcessControlClient::new(&mut rs_client)
92134
.await
93135
.unwrap();
94136

95137
let pid = pc_client
96-
.launch_app(bundle_id, None, None, true, false)
138+
.launch_app(bundle_id, None, None, false, false)
97139
.await
98140
.expect("no launch??");
99141
pc_client

0 commit comments

Comments
 (0)