diff --git a/frida-gum-sys/src/lib.rs b/frida-gum-sys/src/lib.rs index e246f1c7..ef97cc98 100644 --- a/frida-gum-sys/src/lib.rs +++ b/frida-gum-sys/src/lib.rs @@ -17,7 +17,7 @@ mod bindings { pub use bindings::*; #[cfg(not(any(target_os = "windows", target_os = "android", target_vendor = "apple",)))] -pub use _frida_g_object_unref as g_object_unref; +pub use {_frida_g_object_ref as g_object_ref, _frida_g_object_unref as g_object_unref}; /// A single disassembled CPU instruction. #[repr(transparent)] diff --git a/frida-gum/Cargo.toml b/frida-gum/Cargo.toml index 7ec4d6ab..6283e27a 100644 --- a/frida-gum/Cargo.toml +++ b/frida-gum/Cargo.toml @@ -28,6 +28,10 @@ num = { version = "0.4.1", default-features = false } num-derive = { version = "0.4.2", default-features = false } num-traits = { version = "0.2.18", default-features = false } paste = { version = "1", default-features = false } +spin = { version = "0.9.8", default-features = false, features = [ + "mutex", + "spin_mutex", +] } [dev-dependencies] lazy_static = "1" diff --git a/frida-gum/src/interceptor.rs b/frida-gum/src/interceptor.rs index adb16dca..9c08bea2 100644 --- a/frida-gum/src/interceptor.rs +++ b/frida-gum/src/interceptor.rs @@ -8,7 +8,7 @@ //! use { crate::{Error, Gum, NativePointer, Result}, - core::{marker::PhantomData, ptr}, + core::ptr, frida_gum_sys as gum_sys, }; @@ -21,25 +21,36 @@ mod invocation_listener; pub use invocation_listener::*; /// Function hooking engine interface. -pub struct Interceptor<'a> { +pub struct Interceptor { interceptor: *mut gum_sys::GumInterceptor, - phantom: PhantomData<&'a gum_sys::GumInterceptor>, + _gum: Gum, } -impl Drop for Interceptor<'_> { +impl Clone for Interceptor { + fn clone(&self) -> Self { + Interceptor { + interceptor: unsafe { + frida_gum_sys::g_object_ref(self.interceptor as *mut _) as *mut _ + }, + _gum: self._gum.clone(), + } + } +} + +impl Drop for Interceptor { fn drop(&mut self) { unsafe { frida_gum_sys::g_object_unref(self.interceptor as *mut _) } } } -impl<'a> Interceptor<'a> { +impl Interceptor { /// Obtain an Interceptor handle, ensuring that the runtime is properly initialized. This may /// be called as many times as needed, and results in a no-op if the Interceptor is /// already initialized. - pub fn obtain<'b: 'a>(_gum: &'b Gum) -> Interceptor<'b> { + pub fn obtain(gum: &Gum) -> Interceptor { Interceptor { interceptor: unsafe { gum_sys::gum_interceptor_obtain() }, - phantom: PhantomData, + _gum: gum.clone(), } } @@ -221,7 +232,7 @@ impl<'a> Interceptor<'a> { /// Should only be called from within a hook or replacement function. #[cfg(feature = "invocation-listener")] #[cfg_attr(docsrs, doc(cfg(feature = "invocation-listener")))] - pub fn current_invocation() -> InvocationContext<'a> { + pub fn current_invocation<'a>() -> InvocationContext<'a> { InvocationContext::from_raw(unsafe { gum_sys::gum_interceptor_get_current_invocation() }) } diff --git a/frida-gum/src/lib.rs b/frida-gum/src/lib.rs index a1c9d97a..127e3a22 100644 --- a/frida-gum/src/lib.rs +++ b/frida-gum/src/lib.rs @@ -64,7 +64,10 @@ use core::{ }; #[cfg(not(feature = "std"))] -use alloc::string::String; +use alloc::{string::String, sync::Arc}; + +#[cfg(feature = "std")] +use std::sync::Arc; pub mod stalker; @@ -109,24 +112,56 @@ pub use backtracer::*; pub type Result = core::result::Result; /// Context required for instantiation of all structures under the Gum namespace. -pub struct Gum; +#[derive(Clone)] +pub struct Gum { + inner: GumSingletonHandle, +} impl Gum { - /// Obtain a Gum handle, ensuring that the runtime is properly initialized. This may - /// be called as many times as needed, and results in a no-op if the Gum runtime is - /// already initialized. - pub unsafe fn obtain() -> Gum { - frida_gum_sys::gum_init_embedded(); - Gum {} + pub fn obtain() -> Self { + let mut singleton = GUM_SINGLETON.lock(); + let handle = singleton.get_or_insert_with(|| Arc::new(GumSingleton::obtain())); + Self { + inner: Some(handle.clone()), + } } } impl Drop for Gum { + fn drop(&mut self) { + let instance = self.inner.take().expect("instance taken more than once"); + drop(instance); + // If there are no outstanding Gum instances, drop the singleton. + let mut singleton = GUM_SINGLETON.lock(); + let Some(it) = singleton.take_if(|it| Arc::strong_count(it) == 1) else { + return; + }; + Arc::try_unwrap(it) + .ok() + .expect("should destroy the one instance"); + } +} + +static GUM_SINGLETON: spin::Mutex = spin::Mutex::new(None); + +type GumSingletonHandle = Option>; + +// A marker type that exists only while Gum is initialized. +struct GumSingleton; + +impl Drop for GumSingleton { fn drop(&mut self) { unsafe { frida_gum_sys::gum_deinit_embedded() }; } } +impl GumSingleton { + fn obtain() -> Self { + unsafe { frida_gum_sys::gum_init_embedded() }; + Self + } +} + #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] #[repr(transparent)] pub struct NativePointer(pub *mut c_void); diff --git a/frida-gum/src/stalker.rs b/frida-gum/src/stalker.rs index a78e8b73..3444a38a 100644 --- a/frida-gum/src/stalker.rs +++ b/frida-gum/src/stalker.rs @@ -42,7 +42,7 @@ use { crate::{Gum, MemoryRange, NativePointer}, - core::{ffi::c_void, marker::PhantomData}, + core::ffi::c_void, frida_gum_sys as gum_sys, }; @@ -88,12 +88,12 @@ mod observer; pub use observer::*; /// Code tracing engine interface. -pub struct Stalker<'a> { +pub struct Stalker { stalker: *mut frida_gum_sys::GumStalker, - phantom: PhantomData<&'a frida_gum_sys::GumStalker>, + _gum: Gum, } -impl<'a> Stalker<'a> { +impl Stalker { /// Checks if the Stalker is supported on the current platform. pub fn is_supported(_gum: &Gum) -> bool { unsafe { frida_gum_sys::gum_stalker_is_supported() != 0 } @@ -104,12 +104,12 @@ impl<'a> Stalker<'a> { /// This call has the overhead of checking if the Stalker is /// available on the current platform, as creating a Stalker on an /// unsupported platform results in unwanted behaviour. - pub fn new<'b: 'a>(gum: &'b Gum) -> Stalker<'b> { + pub fn new(gum: &Gum) -> Stalker { assert!(Self::is_supported(gum)); Stalker { stalker: unsafe { frida_gum_sys::gum_stalker_new() }, - phantom: PhantomData, + _gum: gum.clone(), } } @@ -119,15 +119,12 @@ impl<'a> Stalker<'a> { /// available on the current platform, as creating a Stalker on an /// unsupported platform results in unwanted behaviour. #[cfg(all(target_arch = "aarch64", feature = "stalker-params"))] - pub fn new_with_params<'b>(gum: &'b Gum, ic_entries: u32) -> Stalker - where - 'b: 'a, - { + pub fn new_with_params(gum: &Gum, ic_entries: u32) -> Stalker { assert!(Self::is_supported(gum)); Stalker { stalker: unsafe { frida_gum_sys::gum_stalker_new_with_params(ic_entries) }, - phantom: PhantomData, + _gum: gum.clone(), } } @@ -137,17 +134,14 @@ impl<'a> Stalker<'a> { /// available on the current platform, as creating a Stalker on an /// unsupported platform results in unwanted behaviour. #[cfg(all(target_arch = "x86_64", feature = "stalker-params"))] - pub fn new_with_params<'b>(gum: &'b Gum, ic_entries: u32, adjacent_blocks: u32) -> Stalker - where - 'b: 'a, - { + pub fn new_with_params(gum: &Gum, ic_entries: u32, adjacent_blocks: u32) -> Stalker { assert!(Self::is_supported(gum)); Stalker { stalker: unsafe { frida_gum_sys::gum_stalker_new_with_params(ic_entries, adjacent_blocks) }, - phantom: PhantomData, + _gum: gum.clone(), } } @@ -303,7 +297,7 @@ impl<'a> Stalker<'a> { } } -impl<'a> Drop for Stalker<'a> { +impl Drop for Stalker { fn drop(&mut self) { unsafe { gum_sys::g_object_unref(self.stalker as *mut c_void) }; }