Expand description
§crabapple
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.
§⚠️ Warning ⚠️
This library is currently in beta state and should not be used in production code.
§Features
- 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
§Installation
This library is available on crates.io.
§Documentation
Documentation is available on docs.rs.
§Quick Start
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::new(udid_folder, &auth)?;
// List all files in the backup
let entries = backup.get_backup_files_list()?;
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.get_decryption_key_hex();
Ok(())
}§Using a Pre-derived Key
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::new(udid_folder, &auth)?;
// ... proceed as normal
Ok(())
}§Getting Basic Device Information
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::new(udid_folder, &auth)?;
println!("Device: {} (iOS {})",
backup.lockdown().device_name,
backup.lockdown().product_version
);
println!("UDID: {}", backup.udid()?);
Ok(())
}§Error Handling
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::new("/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),
}§Crabapple Tree

Re-exports§
pub use backup::Backup;pub use backup::models::auth::Authentication;