diff --git a/Cargo.toml b/Cargo.toml index e1ca77cb..7d226441 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,10 +1,10 @@ [package] authors = ["Elliott Linder "] -description = "Cross-platform detour library" +description = "A cross-platform detour library written in Rust" documentation = "https://docs.rs/detour" homepage = "https://github.com/darfink/detour-rs" keywords = ["detour", "hook", "function", "api", "redirect"] -license = "MIT" +license = "BSD-2-Clause" name = "detour" repository = "https://github.com/darfink/detour-rs" version = "0.1.0" diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..33a0ecb3 --- /dev/null +++ b/LICENSE @@ -0,0 +1,53 @@ +detour-rs - A cross-platform detour library written in Rust +Copyright (C) 2017 Elliott Linder. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +=============================================================================== + +minhook-rs - A minimalist x86/x86-64 hooking library for Rust +Copyright (C) 2015 Jascha Neutelings. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/README.md b/README.md index 8f196619..eb032340 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,7 @@ detour = "0.1.0" ... and this to your crate root: ```rust +#[macro_use] extern crate detour; ``` diff --git a/src/error.rs b/src/error.rs index d2ec35ed..751d5505 100644 --- a/src/error.rs +++ b/src/error.rs @@ -11,8 +11,6 @@ error_chain! { errors { /// A static detour has already been initialized AlreadyExisting { display("detour has already been initialized") } - /// The address is not accessable. - InvalidAddress { display("cannot read from address") } /// The address does not contain valid instructions. InvalidCode { display("address contains invalid assembly") } /// The address has no available area for patching. diff --git a/src/example.rs b/src/example.rs index 5242956a..458572d9 100644 --- a/src/example.rs +++ b/src/example.rs @@ -1,4 +1,4 @@ -//! Example of code generated by the macro. **IT MUST NOT BE USED OUTSIDE +//! Example of code generated by a static detour. **IT MUST NOT BE USED OUTSIDE //! THIS CRATE**. //! //! ```c @@ -8,5 +8,5 @@ //! ``` static_detours! { - pub struct Detour: extern "system" fn(i32, String, bool) -> f32; + pub struct Static: extern "system" fn(i32, String, bool) -> f32; } diff --git a/src/lib.rs b/src/lib.rs index 2e43525a..a5626b0d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,25 +5,44 @@ #![cfg_attr(test, feature(core_intrinsics))] #![cfg_attr(test, feature(asm))] -//! A library for cross-platform detours. +//! A cross-platform detour library written in Rust. //! -//! ## Info +//! ## Intro //! -//! This library provides inline detouring functionality by disassembling and -//! patching functions using low-level assembly opcodes, allocated within -//! executable memory. It modifies the target functions in memory and replaces -//! their prolog with an unconditional jump. +//! This library provides 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, all of which are mentioned in the *README*. +//! cases: //! -//! ## Tools +//! - Relative branches. +//! - RIP relative operands. +//! - Detects NOP-padding. +//! - Relay for large offsets (>2GB) +//! - Supports hot patching. //! -//! Static and dynamic alternatives are available, implemented using -//! [static_detours!](./macro.static_detours.html) or -//! [RawDetour](./struct.RawDetour.html) respectively. -//! If possible, static detours should be preferred, since they enable -//! type-safety. +//! ## Detours +//! +//! Three different types of detours are provided: +//! +//! - [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. +//! +//! - [Static](./macro.static_detours.html): A static & type-safe interface. +//! Thanks to its static nature it can accept a closure as its second +//! argument, but on the other hand, it can only have one detour active at a +//! time. +//! +//! - [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 the types used aren't known until runtime. +//! +//! All detours implement the [Detour](./trait.Detour.html) trait, which exposes +//! several methods, and enforces `Sync + Send`. //! //! ## Procedure //! @@ -65,7 +84,7 @@ //! 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. //! -//! *NOTE: Currently x86/x64 is supported on all major platforms.* +//! *NOTE: Currently x86 & x64 is supported on all major platforms.* #[macro_use] extern crate cfg_if; #[macro_use] extern crate error_chain; @@ -78,19 +97,20 @@ extern crate region; extern crate slice_pool; // Re-exports -pub use variant::RawDetour; -//pub use variant::GenericDetour; +pub use variant::{RawDetour, GenericDetour}; +pub use traits::{Detour, Function, HookableWith}; + +#[macro_use] +mod macros; // Modules pub mod error; +mod traits; mod alloc; mod pic; mod util; mod variant; -#[macro_use] -mod macros; - #[cfg(feature = "example")] pub mod example; diff --git a/src/macros.rs b/src/macros.rs index fcb2b7c5..17218448 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -14,6 +14,7 @@ /// ```rust /// # #[macro_use] extern crate lazy_static; /// # #[macro_use] extern crate detour; +/// # use detour::Detour; /// # fn main() { unsafe { example() } } /// static_detours! { /// struct Test: /* [extern "X"] */ fn(i32) -> i32; @@ -125,53 +126,25 @@ macro_rules! static_detours { ($return_type:ty) ($fn_type:ty)) => { static_detours!(@generate $(#[$attribute])* - $($visibility)* struct $name($crate::RawDetour); + $($visibility)* struct $name(()); ); static_detours!(@generate impl $name { /// Constructs a new detour for the target, and initializes the /// static mutex with the supplied closure. - pub unsafe fn initialize(target: $fn_type, closure: T) - -> $crate::error::Result where - T: Fn($($argument_type),*) -> $return_type + Send + 'static { + pub unsafe fn initialize(target: $fn_type, closure: T) -> + $crate::error::Result<$crate::GenericDetour<$fn_type>> + where T: Fn($($argument_type),*) -> $return_type + Send + 'static { let mut static_closure = Self::closure().lock().unwrap(); if static_closure.is_some() { Err($crate::error::ErrorKind::AlreadyExisting.into()) } else { - let detour = $crate::RawDetour::new( - target as *const (), - Self::callback as *const ())?; + let detour = $crate::GenericDetour::<$fn_type>::new(target, Self::callback)?; *static_closure = Some(Box::new(closure)); - Ok($name(detour)) + Ok(detour) } } - #[allow(dead_code)] - /// Calls the original function regardless whether the function - /// is hooked or not. - pub unsafe fn call(&self, $($argument_name: $argument_type),*) -> $return_type { - let trampoline: $fn_type = ::std::mem::transmute(self.0.trampoline()); - trampoline($($argument_name),*) - } - - #[allow(dead_code)] - /// Enables the detour - pub unsafe fn enable(&mut self) -> $crate::error::Result<()> { - self.0.enable() - } - - #[allow(dead_code)] - /// Disables the detour - pub unsafe fn disable(&mut self) -> $crate::error::Result<()> { - self.0.disable() - } - - #[allow(dead_code)] - /// Returns whether the detour is enabled or not. - pub fn is_enabled(&self) -> bool { - self.0.is_enabled() - } - #[doc(hidden)] #[allow(dead_code)] $($modifier) * fn callback($($argument_name: $argument_type),*) -> $return_type { @@ -191,24 +164,24 @@ macro_rules! static_detours { } } ); - static_detours!(@generate - impl Drop for $name { - /// Disables the detour and frees the associated closure. - fn drop(&mut self) { - unsafe { - self.0.disable().expect("disabling detour on drop"); - *Self::closure().lock().unwrap() = None; - } - } - } - ); + //static_detours!(@generate + // impl Drop for $name { + // /// Disables the detour and frees the associated closure. + // fn drop(&mut self) { + // unsafe { + // self.0.disable().expect("disabling detour on drop"); + // *Self::closure().lock().unwrap() = None; + // } + // } + // } + //); }; // Associates each argument type with a dummy name. (@argument_names ($label:ident) ($($input:tt)*) ($($token:tt)*)) => { static_detours!(@argument_names ($label) ($($input)*)( - __arg_0 __arg_1 __arg_2 __arg_3 __arg_4 __arg_5 __arg_6 __arg_7 - __arg_8 __arg_9 __arg_10 __arg_11 __arg_12 __arg_13 __arg_14 __arg_15 + __arg_0 __arg_1 __arg_2 __arg_3 __arg_4 __arg_5 __arg_6 + __arg_7 __arg_8 __arg_9 __arg_10 __arg_11 __arg_12 __arg_13 )($($token)*)()); }; (@argument_names ($label:ident) ($($input:tt)*) ($hd_name:tt $($tl_name:tt)*) ($hd:tt $($tl:tt)*) ($($acc:tt)*) ) => { @@ -225,3 +198,70 @@ macro_rules! static_detours { static_detours!(@parse_attributes () | $($t)+); }; } + +macro_rules! impl_hookable { + (@recurse () ($($nm:ident : $ty:ident),*)) => { + impl_hookable!(@impl_all ($($nm : $ty),*)); + }; + (@recurse ($hd_nm:ident : $hd_ty:ident $(, $tl_nm:ident : $tl_ty:ident)*) ($($nm:ident : $ty:ident),*)) => { + impl_hookable!(@impl_all ($($nm : $ty),*)); + impl_hookable!(@recurse ($($tl_nm : $tl_ty),*) ($($nm : $ty,)* $hd_nm : $hd_ty)); + }; + + (@impl_all ($($nm:ident : $ty:ident),*)) => { + impl_hookable!(@impl_pair ($($nm : $ty),*) ( fn($($ty),*) -> Ret)); + impl_hookable!(@impl_pair ($($nm : $ty),*) (extern "cdecl" fn($($ty),*) -> Ret)); + impl_hookable!(@impl_pair ($($nm : $ty),*) (extern "stdcall" fn($($ty),*) -> Ret)); + impl_hookable!(@impl_pair ($($nm : $ty),*) (extern "fastcall" fn($($ty),*) -> Ret)); + impl_hookable!(@impl_pair ($($nm : $ty),*) (extern "win64" fn($($ty),*) -> Ret)); + impl_hookable!(@impl_pair ($($nm : $ty),*) (extern "C" fn($($ty),*) -> Ret)); + impl_hookable!(@impl_pair ($($nm : $ty),*) (extern "system" fn($($ty),*) -> Ret)); + }; + + (@impl_pair ($($nm:ident : $ty:ident),*) ($($fn_t:tt)*)) => { + impl_hookable!(@impl_fun ($($nm : $ty),*) ($($fn_t)*) (unsafe $($fn_t)*)); + }; + + (@impl_fun ($($nm:ident : $ty:ident),*) ($safe_type:ty) ($unsafe_type:ty)) => { + impl_hookable!(@impl_core ($($nm : $ty),*) ($safe_type)); + impl_hookable!(@impl_core ($($nm : $ty),*) ($unsafe_type)); + + impl_hookable!(@impl_hookable_with ($($nm : $ty),*) ($unsafe_type) ($safe_type)); + impl_hookable!(@impl_safe ($($nm : $ty),*) ($safe_type)); + }; + + (@impl_hookable_with ($($nm:ident : $ty:ident),*) ($target:ty) ($detour:ty)) => { + unsafe impl HookableWith<$detour> for $target {} + }; + + (@impl_safe ($($nm:ident : $ty:ident),*) ($fn_type:ty)) => { + impl $crate::GenericDetour<$fn_type> { + #[doc(hidden)] + pub fn call(&self, $($nm : $ty),*) -> Ret { + unsafe { + let original: $fn_type = ::std::mem::transmute(self.trampoline()); + original($($nm),*) + } + } + } + }; + + (@impl_core ($($nm:ident : $ty:ident),*) ($fn_type:ty)) => { + unsafe impl Function for $fn_type { + type Arguments = ($($ty,)*); + type Output = Ret; + + unsafe fn from_ptr(ptr: *const ()) -> Self { + ::std::mem::transmute(ptr) + } + + fn to_ptr(&self) -> *const () { + unsafe { ::std::mem::transmute(*self) } + } + } + }; + + ($($nm:ident : $ty:ident),*) => { + impl_hookable!(@recurse ($($nm : $ty),*) ()); + }; +} diff --git a/src/traits.rs b/src/traits.rs new file mode 100644 index 00000000..e9541ab3 --- /dev/null +++ b/src/traits.rs @@ -0,0 +1,49 @@ +//! Traits describing detours and applicable functions. +//! +//! Several of the traits in this module are automatically implemented and should +//! generally not be implemented by users of this library. +use error::*; + +/// Generic trait exposing functionality shared between all detours. +pub unsafe trait Detour: Send + Sync { + /// Enables or disables the detour. + unsafe fn toggle(&mut self, enabled: bool) -> Result<()>; + + /// Enables the detour. + unsafe fn enable(&mut self) -> Result<()> { self.toggle(true) } + + /// Disables the detour + unsafe fn disable(&mut self) -> Result<()> { self.toggle(false) } + + /// Returns whether the detour is enabled or not. + fn is_enabled(&self) -> bool; + + /// Returns a reference to the generated trampoline. + fn trampoline(&self) -> &(); +} + +/// Trait representing a function that can be used as a target or detour for +/// detouring. +pub unsafe trait Function: Sized + Copy + Sync + 'static { + /// The argument types as a tuple. + type Arguments; + + /// The return type. + type Output; + + /// Constructs a `Function` from an untyped pointer. + unsafe fn from_ptr(ptr: *const ()) -> Self; + + /// Returns an untyped pointer for this function. + fn to_ptr(&self) -> *const (); +} + +/// Trait indicating that `Self` can be detoured by the given function `D`. +pub unsafe trait HookableWith: Function {} + +unsafe impl HookableWith for T {} + +impl_hookable! { + __arg_0: A, __arg_1: B, __arg_2: C, __arg_3: D, __arg_4: E, __arg_5: F, __arg_6: G, + __arg_7: H, __arg_8: I, __arg_9: J, __arg_10: K, __arg_11: L, __arg_12: M, __arg_13: N +} diff --git a/src/variant/generic.rs b/src/variant/generic.rs new file mode 100644 index 00000000..5a14ce24 --- /dev/null +++ b/src/variant/generic.rs @@ -0,0 +1,68 @@ +use std::marker::PhantomData; +use error::*; +use {Detour, Function, HookableWith, RawDetour}; + +/// This is a type-safe wrapper around [RawDetour](./struct.RawDetour.html). +/// +/// Therefore documentation related to `RawDetour` affects this interface as well. +/// +/// Due to being generated by a macro, the `GenericDetour::call` method is not +/// exposed in the documentation. +/// It accepts the same arguments as `T`, and shares its result type: +/// +/// ```c +/// // Calls the original function regardless of whether it's hooked or not +/// fn call(&self, T::Arguments) -> T::Output +/// ``` +/// +/// # Example +/// +/// ```rust +/// use detour::{Detour, GenericDetour}; +/// +/// fn add5(val: i32) -> i32 { val + 5 } +/// fn add10(val: i32) -> i32 { val + 10 } +/// +/// let mut hook = unsafe { +/// GenericDetour:: i32>::new(add5, add10).unwrap() +/// }; +/// +/// assert_eq!(add5(5), 10); +/// assert_eq!(hook.call(5), 10); +/// +/// unsafe { hook.enable().unwrap() }; +/// +/// assert_eq!(add5(5), 15); +/// assert_eq!(hook.call(5), 10); +/// +/// unsafe { hook.disable().unwrap() }; +/// +/// assert_eq!(add5(5), 10); +/// ``` +#[derive(Debug)] +pub struct GenericDetour { + detour: RawDetour, + target: PhantomData, +} + +impl GenericDetour { + /// Create a new hook given a target function and a compatible detour + /// function. + pub unsafe fn new(target: T, detour: D) -> Result + where T: HookableWith, + D: Function { + RawDetour::new(target.to_ptr(), detour.to_ptr()).map(|raw| GenericDetour { + target: PhantomData, + detour: raw, + }) + } +} + +unsafe impl Detour for GenericDetour { + unsafe fn toggle(&mut self, enabled: bool) -> Result<()> { self.detour.toggle(enabled) } + fn is_enabled(&self) -> bool { self.detour.is_enabled() } + fn trampoline(&self) -> &() { self.detour.trampoline() } +} + +unsafe impl Send for GenericDetour { } +unsafe impl Sync for GenericDetour { } diff --git a/src/variant/mod.rs b/src/variant/mod.rs index 2dc04c4f..713242e8 100644 --- a/src/variant/mod.rs +++ b/src/variant/mod.rs @@ -1,5 +1,5 @@ -mod typed; +mod generic; mod raw; -pub use self::typed::*; +pub use self::generic::*; pub use self::raw::*; diff --git a/src/variant/raw.rs b/src/variant/raw.rs index d593ce1b..a3193ab0 100644 --- a/src/variant/raw.rs +++ b/src/variant/raw.rs @@ -2,7 +2,7 @@ use std::fmt; use std::sync::Mutex; use error::*; -use {pic, util, arch, alloc}; +use {pic, util, arch, alloc, Detour}; lazy_static! { /// Shared allocator for all detours. @@ -17,32 +17,31 @@ lazy_static! { /// # Example /// /// ```rust -/// # extern crate detour; -/// # fn main() { unsafe { example() } } /// use std::mem; -/// use detour::RawDetour; +/// use detour::{Detour, RawDetour}; /// /// fn add5(val: i32) -> i32 { val + 5 } /// fn add10(val: i32) -> i32 { val + 10 } /// -/// unsafe fn example() { -/// let mut hook = RawDetour::new(add5 as *const (), add10 as *const ()).unwrap(); +/// let mut hook = unsafe { +/// RawDetour::new(add5 as *const (), add10 as *const ()).unwrap() +/// }; /// -/// assert_eq!(add5(5), 10); -/// assert_eq!(hook.is_enabled(), false); +/// assert_eq!(add5(5), 10); +/// assert_eq!(hook.is_enabled(), false); /// +/// unsafe { /// hook.enable().unwrap(); -/// { -/// assert!(hook.is_enabled()); +/// assert!(hook.is_enabled()); /// -/// let original: fn(i32) -> i32 = mem::transmute(hook.trampoline()); +/// let original: fn(i32) -> i32 = mem::transmute(hook.trampoline()); +/// +/// assert_eq!(add5(5), 15); +/// assert_eq!(original(5), 10); /// -/// assert_eq!(add5(5), 15); -/// assert_eq!(original(5), 10); -/// } /// hook.disable().unwrap(); -/// assert_eq!(add5(5), 10); /// } +/// assert_eq!(add5(5), 10); /// ``` pub struct RawDetour { patcher: arch::Patcher, @@ -54,6 +53,12 @@ pub struct RawDetour { // TODO: stop all threads in target during patch? impl RawDetour { /// Constructs a new inline detour patcher. + /// + /// The hook is disabled by default. Even when this function is succesful, + /// there is no guaranteee that the detour function will actually get called + /// when the target function gets called. An invocation of the target + /// function might for example get inlined in which case it is impossible to + /// hook at runtime. pub unsafe fn new(target: *const (), detour: *const ()) -> Result { let mut pool = POOL.lock().unwrap(); if !util::is_executable_address(target)? || !util::is_executable_address(detour)? { @@ -81,28 +86,6 @@ impl RawDetour { }) } - /// Enables the detour. - pub unsafe fn enable(&mut self) -> Result<()> { - let _guard = POOL.lock().unwrap(); - self.patcher.toggle(true) - } - - /// Disables the detour. - pub unsafe fn disable(&mut self) -> Result<()> { - let _guard = POOL.lock().unwrap(); - self.patcher.toggle(false) - } - - /// Returns a callable address to the target. - pub fn trampoline(&self) -> *const () { - self.trampoline.as_ptr() as *const () - } - - /// Returns whether the target is hooked or not. - pub fn is_enabled(&self) -> bool { - self.patcher.is_patched() - } - /// Allocates PIC code at the specified address. fn allocate_code(pool: &mut alloc::Allocator, emitter: &pic::CodeEmitter, @@ -117,6 +100,25 @@ impl RawDetour { } } +unsafe impl Detour for RawDetour { + unsafe fn toggle(&mut self, enabled: bool) -> Result<()> { + let _guard = POOL.lock().unwrap(); + self.patcher.toggle(enabled) + } + + fn is_enabled(&self) -> bool { + self.patcher.is_patched() + } + + fn trampoline(&self) -> &() { + unsafe { + (self.trampoline.as_ptr() as *const ()) + .as_ref() + .expect("trampoline should not be null") + } + } +} + impl Drop for RawDetour { /// Disables the detour, if enabled. fn drop(&mut self) { @@ -125,8 +127,12 @@ impl Drop for RawDetour { } impl fmt::Debug for RawDetour { + /// Output whether the detour is enabled or not. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "RawDetour {{ enabled: {}, trampoline: {:?} }}", + write!(f, "Detour {{ enabled: {}, trampoline: {:?} }}", self.is_enabled(), self.trampoline()) } } + +unsafe impl Send for RawDetour { } +unsafe impl Sync for RawDetour { } diff --git a/src/variant/typed.rs b/src/variant/typed.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/src/x86/mod.rs b/src/x86/mod.rs index 284baa7c..509e490e 100644 --- a/src/x86/mod.rs +++ b/src/x86/mod.rs @@ -37,7 +37,7 @@ fn is_within_2gb(displacement: isize) -> bool { mod tests { use std::mem; use error::*; - use RawDetour; + use {Detour, RawDetour}; /// Detours a C function returning an integer, and asserts its return value. unsafe fn detour_test(target: funcs::CRet, result: i32) { diff --git a/tests/lib.rs b/tests/lib.rs index d0a2c6ce..4b90303a 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -4,6 +4,7 @@ extern crate volatile_cell; use std::mem; use volatile_cell::VolatileCell; +use detour::{Detour, RawDetour, GenericDetour}; type FnAdd = extern "C" fn(i32, i32) -> i32; @@ -18,9 +19,9 @@ extern "C" fn sub_detour(x: i32, y: i32) -> i32 { } #[test] -fn basics() { +fn raw() { unsafe { - let mut hook = detour::RawDetour::new(add as *const (), sub_detour as *const ()) + let mut hook = RawDetour::new(add as *const (), sub_detour as *const ()) .expect("target or source is not usable for detouring"); assert_eq!(add(10, 5), 15); @@ -47,6 +48,25 @@ fn basics() { } } +#[test] +fn generic() { + unsafe { + let mut hook = GenericDetour::::new(add, sub_detour) + .expect("target or source is not usable for detouring"); + + assert_eq!(add(10, 5), 15); + assert_eq!(hook.call(10, 5), 15); + hook.enable().unwrap(); + { + assert_eq!(hook.call(10, 5), 15); + assert_eq!(add(10, 5), 5); + } + hook.disable().unwrap(); + assert_eq!(hook.call(10, 5), 15); + assert_eq!(add(10, 5), 15); + } +} + static_detours! { pub struct DetourAdd: extern "C" fn(i32, i32) -> i32; } @@ -72,3 +92,4 @@ fn static_hook() { assert_eq!(add(10, 5), 15); } } +