Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions examples/gum/process_check/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,5 @@ publish = false
crate-type = ["cdylib"]

[dependencies]
frida-gum = { path = "../../../frida-gum", features = [
"invocation-listener",
# "std",
] }
frida-gum = { path = "../../../frida-gum", features = ["invocation-listener"] }
ctor = "0.6.1"
2 changes: 1 addition & 1 deletion examples/gum/process_check/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ fn init() {
println!(" - Enumerate threads:");
let threads = process.enumerate_threads();
for thread in threads {
println!(" - {:?}", thread);
println!(" - {:#?}", thread);
}

println!(" - Enumerate modules:");
Expand Down
188 changes: 146 additions & 42 deletions frida-gum/src/cpu_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,121 +4,225 @@
* Licence: wxWindows Library Licence, Version 3.1
*/
use {
core::{ffi::c_void, marker::PhantomData},
core::{ffi::c_void, fmt},
frida_gum_sys as gum_sys,
gum_sys::GumCpuContext,
paste::paste,
};

#[cfg(target_arch = "x86_64")]
macro_rules! REG_LIST {
($mac:ident) => {
$mac!(
u64, rip, r15, r14, r13, r12, r11, r10, r9, r8, rdi, rsi, rbp, rsp, rbx, rdx, rcx, rax
);
};
}

#[cfg(target_arch = "x86")]
macro_rules! REG_LIST {
($mac:ident) => {
$mac!(u32, eip, edi, esi, ebp, esp, ebx, edx, ecx, eax);
};
}

#[cfg(target_arch = "arm")]
// TODO(meme) uint32_t r[8];
macro_rules! REG_LIST {
($mac:ident) => {
$mac!(u32, cpsr, pc, sp, r8, r9, r10, r11, r12, lr);
};
}

#[cfg(target_arch = "aarch64")]
// TODO(meme) uint8_t q[128]; uint64_t x[29];
macro_rules! REG_LIST {
($mac:ident) => {
$mac!(u64, pc, sp, fp, lr);
};
}

macro_rules! cpu_accesors {
($reg:ty,$($name:ident),*) => {
$(
pub fn $name(&self) -> $reg {
unsafe { (*self.cpu_context).$name }
unsafe {(*self.as_ptr()).$name}
}

paste! {
pub fn [<set_ $name>](&mut self, $name: $reg) {
unsafe { (*self.cpu_context).$name = $name }
let ctx = self.as_mut_ptr();
unsafe {(*ctx).$name = $name}
}
}
)*
}
}

macro_rules! gen_debug {
($reg:ty,$($name:ident),*) => {
impl fmt::Debug for CpuContext {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut ds = f.debug_struct("CpuContext");
$(
ds.field(stringify!($name), &format_args!("0x{:x}", &self.$name()));
)*
ds.finish()
}
}
}
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CpuContextError {
ReadOnly,
}

pub enum CpuContextAccess {
CpuContextReadOnly = 1,
CpuCcontextReadWrite,
}

pub enum CpuContextType {
ReadOnly(GumCpuContext),
ReadWrite(*mut GumCpuContext),
}

/// Platform-dependent access to processor state.
pub struct CpuContext<'a> {
cpu_context: *mut GumCpuContext,
phantom: PhantomData<&'a GumCpuContext>,
pub struct CpuContext {
cpu_context: CpuContextType,
}

impl<'a> CpuContext<'a> {
pub(crate) fn from_raw(cpu_context: *mut GumCpuContext) -> CpuContext<'a> {
CpuContext {
cpu_context,
phantom: PhantomData,
impl CpuContext {
pub(crate) fn from_raw(
cpu_context: *mut GumCpuContext,
access: CpuContextAccess,
) -> CpuContext {
unsafe {
match access {
CpuContextAccess::CpuContextReadOnly => {
let snapshot = *cpu_context;

CpuContext {
cpu_context: CpuContextType::ReadOnly(snapshot),
}
}
CpuContextAccess::CpuCcontextReadWrite => CpuContext {
cpu_context: CpuContextType::ReadWrite(cpu_context),
},
}
}
}

fn as_ptr(&self) -> *const GumCpuContext {
match self.cpu_context {
CpuContextType::ReadOnly(ctx) => &ctx as *const GumCpuContext,
CpuContextType::ReadWrite(ctx) => ctx as *const GumCpuContext,
}
}

fn as_mut_ptr(&mut self) -> *mut GumCpuContext {
match self.cpu_context {
CpuContextType::ReadOnly(_) => {
panic!("Error: trying to write a read only CPU context.")
}
CpuContextType::ReadWrite(ctx) => ctx as *mut GumCpuContext,
}
}

/// Get a numbered argument from the processor context, determined by the platform calling convention.
pub fn arg(&self, n: u32) -> usize {
unsafe { gum_sys::gum_cpu_context_get_nth_argument(self.cpu_context, n) as usize }
unsafe { gum_sys::gum_cpu_context_get_nth_argument(self.as_ptr() as *mut _, n) as usize }
}

/// Set a numbered argument in the processor context, determined by the platform calling convention.
pub fn set_arg(&mut self, n: u32, value: usize) {
unsafe {
gum_sys::gum_cpu_context_replace_nth_argument(self.cpu_context, n, value as *mut c_void)
pub fn set_arg(&mut self, n: u32, value: usize) -> Result<(), CpuContextError> {
match self.cpu_context {
CpuContextType::ReadOnly(_) => Err(CpuContextError::ReadOnly),
CpuContextType::ReadWrite(_) => {
unsafe {
gum_sys::gum_cpu_context_replace_nth_argument(
self.as_mut_ptr(),
n,
value as *mut c_void,
)
};

Ok(())
}
}
}

/// Get the value of the register used for the platform calling convention's return value.
pub fn return_value(&self) -> usize {
unsafe { gum_sys::gum_cpu_context_get_return_value(self.cpu_context) as usize }
unsafe { gum_sys::gum_cpu_context_get_return_value(self.as_ptr() as *mut _) as usize }
}

/// Set the value of the register used for the platform calling convention's return value.
pub fn set_return_value(&mut self, value: usize) {
unsafe {
gum_sys::gum_cpu_context_replace_return_value(self.cpu_context, value as *mut c_void)
pub fn set_return_value(&mut self, value: usize) -> Result<(), CpuContextError> {
match self.cpu_context {
CpuContextType::ReadOnly(_) => Err(CpuContextError::ReadOnly),
CpuContextType::ReadWrite(_) => {
unsafe {
gum_sys::gum_cpu_context_replace_return_value(
self.as_mut_ptr(),
value as *mut c_void,
)
};
Ok(())
}
}
}

#[cfg(target_arch = "x86_64")]
cpu_accesors!(
u64, rip, r15, r14, r13, r12, r11, r10, r9, r8, rdi, rsi, rbp, rsp, rbx, rdx, rcx, rax
);

#[cfg(target_arch = "x86")]
cpu_accesors!(u32, eip, edi, esi, ebp, esp, ebx, edx, ecx, eax);

#[cfg(target_arch = "arm")]
cpu_accesors!(u32, cpsr, pc, sp, r8, r9, r10, r11, r12, lr);
// TODO(meme) uint32_t r[8];

#[cfg(target_arch = "aarch64")]
cpu_accesors!(u64, pc, sp, fp, lr);
// TODO(meme) uint8_t q[128]; uint64_t x[29];
REG_LIST!(cpu_accesors);

#[cfg(target_arch = "aarch64")]
/// Get the value of the specified general purpose register.
pub fn reg(&self, index: usize) -> u64 {
assert!(index < 29);
unsafe { (*self.cpu_context).x[index] }
unsafe { (*self.as_ptr()).x[index] }
}

#[cfg(target_arch = "aarch64")]
/// Set the value of the specified general purpose register.
pub fn set_reg(&mut self, index: usize, value: u64) {
assert!(index < 29);
unsafe { (*self.cpu_context).x[index] = value };
pub fn set_reg(&mut self, index: usize, value: u64) -> Result<(), CpuContextError> {
match self.cpu_context {
CpuContextType::ReadOnly(_) => Err(CpuContextError::ReadOnly),
CpuContextType::ReadWrite(_) => {
assert!(index < 29);
unsafe { (*self.as_mut_ptr()).x[index] = value };
Ok(())
}
}
}

#[cfg(feature = "backtrace")]
#[cfg_attr(docsrs, doc(cfg(feature = "backtrace")))]
/// Get an accurate backtrace from this CPU context.
pub fn backtrace_accurate(&self) -> Vec<usize> {
crate::Backtracer::accurate_with_context(unsafe { &*self.cpu_context })
crate::Backtracer::accurate_with_context(unsafe { &*self.as_ptr() })
}

#[cfg(feature = "backtrace")]
#[cfg_attr(docsrs, doc(cfg(feature = "backtrace")))]
/// Get a fuzzy backtrace from this CPU context.
pub fn backtrace_fuzzy(&self) -> Vec<usize> {
crate::Backtracer::fuzzy_with_context(unsafe { &*self.cpu_context })
crate::Backtracer::fuzzy_with_context(unsafe { &*self.as_ptr() })
}

#[cfg(feature = "backtrace")]
#[cfg_attr(docsrs, doc(cfg(feature = "backtrace")))]
/// Get an accurate backtrace from this CPU context.
pub fn backtrace_accurate_with_limit(&self, limit: u32) -> Vec<usize> {
crate::Backtracer::accurate_with_context_and_limit(unsafe { &*self.cpu_context }, limit)
crate::Backtracer::accurate_with_context_and_limit(unsafe { &*self.as_ptr() }, limit)
}

#[cfg(feature = "backtrace")]
#[cfg_attr(docsrs, doc(cfg(feature = "backtrace")))]
/// Get a fuzzy backtrace from this CPU context.
pub fn backtrace_fuzzy_with_limit(&self, limit: u32) -> Vec<usize> {
crate::Backtracer::fuzzy_with_context_and_limit(unsafe { &*self.cpu_context }, limit)
crate::Backtracer::fuzzy_with_context_and_limit(unsafe { &*self.as_ptr() }, limit)
}
}

REG_LIST!(gen_debug);
9 changes: 6 additions & 3 deletions frida-gum/src/interceptor/invocation_listener.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
)]

use {
crate::{CpuContext, NativePointer},
crate::{CpuContext, CpuContextAccess, NativePointer},
core::{ffi::c_void, marker::PhantomData},
frida_gum_sys as gum_sys,
};
Expand Down Expand Up @@ -167,7 +167,10 @@ impl<'a> InvocationContext<'a> {
}

/// Get the [`CpuContext`] at the time of invocation.
pub fn cpu_context(&self) -> CpuContext<'_> {
CpuContext::from_raw(unsafe { (*self.context).cpu_context })
pub fn cpu_context(&self) -> CpuContext {
CpuContext::from_raw(
unsafe { (*self.context).cpu_context },
CpuContextAccess::CpuCcontextReadWrite,
)
}
}
7 changes: 6 additions & 1 deletion frida-gum/src/stalker/transformer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
* Licence: wxWindows Library Licence, Version 3.1
*/

use crate::CpuContextAccess;
use frida_gum_sys::Insn;

use {
crate::{instruction_writer::TargetInstructionWriter, CpuContext, Gum},
core::{ffi::c_void, marker::PhantomData},
Expand All @@ -23,7 +25,10 @@ extern "C" fn put_callout_callback(
user_data: *mut c_void,
) {
let mut f = unsafe { Box::from_raw(user_data as *mut Box<dyn FnMut(CpuContext)>) };
f(CpuContext::from_raw(cpu_context));
f(CpuContext::from_raw(
cpu_context,
CpuContextAccess::CpuCcontextReadWrite,
));
// Leak the box again, we want to destruct it in the data_destroy callback.
//
Box::leak(f);
Expand Down
15 changes: 8 additions & 7 deletions frida-gum/src/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use num::FromPrimitive;

#[cfg(feature = "backtrace")]
use crate::Backtracer;
use crate::{CpuContext, NativePointer};
use crate::{CpuContext, CpuContextAccess, NativePointer};

bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
Expand Down Expand Up @@ -81,11 +81,11 @@ impl Thread {
unsafe { &(*self.thread).cpu_context }
}

// todo: return an immutable reference instead of an owned object; without this multiple threads can obtain a Process instance,
// get the same CpuContexts and cause a data race
/// Modifying CpuContexts from multiple threads is unsound, as they may share the same pointer
pub fn cpu_context(&self) -> CpuContext<'_> {
CpuContext::from_raw(self.gum_cpu_context() as *const _ as *mut GumCpuContext)
pub fn cpu_context(&self) -> CpuContext {
CpuContext::from_raw(
self.gum_cpu_context() as *const _ as *mut GumCpuContext,
CpuContextAccess::CpuContextReadOnly,
)
}

pub fn entrypoint(&self) -> Entrypoint {
Expand Down Expand Up @@ -119,6 +119,7 @@ impl Debug for Thread {
.field("id", &self.id())
.field("name", &self.name())
.field("state", &self.state())
.finish_non_exhaustive()
.field("context", &self.cpu_context())
.finish()
}
}
Loading