crabapple is a Rust library for reading, inspecting, and extracting data from encrypted iOS backups created by Finder, Apple Devices, or iTunes.
Inspired by imessage-exporter, crabapple provides a flexible foundation for any project that needs to access iOS backup data.
- Load and parse the backup's
Manifest.plistto obtain metadata, device info, and encryption parameters - Derive encryption keys using
PBKDF2(HMAC-SHA256thenHMAC-SHA1) and unwrap protection class keys via AES Key Wrap (RFC 3394) - Decrypt and query the
AES-256encryptedManifest.db, exposing backup file metadata viarusqlite - Retrieve and decrypt individual files by protection class (per-file
AES-CBCwithPKCS7padding) - Cross-platform support for macOS, Windows, and Linux
This library is available on crates.io.
Documentation is available on docs.rs.
use std::{io::copy, fs::File}; use crabapple::{Backup, Authentication}; fn main() -> Result<(), Box<dyn std::error::Error>> { // Initialize a backup session for a device UDID with a password let udid_folder = "/Users/you/Library/Application Support/MobileSync/Backup/DEVICE_UDID"; let auth = Authentication::Password("your_password".into()); let backup = Backup::open(udid_folder, &auth)?; // List all files in the backup let entries = backup.entries()?; for entry in &entries { println!("{} - {}/{}", entry.file_id, entry.domain, entry.relative_path); } // Decrypt and read a file entry as a stream if let Some(entry) = entries.first() { let mut stream = backup.decrypt_entry_stream(&entry)?; // Write the stream to a file let mut file = File::create("decrypted.txt")?; copy(&mut stream, &mut file)?; } // Alternatively, decrypt and read a file entry into memory if let Some(entry) = entries.get(2) { let data = backup.decrypt_entry(&entry)?; println!("Decrypted {} ({} bytes)", entry.relative_path, data.len()); } // Get the derived key for use elsewhere: let derived_key = backup.decryption_key_hex(); Ok(()) }Pre-derived keys bypass the expensive key derivation process:
use crabapple::{Backup, Authentication}; fn main() -> Result<(), Box<dyn std::error::Error>> { let udid_folder = "/path/to/backup"; let hex_key = "abcdef0123456789..."; let auth = Authentication::DerivedKey(hex_key.to_string()); let backup = Backup::open(udid_folder, &auth)?; // ... proceed as normal Ok(()) }You can retrieve device metadata (like device name, iOS version, and UDID) without opening the full backup database:
use std::path::Path; use crabapple::backup::device::get_device_basic_info; fn main() -> Result<(), Box<dyn std::error::Error>> { let udid_folder = Path::new("/Users/you/Library/Application Support/MobileSync/Backup/DEVICE_UDID"); let info = get_device_basic_info(udid_folder)?; println!("Device: {} (iOS {})", info.device_name, info.product_version); println!("UDID: {}", info.unique_device_id); Ok(()) }This information is also present on a decrypted Backup instance:
use crabapple::{Backup, Authentication}; fn main() -> Result<(), Box<dyn std::error::Error>> { let udid_folder = "/path/to/backup"; let hex_key = "abcdef0123456789..."; let auth = Authentication::DerivedKey(hex_key.to_string()); let backup = Backup::open(udid_folder, &auth)?; println!("Device: {} (iOS {})", backup.lockdown().device_name, backup.lockdown().product_version ); println!("UDID: {}", backup.udid()?); Ok(()) }crabapple uses a custom BackupError enum for error reporting. You can match on specific cases:
use crabapple::{Backup, Authentication}; use crabapple::error::BackupError; match Backup::open("/bad/path", &Authentication::Password("pass".into())) { Ok(b) => println!("Loaded backup successfully"), Err(BackupError::ManifestPlistNotFound(path)) => eprintln!("Missing Manifest.plist: {}", path), Err(err) => eprintln!("Error initializing backup: {}", err), }This library targets the current latest public release for iOS. It should work with backups from iOS 10.2 or later, but all features may not be available.
