crabapple
Crabapple is a Rust library for reading, inspecting, and extracting data from encrypted iOS backups created by Finder, Apple Devices, or iTunes.
⚠️ Warning ⚠️
This library is currently in beta state and should not be used in production code.
Features
- Load and parse the backup's
Manifest.plist to obtain metadata, device info, and encryption parameters
- Derive encryption keys using
PBKDF2 (HMAC-SHA256 then HMAC-SHA1) and unwrap protection class keys via AES Key Wrap (RFC 3394)
- Decrypt and query the
AES-256 encrypted Manifest.db, exposing backup file metadata via rusqlite
- Retrieve and decrypt individual files by protection class (per-file
AES-CBC with PKCS7 padding)
- 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>> {
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)?;
let entries = backup.get_backup_files_list()?;
for entry in &entries {
println!("{} - {}/{}", entry.file_id, entry.domain, entry.relative_path);
}
if let Some(entry) = entries.first() {
let mut stream = backup.decrypt_entry_stream(&entry)?;
let mut file = File::create("decrypted.txt")?;
copy(&mut stream, &mut file)?;
}
if let Some(entry) = entries.get(2) {
let data = backup.decrypt_entry(&entry)?;
println!("Decrypted {} ({} bytes)", entry.relative_path, data.len());
}
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)?;
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
