From efe49dfc67db3adaea79a21da71a5a0a5551d6bb Mon Sep 17 00:00:00 2001 From: Fabian Freyer Date: Mon, 18 Sep 2023 16:28:33 +0200 Subject: [PATCH] feat(device): Add spawn/resume methods --- frida/src/device.rs | 81 +++++++++++++++++++++++++- frida/src/error.rs | 27 +++++++++ frida/src/process.rs | 134 ++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 239 insertions(+), 3 deletions(-) diff --git a/frida/src/device.rs b/frida/src/device.rs index e07e933..9a996fe 100644 --- a/frida/src/device.rs +++ b/frida/src/device.rs @@ -12,7 +12,7 @@ use std::marker::PhantomData; use crate::process::Process; use crate::session::Session; use crate::variant::Variant; -use crate::{Error, Result}; +use crate::{Error, Result, SpawnOptions}; /// Access to a Frida device. pub struct Device<'a> { @@ -172,6 +172,85 @@ impl<'a> Device<'a> { Err(Error::DeviceAttachError) } } + + /// Spawn a process on the device + /// + /// Returns the PID of the newly spawned process. + /// On spawn, the process will be halted, and [`resume`](Device::resume) will need to be + /// called to continue execution. + pub fn spawn>(&mut self, program: S, options: &SpawnOptions) -> Result { + let mut error: *mut frida_sys::GError = std::ptr::null_mut(); + let program = CString::new(program.as_ref()).unwrap(); + + let pid = unsafe { + frida_sys::frida_device_spawn_sync( + self.device_ptr, + program.as_ptr(), + options.options_ptr, + std::ptr::null_mut(), + &mut error, + ) + }; + + if !error.is_null() { + let message = unsafe { CString::from_raw((*error).message) } + .into_string() + .map_err(|_| Error::CStringFailed)?; + let code = unsafe { (*error).code }; + + return Err(Error::SpawnFailed { code, message }); + } + + Ok(pid) + } + + /// Resumes the process with given pid. + pub fn resume(&self, pid: u32) -> Result<()> { + let mut error: *mut frida_sys::GError = std::ptr::null_mut(); + unsafe { + frida_sys::frida_device_resume_sync( + self.device_ptr, + pid, + std::ptr::null_mut(), + &mut error, + ) + }; + + if !error.is_null() { + let message = unsafe { CString::from_raw((*error).message) } + .into_string() + .map_err(|_| Error::CStringFailed)?; + let code = unsafe { (*error).code }; + + return Err(Error::ResumeFailed { code, message }); + } + + Ok(()) + } + + /// Kill a process on the device + pub fn kill(&mut self, pid: u32) -> Result<()> { + let mut error: *mut frida_sys::GError = std::ptr::null_mut(); + unsafe { + frida_sys::frida_device_kill_sync( + self.device_ptr, + pid, + std::ptr::null_mut(), + &mut error, + ) + }; + + if !error.is_null() { + let message = unsafe { CString::from_raw((*error).message) } + .into_string() + .map_err(|_| Error::CStringFailed)?; + let code = unsafe { (*error).code }; + + return Err(Error::KillFailed { code, message }); + } + + Ok(()) + } } impl<'a> Drop for Device<'a> { diff --git a/frida/src/error.rs b/frida/src/error.rs index e0c8800..0c10e09 100644 --- a/frida/src/error.rs +++ b/frida/src/error.rs @@ -50,4 +50,31 @@ pub enum Error { /// Error message message: String, }, + + /// Failed to spawn program + #[error("Failed to spawn program ({code}) {message}")] + SpawnFailed { + /// Error code + code: i32, + /// Error message + message: String, + }, + + /// Failed to resume + #[error("Failed to resume ({code}) {message}")] + ResumeFailed { + /// Error code + code: i32, + /// Error message + message: String, + }, + + /// Failed to kill + #[error("Failed to kill PID ({code}) {message}")] + KillFailed { + /// Error code + code: i32, + /// Error message + message: String, + }, } diff --git a/frida/src/process.rs b/frida/src/process.rs index 17ac493..4741c64 100644 --- a/frida/src/process.rs +++ b/frida/src/process.rs @@ -4,8 +4,9 @@ * Licence: wxWindows Library Licence, Version 3.1 */ -use frida_sys::_FridaProcess; -use std::ffi::CStr; +use frida_sys::{FridaSpawnOptions, _FridaProcess}; +use std::convert::TryInto; +use std::ffi::{CStr, CString}; use std::marker::PhantomData; /// Process management in Frida. @@ -41,3 +42,132 @@ impl<'a> Drop for Process<'a> { unsafe { frida_sys::frida_unref(self.process_ptr as _) } } } + +#[repr(u32)] +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] +/// Standard I/O routing for a spawn +pub enum SpawnStdio { + /// Inherit parent's Standard I/O + Inherit = 0, + + /// Use pipes for Standard I/O + Pipe = 1, +} + +/// Process Spawn Options +pub struct SpawnOptions<'a> { + pub(crate) options_ptr: *mut FridaSpawnOptions, + phantom: PhantomData<&'a FridaSpawnOptions>, +} + +impl<'a> SpawnOptions<'a> { + pub(crate) fn from_raw(options_ptr: *mut FridaSpawnOptions) -> Self { + Self { + options_ptr, + phantom: PhantomData, + } + } + + /// Create an empty SpawnOptions instance + pub fn new() -> Self { + Self::from_raw(unsafe { frida_sys::frida_spawn_options_new() }) + } + + /// Set the argv vector + pub fn argv(self, args: L) -> Self + where + S: AsRef, + L: IntoIterator, + { + let args: Vec = args + .into_iter() + .map(|s| CString::new(s.as_ref()).unwrap()) + .collect(); + let mut arg_ptrs: Vec<*mut _> = args.iter().map(|s| s.as_ptr() as *mut _).collect(); + unsafe { + frida_sys::frida_spawn_options_set_argv( + self.options_ptr, + arg_ptrs.as_mut_ptr(), + arg_ptrs.len().try_into().unwrap(), + ); + } + self + } + + /// Set the working directory + pub fn cwd>(self, cwd: S) -> Self { + unsafe { + frida_sys::frida_spawn_options_set_cwd( + self.options_ptr, + cwd.as_ref().as_ptr() as *mut _, + ); + } + self + } + + /// Set the env vector + pub fn env(self, env: M) -> Self + where + K: AsRef, + V: AsRef, + M: IntoIterator, + { + let env: Vec = env + .into_iter() + .map(|(key, value)| { + CString::new(format!("{}={}", key.as_ref(), value.as_ref())).unwrap() + }) + .collect(); + let mut env_ptrs: Vec<*mut _> = env.iter().map(|s| s.as_ptr() as *mut _).collect(); + unsafe { + frida_sys::frida_spawn_options_set_env( + self.options_ptr, + env_ptrs.as_mut_ptr(), + env_ptrs.len().try_into().unwrap(), + ); + } + self + } + + /// Set the envp vector + pub fn envp(self, envp: M) -> Self + where + K: AsRef, + V: AsRef, + M: IntoIterator, + { + let envp: Vec = envp + .into_iter() + .map(|(key, value)| { + CString::new(format!("{}={}", key.as_ref(), value.as_ref())).unwrap() + }) + .collect(); + let mut envp_ptrs: Vec<*mut _> = envp.iter().map(|s| s.as_ptr() as *mut _).collect(); + unsafe { + frida_sys::frida_spawn_options_set_envp( + self.options_ptr, + envp_ptrs.as_mut_ptr(), + envp_ptrs.len().try_into().unwrap(), + ); + } + self + } + + /// Set the Standard I/O handling + pub fn stdio(self, stdio: SpawnStdio) -> Self { + unsafe { frida_sys::frida_spawn_options_set_stdio(self.options_ptr, stdio as _) } + self + } +} + +impl<'a> Default for SpawnOptions<'a> { + fn default() -> Self { + Self::new() + } +} + +impl<'a> Drop for SpawnOptions<'a> { + fn drop(&mut self) { + unsafe { frida_sys::frida_unref(self.options_ptr as _) } + } +}