dobby_rs/
lib.rs

1//! This crate is a rusty binding of [Dobby](https://github.com/jmpews/Dobby).
2//!
3//! # Quickstart
4//!
5//! ```
6//! use dobby_rs::{resolve_symbol, hook, Address};
7//! use std::mem::transmute;
8//!
9//! #[inline(never)]
10//! #[no_mangle]
11//! extern "C" fn add(a: u64, b: u64) -> u64 {
12//!     a + b
13//! }
14//!
15//! #[inline(never)]
16//! #[no_mangle]
17//! extern "C" fn sub(a: u64, b: u64) -> u64 {
18//!     a - b
19//! }
20//!
21//! unsafe {
22//!     let addr = add as usize as Address;
23//!     let replace = sub as usize as Address;
24//!
25//!     let origin = hook(addr, replace).unwrap();
26//!     let origin: extern "C" fn(u64, u64) -> u64 = transmute(origin);
27//!
28//!     assert_eq!(origin(2, 1), 2 + 1);
29//!     assert_eq!(add(2, 1), 2 - 1);
30//! }
31//! ```
32
33use dobby_sys::ffi;
34use std::ffi::{c_void, CString};
35
36pub type Address = *mut c_void;
37
38/// Locate a symbol name within an image.
39/// This function will return `None` if the symbol does not exist or the image has not been loaded yet.
40/// # Panics
41/// Panic if `image_name` or `symbol_name` contains byte '\x00'
42pub fn resolve_symbol(image_name: &str, symbol_name: &str) -> Option<Address> {
43    let image_name = CString::new(image_name).unwrap();
44    let symbol_name = CString::new(symbol_name).unwrap();
45
46    let addr = unsafe { ffi::DobbySymbolResolver(image_name.as_ptr(), symbol_name.as_ptr()) };
47    if addr.is_null() {
48        None
49    } else {
50        Some(addr)
51    }
52}
53
54#[derive(Debug, thiserror::Error)]
55pub enum DobbyHookError {
56    #[error("hook error")]
57    HookError,
58}
59
60/// Set up a hook at `addr`, return the trampoline address of the original function.
61/// # Safety
62/// THIS FUNCTION IS NOT SAFE.
63pub unsafe fn hook(addr: Address, replace: Address) -> Result<Address, DobbyHookError> {
64    let mut origin = std::ptr::null_mut();
65    hook_and_update_origin(addr, replace, &mut origin)?;
66    Ok(origin)
67}
68
69/// Set up a hook at `addr`. The trampoline address will be set simultaneously.
70/// # Safety
71/// THIS FUNCTION IS NOT SAFE.
72pub unsafe fn hook_and_update_origin(
73    addr: Address,
74    replace: Address,
75    origin: &mut Address,
76) -> Result<(), DobbyHookError> {
77    let ret = ffi::DobbyHook(addr, replace, origin as *mut _);
78    if ret == 0 {
79        Ok(())
80    } else {
81        Err(DobbyHookError::HookError)
82    }
83}
84
85/// Undo all hooks at `addr`.
86/// # Safety
87/// THIS FUNCTION IS NOT SAFE.
88pub unsafe fn unhook(addr: Address) -> Result<(), DobbyHookError> {
89    let ret = ffi::DobbyDestroy(addr);
90    if ret == 0 {
91        Ok(())
92    } else {
93        Err(DobbyHookError::HookError)
94    }
95}
96
97#[derive(Debug, thiserror::Error)]
98pub enum DobbyMemoryOperationError {
99    #[error("memory operation error")]
100    MemoryOperationError,
101    #[error("not enough memory")]
102    NotEnough,
103    #[error("not support allocate executable memory")]
104    NotSupportAllocateExecutableMemory,
105    #[error("unknown error")]
106    None,
107}
108
109/// Patch the code at `addr` with supplied bytes.
110/// # Safety
111/// THIS FUNCTION IS NOT SAFE.
112pub unsafe fn patch_code(addr: Address, code: &[u8]) -> Result<(), DobbyMemoryOperationError> {
113    let ret = ffi::CodePatch(addr, code.as_ptr() as *mut _, code.len() as u32);
114    match ret {
115        ffi::MemoryOperationError_kMemoryOperationSuccess => Ok(()),
116        ffi::MemoryOperationError_kMemoryOperationError => {
117            Err(DobbyMemoryOperationError::MemoryOperationError)
118        }
119        ffi::MemoryOperationError_kNotEnough => Err(DobbyMemoryOperationError::NotEnough),
120        ffi::MemoryOperationError_kNotSupportAllocateExecutableMemory => {
121            Err(DobbyMemoryOperationError::NotSupportAllocateExecutableMemory)
122        }
123        ffi::MemoryOperationError_kNone => Err(DobbyMemoryOperationError::None),
124        _ => unreachable!(),
125    }
126}