1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
/*
* Copyright © 2020-2021 Keegan Saunders
*
* Licence: wxWindows Library Licence, Version 3.1
*/
//! Gum bindings for Rust
//!
//! Gum provides a number of utilities for instrumenting binary applications,
//! and traditionally is consumed via the JavaScript API known as GumJS.
//! This crate aims to provide a complete interface to the instrumentation
//! API provided by Gum, rather than GumJS (s.t. these bindings exclude the `Java` and `ObjC`
//! modules).
//!
//! # Quick Start
//! First, ensure that your platform is supported by Gum. You can find a listing of
//! development kits on the [Frida releases page](https://github.com/frida/frida/releases).
//! To get started using Gum, you need to obtain a global [`Gum`] object; this is required
//! to safely ensure that Gum has been properly initialized as required. Next, you are
//! free to use any available APIs, such as the [`stalker::Stalker`]:
//! ```
//! use frida_gum::{Gum, stalker::{Stalker, Transformer}};
//! #[cfg(feature = "event-sink")]
//! use frida_gum::stalker::NoneEventSink;
//! use lazy_static::lazy_static;
//!
//! lazy_static! {
//! static ref GUM: Gum = unsafe { Gum::obtain() };
//! }
//!
//! fn main() {
//! let mut stalker = Stalker::new(&GUM);
//!
//! let transformer = Transformer::from_callback(&GUM, |basic_block, _output| {
//! for instr in basic_block {
//! instr.keep();
//! }
//! });
//!
//! #[cfg(feature = "event-sink")]
//! stalker.follow_me::<NoneEventSink>(&transformer, None);
//! #[cfg(not(feature = "event-sink"))]
//! stalker.follow_me(&transformer);
//! stalker.unfollow_me();
//! }
//! ```
#![cfg_attr(
not(any(
feature = "module-names",
feature = "backtrace",
feature = "memory-access-monitor"
)),
no_std
)]
#![cfg_attr(doc_cfg, feature(doc_cfg))]
#![deny(warnings)]
#![allow(clippy::needless_doctest_main)]
#![allow(clippy::missing_safety_doc)]
#[cfg(not(feature = "module-names"))]
extern crate alloc;
extern crate num;
#[allow(unused_imports)]
#[macro_use]
extern crate num_derive;
use core::{
ffi::{c_char, c_void, CStr},
fmt::{Debug, Display, Formatter, LowerHex, UpperHex},
};
#[cfg(not(feature = "module-names"))]
use alloc::string::String;
pub mod stalker;
pub mod interceptor;
pub mod instruction_writer;
mod module;
pub use module::*;
mod module_map;
pub use module_map::*;
mod error;
pub use error::Error;
mod cpu_context;
pub use cpu_context::*;
#[cfg(feature = "memory-access-monitor")]
mod memory_access_monitor;
#[cfg(feature = "memory-access-monitor")]
pub use memory_access_monitor::*;
mod memory_range;
pub use memory_range::*;
mod range_details;
pub use range_details::*;
mod debug_symbol;
pub use debug_symbol::*;
#[cfg(feature = "backtrace")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "backtrace")))]
mod backtracer;
#[cfg(feature = "backtrace")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "backtrace")))]
pub use backtracer::*;
#[doc(hidden)]
pub type Result<T> = core::result::Result<T, error::Error>;
/// Context required for instantiation of all structures under the Gum namespace.
pub struct Gum;
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 {}
}
}
impl Drop for Gum {
fn drop(&mut self) {
unsafe { frida_gum_sys::gum_deinit_embedded() };
}
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
#[repr(transparent)]
pub struct NativePointer(pub *mut c_void);
impl NativePointer {
/// Check if the pointer is NULL.
pub fn is_null(&self) -> bool {
self.0.is_null()
}
}
impl From<&NativePointer> for *mut c_void {
fn from(other: &NativePointer) -> Self {
other.0
}
}
impl From<NativePointer> for *mut c_void {
fn from(other: NativePointer) -> Self {
other.0
}
}
impl TryFrom<NativePointer> for String {
type Error = Error;
fn try_from(ptr: NativePointer) -> Result<Self> {
if ptr.is_null() {
Err(Error::MemoryAccessError)
} else {
unsafe {
Ok(
Self::from_utf8_lossy(CStr::from_ptr(ptr.0 as *const c_char).to_bytes())
.into_owned(),
)
}
}
}
}
impl AsRef<NativePointer> for NativePointer {
fn as_ref(&self) -> &NativePointer {
self
}
}
impl LowerHex for NativePointer {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
LowerHex::fmt(&(self.0 as usize), f)
}
}
impl UpperHex for NativePointer {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
UpperHex::fmt(&(self.0 as usize), f)
}
}
impl Display for NativePointer {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
Display::fmt(&(self.0 as usize), f)
}
}