ios_core/services/amfi/
mod.rs1use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
6
7pub const SERVICE_NAME: &str = "com.apple.amfi.lockdown";
8
9#[derive(Debug, thiserror::Error)]
10pub enum AmfiError {
11 #[error("IO error: {0}")]
12 Io(#[from] std::io::Error),
13 #[error("plist error: {0}")]
14 Plist(String),
15 #[error("error: {0}")]
16 Device(String),
17}
18
19pub async fn enable_developer_mode<S>(stream: &mut S) -> Result<(), AmfiError>
23where
24 S: AsyncRead + AsyncWrite + Unpin,
25{
26 let req = plist::Value::Dictionary({
27 let mut d = plist::Dictionary::new();
28 d.insert("action".to_string(), plist::Value::Integer(1.into()));
29 d
30 });
31
32 let mut buf = Vec::new();
33 plist::to_writer_xml(&mut buf, &req).map_err(|e| AmfiError::Plist(e.to_string()))?;
34 stream.write_all(&(buf.len() as u32).to_be_bytes()).await?;
35 stream.write_all(&buf).await?;
36 stream.flush().await?;
37
38 let mut len_buf = [0u8; 4];
39 stream.read_exact(&mut len_buf).await?;
40 let len = u32::from_be_bytes(len_buf) as usize;
41 const MAX_PLIST_SIZE: usize = 4 * 1024 * 1024;
42 if len > MAX_PLIST_SIZE {
43 return Err(AmfiError::Io(std::io::Error::new(
44 std::io::ErrorKind::InvalidData,
45 format!("plist length {len} exceeds maximum of {MAX_PLIST_SIZE}"),
46 )));
47 }
48 let mut resp_buf = vec![0u8; len];
49 stream.read_exact(&mut resp_buf).await?;
50
51 let val: plist::Value =
52 plist::from_bytes(&resp_buf).map_err(|e| AmfiError::Plist(e.to_string()))?;
53
54 if let Some(dict) = val.as_dictionary() {
55 if let Some(err) = dict.get("Error").and_then(|v| v.as_string()) {
56 return Err(AmfiError::Device(err.to_string()));
57 }
58 }
59 Ok(())
60}