Skip to main content

ios_core/services/amfi/
mod.rs

1//! AMFI (Apple Mobile File Integrity) – developer mode control.
2//!
3//! Service: `com.apple.amfi.lockdown`
4
5use 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
19/// Enable developer mode on the device.
20///
21/// After calling this, the device needs to be rebooted.
22pub 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}