diff --git a/examples/gum/process_check/Cargo.toml b/examples/gum/process_check/Cargo.toml new file mode 100644 index 00000000..55e0f23a --- /dev/null +++ b/examples/gum/process_check/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "process-check" +version = "0.1.0" +authors = ["Xoffio / Ricardo J Marques Montilla"] +edition = "2018" +license = "MIT" +publish = false + +[lib] +crate-type = ["cdylib"] + +[dependencies] +frida-gum = { path = "../../../frida-gum", features = [ + "invocation-listener", + # "std", +] } +ctor = "0.6.1" diff --git a/examples/gum/process_check/README.md b/examples/gum/process_check/README.md new file mode 100644 index 00000000..661e2a13 --- /dev/null +++ b/examples/gum/process_check/README.md @@ -0,0 +1,7 @@ +Example to show all available `Process` functions. + +```sh +cargo build --release + +LD_PRELOAD=../../../target/release/libprocess_check.so cat /tmp/test.txt +``` diff --git a/examples/gum/process_check/src/lib.rs b/examples/gum/process_check/src/lib.rs new file mode 100644 index 00000000..49e73465 --- /dev/null +++ b/examples/gum/process_check/src/lib.rs @@ -0,0 +1,85 @@ +use ctor::ctor; +use frida_gum::{ + interceptor::{Interceptor, InvocationContext, ProbeListener}, + Gum, Module, +}; +use std::{sync::OnceLock, thread}; + +#[derive(Default, Debug)] +struct OpenProbeListener; + +fn sleep_loop(secs: usize) { + for _ in 0..secs { + thread::sleep(std::time::Duration::from_millis(1000)); + } +} + +// I just made this function to test Process.enumerate_threads +impl ProbeListener for OpenProbeListener { + fn on_hit(&mut self, _context: InvocationContext<'_>) { + // println!("on_hit: open()"); + + let mut tasks = Vec::new(); + for _ in 0..3 { + let task = thread::spawn(|| sleep_loop(3)); + tasks.push(task); + } + } +} + +#[ctor] +fn init() { + static CELL: OnceLock = OnceLock::new(); + let gum = CELL.get_or_init(|| Gum::obtain()); + + let handle = thread::spawn(move || { + let mut interceptor = Interceptor::obtain(gum); + + // Load libc and find the export for open() + let module = Module::load(gum, "libc.so.6"); + let open = module.find_export_by_name("open").unwrap(); + + let mut listener = OpenProbeListener; + interceptor.attach_instruction(open, &mut listener).unwrap(); + }); + + let process = frida_gum::Process::obtain(&gum); + + println!("Process Information"); + println!("-------------------"); + println!(" - ID: {}", process.id()); + println!(" - Platform {:?}", process.platform()); + println!( + " - Code signing policy: {:?}", + process.code_signing_policy() + ); + println!(" - Main module: {:?}", process.main_module()); + println!(" - Current directory: {}", process.current_dir()); + println!(" - Home directory: {}", process.home_dir()); + println!(" - Tmp directory: {}", process.tmp_dir()); + println!( + " - Is debugger attached? {}", + process.is_debugger_attached() + ); + println!(" - Current thread ID: {}", process.current_thread_id()); + + println!(" - Enumerate threads:"); + let threads = process.enumerate_threads(); + for thread in threads { + println!(" - {:?}", thread); + } + + println!(" - Enumerate modules:"); + let ranges = process.enumerate_modules(); + for module in ranges { + println!(" - {:?}", module); + } + + println!(" - Enumerate ranges (rwx):"); + let ranges = process.enumerate_ranges(frida_gum::PageProtection::ReadWriteExecute); + for range in ranges { + println!(" - {:#?}", range); + } + + handle.join(); +} diff --git a/frida-gum/src/module.rs b/frida-gum/src/module.rs index f6f309e8..2d0ce5f5 100644 --- a/frida-gum/src/module.rs +++ b/frida-gum/src/module.rs @@ -19,7 +19,7 @@ use std::{ffi::CStr, string::ToString}; use { crate::{Gum, NativePointer, PageProtection, RangeDetails}, - core::{ffi::c_void, fmt}, + core::{ffi::c_void, fmt, fmt::Debug}, cstr_core::CString, frida_gum_sys as gum_sys, frida_gum_sys::{ @@ -28,7 +28,10 @@ use { }; #[cfg(not(feature = "std"))] -use alloc::{boxed::Box, string::String, vec, vec::Vec}; +use { + alloc::{boxed::Box, string::String, string::ToString, vec, vec::Vec}, + core::ffi::CStr, +}; extern "C" fn enumerate_ranges_callout( range_details: *const gum_sys::_GumRangeDetails, @@ -98,6 +101,7 @@ impl Module { } Self { inner: module } } + /// Load a module by name pub fn load(_gum: &Gum, module_name: &str) -> Self { let module_name = CString::new(module_name).unwrap(); @@ -108,7 +112,6 @@ impl Module { } } - #[cfg(feature = "std")] /// Get the name of this module pub fn name(&self) -> String { unsafe { @@ -118,7 +121,6 @@ impl Module { } } - #[cfg(feature = "std")] /// Get the path of this module pub fn path(&self) -> String { unsafe { @@ -310,3 +312,12 @@ impl Module { result } } + +impl Debug for Module { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Module") + .field("name", &self.name()) + .field("path", &self.path()) + .finish_non_exhaustive() + } +} diff --git a/frida-gum/src/process.rs b/frida-gum/src/process.rs index fbf70cb5..8bb1653d 100644 --- a/frida-gum/src/process.rs +++ b/frida-gum/src/process.rs @@ -11,6 +11,7 @@ use crate::{FileMapping, Module, NativePointer, Thread}; use { crate::{Gum, PageProtection, RangeDetails}, core::ffi::{c_char, c_void, CStr}, + core::{fmt, fmt::Debug}, frida_gum_sys as gum_sys, frida_gum_sys::{gboolean, gpointer}, }; @@ -44,15 +45,15 @@ pub enum CodeSigningPolicy { #[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, + Windows = gum_sys::_GumOS_GUM_OS_WINDOWS as u32, + Macos = gum_sys::_GumOS_GUM_OS_MACOS as u32, + Linux = gum_sys::_GumOS_GUM_OS_LINUX as u32, + Ios = gum_sys::_GumOS_GUM_OS_IOS as u32, + Watchos = gum_sys::_GumOS_GUM_OS_WATCHOS as u32, + Tvos = gum_sys::_GumOS_GUM_OS_TVOS as u32, + Android = gum_sys::_GumOS_GUM_OS_ANDROID as u32, + Freebsd = gum_sys::_GumOS_GUM_OS_FREEBSD as u32, + Qnx = gum_sys::_GumOS_GUM_OS_QNX as u32, } pub struct Range<'a> { @@ -72,37 +73,36 @@ pub struct Process<'a> { // 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, } 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 { + Process { _gum: gum } + } + + /// Property containing the PID as a number + pub fn id(&self) -> u32 { + unsafe { gum_sys::gum_process_get_id() } + } + + /// Properly specifying the current platform. + pub fn platform(&self) -> Os { + num::FromPrimitive::from_u32(unsafe { gum_sys::gum_process_get_native_os() }).unwrap() + } + + /// Returns 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 fn code_signing_policy(&self) -> CodeSigningPolicy { + num::FromPrimitive::from_u32(unsafe { gum_sys::gum_process_get_code_signing_policy() as u32 }) - .unwrap(); - - let main_module = unsafe { Module::from_raw(gum_sys::gum_process_get_main_module()) }; + .unwrap() + } - Process { - _gum: gum, - id, - platform, - code_signing_policy, - main_module, - } + /// Returns a Module representing the main executable of the process. + pub fn main_module(&self) -> Module { + unsafe { Module::from_raw(gum_sys::gum_process_get_main_module()) } } pub fn find_module_by_name(&self, module_name: &str) -> Option { @@ -127,6 +127,7 @@ impl<'a> Process<'a> { } } } + /// Returns a string specifying the filesystem path to the current working directory pub fn current_dir(&self) -> String { unsafe { @@ -276,3 +277,14 @@ impl<'a> Process<'a> { callback_data } } + +impl Debug for Range<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Range") + .field("base", &self.base) + .field("size", &self.size) + .field("protection", &self.protection) + .field("file", &self.file) + .finish() + } +} diff --git a/frida-gum/src/range_details.rs b/frida-gum/src/range_details.rs index f5abf9e2..9ffbf099 100644 --- a/frida-gum/src/range_details.rs +++ b/frida-gum/src/range_details.rs @@ -56,7 +56,7 @@ impl fmt::Display for PageProtection { } /// The file association to a page. -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct FileMapping<'a> { path: String, size: usize,