Skip to content

Commit

Permalink
Add a couple of useful features to Device
Browse files Browse the repository at this point in the history
* feat(device): Add method to query system params

This requires some GVariant handling which was mostly inspired by the
implementation in frida-go's types_converter.go.

* feat(device): Add a getter for the device type

* feat(device): Add spawn/resume methods
  • Loading branch information
fabianfreyer authored Nov 14, 2023
1 parent 8a35b6d commit a31c6eb
Show file tree
Hide file tree
Showing 6 changed files with 514 additions and 10 deletions.
11 changes: 10 additions & 1 deletion frida-sys/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,17 @@ pub use bindings::*;
#[cfg(not(any(target_vendor = "apple", target_os = "windows")))]
pub use crate::{
_frida_g_bytes_new as g_bytes_new, _frida_g_bytes_unref as g_bytes_unref,
_frida_g_clear_object as g_clear_object, _frida_g_idle_source_new as g_idle_source_new,
_frida_g_clear_object as g_clear_object,
_frida_g_hash_table_iter_init as g_hash_table_iter_init,
_frida_g_hash_table_iter_next as g_hash_table_iter_next,
_frida_g_hash_table_size as g_hash_table_size, _frida_g_idle_source_new as g_idle_source_new,
_frida_g_signal_connect_data as g_signal_connect_data,
_frida_g_source_attach as g_source_attach,
_frida_g_source_set_callback as g_source_set_callback, _frida_g_source_unref as g_source_unref,
_frida_g_variant_get_boolean as g_variant_get_boolean,
_frida_g_variant_get_int64 as g_variant_get_int64,
_frida_g_variant_get_string as g_variant_get_string,
_frida_g_variant_get_type_string as g_variant_get_type_string,
_frida_g_variant_iter_init as g_variant_iter_init,
_frida_g_variant_iter_loop as g_variant_iter_loop,
};
205 changes: 198 additions & 7 deletions frida/src/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@
*/

use frida_sys::_FridaDevice;
use std::ffi::CStr;
use std::collections::HashMap;
use std::ffi::{CStr, CString};
use std::marker::PhantomData;

use crate::process::Process;
use crate::session::Session;
use crate::{Error, Result};
use crate::variant::Variant;
use crate::{Error, Result, SpawnOptions};

/// Access to a Frida device.
pub struct Device<'a> {
Expand Down Expand Up @@ -39,6 +41,78 @@ impl<'a> Device<'a> {
id.to_str().unwrap_or_default()
}

/// Returns the device's type
///
/// # Example
/// ```
///# use frida::DeviceType;
///# let frida = unsafe { frida::Frida::obtain() };
///# let device_manager = frida::DeviceManager::obtain(&frida);
///# let device = device_manager.enumerate_all_devices().into_iter().find(|device| device.get_id() == "local").unwrap();
/// assert_eq!(device.get_type(), DeviceType::Local);
/// ```
pub fn get_type(&self) -> DeviceType {
unsafe { frida_sys::frida_device_get_dtype(self.device_ptr).into() }
}

/// Returns the device's system parameters
///
/// # Example
/// ```
///# use std::collections::HashMap;
///# let frida = unsafe { frida::Frida::obtain() };
///# let device_manager = frida::DeviceManager::obtain(&frida);
///# let device = device_manager.enumerate_all_devices().into_iter().find(|device| device.get_id() == "local").unwrap();
/// let params = device.query_system_parameters().unwrap();
/// let os_version = params
/// .get("os")
/// .expect("No parameter \"os\" present")
/// .get_map()
/// .expect("Parameter \"os\" was not a mapping")
/// .get("version")
/// .expect("Parameter \"os\" did not contain a version field")
/// .get_string()
/// .expect("Version is not a string");
/// ```
pub fn query_system_parameters(&self) -> Result<HashMap<String, Variant>> {
let mut error: *mut frida_sys::GError = std::ptr::null_mut();

let ht = unsafe {
frida_sys::frida_device_query_system_parameters_sync(
self.device_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::DeviceQuerySystemParametersFailed { code, message });
}

let mut iter: frida_sys::GHashTableIter =
unsafe { std::mem::MaybeUninit::zeroed().assume_init() };
unsafe { frida_sys::g_hash_table_iter_init(&mut iter, ht) };
let size = unsafe { frida_sys::g_hash_table_size(ht) };
let mut map = HashMap::with_capacity(size as usize);

let mut key = std::ptr::null_mut();
let mut val = std::ptr::null_mut();
while (unsafe { frida_sys::g_hash_table_iter_next(&mut iter, &mut key, &mut val) }
!= frida_sys::FALSE as _)
{
let key = unsafe { CStr::from_ptr(key as _) };
let val = unsafe { Variant::from_ptr(val as _) };
map.insert(key.to_string_lossy().to_string(), val);
}

Ok(map)
}

/// Returns if the device is lost or not.
pub fn is_lost(&self) -> bool {
unsafe { frida_sys::frida_device_is_lost(self.device_ptr) == 1 }
Expand Down Expand Up @@ -98,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 All @@ -106,14 +259,46 @@ impl<'a> Drop for Device<'a> {
}
}

#[repr(u32)]
#[non_exhaustive]
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
/// Frida device type.
///
/// Represents different connection types
// On Windows, the constants are i32 instead of u32, so we need to cast accordingly.
pub enum DeviceType {
/// Local Frida device.
Local,
/// Remote Frida device.
Remote,
/// USB Frida device.
USB,
Local = frida_sys::FridaDeviceType_FRIDA_DEVICE_TYPE_LOCAL as _,

/// Remote Frida device, connected via network
Remote = frida_sys::FridaDeviceType_FRIDA_DEVICE_TYPE_REMOTE as _,

/// Device connected via USB
USB = frida_sys::FridaDeviceType_FRIDA_DEVICE_TYPE_USB as _,
}

#[cfg(not(target_family = "windows"))]
impl From<u32> for DeviceType {
fn from(value: u32) -> Self {
match value {
frida_sys::FridaDeviceType_FRIDA_DEVICE_TYPE_LOCAL => Self::Local,
frida_sys::FridaDeviceType_FRIDA_DEVICE_TYPE_REMOTE => Self::Remote,
frida_sys::FridaDeviceType_FRIDA_DEVICE_TYPE_USB => Self::USB,
value => unreachable!("Invalid Device type {}", value),
}
}
}

#[cfg(target_family = "windows")]
impl From<i32> for DeviceType {
fn from(value: i32) -> Self {
match value {
frida_sys::FridaDeviceType_FRIDA_DEVICE_TYPE_LOCAL => Self::Local,
frida_sys::FridaDeviceType_FRIDA_DEVICE_TYPE_REMOTE => Self::Remote,
frida_sys::FridaDeviceType_FRIDA_DEVICE_TYPE_USB => Self::USB,
value => unreachable!("Invalid Device type {}", value),
}
}
}

impl From<DeviceType> for frida_sys::FridaDeviceType {
Expand All @@ -125,3 +310,9 @@ impl From<DeviceType> for frida_sys::FridaDeviceType {
}
}
}

impl std::fmt::Display for DeviceType {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}
36 changes: 36 additions & 0 deletions frida/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,40 @@ pub enum Error {
/// Error message
message: String,
},

/// Failed to query device parameters
#[error("Failed to query device system parameters ({code}) {message}")]
DeviceQuerySystemParametersFailed {
/// Error code
code: i32,
/// 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,
},
}
3 changes: 3 additions & 0 deletions frida/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ pub use script::*;
mod session;
pub use session::*;

mod variant;
pub use variant::*;

#[doc(hidden)]
pub type Result<T> = std::result::Result<T, error::Error>;

Expand Down
Loading

0 comments on commit a31c6eb

Please sign in to comment.