Merge branch 'master' into xpc-rewrite

This commit is contained in:
Jackson Coxson
2025-05-26 20:39:24 -06:00
committed by GitHub
6 changed files with 267 additions and 14 deletions

View File

@@ -23,6 +23,8 @@ pub mod logging;
pub mod misagent;
#[cfg(feature = "mobile_image_mounter")]
pub mod mobile_image_mounter;
#[cfg(feature = "os_trace_relay")]
pub mod os_trace_relay;
mod pairing_file;
#[cfg(feature = "dvt")]
pub mod process_control;

217
ffi/src/os_trace_relay.rs Normal file
View File

@@ -0,0 +1,217 @@
use std::os::raw::c_char;
use std::ffi::CString;
use idevice::{os_trace_relay::OsTraceRelayClient, IdeviceError, IdeviceService};
use crate::{
provider::TcpProviderHandle, IdeviceErrorCode, RUNTIME
};
pub struct OsTraceRelayClientHandle(pub OsTraceRelayClient);
pub struct OsTraceRelayReceiverHandle(pub idevice::os_trace_relay::OsTraceRelayReceiver);
#[repr(C)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct OsTraceLog {
pub pid: u32,
pub timestamp: i64,
pub level: u8,
pub image_name: *const c_char,
pub filename: *const c_char,
pub message: *const c_char,
pub label: *const SyslogLabel,
}
#[repr(C)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SyslogLabel {
pub subsystem: *const c_char,
pub category: *const c_char,
}
#[unsafe(no_mangle)]
pub extern "C" fn os_trace_relay_connect_tcp(
provider: *mut TcpProviderHandle,
client: *mut *mut OsTraceRelayClientHandle
) -> IdeviceErrorCode {
if provider.is_null() {
log::error!("Null pointer provided");
return IdeviceErrorCode::InvalidArg;
}
let res: Result<OsTraceRelayClient, IdeviceError> = RUNTIME.block_on(async move {
let provider_box = unsafe { Box::from_raw(provider) };
let provider_ref = &provider_box.0;
let result = OsTraceRelayClient::connect(provider_ref).await;
std::mem::forget(provider_box);
result
});
match res {
Ok(c) => {
let boxed = Box::new(OsTraceRelayClientHandle(c));
unsafe { *client = Box::into_raw(boxed) };
IdeviceErrorCode::IdeviceSuccess
}
Err(e) => {
let _ = unsafe { Box::from_raw(provider) };
e.into()
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn os_trace_relay_free(
handle: *mut OsTraceRelayClientHandle
) {
if !handle.is_null() {
log::debug!("Freeing os trace relay client");
let _ = unsafe { Box::from_raw(handle) };
}
}
#[unsafe(no_mangle)]
pub extern "C" fn os_trace_relay_start_trace(
client: *mut OsTraceRelayClientHandle,
receiver: *mut *mut OsTraceRelayReceiverHandle,
pid: *const u32,
) -> IdeviceErrorCode {
if receiver.is_null() || client.is_null() {
log::error!("Null pointer provided");
return IdeviceErrorCode::InvalidArg;
}
let pid_option = if pid.is_null() {
None
} else {
Some(unsafe { *pid })
};
let client_owned = unsafe { Box::from_raw(client) };
let res= RUNTIME.block_on(async {
client_owned
.0
.start_trace(pid_option)
.await
});
match res {
Ok(relay) => {
let boxed = Box::new(OsTraceRelayReceiverHandle(relay));
unsafe { *receiver = Box::into_raw(boxed) };
IdeviceErrorCode::IdeviceSuccess
},
Err(e) => e.into(),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn os_trace_relay_receiver_free(
handle: *mut OsTraceRelayReceiverHandle
) {
if !handle.is_null() {
log::debug!("Freeing syslog relay client");
let _ = unsafe { Box::from_raw(handle) };
}
}
#[unsafe(no_mangle)]
pub extern "C" fn os_trace_relay_get_pid_list(
client: *mut OsTraceRelayClientHandle,
list: *mut *mut Vec<u64>,
) -> IdeviceErrorCode {
let res = RUNTIME.block_on(async {
unsafe { &mut *client }
.0
.get_pid_list()
.await
});
match res {
Ok(r) => {
unsafe { *list = Box::into_raw(Box::new(r)) };
IdeviceErrorCode::IdeviceSuccess
},
Err(e) => e.into(),
}
}
#[unsafe(no_mangle)]
pub extern "C" fn os_trace_relay_next(
client: *mut OsTraceRelayReceiverHandle,
log: *mut *mut OsTraceLog
) -> IdeviceErrorCode {
if client.is_null() {
log::error!("Null pointer provided");
return IdeviceErrorCode::InvalidArg;
}
let res = RUNTIME.block_on(async {
unsafe { &mut *client }
.0
.next()
.await
});
match res {
Ok(r) => {
let log_entry = Box::new(OsTraceLog {
pid: r.pid,
timestamp: r.timestamp.and_utc().timestamp(),
level: r.level as u8,
image_name: CString::new(r.image_name).unwrap().into_raw(),
filename: CString::new(r.filename).unwrap().into_raw(),
message: CString::new(r.message).unwrap().into_raw(),
label: if let Some(label) = r.label {
Box::into_raw(Box::new(SyslogLabel {
subsystem: CString::new(label.subsystem).unwrap().into_raw(),
category: CString::new(label.category).unwrap().into_raw(),
}))
} else {
std::ptr::null()
},
});
unsafe { *log = Box::into_raw(log_entry) };
IdeviceErrorCode::IdeviceSuccess
}
Err(e) => e.into(),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn os_trace_relay_free_log(log: *mut OsTraceLog) {
if !log.is_null() {
unsafe {
if !(*log).image_name.is_null() {
let _ = CString::from_raw((*log).image_name as *mut c_char);
}
if !(*log).filename.is_null() {
let _ = CString::from_raw((*log).filename as *mut c_char);
}
if !(*log).message.is_null() {
let _ = CString::from_raw((*log).message as *mut c_char);
}
if !(*log).label.is_null() {
let label = &*(*log).label;
if !label.subsystem.is_null() {
let _ = CString::from_raw(label.subsystem as *mut c_char);
}
if !label.category.is_null() {
let _ = CString::from_raw(label.category as *mut c_char);
}
let _ = Box::from_raw((*log).label as *mut SyslogLabel);
}
let _ = Box::from_raw(log);
}
}
}

View File

@@ -79,13 +79,17 @@ impl CrashReportCopyMobileClient {
/// Lists crash report files in the root of the crash logs directory.
///
/// # Arguments
/// * `dir_path` - The directory to pull logs from. Default is /
///
/// # Returns
/// A list of filenames.
///
/// # Errors
/// Returns `IdeviceError` if listing the directory fails.
pub async fn ls(&mut self) -> Result<Vec<String>, IdeviceError> {
let mut res = self.afc_client.list_dir("/").await?;
pub async fn ls(&mut self, dir_path: Option<&str>) -> Result<Vec<String>, IdeviceError> {
let path = dir_path.unwrap_or("/");
let mut res = self.afc_client.list_dir(path).await?;
if res.len() > 2 {
if &res[0] == "." {
res.swap_remove(0);

View File

@@ -66,19 +66,19 @@ pub struct OsTraceRelayReceiver {
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct OsTraceLog {
pid: u32,
timestamp: NaiveDateTime,
level: LogLevel,
image_name: String,
filename: String,
message: String,
label: Option<SyslogLabel>,
pub pid: u32,
pub timestamp: NaiveDateTime,
pub level: LogLevel,
pub image_name: String,
pub filename: String,
pub message: String,
pub label: Option<SyslogLabel>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SyslogLabel {
subsystem: String,
category: String,
pub subsystem: String,
pub category: String,
}
#[derive(Debug, Clone, PartialEq, Eq)]

View File

@@ -43,7 +43,8 @@ async fn main() {
Command::new("pull")
.about("Pulls a log")
.arg(Arg::new("path").required(true).index(1))
.arg(Arg::new("save").required(true).index(2)),
.arg(Arg::new("save").required(true).index(2))
.arg(Arg::new("dir").required(false).index(3)),
)
.get_matches();
@@ -68,8 +69,12 @@ async fn main() {
.await
.expect("Unable to connect to misagent");
if let Some(_matches) = matches.subcommand_matches("list") {
let res = crash_client.ls().await.expect("Failed to read dir");
if let Some(matches) = matches.subcommand_matches("list") {
let dir_path: Option<&String> = matches.get_one("dir");
let res = crash_client
.ls(dir_path.map(|x| x.as_str()))
.await
.expect("Failed to read dir");
println!("{res:#?}");
} else if matches.subcommand_matches("flush").is_some() {
flush_reports(&*provider).await.expect("Failed to flush");

View File

@@ -39,6 +39,11 @@ async fn main() {
.subcommand(Command::new("lookup").about("Gets the apps on the device"))
.subcommand(Command::new("browse").about("Browses the apps on the device"))
.subcommand(Command::new("check_capabilities").about("Check the capabilities"))
.subcommand(
Command::new("install")
.about("Install an app in the AFC jail")
.arg(Arg::new("path")),
)
.get_matches();
if matches.get_flag("about") {
@@ -78,6 +83,26 @@ async fn main() {
.check_capabilities_match(Vec::new(), None)
.await
.expect("check failed");
} else if let Some(matches) = matches.subcommand_matches("install") {
let path: &String = match matches.get_one("path") {
Some(p) => p,
None => {
eprintln!("No path passed, pass -h for help");
return;
}
};
instproxy_client
.install_with_callback(
path,
None,
async |(percentage, _)| {
println!("Installing: {percentage}");
},
(),
)
.await
.expect("Failed to install")
} else {
eprintln!("Invalid usage, pass -h for help");
}