use frida_sys::_FridaDevice;
use std::collections::HashMap;
use std::ffi::{CStr, CString};
use std::marker::PhantomData;
use crate::process::Process;
use crate::session::Session;
use crate::variant::Variant;
use crate::{Error, Result, SpawnOptions};
pub struct Device<'a> {
pub(crate) device_ptr: *mut _FridaDevice,
phantom: PhantomData<&'a _FridaDevice>,
}
impl<'a> Device<'a> {
pub(crate) fn from_raw(device_ptr: *mut _FridaDevice) -> Device<'a> {
Device {
device_ptr,
phantom: PhantomData,
}
}
pub fn get_name(&self) -> &str {
let name =
unsafe { CStr::from_ptr(frida_sys::frida_device_get_name(self.device_ptr) as _) };
name.to_str().unwrap_or_default()
}
pub fn get_id(&self) -> &str {
let id = unsafe { CStr::from_ptr(frida_sys::frida_device_get_id(self.device_ptr) as _) };
id.to_str().unwrap_or_default()
}
pub fn get_type(&self) -> DeviceType {
unsafe { frida_sys::frida_device_get_dtype(self.device_ptr).into() }
}
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 i32)
{
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)
}
pub fn is_lost(&self) -> bool {
unsafe { frida_sys::frida_device_is_lost(self.device_ptr) == 1 }
}
pub fn enumerate_processes<'b>(&'a self) -> Vec<Process<'b>>
where
'a: 'b,
{
let mut processes = Vec::new();
let mut error: *mut frida_sys::GError = std::ptr::null_mut();
let processes_ptr = unsafe {
frida_sys::frida_device_enumerate_processes_sync(
self.device_ptr,
std::ptr::null_mut(),
std::ptr::null_mut(),
&mut error,
)
};
if error.is_null() {
let num_processes = unsafe { frida_sys::frida_process_list_size(processes_ptr) };
processes.reserve(num_processes as usize);
for i in 0..num_processes {
let process_ptr = unsafe { frida_sys::frida_process_list_get(processes_ptr, i) };
let process = Process::from_raw(process_ptr);
processes.push(process);
}
}
unsafe { frida_sys::frida_unref(processes_ptr as _) };
processes
}
pub fn attach<'b>(&'a self, pid: u32) -> Result<Session<'b>>
where
'a: 'b,
{
let mut error: *mut frida_sys::GError = std::ptr::null_mut();
let session = unsafe {
frida_sys::frida_device_attach_sync(
self.device_ptr,
pid,
std::ptr::null_mut(),
std::ptr::null_mut(),
&mut error,
)
};
if error.is_null() {
Ok(Session::from_raw(session))
} else {
Err(Error::DeviceAttachError)
}
}
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)
}
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(())
}
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 Drop for Device<'_> {
fn drop(&mut self) {
unsafe { frida_sys::frida_unref(self.device_ptr as _) }
}
}
#[repr(u32)]
#[non_exhaustive]
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
pub enum DeviceType {
Local = frida_sys::FridaDeviceType_FRIDA_DEVICE_TYPE_LOCAL as _,
Remote = frida_sys::FridaDeviceType_FRIDA_DEVICE_TYPE_REMOTE as _,
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 {
fn from(value: DeviceType) -> Self {
match value {
DeviceType::Local => frida_sys::FridaDeviceType_FRIDA_DEVICE_TYPE_LOCAL,
DeviceType::Remote => frida_sys::FridaDeviceType_FRIDA_DEVICE_TYPE_REMOTE,
DeviceType::USB => frida_sys::FridaDeviceType_FRIDA_DEVICE_TYPE_USB,
}
}
}
impl std::fmt::Display for DeviceType {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}