From ce60e74842914ffdfd03daeb04e306439b66dd42 Mon Sep 17 00:00:00 2001 From: xoffio <38369407+Xoffio@users.noreply.github.com> Date: Tue, 17 Dec 2024 09:11:26 -0500 Subject: [PATCH] adding process (#182) * adding process * fix CI check * fix CI issue * Fix CI issue * documentaion updated * Fix CI issue Github CI is now using rustc 1.82.0 --- frida-gum/src/lib.rs | 3 + frida-gum/src/module.rs | 49 ++++++++---- frida-gum/src/process.rs | 139 +++++++++++++++++++++++++++++++++ frida-gum/src/range_details.rs | 2 +- 4 files changed, 175 insertions(+), 18 deletions(-) create mode 100644 frida-gum/src/process.rs diff --git a/frida-gum/src/lib.rs b/frida-gum/src/lib.rs index 6d30617..9041fd2 100644 --- a/frida-gum/src/lib.rs +++ b/frida-gum/src/lib.rs @@ -81,6 +81,9 @@ pub use module::*; mod module_map; pub use module_map::*; +mod process; +pub use process::*; + mod error; pub use error::Error; diff --git a/frida-gum/src/module.rs b/frida-gum/src/module.rs index 0935f33..37bca9d 100644 --- a/frida-gum/src/module.rs +++ b/frida-gum/src/module.rs @@ -53,6 +53,37 @@ impl fmt::Display for ExportType { } } +impl ModuleDetailsOwned { + pub unsafe fn from_module_details(details: *const GumModuleDetails) -> Self { + let name: String = NativePointer((*details).name as *mut _) + .try_into() + .unwrap_or_default(); + let path: String = NativePointer((*details).path as *mut _) + .try_into() + .unwrap_or_default(); + let range = (*details).range; + let base_address = (*range).base_address as usize; + let size = (*range).size as usize; + + ModuleDetailsOwned { + name, + path, + base_address, + size, + } + } +} + +impl fmt::Display for ModuleDetailsOwned { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + fmt, + "{{\n\tbase: 0x{:x}\n\tname: {}\n\tpath: {}\n\tsize: {}\n}}", + self.base_address, self.name, self.path, self.size + ) + } +} + /// Module symbol details returned by [`Module::enumerate_symbols`]. pub struct SymbolDetails { pub name: String, @@ -198,23 +229,7 @@ impl Module<'_> { user_data: gpointer, ) -> gboolean { let res = &mut *(user_data as *mut Vec); - - let name: String = NativePointer((*details).name as *mut _) - .try_into() - .unwrap_or_default(); - let path: String = NativePointer((*details).path as *mut _) - .try_into() - .unwrap_or_default(); - let range = (*details).range; - let base_address = (*range).base_address as usize; - let size = (*range).size as usize; - let module_details = ModuleDetailsOwned { - name, - path, - base_address, - size, - }; - res.push(module_details); + res.push(ModuleDetailsOwned::from_module_details(details)); 1 } diff --git a/frida-gum/src/process.rs b/frida-gum/src/process.rs new file mode 100644 index 0000000..fff714a --- /dev/null +++ b/frida-gum/src/process.rs @@ -0,0 +1,139 @@ +//! Process helpers. +//! + +#![cfg_attr( + any(target_arch = "x86_64", target_arch = "x86"), + allow(clippy::unnecessary_cast) +)] + +use crate::{FileMapping, NativePointer}; + +use { + crate::{module, Gum, PageProtection, RangeDetails}, + core::ffi::c_void, + frida_gum_sys as gum_sys, + frida_gum_sys::{gboolean, gpointer}, +}; + +#[cfg(not(feature = "std"))] +use alloc::vec::Vec; + +#[derive(Clone, FromPrimitive, Debug)] +#[repr(u32)] +pub enum CodeSigningPolicy { + CodeSigningOptional = gum_sys::GumCodeSigningPolicy_GUM_CODE_SIGNING_OPTIONAL as u32, + CodeSigningRequired = gum_sys::GumCodeSigningPolicy_GUM_CODE_SIGNING_REQUIRED as u32, +} + +#[derive(Clone, FromPrimitive, Debug)] +#[repr(u32)] +pub enum Os { + OsWindows = gum_sys::_GumOS_GUM_OS_WINDOWS as u32, + OsMacos = gum_sys::_GumOS_GUM_OS_MACOS as u32, + OsLinux = gum_sys::_GumOS_GUM_OS_LINUX as u32, + OsIos = gum_sys::_GumOS_GUM_OS_IOS as u32, + OsWatchos = gum_sys::_GumOS_GUM_OS_WATCHOS as u32, + OsTvos = gum_sys::_GumOS_GUM_OS_TVOS as u32, + OsAndroid = gum_sys::_GumOS_GUM_OS_ANDROID as u32, + OsFreebsd = gum_sys::_GumOS_GUM_OS_FREEBSD as u32, + OsQnx = gum_sys::_GumOS_GUM_OS_QNX as u32, +} + +pub struct Range<'a> { + /// Base address + pub base: NativePointer, + /// Size in bytes + pub size: usize, + /// Protection flag (e.g., Read, Write, Execute) + pub protection: PageProtection, + /// When available, file mapping details. + pub file: Option>, +} + +pub struct Process<'a> { + // This is to verify that Gum is initialized before using any Module methods which requires + // intialization. + // Note that Gum is expected to be initialized via OnceCell which provides &Gum for every + // instance. + _gum: &'a Gum, + /// Property containing the PID as a number + pub id: u32, + /// Properly specifying the current platform. + pub platform: Os, + /// Property which can be `optional` or `required`, where the latter means Frida will avoid modifying + /// existing code in memory and will not try to run unsigned code. + pub code_signing_policy: CodeSigningPolicy, + /// Contains a Module representing the main executable of the process. + pub main_module: module::ModuleDetailsOwned, +} + +impl<'a> Process<'a> { + /// Initialize a new process + pub fn obtain(gum: &'a Gum) -> Process<'a> { + let id = unsafe { gum_sys::gum_process_get_id() }; + let platform = + num::FromPrimitive::from_u32(unsafe { gum_sys::gum_process_get_native_os() }).unwrap(); + let code_signing_policy = num::FromPrimitive::from_u32(unsafe { + gum_sys::gum_process_get_code_signing_policy() as u32 + }) + .unwrap(); + + let main_module = unsafe { + module::ModuleDetailsOwned::from_module_details(gum_sys::gum_process_get_main_module()) + }; + + Process { + _gum: gum, + id, + platform, + code_signing_policy, + main_module, + } + } + + /// Enumerates memory ranges satisfying `protection` given + pub fn enumerate_ranges(&self, protection: PageProtection) -> Vec> { + struct CallbackData<'a> { + ranges: Vec>, + protection: PageProtection, + } + + unsafe extern "C" fn enumerate_ranges_callback( + details: *const gum_sys::GumRangeDetails, + user_data: gpointer, + ) -> gboolean { + let res = &mut *(user_data as *mut CallbackData); + let r_details = RangeDetails::from_raw(details); + + let prot = r_details.protection(); + if res.protection == prot { + let m_range = r_details.memory_range(); + let file_map = r_details.file_mapping(); + + res.ranges.push(Range { + base: m_range.base_address(), + size: m_range.size(), + protection: prot, + file: file_map, + }); + } + + 1 + } + + let callback_data = CallbackData { + ranges: Vec::new(), + protection: protection.clone(), + }; + + unsafe { + gum_sys::gum_process_enumerate_ranges( + protection as u32, + Some(enumerate_ranges_callback), + &callback_data as *const _ as *mut c_void, + ); + } + + callback_data.ranges + } +} diff --git a/frida-gum/src/range_details.rs b/frida-gum/src/range_details.rs index a9ec40e..092497a 100644 --- a/frida-gum/src/range_details.rs +++ b/frida-gum/src/range_details.rs @@ -25,7 +25,7 @@ use { use alloc::{boxed::Box, string::String}; /// The memory protection of an unassociated page. -#[derive(Clone, FromPrimitive, Debug)] +#[derive(Clone, FromPrimitive, Debug, PartialEq)] #[repr(u32)] pub enum PageProtection { NoAccess = gum_sys::_GumPageProtection_GUM_PAGE_NO_ACCESS as u32,