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
#![recursion_limit = "1024"]
#![cfg_attr(
feature = "nightly",
feature(const_fn_trait_bound, unboxed_closures, abi_thiscall)
)]
#![cfg_attr(
all(feature = "nightly", test),
feature(naked_functions, core_intrinsics, asm)
)]
//! A cross-platform detour library written in Rust.
//!
//! ## Intro
//!
//! This library provides a thread-safe, inline detouring functionality by
//! disassembling and patching functions during runtime, using assembly opcodes
//! allocated within executable memory. It modifies the target functions and
//! replaces their prolog with an unconditional jump.
//!
//! Beyond the basic functionality this library handles several different edge
//! cases:
//!
//! - Relative branches.
//! - RIP relative operands.
//! - Detects NOP-padding.
//! - Relay for large offsets (>2GB).
//! - Supports hot patching.
//!
//! ## Detours
//!
//! Three different types of detours are provided:
//!
//! - [Static](./struct.StaticDetour.html): A static & type-safe interface.
//! Thanks to its static nature it can accept a closure as its detour, but is
//! required to be statically defined at compile time.
//!
//! - [Generic](./struct.GenericDetour.html): A type-safe interface — the same
//! prototype is enforced for both the target and the detour. It is also
//! enforced when invoking the original target.
//!
//! - [Raw](./struct.RawDetour.html): The underlying building block that the
//! others types abstract upon. It has no type-safety and interacts with raw
//! pointers. It should be avoided unless any types are references, or not
//! known until runtime.
//!
//! ## Features
//!
//! - **nightly**: Enabled by default. Required for static detours, due to usage
//! of *const_fn* & *unboxed_closures*. The feature also enables a more
//! extensive test suite.
//!
//! ## Platforms
//!
//! - Both `x86` & `x86-64` are supported.
//!
//! ## Procedure
//!
//! To illustrate a detour on an x86 platform:
//!
//! ```c
//! 0 int return_five() {
//! 1 return 5;
//! 00400020 [b8 05 00 00 00] mov eax, 5
//! 00400025 [c3] ret
//! 2 }
//! 3
//! 4 int detour_function() {
//! 5 return 10;
//! 00400040 [b8 0A 00 00 00] mov eax, 10
//! 00400045 [c3] ret
//! 6 }
//! ```
//!
//! To detour `return_five` the library by default tries to replace five bytes
//! with a relative jump (the optimal scenario), which works in this case.
//! Executable memory will be allocated for the instruction and the function's
//! prolog will be replaced.
//!
//! ```c
//! 0 int return_five() {
//! 1 return detour_function();
//! 00400020 [e9 16 00 00 00] jmp 1b <detour_function>
//! 00400025 [c3] ret
//! 2 }
//! 3
//! 4 int detour_function() {
//! 5 return 10;
//! 00400040 [b8 0a 00 00 00] mov eax, 10
//! 00400045 [c3] ret
//! 6 }
//! ```
//!
//! Beyond what is shown here, a trampoline is also generated so the original
//! function can be called regardless whether the function is hooked or not.
// Re-exports
pub use detours::*;
pub use error::{Error, Result};
pub use traits::{Function, HookableWith};
#[macro_use]
mod macros;
// Modules
mod alloc;
mod arch;
mod detours;
mod error;
mod pic;
mod traits;
mod util;
#[cfg(test)]
mod tests {
use super::*;
use crate::Result;
use matches::assert_matches;
#[test]
fn detours_share_target() -> Result<()> {
#[inline(never)]
extern "C" fn add(x: i32, y: i32) -> i32 {
unsafe { std::ptr::read_volatile(&x as *const i32) + y }
}
let hook1 = unsafe {
extern "C" fn sub(x: i32, y: i32) -> i32 {
x - y
}
GenericDetour::<extern "C" fn(i32, i32) -> i32>::new(add, sub)?
};
unsafe { hook1.enable()? };
assert_eq!(add(5, 5), 0);
let hook2 = unsafe {
extern "C" fn div(x: i32, y: i32) -> i32 {
x / y
}
GenericDetour::<extern "C" fn(i32, i32) -> i32>::new(add, div)?
};
unsafe { hook2.enable()? };
// This will call the previous hook's detour
assert_eq!(hook2.call(5, 5), 0);
assert_eq!(add(10, 5), 2);
Ok(())
}
#[test]
fn same_detour_and_target() {
#[inline(never)]
extern "C" fn add(x: i32, y: i32) -> i32 {
unsafe { std::ptr::read_volatile(&x as *const i32) + y }
}
let err = unsafe { RawDetour::new(add as *const (), add as *const ()).unwrap_err() };
assert_matches!(err, Error::SameAddress);
}
}