Skip to content

Commit

Permalink
frida-gum: use a singleton gum instance
Browse files Browse the repository at this point in the history
This makes the Gum APIs sane with regards to calling obtain. If this is
liked, I'll send another patch with a similar API for the frida crate.
One note is that a spinlock was added to make it easier to implement the
drop logic correctly.
  • Loading branch information
ajwerner committed Sep 11, 2024
1 parent 35bd22e commit 039580a
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 33 deletions.
2 changes: 1 addition & 1 deletion frida-gum-sys/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
4 changes: 4 additions & 0 deletions frida-gum/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
27 changes: 19 additions & 8 deletions frida-gum/src/interceptor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
//!
use {
crate::{Error, Gum, NativePointer, Result},
core::{marker::PhantomData, ptr},
core::ptr,
frida_gum_sys as gum_sys,
};

Expand All @@ -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(),
}
}

Expand Down Expand Up @@ -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() })
}

Expand Down
51 changes: 43 additions & 8 deletions frida-gum/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -109,24 +112,56 @@ pub use backtracer::*;
pub type Result<T> = core::result::Result<T, error::Error>;

/// 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<GumSingletonHandle> = spin::Mutex::new(None);

type GumSingletonHandle = Option<Arc<GumSingleton>>;

// 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);
Expand Down
27 changes: 11 additions & 16 deletions frida-gum/src/stalker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};

Expand Down Expand Up @@ -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 }
Expand All @@ -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(),
}
}

Expand All @@ -119,15 +119,13 @@ 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(),
}
}

Expand All @@ -137,17 +135,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(),
}
}

Expand Down Expand Up @@ -303,7 +298,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) };
}
Expand Down

0 comments on commit 039580a

Please sign in to comment.