diff --git a/Cargo.toml b/Cargo.toml index 74440f9..0e8baca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,10 @@ path = "src/tools/ideviceinfo.rs" name = "heartbeat_client" path = "src/tools/heartbeat_client.rs" +[[bin]] +name = "instproxy" +path = "src/tools/instproxy.rs" + [dependencies] plist = { version = "1.7" } serde = { version = "1", features = ["derive"] } diff --git a/src/installation_proxy.rs b/src/installation_proxy.rs new file mode 100644 index 0000000..7134076 --- /dev/null +++ b/src/installation_proxy.rs @@ -0,0 +1,50 @@ +// Jackson Coxson +// Incomplete implementation for installation_proxy + +use std::collections::HashMap; + +use crate::{Idevice, IdeviceError}; + +pub struct InstallationProxyClient { + pub idevice: Idevice, +} + +impl InstallationProxyClient { + pub fn new(idevice: Idevice) -> Self { + Self { idevice } + } + + /// Gets installed apps on the device + /// # Arguments + /// `application_type` - The application type to filter by + /// `bundle_identifiers` - The identifiers to filter by + pub fn get_apps( + &mut self, + application_type: Option, + bundle_identifiers: Option>, + ) -> Result, IdeviceError> { + let application_type = application_type.unwrap_or("Any".to_string()); + let mut options = plist::Dictionary::new(); + if let Some(ids) = bundle_identifiers { + let ids = ids + .into_iter() + .map(plist::Value::String) + .collect::>(); + options.insert("BundleIDs".into(), ids.into()).unwrap(); + } + options.insert("ApplicationType".into(), application_type.into()); + + let mut req = plist::Dictionary::new(); + req.insert("Command".into(), "Lookup".into()); + // req.insert("ClientOptions".into(), plist::Value::Dictionary(options)); + self.idevice.send_plist(plist::Value::Dictionary(req))?; + + let mut res = self.idevice.read_plist()?; + match res.remove("LookupResult") { + Some(plist::Value::Dictionary(res)) => { + Ok(res.into_iter().collect::>()) + } + _ => Err(IdeviceError::UnexpectedResponse), + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 79d5223..940b06a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,7 @@ // Jackson Coxson pub mod heartbeat; +pub mod installation_proxy; pub mod lockdownd; pub mod pairing_file; diff --git a/src/tools/instproxy.rs b/src/tools/instproxy.rs new file mode 100644 index 0000000..86303c2 --- /dev/null +++ b/src/tools/instproxy.rs @@ -0,0 +1,92 @@ +// Jackson Coxson +// Just lists apps for now + +use idevice::{ + installation_proxy::InstallationProxyClient, + lockdownd::{self, LockdowndClient}, + pairing_file::PairingFile, + Idevice, +}; + +use std::{ + net::{Ipv4Addr, SocketAddrV4}, + str::FromStr, +}; + +fn main() { + env_logger::init(); + let mut host = None; + let mut pairing_file = None; + + // Loop through args + let mut i = 0; + while i < std::env::args().len() { + match std::env::args().nth(i).unwrap().as_str() { + "--host" => { + host = Some(std::env::args().nth(i + 1).unwrap().to_string()); + i += 2; + } + "--pairing-file" => { + pairing_file = Some(std::env::args().nth(i + 1).unwrap().to_string()); + i += 2; + } + "-h" | "--help" => { + println!("ideviceinfo - get information from the idevice"); + println!("Usage:"); + println!(" ideviceinfo [options]"); + println!("Options:"); + println!(" --host "); + println!(" --pairing_file "); + println!(" -h, --help"); + println!(" --about"); + println!("\n\nSet RUST_LOG to info, debug, warn, error, or trace to see more logs. Default is error."); + std::process::exit(0); + } + "--about" => { + println!("ideviceinfo - get information from the idevice. Reimplementation of libimobiledevice's binary."); + println!("Copyright (c) 2025 Jackson Coxson"); + } + _ => { + i += 1; + } + } + } + if host.is_none() { + println!("Invalid arguments! Pass the IP of the device with --host"); + return; + } + if pairing_file.is_none() { + println!("Invalid arguments! Pass the path the the pairing file with --pairing-file"); + return; + } + let ip = Ipv4Addr::from_str(host.unwrap().as_str()).unwrap(); + let socket = SocketAddrV4::new(ip, lockdownd::LOCKDOWND_PORT); + + let socket = std::net::TcpStream::connect(socket).unwrap(); + let socket = Box::new(socket); + let idevice = Idevice::new(socket, "heartbeat_client"); + + let p = PairingFile::read_from_file(pairing_file.as_ref().unwrap()).unwrap(); + + let mut lockdown_client = LockdowndClient { idevice }; + lockdown_client.start_session(p).unwrap(); + + let (port, _) = lockdown_client + .start_service("com.apple.mobile.installation_proxy") + .unwrap(); + + let socket = SocketAddrV4::new(ip, port); + let socket = std::net::TcpStream::connect(socket).unwrap(); + let socket = Box::new(socket); + let mut idevice = Idevice::new(socket, "instproxy-client"); + + let p = PairingFile::read_from_file(pairing_file.unwrap()).unwrap(); + + idevice.start_session(p).unwrap(); + + let mut instproxy_client = InstallationProxyClient::new(idevice); + let apps = instproxy_client.get_apps(None, None).unwrap(); + for app in apps.keys() { + println!("{app}"); + } +}