From fd259c6caa4b409d2525fb020df907a1aaf50057 Mon Sep 17 00:00:00 2001 From: Fabian Freyer Date: Mon, 18 Sep 2023 15:13:41 +0200 Subject: [PATCH] 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. --- frida-sys/src/lib.rs | 11 +++- frida/src/device.rs | 62 +++++++++++++++++++- frida/src/error.rs | 9 +++ frida/src/lib.rs | 3 + frida/src/variant.rs | 135 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 218 insertions(+), 2 deletions(-) create mode 100644 frida/src/variant.rs diff --git a/frida-sys/src/lib.rs b/frida-sys/src/lib.rs index 8abb8f0..5473465 100644 --- a/frida-sys/src/lib.rs +++ b/frida-sys/src/lib.rs @@ -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, }; diff --git a/frida/src/device.rs b/frida/src/device.rs index b89e591..a3b7e03 100644 --- a/frida/src/device.rs +++ b/frida/src/device.rs @@ -5,11 +5,13 @@ */ 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::variant::Variant; use crate::{Error, Result}; /// Access to a Frida device. @@ -39,6 +41,64 @@ impl<'a> Device<'a> { id.to_str().unwrap_or_default() } + /// 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> { + 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 } diff --git a/frida/src/error.rs b/frida/src/error.rs index 9b1ebd9..419b255 100644 --- a/frida/src/error.rs +++ b/frida/src/error.rs @@ -45,4 +45,13 @@ 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, + }, } diff --git a/frida/src/lib.rs b/frida/src/lib.rs index 30992c6..0dde5b2 100644 --- a/frida/src/lib.rs +++ b/frida/src/lib.rs @@ -35,6 +35,9 @@ pub use script::*; mod session; pub use session::*; +mod variant; +pub use variant::*; + #[doc(hidden)] pub type Result = std::result::Result; diff --git a/frida/src/variant.rs b/frida/src/variant.rs new file mode 100644 index 0000000..823089b --- /dev/null +++ b/frida/src/variant.rs @@ -0,0 +1,135 @@ +use std::collections::HashMap; +use std::ffi::{CStr, CString}; + +#[derive(Clone, PartialEq, Eq)] +/// GVariant types used by Frida +pub enum Variant { + /// String + String(String), + + /// Boolean value + Boolean(bool), + + /// Integer value + Int64(i64), + + /// Map + Map(HashMap), + + /// Array of Maps + MapList(Vec>), +} + +impl Variant { + /// Construct a GVariant from a raw pointer + pub(crate) unsafe fn from_ptr(variant: *mut frida_sys::GVariant) -> Self { + match variant_string(variant).as_str() { + "s" => { + let mut sz = 0; + let value = CStr::from_ptr(frida_sys::g_variant_get_string(variant, &mut sz)) + .to_string_lossy() + .to_string(); + Self::String(value) + } + "b" => { + Self::Boolean(frida_sys::g_variant_get_boolean(variant) != frida_sys::FALSE as _) + } + "x" => Self::Int64(frida_sys::g_variant_get_int64(variant)), + "a{sv}" => Self::Map(sv_array_to_map(variant)), + "aa{sv}" => Self::MapList(asv_array_to_maplist(variant)), + other => todo!("Unimplemented variant: {other}"), + } + } + + /// Get the string value of a variant, if any + pub fn get_string(&self) -> Option<&str> { + let Self::String(ref s) = self else { + return None; + }; + Some(s) + } + + /// Get the integer value of a variant, if any + pub fn get_int(&self) -> Option { + let Self::Int64(i) = self else { return None }; + Some(*i) + } + + /// Get the boolean value of a variant, if any + pub fn get_bool(&self) -> Option { + let Self::Boolean(b) = self else { return None }; + Some(*b) + } + + /// Get the mapping value of a variant, if any + pub fn get_map(&self) -> Option<&HashMap> { + let Self::Map(ref m) = self else { return None }; + Some(m) + } + + /// Get the mapping list value of a variant, if any + pub fn get_maplist(&self) -> Option<&[HashMap]> { + let Self::MapList(ref l) = self else { + return None; + }; + Some(l) + } +} + +impl std::fmt::Debug for Variant { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + Self::String(s) => s.fmt(f), + Self::Int64(num) => num.fmt(f), + Self::Boolean(b) => b.fmt(f), + Self::Map(m) => m.fmt(f), + Self::MapList(l) => l.fmt(f), + } + } +} + +unsafe fn variant_string(variant: *mut frida_sys::GVariant) -> String { + CStr::from_ptr(frida_sys::g_variant_get_type_string(variant)) + .to_string_lossy() + .to_string() +} + +unsafe fn sv_array_to_map(variant: *mut frida_sys::GVariant) -> HashMap { + let mut ret = HashMap::new(); + + let mut iter: frida_sys::GVariantIter = std::mem::MaybeUninit::zeroed().assume_init(); + let mut value: *mut frida_sys::GVariant = std::ptr::null_mut(); + let mut key: *const i8 = std::ptr::null_mut(); + + frida_sys::g_variant_iter_init(&mut iter, variant); + let sv = CString::new("{sv}").unwrap(); + while frida_sys::g_variant_iter_loop(&mut iter, sv.as_ptr(), &mut key, &mut value) != 0 { + let key = CStr::from_ptr(key).to_string_lossy().to_string(); + let value = Variant::from_ptr(value); + ret.insert(key, value); + } + ret +} + +unsafe fn asv_array_to_maplist(variant: *mut frida_sys::GVariant) -> Vec> { + let mut ret = Vec::new(); + let mut outer: frida_sys::GVariantIter = std::mem::MaybeUninit::zeroed().assume_init(); + let mut inner = std::ptr::null_mut(); + let mut key: *const i8 = std::ptr::null_mut(); + let mut value: *mut frida_sys::GVariant = std::ptr::null_mut(); + + frida_sys::g_variant_iter_init(&mut outer, variant); + let asv = CString::new("a{sv}").unwrap(); + let sv = CString::new("{sv}").unwrap(); + while frida_sys::g_variant_iter_loop(&mut outer, asv.as_ptr(), &mut inner) != 0 { + let mut map = HashMap::new(); + while frida_sys::g_variant_iter_loop(inner, sv.as_ptr(), &mut key, &mut value) != 0 { + let key = CStr::from_ptr(key).to_string_lossy().to_string(); + let value = Variant::from_ptr(value); + map.insert(key, value); + } + ret.push(map) + } + + ret +}