From 74e0f8e2264ff99fb4f978d795a4d5670b000db7 Mon Sep 17 00:00:00 2001 From: Jackson Coxson Date: Wed, 5 Feb 2025 11:40:34 -0700 Subject: [PATCH] Use correct packets for mounting --- idevice/Cargo.toml | 4 +++- idevice/src/lib.rs | 23 ++++++++++++++++++++++- idevice/src/mounter.rs | 39 ++++++++++++++++++++++++++++++--------- idevice/src/tss.rs | 3 +++ 4 files changed, 58 insertions(+), 11 deletions(-) diff --git a/idevice/Cargo.toml b/idevice/Cargo.toml index 7d93c10..cd85380 100644 --- a/idevice/Cargo.toml +++ b/idevice/Cargo.toml @@ -32,12 +32,14 @@ byteorder = { version = "1.5", optional = true } reqwest = { version = "0.12", features = ["json"], optional = true } +sha2 = { version = "0.10", optional = true } + [features] core_device_proxy = ["dep:serde_json", "dep:json", "dep:byteorder"] heartbeat = [] installation_proxy = [] -mounter = [] +mounter = ["dep:sha2"] usbmuxd = [] tcp = ["tokio/net"] tss = ["dep:uuid", "dep:reqwest"] diff --git a/idevice/src/lib.rs b/idevice/src/lib.rs index 6ba1da4..4dfadff 100644 --- a/idevice/src/lib.rs +++ b/idevice/src/lib.rs @@ -86,7 +86,24 @@ impl Idevice { /// Sends raw bytes to the socket async fn send_raw(&mut self, message: &[u8]) -> Result<(), IdeviceError> { if let Some(socket) = &mut self.socket { - Ok(socket.write_all(message).await?) + let mut trash = [0; 2048]; + let _ = socket.read(&mut trash).await.ok(); + + let message_parts = message.chunks(2048); + let part_len = message_parts.len(); + + let mut err = 5; + for (i, part) in message_parts.enumerate() { + debug!("Writing {i}/{part_len}"); + while let Err(e) = socket.write_all(part).await { + err -= 1; + if err == 0 { + return Err(e.into()); + } + } + err = 5; + } + Ok(()) } else { Err(IdeviceError::NoEstablishedConnection) } @@ -210,6 +227,9 @@ pub enum IdeviceError { #[error("device not found")] DeviceNotFound, + #[error("device lockded")] + DeviceLocked, + #[error("device refused connection")] UsbConnectionRefused, #[error("bad command")] @@ -236,6 +256,7 @@ impl IdeviceError { "GetProhibited" => Some(Self::GetProhibited), "InvalidHostID" => Some(Self::InvalidHostID), "SessionInactive" => Some(Self::SessionInactive), + "DeviceLocked" => Some(Self::DeviceLocked), _ => None, } } diff --git a/idevice/src/mounter.rs b/idevice/src/mounter.rs index 1bc30c7..bc31280 100644 --- a/idevice/src/mounter.rs +++ b/idevice/src/mounter.rs @@ -1,9 +1,9 @@ // Jackson Coxson -use crate::{ - lockdownd::LockdowndClient, tss::TSSRequest, util::hashmap_to_dictionary, Idevice, - IdeviceError, IdeviceService, -}; +use log::debug; +use openssl::sha::Sha384; + +use crate::{lockdownd::LockdowndClient, tss::TSSRequest, Idevice, IdeviceError, IdeviceService}; pub struct ImageMounter { idevice: Idevice, @@ -81,11 +81,18 @@ impl ImageMounter { signature: Vec, ) -> Result<(), IdeviceError> { let image_type = image_type.into(); + let image_size = match u64::try_from(image.len()) { + Ok(i) => i, + Err(e) => { + log::error!("Could not parse image size as u64: {e:?}"); + return Err(IdeviceError::UnexpectedResponse); + } + }; let mut req = plist::Dictionary::new(); req.insert("Command".into(), "ReceiveBytes".into()); req.insert("ImageType".into(), image_type.into()); - req.insert("ImageSize".into(), (image.len() as u64).into()); + req.insert("ImageSize".into(), image_size.into()); req.insert("ImageSignature".into(), plist::Value::Data(signature)); self.idevice .send_plist(plist::Value::Dictionary(req)) @@ -102,7 +109,9 @@ impl ImageMounter { _ => return Err(IdeviceError::UnexpectedResponse), } + debug!("Sending image bytes"); self.idevice.send_raw(image).await?; + debug!("Image bytes written"); let res = self.idevice.read_plist().await?; match res.get("Status") { @@ -278,7 +287,7 @@ impl ImageMounter { image: &[u8], signature: Vec, ) -> Result<(), IdeviceError> { - self.upload_image("Developer", &image, signature.clone()) + self.upload_image("Developer", image, signature.clone()) .await?; self.mount_image( "Developer", @@ -291,8 +300,11 @@ impl ImageMounter { Ok(()) } + /// Calling this has the potential of closing the socket, + /// so a provider is required for this abstraction. pub async fn mount_personalized( &mut self, + provider: &dyn crate::provider::IdeviceProvider, image: Vec, trust_cache: Vec, build_manifest: &[u8], @@ -300,20 +312,28 @@ impl ImageMounter { unique_chip_id: u64, ) -> Result<(), IdeviceError> { // Try to fetch personalization manifest + let mut hasher = Sha384::new(); + hasher.update(&image); + let image_hash = hasher.finish(); let manifest = match self - .query_personalization_manifest("DeveloperDiskImage", image.clone()) // TODO: + .query_personalization_manifest("DeveloperDiskImage", image_hash.to_vec()) .await { Ok(manifest) => manifest, - Err(IdeviceError::NotFound) => { + Err(e) => { + debug!("Device didn't contain a manifest: {e:?}, fetching from TSS"); + + // On failure, the socket closes. Open a new one. + self.idevice = Self::connect(provider).await?.idevice; + // Get manifest from TSS let manifest_dict: plist::Dictionary = plist::from_bytes(build_manifest)?; self.get_manifest_from_tss(&manifest_dict, unique_chip_id) .await? } - Err(e) => return Err(e), }; + debug!("Uploading imaage"); self.upload_image("Personalized", &image, manifest.clone()) .await?; @@ -326,6 +346,7 @@ impl ImageMounter { plist::Value::Data(trust_cache.clone()), ); + debug!("Mounting image"); self.mount_image( "Personalized", manifest, diff --git a/idevice/src/tss.rs b/idevice/src/tss.rs index e166b51..1de0ea9 100644 --- a/idevice/src/tss.rs +++ b/idevice/src/tss.rs @@ -49,12 +49,15 @@ impl TSSRequest { .await?; debug!("Apple responeded with {res}"); + let res = res.trim_start_matches("STATUS=0&"); let res = res.trim_start_matches("MESSAGE="); if !res.starts_with("SUCCESS") { + warn!("TSS responded with non-success value"); return Err(IdeviceError::UnexpectedResponse); } let res = res.split("REQUEST_STRING=").collect::>(); if res.len() < 2 { + warn!("Response didn't contain a request string"); return Err(IdeviceError::UnexpectedResponse); } Ok(plist::from_bytes(res[1].as_bytes())?)