Skip to content

Commit

Permalink
feat: split InlineDetour → RawDetour/GenericDetour
Browse files Browse the repository at this point in the history
  • Loading branch information
darfink committed Jun 16, 2017
1 parent 5d43691 commit 6c3aa72
Show file tree
Hide file tree
Showing 14 changed files with 371 additions and 115 deletions.
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
[package]
authors = ["Elliott Linder <elliott.darfink@gmail.com>"]
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"
Expand Down
53 changes: 53 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -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.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ detour = "0.1.0"
... and this to your crate root:

```rust
#[macro_use]
extern crate detour;
```

Expand Down
2 changes: 0 additions & 2 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
4 changes: 2 additions & 2 deletions src/example.rs
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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;
}
58 changes: 39 additions & 19 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
//!
Expand Down Expand Up @@ -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;
Expand All @@ -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;

Expand Down
134 changes: 87 additions & 47 deletions src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<T>(target: $fn_type, closure: T)
-> $crate::error::Result<Self> where
T: Fn($($argument_type),*) -> $return_type + Send + 'static {
pub unsafe fn initialize<T>(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 {
Expand All @@ -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)*) ) => {
Expand All @@ -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<Ret: 'static, $($ty: 'static),*> HookableWith<$detour> for $target {}
};

(@impl_safe ($($nm:ident : $ty:ident),*) ($fn_type:ty)) => {
impl<Ret: 'static, $($ty: 'static),*> $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<Ret: 'static, $($ty: 'static),*> 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),*) ());
};
}
Loading

0 comments on commit 6c3aa72

Please sign in to comment.