Create new RSD service trait

This commit is contained in:
Jackson Coxson
2025-05-25 13:11:59 -06:00
parent 2a8a921951
commit a39cbd1d3d
9 changed files with 166 additions and 71 deletions

View File

@@ -25,7 +25,7 @@ pub use services::*;
pub use xpc::RemoteXpcClient; pub use xpc::RemoteXpcClient;
use log::{debug, error, trace}; use log::{debug, error, trace};
use provider::IdeviceProvider; use provider::{IdeviceProvider, RsdProvider};
use rustls::{crypto::CryptoProvider, pki_types::ServerName}; use rustls::{crypto::CryptoProvider, pki_types::ServerName};
use std::{ use std::{
io::{self, BufWriter}, io::{self, BufWriter},
@@ -65,6 +65,25 @@ pub trait IdeviceService: Sized {
) -> impl std::future::Future<Output = Result<Self, IdeviceError>> + Send; ) -> impl std::future::Future<Output = Result<Self, IdeviceError>> + Send;
} }
pub trait RsdService: Sized {
fn rsd_service_name() -> &'static str;
fn from_stream(
stream: Self::Stream,
) -> impl std::future::Future<Output = Result<Self, IdeviceError>> + Send;
fn connect_rsd<'a, S>(
provider: &'a mut impl RsdProvider<'a, Stream = S>,
handshake: &mut rsd::RsdHandshake,
) -> impl std::future::Future<Output = Result<Self, IdeviceError>>
where
Self: crate::RsdService<Stream = S>,
S: ReadWrite,
{
handshake.connect(provider)
}
type Stream: ReadWrite;
}
/// Type alias for boxed device connection sockets /// Type alias for boxed device connection sockets
/// ///
/// Used to enable dynamic dispatch of different connection types while maintaining /// Used to enable dynamic dispatch of different connection types while maintaining
@@ -424,6 +443,8 @@ pub enum IdeviceError {
HeartbeatTimeout, HeartbeatTimeout,
#[error("not found")] #[error("not found")]
NotFound, NotFound,
#[error("service not found")]
ServiceNotFound,
#[error("CDTunnel packet too short")] #[error("CDTunnel packet too short")]
CdtunnelPacketTooShort, CdtunnelPacketTooShort,
#[error("CDTunnel packet invalid magic")] #[error("CDTunnel packet invalid magic")]

View File

@@ -8,7 +8,7 @@ use std::{future::Future, pin::Pin};
#[cfg(feature = "tcp")] #[cfg(feature = "tcp")]
use tokio::net::TcpStream; use tokio::net::TcpStream;
use crate::{pairing_file::PairingFile, Idevice, IdeviceError}; use crate::{pairing_file::PairingFile, Idevice, IdeviceError, ReadWrite};
#[cfg(feature = "usbmuxd")] #[cfg(feature = "usbmuxd")]
use crate::usbmuxd::UsbmuxdAddr; use crate::usbmuxd::UsbmuxdAddr;
@@ -42,6 +42,14 @@ pub trait IdeviceProvider: Unpin + Send + Sync + std::fmt::Debug {
) -> Pin<Box<dyn Future<Output = Result<PairingFile, IdeviceError>> + Send>>; ) -> Pin<Box<dyn Future<Output = Result<PairingFile, IdeviceError>> + Send>>;
} }
pub trait RsdProvider<'a>: Unpin + Send + Sync + std::fmt::Debug {
fn connect_to_service_port(
&'a mut self,
port: u16,
) -> impl std::future::Future<Output = Result<Self::Stream, IdeviceError>> + Send;
type Stream: ReadWrite;
}
/// TCP-based device connection provider /// TCP-based device connection provider
#[cfg(feature = "tcp")] #[cfg(feature = "tcp")]
#[derive(Debug)] #[derive(Debug)]

View File

@@ -8,10 +8,22 @@ use log::debug;
use std::fmt::Write; use std::fmt::Write;
use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::io::{AsyncReadExt, AsyncWriteExt};
use crate::{IdeviceError, ReadWrite}; use crate::{IdeviceError, ReadWrite, RsdService};
/// The service name for the debug proxy as registered with lockdownd impl<R: ReadWrite> RsdService for DebugProxyClient<R> {
pub const SERVICE_NAME: &str = "com.apple.internal.dt.remote.debugproxy"; fn rsd_service_name() -> &'static str {
"com.apple.internal.dt.remote.debugproxy"
}
async fn from_stream(stream: R) -> Result<Self, IdeviceError> {
Ok(Self {
socket: stream,
noack_mode: false,
})
}
type Stream = R;
}
/// Client for interacting with the iOS debug proxy service /// Client for interacting with the iOS debug proxy service
/// ///
@@ -300,4 +312,3 @@ impl From<&str> for DebugserverCommand {
s.to_string().into() s.to_string().into()
} }
} }

View File

@@ -1,9 +1,21 @@
// Jackson Coxson // Jackson Coxson
use crate::{IdeviceError, ReadWrite, RsdService};
#[cfg(feature = "location_simulation")] #[cfg(feature = "location_simulation")]
pub mod location_simulation; pub mod location_simulation;
pub mod message; pub mod message;
pub mod process_control; pub mod process_control;
pub mod remote_server; pub mod remote_server;
pub const SERVICE_NAME: &str = "com.apple.instruments.dtservicehub"; impl<R: ReadWrite> RsdService for remote_server::RemoteServerClient<R> {
fn rsd_service_name() -> &'static str {
"com.apple.instruments.dtservicehub"
}
async fn from_stream(stream: R) -> Result<Self, IdeviceError> {
Ok(Self::new(stream))
}
type Stream = R;
}

View File

@@ -6,7 +6,7 @@ use std::collections::HashMap;
use log::warn; use log::warn;
use serde::Deserialize; use serde::Deserialize;
use crate::{IdeviceError, ReadWrite, RemoteXpcClient}; use crate::{provider::RsdProvider, IdeviceError, ReadWrite, RemoteXpcClient};
/// Describes an available XPC service /// Describes an available XPC service
#[derive(Debug, Clone, Deserialize)] #[derive(Debug, Clone, Deserialize)]
@@ -23,21 +23,19 @@ pub struct RsdService {
pub service_version: Option<i64>, pub service_version: Option<i64>,
} }
pub struct RsdClient<R: ReadWrite> { pub struct RsdHandshake {
inner: RemoteXpcClient<R>, pub services: HashMap<String, RsdService>,
pub protocol_version: usize,
pub properties: HashMap<String, plist::Value>,
pub uuid: String,
} }
impl<R: ReadWrite> RsdClient<R> { impl RsdHandshake {
pub async fn new(socket: R) -> Result<Self, IdeviceError> { pub async fn new(socket: impl ReadWrite) -> Result<Self, IdeviceError> {
Ok(Self { let mut xpc_client = RemoteXpcClient::new(socket).await?;
inner: RemoteXpcClient::new(socket).await?, let data = xpc_client.do_handshake().await?;
})
}
pub async fn get_services(&mut self) -> Result<HashMap<String, RsdService>, IdeviceError> { let services_dict = match data
let data = self.inner.do_handshake().await?;
let data = match data
.as_dictionary() .as_dictionary()
.and_then(|x| x.get("Services")) .and_then(|x| x.get("Services"))
.and_then(|x| x.as_dictionary()) .and_then(|x| x.as_dictionary())
@@ -47,8 +45,8 @@ impl<R: ReadWrite> RsdClient<R> {
}; };
// Parse available services // Parse available services
let mut services = HashMap::new(); let mut services: HashMap<String, RsdService> = HashMap::new();
for (name, service) in data.into_iter() { for (name, service) in services_dict.into_iter() {
match service.as_dictionary() { match service.as_dictionary() {
Some(service) => { Some(service) => {
let entitlement = match service.get("Entitlement").and_then(|x| x.as_string()) { let entitlement = match service.get("Entitlement").and_then(|x| x.as_string()) {
@@ -116,6 +114,64 @@ impl<R: ReadWrite> RsdClient<R> {
} }
} }
Ok(services) let protocol_version = match data.as_dictionary().and_then(|x| {
x.get("MessagingProtocolVersion")
.and_then(|x| x.as_signed_integer())
}) {
Some(p) => p as usize,
None => {
return Err(IdeviceError::UnexpectedResponse);
}
};
let uuid = match data
.as_dictionary()
.and_then(|x| x.get("UUID").and_then(|x| x.as_string()))
{
Some(u) => u.to_string(),
None => {
return Err(IdeviceError::UnexpectedResponse);
}
};
let properties = match data
.as_dictionary()
.and_then(|x| x.get("Properties").and_then(|x| x.as_dictionary()))
{
Some(d) => d
.into_iter()
.map(|(name, prop)| (name.to_owned(), prop.to_owned()))
.collect::<HashMap<String, plist::Value>>(),
None => {
return Err(IdeviceError::UnexpectedResponse);
}
};
Ok(Self {
services,
protocol_version,
properties,
uuid,
})
}
pub async fn connect<'a, T, S>(
&mut self,
provider: &'a mut impl RsdProvider<'a, Stream = S>,
) -> Result<T, IdeviceError>
where
T: crate::RsdService<Stream = S>,
S: ReadWrite,
{
let service_name = T::rsd_service_name();
let service = match self.services.get(service_name) {
Some(s) => s,
None => {
return Err(IdeviceError::ServiceNotFound);
}
};
let stream = provider.connect_to_service_port(service.port).await?;
T::from_stream(stream).await
} }
} }

View File

@@ -6,8 +6,11 @@ use std::{
}; };
use log::debug; use log::debug;
use stream::AdapterStream;
use tokio::io::AsyncWriteExt; use tokio::io::AsyncWriteExt;
use crate::provider::RsdProvider;
pub mod adapter; pub mod adapter;
pub mod packets; pub mod packets;
pub mod stream; pub mod stream;
@@ -36,6 +39,18 @@ pub(crate) fn log_packet(file: &Arc<tokio::sync::Mutex<tokio::fs::File>>, packet
}); });
} }
impl<'a> RsdProvider<'a> for adapter::Adapter {
async fn connect_to_service_port(
&'a mut self,
port: u16,
) -> Result<stream::AdapterStream<'a>, crate::IdeviceError> {
let s = stream::AdapterStream::connect(self, port).await?;
Ok(s)
}
type Stream = AdapterStream<'a>;
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::{ use std::{

View File

@@ -4,8 +4,8 @@ use std::io::Write;
use clap::{Arg, Command}; use clap::{Arg, Command};
use idevice::{ use idevice::{
core_device_proxy::CoreDeviceProxy, debug_proxy::DebugProxyClient, rsd::RsdClient, core_device_proxy::CoreDeviceProxy, debug_proxy::DebugProxyClient, rsd::RsdHandshake,
tcp::stream::AdapterStream, IdeviceService, tcp::stream::AdapterStream, IdeviceService, RsdService,
}; };
mod common; mod common;
@@ -77,22 +77,12 @@ async fn main() {
.expect("no RSD connect"); .expect("no RSD connect");
// Make the connection to RemoteXPC // Make the connection to RemoteXPC
let mut client = RsdClient::new(stream).await.unwrap(); let mut handshake = RsdHandshake::new(stream).await.unwrap();
println!("{:?}", handshake.services);
// Get the debug proxy let mut dp = DebugProxyClient::connect_rsd(&mut adapter, &mut handshake)
let service = client
.get_services()
.await .await
.unwrap() .expect("no connect");
.get(idevice::debug_proxy::SERVICE_NAME)
.expect("Client did not contain debug proxy service")
.to_owned();
let stream = AdapterStream::connect(&mut adapter, service.port)
.await
.unwrap();
let mut dp = DebugProxyClient::new(stream);
println!("Shell connected!"); println!("Shell connected!");
loop { loop {

View File

@@ -3,7 +3,8 @@
use clap::{Arg, Command}; use clap::{Arg, Command};
use idevice::{ use idevice::{
core_device_proxy::CoreDeviceProxy, rsd::RsdClient, tcp::stream::AdapterStream, IdeviceService, core_device_proxy::CoreDeviceProxy, rsd::RsdHandshake, tcp::stream::AdapterStream,
IdeviceService, RsdService,
}; };
mod common; mod common;
@@ -76,22 +77,12 @@ async fn main() {
.expect("no RSD connect"); .expect("no RSD connect");
// Make the connection to RemoteXPC // Make the connection to RemoteXPC
let mut client = RsdClient::new(stream).await.unwrap(); let mut handshake = RsdHandshake::new(stream).await.unwrap();
// Get the debug proxy let mut ls_client =
let service = client idevice::dvt::remote_server::RemoteServerClient::connect_rsd(&mut adapter, &mut handshake)
.get_services()
.await .await
.unwrap() .expect("Failed to connect");
.get(idevice::dvt::SERVICE_NAME)
.expect("Client did not contain DVT service")
.to_owned();
let stream = AdapterStream::connect(&mut adapter, service.port)
.await
.unwrap();
let mut ls_client = idevice::dvt::remote_server::RemoteServerClient::new(stream);
ls_client.read_message(0).await.expect("no read??"); ls_client.read_message(0).await.expect("no read??");
let mut ls_client = let mut ls_client =

View File

@@ -2,7 +2,8 @@
use clap::{Arg, Command}; use clap::{Arg, Command};
use idevice::{ use idevice::{
core_device_proxy::CoreDeviceProxy, rsd::RsdClient, tcp::stream::AdapterStream, IdeviceService, core_device_proxy::CoreDeviceProxy, rsd::RsdHandshake, tcp::stream::AdapterStream,
IdeviceService, RsdService,
}; };
mod common; mod common;
@@ -84,22 +85,12 @@ async fn main() {
.expect("no RSD connect"); .expect("no RSD connect");
// Make the connection to RemoteXPC // Make the connection to RemoteXPC
let mut client = RsdClient::new(stream).await.unwrap(); let mut handshake = RsdHandshake::new(stream).await.unwrap();
// Get the debug proxy let mut rs_client =
let service = client idevice::dvt::remote_server::RemoteServerClient::connect_rsd(&mut adapter, &mut handshake)
.get_services()
.await .await
.unwrap() .expect("no connect");
.get(idevice::dvt::SERVICE_NAME)
.expect("Client did not contain DVT service")
.to_owned();
let stream = AdapterStream::connect(&mut adapter, service.port)
.await
.unwrap();
let mut rs_client = idevice::dvt::remote_server::RemoteServerClient::new(stream);
rs_client.read_message(0).await.expect("no read??"); rs_client.read_message(0).await.expect("no read??");
let mut pc_client = idevice::dvt::process_control::ProcessControlClient::new(&mut rs_client) let mut pc_client = idevice::dvt::process_control::ProcessControlClient::new(&mut rs_client)
.await .await