mirror of
https://github.com/jkcoxson/idevice.git
synced 2026-03-02 14:36:16 +01:00
Use correct packets for mounting
This commit is contained in:
@@ -32,12 +32,14 @@ byteorder = { version = "1.5", optional = true }
|
|||||||
|
|
||||||
reqwest = { version = "0.12", features = ["json"], optional = true }
|
reqwest = { version = "0.12", features = ["json"], optional = true }
|
||||||
|
|
||||||
|
sha2 = { version = "0.10", optional = true }
|
||||||
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
core_device_proxy = ["dep:serde_json", "dep:json", "dep:byteorder"]
|
core_device_proxy = ["dep:serde_json", "dep:json", "dep:byteorder"]
|
||||||
heartbeat = []
|
heartbeat = []
|
||||||
installation_proxy = []
|
installation_proxy = []
|
||||||
mounter = []
|
mounter = ["dep:sha2"]
|
||||||
usbmuxd = []
|
usbmuxd = []
|
||||||
tcp = ["tokio/net"]
|
tcp = ["tokio/net"]
|
||||||
tss = ["dep:uuid", "dep:reqwest"]
|
tss = ["dep:uuid", "dep:reqwest"]
|
||||||
|
|||||||
@@ -86,7 +86,24 @@ impl Idevice {
|
|||||||
/// Sends raw bytes to the socket
|
/// Sends raw bytes to the socket
|
||||||
async fn send_raw(&mut self, message: &[u8]) -> Result<(), IdeviceError> {
|
async fn send_raw(&mut self, message: &[u8]) -> Result<(), IdeviceError> {
|
||||||
if let Some(socket) = &mut self.socket {
|
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 {
|
} else {
|
||||||
Err(IdeviceError::NoEstablishedConnection)
|
Err(IdeviceError::NoEstablishedConnection)
|
||||||
}
|
}
|
||||||
@@ -210,6 +227,9 @@ pub enum IdeviceError {
|
|||||||
#[error("device not found")]
|
#[error("device not found")]
|
||||||
DeviceNotFound,
|
DeviceNotFound,
|
||||||
|
|
||||||
|
#[error("device lockded")]
|
||||||
|
DeviceLocked,
|
||||||
|
|
||||||
#[error("device refused connection")]
|
#[error("device refused connection")]
|
||||||
UsbConnectionRefused,
|
UsbConnectionRefused,
|
||||||
#[error("bad command")]
|
#[error("bad command")]
|
||||||
@@ -236,6 +256,7 @@ impl IdeviceError {
|
|||||||
"GetProhibited" => Some(Self::GetProhibited),
|
"GetProhibited" => Some(Self::GetProhibited),
|
||||||
"InvalidHostID" => Some(Self::InvalidHostID),
|
"InvalidHostID" => Some(Self::InvalidHostID),
|
||||||
"SessionInactive" => Some(Self::SessionInactive),
|
"SessionInactive" => Some(Self::SessionInactive),
|
||||||
|
"DeviceLocked" => Some(Self::DeviceLocked),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
// Jackson Coxson
|
// Jackson Coxson
|
||||||
|
|
||||||
use crate::{
|
use log::debug;
|
||||||
lockdownd::LockdowndClient, tss::TSSRequest, util::hashmap_to_dictionary, Idevice,
|
use openssl::sha::Sha384;
|
||||||
IdeviceError, IdeviceService,
|
|
||||||
};
|
use crate::{lockdownd::LockdowndClient, tss::TSSRequest, Idevice, IdeviceError, IdeviceService};
|
||||||
|
|
||||||
pub struct ImageMounter {
|
pub struct ImageMounter {
|
||||||
idevice: Idevice,
|
idevice: Idevice,
|
||||||
@@ -81,11 +81,18 @@ impl ImageMounter {
|
|||||||
signature: Vec<u8>,
|
signature: Vec<u8>,
|
||||||
) -> Result<(), IdeviceError> {
|
) -> Result<(), IdeviceError> {
|
||||||
let image_type = image_type.into();
|
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();
|
let mut req = plist::Dictionary::new();
|
||||||
req.insert("Command".into(), "ReceiveBytes".into());
|
req.insert("Command".into(), "ReceiveBytes".into());
|
||||||
req.insert("ImageType".into(), image_type.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));
|
req.insert("ImageSignature".into(), plist::Value::Data(signature));
|
||||||
self.idevice
|
self.idevice
|
||||||
.send_plist(plist::Value::Dictionary(req))
|
.send_plist(plist::Value::Dictionary(req))
|
||||||
@@ -102,7 +109,9 @@ impl ImageMounter {
|
|||||||
_ => return Err(IdeviceError::UnexpectedResponse),
|
_ => return Err(IdeviceError::UnexpectedResponse),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
debug!("Sending image bytes");
|
||||||
self.idevice.send_raw(image).await?;
|
self.idevice.send_raw(image).await?;
|
||||||
|
debug!("Image bytes written");
|
||||||
|
|
||||||
let res = self.idevice.read_plist().await?;
|
let res = self.idevice.read_plist().await?;
|
||||||
match res.get("Status") {
|
match res.get("Status") {
|
||||||
@@ -278,7 +287,7 @@ impl ImageMounter {
|
|||||||
image: &[u8],
|
image: &[u8],
|
||||||
signature: Vec<u8>,
|
signature: Vec<u8>,
|
||||||
) -> Result<(), IdeviceError> {
|
) -> Result<(), IdeviceError> {
|
||||||
self.upload_image("Developer", &image, signature.clone())
|
self.upload_image("Developer", image, signature.clone())
|
||||||
.await?;
|
.await?;
|
||||||
self.mount_image(
|
self.mount_image(
|
||||||
"Developer",
|
"Developer",
|
||||||
@@ -291,8 +300,11 @@ impl ImageMounter {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Calling this has the potential of closing the socket,
|
||||||
|
/// so a provider is required for this abstraction.
|
||||||
pub async fn mount_personalized(
|
pub async fn mount_personalized(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
provider: &dyn crate::provider::IdeviceProvider,
|
||||||
image: Vec<u8>,
|
image: Vec<u8>,
|
||||||
trust_cache: Vec<u8>,
|
trust_cache: Vec<u8>,
|
||||||
build_manifest: &[u8],
|
build_manifest: &[u8],
|
||||||
@@ -300,20 +312,28 @@ impl ImageMounter {
|
|||||||
unique_chip_id: u64,
|
unique_chip_id: u64,
|
||||||
) -> Result<(), IdeviceError> {
|
) -> Result<(), IdeviceError> {
|
||||||
// Try to fetch personalization manifest
|
// Try to fetch personalization manifest
|
||||||
|
let mut hasher = Sha384::new();
|
||||||
|
hasher.update(&image);
|
||||||
|
let image_hash = hasher.finish();
|
||||||
let manifest = match self
|
let manifest = match self
|
||||||
.query_personalization_manifest("DeveloperDiskImage", image.clone()) // TODO:
|
.query_personalization_manifest("DeveloperDiskImage", image_hash.to_vec())
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(manifest) => manifest,
|
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
|
// Get manifest from TSS
|
||||||
let manifest_dict: plist::Dictionary = plist::from_bytes(build_manifest)?;
|
let manifest_dict: plist::Dictionary = plist::from_bytes(build_manifest)?;
|
||||||
self.get_manifest_from_tss(&manifest_dict, unique_chip_id)
|
self.get_manifest_from_tss(&manifest_dict, unique_chip_id)
|
||||||
.await?
|
.await?
|
||||||
}
|
}
|
||||||
Err(e) => return Err(e),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
debug!("Uploading imaage");
|
||||||
self.upload_image("Personalized", &image, manifest.clone())
|
self.upload_image("Personalized", &image, manifest.clone())
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
@@ -326,6 +346,7 @@ impl ImageMounter {
|
|||||||
plist::Value::Data(trust_cache.clone()),
|
plist::Value::Data(trust_cache.clone()),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
debug!("Mounting image");
|
||||||
self.mount_image(
|
self.mount_image(
|
||||||
"Personalized",
|
"Personalized",
|
||||||
manifest,
|
manifest,
|
||||||
|
|||||||
@@ -49,12 +49,15 @@ impl TSSRequest {
|
|||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
debug!("Apple responeded with {res}");
|
debug!("Apple responeded with {res}");
|
||||||
|
let res = res.trim_start_matches("STATUS=0&");
|
||||||
let res = res.trim_start_matches("MESSAGE=");
|
let res = res.trim_start_matches("MESSAGE=");
|
||||||
if !res.starts_with("SUCCESS") {
|
if !res.starts_with("SUCCESS") {
|
||||||
|
warn!("TSS responded with non-success value");
|
||||||
return Err(IdeviceError::UnexpectedResponse);
|
return Err(IdeviceError::UnexpectedResponse);
|
||||||
}
|
}
|
||||||
let res = res.split("REQUEST_STRING=").collect::<Vec<&str>>();
|
let res = res.split("REQUEST_STRING=").collect::<Vec<&str>>();
|
||||||
if res.len() < 2 {
|
if res.len() < 2 {
|
||||||
|
warn!("Response didn't contain a request string");
|
||||||
return Err(IdeviceError::UnexpectedResponse);
|
return Err(IdeviceError::UnexpectedResponse);
|
||||||
}
|
}
|
||||||
Ok(plist::from_bytes(res[1].as_bytes())?)
|
Ok(plist::from_bytes(res[1].as_bytes())?)
|
||||||
|
|||||||
Reference in New Issue
Block a user