Skip to content

Commit

Permalink
feat(device): Add spawn/resume methods
Browse files Browse the repository at this point in the history
  • Loading branch information
fabianfreyer committed Sep 18, 2023
1 parent bd2151f commit efe49df
Show file tree
Hide file tree
Showing 3 changed files with 239 additions and 3 deletions.
81 changes: 80 additions & 1 deletion frida/src/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand Down Expand Up @@ -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<S: AsRef<str>>(&mut self, program: S, options: &SpawnOptions) -> Result<u32> {
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> {
Expand Down
27 changes: 27 additions & 0 deletions frida/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
}
134 changes: 132 additions & 2 deletions frida/src/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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<S, L>(self, args: L) -> Self
where
S: AsRef<str>,
L: IntoIterator<Item = S>,
{
let args: Vec<CString> = 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<S: AsRef<CStr>>(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<K, V, M>(self, env: M) -> Self
where
K: AsRef<str>,
V: AsRef<str>,
M: IntoIterator<Item = (K, V)>,
{
let env: Vec<CString> = 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<K, V, M>(self, envp: M) -> Self
where
K: AsRef<str>,
V: AsRef<str>,
M: IntoIterator<Item = (K, V)>,
{
let envp: Vec<CString> = 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 _) }
}
}

0 comments on commit efe49df

Please sign in to comment.