Giter Club home page Giter Club logo

detour-rs's Introduction

detour-rs

Azure build Status crates.io version Documentation Language (Rust)

This is a cross-platform detour library developed in Rust. Beyond the basic functionality, this library handles branch redirects, RIP-relative instructions, hot-patching, NOP-padded functions, and allows the original function to be called using a trampoline whilst hooked.

This is one of few cross-platform detour libraries that exists, and to maintain this feature, not all desired functionality can be supported due to lack of cross-platform APIs. Therefore EIP relocation is not supported.

NOTE: Nightly is currently required for static_detour! and is enabled by default.

Platforms

This library provides CI for these targets:

  • Linux
    • i686-unknown-linux-gnu
    • x86_64-unknown-linux-gnu
    • x86_64-unknown-linux-musl
  • Windows
    • i686-pc-windows-gnu
    • i686-pc-windows-msvc
    • x86_64-pc-windows-gnu
    • x86_64-pc-windows-msvc
  • macOS
    • i686-apple-darwin
    • x86_64-apple-darwin

Installation

Add this to your Cargo.toml:

[dependencies]
detour = "0.8.0"

Example

  • A static detour (one of three different detours):
use std::error::Error;
use detour::static_detour;

static_detour! {
  static Test: /* extern "X" */ fn(i32) -> i32;
}

fn add5(val: i32) -> i32 {
  val + 5
}

fn add10(val: i32) -> i32 {
  val + 10
}

fn main() -> Result<(), Box<dyn Error>> {
  // Reroute the 'add5' function to 'add10' (can also be a closure)
  unsafe { Test.initialize(add5, add10)? };

  assert_eq!(add5(1), 6);
  assert_eq!(Test.call(1), 6);

  // Hooks must be enabled to take effect
  unsafe { Test.enable()? };

  // The original function is detoured to 'add10'
  assert_eq!(add5(1), 11);

  // The original function can still be invoked using 'call'
  assert_eq!(Test.call(1), 6);

  // It is also possible to change the detour whilst hooked
  Test.set_detour(|val| val - 5);
  assert_eq!(add5(5), 0);

  unsafe { Test.disable()? };

  assert_eq!(add5(1), 6);
  Ok(())
}
  • A Windows API hooking example is available here; build it by running:
$ cargo build --example messageboxw_detour

Mentions

Part of the library's external user interface was inspired by minhook-rs, created by Jascha-N, and it contains derivative code of his work.

Appendix

  • EIP relocation

    Should be performed whenever a function's prolog instructions are being executed, simultaneously as the function itself is being detoured. This is done by halting all affected threads, copying the affected instructions and appending a JMP to return to the function. This is barely ever an issue, and never in single-threaded environments, but YMMV.

  • NOP-padding

    int function() { return 0; }
    // xor eax, eax
    // ret
    // nop
    // nop
    // ...

    Functions such as this one, lacking a hot-patching area, and too small to be hooked with a 5-byte jmp, are supported thanks to the detection of code padding (NOP/INT3 instructions). Therefore the required amount of trailing NOP instructions will be replaced, to make room for the detour.

detour-rs's People

Contributors

darfink avatar definitelynobody avatar flawedmatrix avatar hybrideidolon avatar marcaddeo avatar stuarth avatar super-continent avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

detour-rs's Issues

Improve cross-platform support for platforms with greater r^x restrictions

Motivation

Currently, this library uses a few abstractions which assume rwx abstractions exist. This model, however, is becoming increasingly less popular in embedded devices, with iOS and the Nintendo Switch both using doubly mapped memory for JIT, (one being rw- and another being either r-x or --x) rather than remapping existing memory, as this reduces the attack surface for ROP to escalate into arbitrary code execution. This approach is also becoming popular in other systems (another example being .NET 6 using this to harden their JIT against escalation of vulnerabilities)

In order to support more targets, it'd likely be better to abstract away the actual read/write accesses behind a Deref<Target = [u8]> implementation in order to allow target-specific access patterns.

My personal motivation here is possibly working with @Raytwo to add aarch64 support and hopefully eventually make use of it for providing a unified library for modding games on both PC and the Nintendo Switch. Since the Switch doesn't support rwx and has no direct equivalent to mmap*, these changes are necessary in order to make the library workable for our usecase.

*sorta

Implementation Proposal

From reading through the source I'm primarily seeing 2 things which rely on this assumption:

  1. region::protect_with_handle - Being used to rwx map existing r-x memory temporarily in order to enable patching existing code
    • Rework to use a function for getting a RAII guard for accessing the memory as rw-. The RwGuard<'a> would implement Deref<Target = [u8]> in order to deref to the rw- mapping for the memory. For platforms continuing to use the region library, this would simply Deref to the rwx memory, for other platforms it would Deref to the second rw- mapping which would have different addresses but maps to the same memory. This guard would also implement Drop to handle any locking/unlocking/icache clearing that would need to be done.
fn get_as_rw<'a, T>(addr: *const T, size: usize) -> RwGuard<'a>;

impl<'a> Deref for RwGuard<'a> {
    type Target = [u8];
}

impl<'a> DerefMut for RwGuard<'a> {}
  1. mmap - Being used to rwx map entirely new memory to a specific address
    • This one is easier, similar approach to the last one (return a struct which implements Deref<Target=[u8]>) but with less of a RAII guard style usage and more just "this is now a mapping that is owned by this type".
impl RwxMap {
    fn new(addr: *const T, size: usize) -> Result<Self, Error>;
    fn addr(&self) -> *const c_void;
    // ...
}
  • This may require a .lock() method to return a RwGuard or something similar, in order to properly lock/unlock/protect/unprotect memory

Process stopped with exit code 0xC0000409 (STATUS_STACK_BUFFER_OVERRUN)

When hooking a function call within a binary the process stops with this exit code:

Process stopped with exit code 0xC0000409 (STATUS_STACK_BUFFER_OVERRUN)

I read up on this issue here: https://devblogs.microsoft.com/oldnewthing/20190108-00/?p=100655

On functions that the compiler recognizes as subject to buffer overrun problems, the compiler allocates space on the stack before the return address. On function entry, the allocated space is loaded with a security cookie that is computed once at module load. On function exit, and during frame unwinding on 64-bit operating systems, a helper function is called to make sure that the value of the cookie is still the same. A different value indicates that an overwrite of the stack may have occurred. If a different value is detected, the process is terminated.

Can I do something about this with this library or do I need to approach it differently?

Multiple tests fail on macOS Catalina 10.15.2

The tests no longer work on macOS Catalina due changes of their linker (ld64). The default value of the maxprot attribute of the __TEXT segment in the Mach-O header has been changed.

Previously maxprot default value was 0x7 (PROT_READ | PROT_WRITE | PROT_EXEC).
The default value has now been changed to 0x5 (PROT_READ | PROT_EXEC).

This means that mprotect cannot make any region that resides within __TEXT writable.

In theory, this should be resolved by providing the linker flag -Wl,-segprot,__TEXT,rwx,rx but it seems as if the max_prot field is ignored (ref: -segprot,<segment>,<max_prot>,<init_prot>).

In practice max_prot & init_prot are both set to the value of segprot's init_prot.

To top it all off, this becomes even worse due to the fact that init_prot cannot be set to rwx (macOS refuses to execute a file which has a writable __TEXT(init_prot) attribute).

A shoddy workaround is to manually modify and set __TEXT(max_prot) to 0x7 after linking.

printf '\x07' | dd of=<input_file> bs=1 seek=160 count=1 conv=notrunc

NOTE: This relies on the default __TEXT(max_prot) offset to be 0xA0 (the default with ld64).

To be continued...

See agiledragon/gomonkey#10
See Zeex/subhook#45

Detouring a function with a reference parameter

Something goes wrong deep in the innards of the macro when you try to detour an extern "Rust" function that takes a reference as a parameter:

static_detours! {
    struct DetourPrintln: fn(&str);
}
error[E0308]: mismatched types
  --> crates\core\src\lib.rs:21:5
   |
21 | /     static_detours! {
22 | |         pub struct DetourPrintln: fn(&str);
23 | |     }
   | |_____^ one type is more general than the other
   |
   = note: expected type `detour::Function`
              found type `detour::Function`
   = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)

Build Error: 'const_fn' feature removed

I get this error when running cargo build:

   Compiling libudis86-sys v0.2.1
   Compiling mmap-fixed v0.1.5
   Compiling detour v0.8.0
error[E0557]: feature has been removed
 --> E:\Users\justin\.cargo\registry\src\github.com-1ecc6299db9ec823\detour-0.8.0\src\lib.rs:4:11
  |
4 |   feature(const_fn, const_fn_trait_bound, unboxed_closures, abi_thiscall)
  |           ^^^^^^^^ feature has been removed
  |
  = note: split into finer-grained feature gates

error: aborting due to previous error

For more information about this error, try `rustc --explain E0557`.
error: could not compile `detour`

To learn more, run the command again with --verbose.

I'm running Windows and have tried both 32-bit targets, i686-pc-windows-gnu and i686-pc-windows-msvc

Is nightly still needed?

error[E0554]: `#![feature]` may not be used on the stable release channel
 --> C:\Users\Brandon\.cargo\registry\src\github.com-1ecc6299db9ec823\detour-0.8.1\src\lib.rs:4:3
  |
4 |   feature(const_fn_trait_bound, unboxed_closures, abi_thiscall)
  |   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = help: the feature `const_fn_trait_bound` has been stable since 1.61.0 and no longer requires an attribute to enable

For more information about this error, try `rustc --explain E0554`.
error: could not compile `detour` due to previous error

Client Build problem

Compiling detour v0.8.0 (https://github.com/darfink/detour-rs?rev=3b6f17a#3b6f17a8)
error[E0059]: type parameter to bare Fn trait must be a tuple
--> C:\Users\Станислав.cargo\git\checkouts\detour-rs-497fa4e2739f3073\3b6f17a\src\detours\statik.rs:106:8
|
106 | D: Fn<T::Arguments, Output = T::Output> + Send + 'static,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait Tuple is not implemented for ::Arguments
|
note: required by a bound in Fn
help: consider further restricting the associated type
|
106 | D: Fn<T::Arguments, Output = T::Output> + Send + 'static, ::Arguments: Tuple
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

error[E0059]: type parameter to bare Fn trait must be a tuple
--> C:\Users\Станислав.cargo\git\checkouts\detour-rs-497fa4e2739f3073\3b6f17a\src\detours\statik.rs:157:8
|
157 | C: Fn<T::Arguments, Output = T::Output> + Send + 'static,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait Tuple is not implemented for ::Arguments
|
note: required by a bound in Fn
help: consider further restricting the associated type
|
157 | C: Fn<T::Arguments, Output = T::Output> + Send + 'static, ::Arguments: Tuple
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

For more information about this error, try rustc --explain E0059.
error: could not compile detour due to 2 previous errors
warning: build failed, waiting for other jobs to finish...

Named inline assembly labels are denied by default

   Compiling matches v0.1.8
   Compiling detour v0.8.1 (/build/detour-0.8.1.tar.gz)
error: avoid using named labels in inline assembly
  --> src/arch/x86/mod.rs:46:16
   |
46 |                je ret5
   |   ________________^
47 |  |             mov eax, 2
48 |  |             jmp done
   |  |_________________^
49 | ||           ret5:
   | ||______________^
   | |_______________|
   | 
   |
   = note: `#[deny(named_asm_labels)]` on by default
   = help: only local labels of the form `<number>:` should be used in inline asm
   = note: see the asm section of the unstable book <https://doc.rust-lang.org/nightly/unstable-book/library-features/asm.html#labels> for more information

error: avoid using named labels in inline assembly
   --> src/arch/x86/mod.rs:105:18
    |
105 |               loop dest
    |  __________________^
106 | |             nop
107 | |             nop
108 | |             nop
109 | |             dest:",
    | |________________^
    |
    = help: only local labels of the form `<number>:` should be used in inline asm
    = note: see the asm section of the unstable book <https://doc.rust-lang.org/nightly/unstable-book/library-features/asm.html#labels> for more information

error: could not compile `detour` due to 2 previous errors

Update mmap-fixed's ``winapi`` dependency

I apologize in advance as this isn't an issue with this repository exactly, but you don't have a repo dedicated for this.

The dependency mmap-fixed is doubling my compile time simply because of the dependency on winapi 0.2 (which takes 4x as long to compile in comparison to 0.3), and is now causing warnings from cargo about code that will soon be rejected by Rust..

Would appreciate if the winapi dependency could be updated to 0.3+

unwrap fails on value: RegionFailure(FreeMemory).

thread '<unnamed>' panicked at 'called `Result::unwrap()` on an `Err` value: RegionFailure(FreeMemory)', src\lib.rs:59:89

I'm making a dll exploit with rust and detour-rs, I'm using it to manipulate lua vms and so far this library is great. I got it working on other version of the executable but this one i'm trying to do right now isn't working

Line 59 (causing the error) is this

// static mut variable
static mut GETTOP_HOOK: Option<GenericDetour<fn(i32) -> i32>> = None;
...
// LUA_GETTOP is an address
    let orig_lua_gettop =
            std::mem::transmute::<*const usize, fn(i32) -> i32>(LUA_GETTOP as *const usize);
...
// fails on here, hooked_function is a function.
  GETTOP_HOOK =
            Some(GenericDetour::<fn(i32) -> i32>::new(orig_lua_gettop, hooked_function).unwrap());

The unwrap fails for something giving me the RegionFailure error, I don't know why. This same code works with a different exe with different addresses. Also, i checked and LUA_GETTOP is the right addresses.

Compiled for i686-pc-windows-msvc and release, is there something that could be relating to this problem?

Not able to hook COM functions properly?

Trying the following trivial-ish example to test out hooking Direct3D11 Present but the hooked application just hangs on call. "present" is never printed to stdout but the hooked application will hang indefinitely until manually closed. SEV just gives me AppHangB1 with no useful output. Function pointers seem to be fetched properly and target process and hook DLL is x64.

I'm using dll-syringe to do my injection, which seemed to work for the MessageBox example. However unlike the MessageBox I get the function pointer to hook from the COM vtable as one would do in traditional hooking of COM interface methods.

use std::error::Error;
use std::mem::size_of;
use std::ptr::{null, null_mut};
use detour::static_detour;

use winapi::shared::dxgi::*;
use winapi::shared::dxgitype::*;
use winapi::shared::minwindef::*;
use winapi::shared::windef::*;
use winapi::um::d3d11::*;
use winapi::um::d3dcommon::*;
use winapi::shared::dxgiformat::*;

use winapi::um::libloaderapi::{DisableThreadLibraryCalls, GetModuleHandleW};
use winapi::um::unknwnbase::IUnknown;
use winapi::um::winnt::*;
use winapi::um::winuser::*;
use windy_macros::wstr;
use windy::WStr;

static_detour! {
    static PresentHook:  unsafe extern "system" fn(*mut IDXGISwapChain, UINT, UINT) -> HRESULT;
}

#[allow(non_snake_case)]
fn present(This: *mut IDXGISwapChain, SyncInterval: UINT, Flags: UINT) -> HRESULT {
    println!("present");
    unsafe { PresentHook.call(This, SyncInterval, Flags) }
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern "system" fn DllMain(
    module: HINSTANCE,
    call_reason: DWORD,
    _reserved: LPVOID,
) -> BOOL {
    unsafe { DisableThreadLibraryCalls(module); }

    if call_reason == DLL_PROCESS_ATTACH {
        unsafe { winapi::um::consoleapi::AllocConsole(); }

        std::thread::spawn(|| unsafe {
            match crate::main() {
                Ok(()) => 0 as u32,
                Err(e) => {
                    println!("Error occurred when injecting: {}", e);
                    1
                }
            }
        });
    }
    winapi::shared::minwindef::TRUE
}

unsafe fn main() -> Result<(), Box<dyn Error>> {
    let vtable = get_d3d11_vtables().as_ref().unwrap();
    println!("Found Present Pointer at {:p}", vtable.Present as *const ());
    PresentHook.initialize(vtable.Present, present)?;

    PresentHook.enable()?;

    // prints 'Hook activated' but target program hangs on `Present`
    println!("Hook activated");
    Ok(())
}

unsafe fn get_render_window() -> (WNDCLASSEXW, HWND) {
    let window_class_name = wstr!("DxHookWindowClass");
    let window_class = WNDCLASSEXW {
        cbSize: size_of::<WNDCLASSEXW>() as UINT,
        style: CS_HREDRAW | CS_VREDRAW,
        lpfnWndProc: Some(DefWindowProcW),
        cbClsExtra: 0,
        cbWndExtra: 0,
        hInstance: GetModuleHandleW(null()),
        hIcon: 0 as HICON,
        hCursor: 0 as HCURSOR,
        hbrBackground: 0 as HBRUSH,
        lpszMenuName: 0 as LPCWSTR,
        lpszClassName: window_class_name.as_ptr(),
        hIconSm: 0 as HICON
    };

    RegisterClassExW(&window_class);

    let hwnd = CreateWindowExW(0,
                               window_class.lpszClassName,
                               wstr!("DirectXWindow").as_ptr(),
        WS_OVERLAPPEDWINDOW,
        0, 0, 100, 100, 0 as HWND,
                               0 as HMENU, window_class.hInstance,
                               0 as LPVOID
    );

    (window_class, hwnd)
}

unsafe fn get_d3d11_vtables() -> *const IDXGISwapChainVtbl {
    let (window_class, hwnd) = get_render_window();
    println!("made new hwnd {:?}", hwnd);
    let swapchain_desc = DXGI_SWAP_CHAIN_DESC {
        BufferDesc: DXGI_MODE_DESC {
            Width: 100,
            Height: 100,
            RefreshRate: DXGI_RATIONAL {
                Numerator: 60,
                Denominator: 1
            },
            Format: DXGI_FORMAT_R8G8B8A8_UNORM,
            ScanlineOrdering: DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED,
            Scaling: 0
        },
        SampleDesc: DXGI_SAMPLE_DESC {
            Count: 1,
            Quality: 0
        },
        BufferUsage: DXGI_USAGE_RENDER_TARGET_OUTPUT,
        BufferCount: 1,
        OutputWindow: hwnd,
        Windowed: 1,
        SwapEffect: DXGI_SWAP_EFFECT_DISCARD,
        Flags: DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH
    };

    let feature_levels = [D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_11_1];
    let mut out_swapchain = null_mut::<IDXGISwapChain>();
    let mut out_device = null_mut::<ID3D11Device>();
    let mut out_context = null_mut::<ID3D11DeviceContext>();
    let mut out_feature_level : D3D_FEATURE_LEVEL = 0;
    //
    let result = D3D11CreateDeviceAndSwapChain(
        null_mut::<_>(),
        D3D_DRIVER_TYPE_HARDWARE,
        0 as HMODULE,
        0,
        feature_levels.as_ptr(),
        feature_levels.len() as UINT,
        D3D11_SDK_VERSION,
        &swapchain_desc,
        &mut out_swapchain,
        &mut out_device,
        &mut out_feature_level,
        &mut out_context
    );
    println!("d3dhresult {:x?}", 0);

    let swapchain_vtbl = (*out_swapchain).lpVtbl;

    out_swapchain.as_ref().map(|f| f.Release());
    out_device.as_ref().map(|f| f.Release());
    out_context.as_ref().map(|f| f.Release());

    CloseWindow(hwnd);
    UnregisterClassW(window_class.lpszClassName, window_class.hInstance);
    
    swapchain_vtbl
}

Can not compile then call original function.

Hi. I try use detour for ws2_32::send.
But then i try to execute original function from detour closure:

pub(super) type Function = unsafe extern "system" fn(winapi::SOCKET, *const winapi::c_char, winapi::c_int, winapi::c_int) -> winapi::c_int;

static_detours! {
    struct Send: unsafe extern "system" fn(winapi::SOCKET, *const winapi::c_char, winapi::c_int, winapi::c_int) -> winapi::c_int;
}

Send.initialize(
                ws2_32::send,
                move |socket, buffer, length, flags| {
...
let original = Send.get().expect("Get original function fail.");
let result = original.call(
    socket as u64,
    buf.as_ptr() as *const i8,
    buf.len() as i32,
    flags as i32,
);
...

Code does not compiles.
Error:

error[E0599]: no method named `call` found for type `&detour::GenericDetour<unsafe extern "system" fn(u64, *const i8, i32, i32) -> i32>` in the current scope

let result = original.call(
                      ^^^^

note: the method `call` exists but the following trait bounds were not satisfied:
            `&detour::GenericDetour<unsafe extern "system" fn(u64, *const i8, i32, i32) -> i32> : std::ops::Fn<_>`

Where there can be an error?

Infinite recursion in static_detour macro

use jni::JNIEnv;
use jni::sys::{jobject, jbyte, jsize, jclass};

static_detour! {
    static JVM_DefineClassWithSourceHook: unsafe extern "system" fn(env: JNIEnv, name: *const i8, loader: jobject, buf: usize, len: jsize, pd: jobject, source: *const i8) -> jclass;
}

Macro backtrace:

error: recursion limit reached while expanding the macro `static_detour`
   --> <::detour::macros::static_detour macros>:7:1
    |
1   |       / (
2   |       | @ parse_attributes ( $ ( $ input : tt ) * ) | # [ $ attribute : meta ] $ (
3   |       | $ rest : tt ) * ) => {
4   |       | static_detour ! (     
...         |
7   | /     | static_detour ! (
8   | |     | @ parse_access_modifier ( ( $ ( $ input ) * ) ) | $ ( $ rest ) * ) ; } ; (
    | |     |                                                                    ^ in this macro invocation (#3)
    | |_____|____________________________________________________________________|
    |       |
...         |
24  |       | static_detour ! ( @ parse_name ( $ ( $ input ) * (  ) ) | $ ( $ rest ) * ) ; }
    |       | ---------------------------------------------------------------------------- in this macro invocation (#4)
...         |
28  | /     | static_detour ! (
29  | |     | @ parse_unsafe ( $ ( $ input ) * ( $ name ) ) | $ ( $ rest ) * ) ; } ; (
    | |_____|__________________________________________________________________- in this macro invocation (#5)
30  |       | @ parse_unsafe ( $ ( $ input : tt ) * ) | unsafe $ ( $ rest : tt ) * ) => {
31  | /     | static_detour ! (
32  | |     | @ parse_calling_convention ( $ ( $ input ) * ) ( unsafe ) | $ ( $ rest ) * ) ;
    | |_____|______________________________________________________________________________- in this macro invocation (#6)
...         |
38  | /     | static_detour ! (
39  | |     | @ parse_prototype ( $ ( $ input ) * ( $ ( $ modifier ) * extern $ cc ) ) | $ (
40  | |     | $ rest ) * ) ; } ; (
    | |_____|______________- in this macro invocation (#7)
...         |
106 |       | @ generate $ item : item ) => { $ item } ; ( $ ( $ t : tt ) + ) => {
    |       | ---------------------------------------------------------------------------- in this macro invocation (#4)
...         |
28  | /     | static_detour ! (
29  | |     | @ parse_unsafe ( $ ( $ input ) * ( $ name ) ) | $ ( $ rest ) * ) ; } ; (
    | |_____|__________________________________________________________________- in this macro invocation (#5)
30  |       | @ parse_unsafe ( $ ( $ input : tt ) * ) | unsafe $ ( $ rest : tt ) * ) => {
31  | /     | static_detour ! (
32  | |     | @ parse_calling_convention ( $ ( $ input ) * ) ( unsafe ) | $ ( $ rest ) * ) ;
    | |_____|______________________________________________________________________________- in this macro invocation (#6)
...         |
38  | /     | static_detour ! (
39  | |     | @ parse_prototype ( $ ( $ input ) * ( $ ( $ modifier ) * extern $ cc ) ) | $ (
40  | |     | $ rest ) * ) ; } ; (
    | |_____|______________- in this macro invocation (#7)
...         |
107 |       | static_detour ! ( @ parse_attributes (  ) | $ ( $ t ) + ) ; } ;
    |       | -----------------------------------------------------------   -
    |       | |                                                             |
    |       | |                                                             in this expansion of `static_detour!` (#1)
    |       | |                                                             in this expansion of `static_detour!` (#2)
    |       | |                                                             in this expansion of `static_detour!` (#3)
    |       | |                                                             in this expansion of `static_detour!` (#4)
    |       | |                                                             in this expansion of `static_detour!` (#5)
    |       | |                                                             in this expansion of `static_detour!` (#6)
    |       |_|_____________________________________________________________in this expansion of `static_detour!` (#7)
    |         |                                                             in this expansion of `static_detour!` (#8)
    |         in this macro invocation (#2)
    |         in this macro invocation (#8)
    |
   ::: src\lib.rs:16:1
    |
16  | /       static_detour! {
17  | |           static JVMDefineClassWithSourceHook: unsafe extern "system" fn(env: JNIEnv, name: *const i8, loader: jobject, buf: usize, len: jsize, pd: jobject, source: *const i8) -> jclass;
18  | |
19  | |       }
    | |_______- in this macro invocation (#1)
    |

Error then detour has attribute.

This code:

static_detours! {
    #[windows]
    struct Test: /* extern "X" */ fn(i32) -> i32;
}

Throws error:

error: local ambiguity: multiple parsing options: built-in NTs tt ('next') or 1 other option.
  --> examples\temp.rs:19:5
   |
19 |     #[windows]
   |     ^

error: Could not compile `windows`.

To learn more, run the command again with --verbose.

detour function from dll not working?

This example panics with could not find 'MessageBoxW' address for me. Is it still functional, or am I using this incorrectly?

I compile it as a cdylib, and use libloading to load the library in another program. Is this the best way to call the dll for it to override functions? Does it override them for all processes, or no? I'm trying to intercept calls to RegQueryValueExA in the Advapi32.dll library.

Thanks

initialize returning ()?

    let mut hook = unsafe {
        DetourStruct.initialize(createmove_hook, closure_for_createmove);
    };

Seems to return a (). Therefore I can't access any elements.

static_detours! {
    struct DetourStruct: fn(f32, *mut UserCmd) -> bool;
}

Heres my detour struct. Thanks!

How to go about using a custom calling convention

I'm dealing with a variation of thiscall where the this pointer is passed via EAX instead of ECX. Apparently this is some msvc-only optimization. While the detour is working with "thiscall" from #8, it is preserving ECX while I need EAX.

https://reverseengineering.stackexchange.com/questions/16919/what-compiler-uses-a-calling-convention-that-uses-eax-as-the-first-argument-the

Is there any way that I can tell the lib to use EAX or to control what registers go where? Would like to avoid writing my own asm to fix this.

Awesome library, thanks so much for the hard work!

error[E0277]: expected a `Fn<(u32, u32, *mut winapi::ctypes::c_void, u32)>` closure, found `unsafe extern "system" fn(u32, u32, *mut winapi::ctypes::c_void, u32) -> u32 {our_GetSystemFirmwareTable}`

use std::ffi::c_void;
use detour::static_detour;
use winapi::shared::minwindef::{HINSTANCE, DWORD, BOOL, TRUE, LPVOID, UINT};
use winapi::um::libloaderapi::{GetModuleHandleA, GetProcAddress};

static_detour! {
  static GetSystemFirmwareTableHook: unsafe extern "system" fn(DWORD, DWORD, LPVOID, DWORD) -> UINT;
}

type GetSystemFirmwareTableFn = unsafe extern "system" fn(DWORD, DWORD, LPVOID, DWORD) -> UINT;

unsafe extern "system" fn our_GetSystemFirmwareTable(FirmwareTableProviderSignature: DWORD, FirmwareTableID: DWORD, pFirmwareTableBuffer: LPVOID, BufferSize: DWORD) -> UINT {
  unsafe { GetSystemFirmwareTableHook.disable().unwrap() };
  let ret_val = unsafe { GetSystemFirmwareTableHook.call(FirmwareTableProviderSignature, FirmwareTableID, pFirmwareTableBuffer, BufferSize) };
  unsafe { GetSystemFirmwareTableHook.enable().unwrap() };
  return ret_val;
}

fn main() {
  // hook GetSystemFirmwareTable
  let kernel32Module = GetModuleHandleA("kernel32.dll\0".as_ptr() as _);
  let GetSystemFirmwareTableAddress = GetProcAddress(kernel32Module, "GetSystemFirmwareTable\0".as_ptr() as _);
  let ori_GetSystemFirmwareTable: GetSystemFirmwareTableFn = unsafe { std::mem::transmute(GetSystemFirmwareTableAddress) };
  unsafe { GetSystemFirmwareTableHook.initialize(ori_GetSystemFirmwareTable, our_GetSystemFirmwareTable).unwrap() };
  unsafe { GetSystemFirmwareTableHook.enable().unwrap() };
}

C:\Users\Brandon\Desktop\redacted\rust>cargo +nightly-i686-pc-windows-msvc build
   Compiling redacted v0.0.1 (C:\Users\Brandon\Desktop\redacted\rust)
warning: unused import: `std::ffi::c_void`
 --> main.rs:1:5
  |
1 | use std::ffi::c_void;
  |     ^^^^^^^^^^^^^^^^
  |
  = note: `#[warn(unused_imports)]` on by default

warning: unused imports: `BOOL`, `HINSTANCE`, `TRUE`
 --> main.rs:3:33
  |
3 | use winapi::shared::minwindef::{HINSTANCE, DWORD, BOOL, TRUE, LPVOID, UINT};
  |                                 ^^^^^^^^^         ^^^^  ^^^^

error[E0277]: expected a `Fn<(u32, u32, *mut winapi::ctypes::c_void, u32)>` closure, found `unsafe extern "system" fn(u32, u32, *mut winapi::ctypes::c_void, u32) -> u32 {our_GetSystemFirmwareTable}`
   --> main.rs:24:78
    |
24  |   unsafe { GetSystemFirmwareTableHook.initialize(ori_GetSystemFirmwareTable, our_GetSystemFirmwareTable).unwrap() };
    |                                       ----------                             ^^^^^^^^^^^^^^^^^^^^^^^^^^ call the function in a closure: `|| unsafe { /* code */ }`
    |                                       |
    |                                       required by a bound introduced by this call
    |
    = help: the trait `Fn<(u32, u32, *mut winapi::ctypes::c_void, u32)>` is not implemented for `unsafe extern "system" fn(u32, u32, *mut winapi::ctypes::c_void, u32) -> u32 {our_GetSystemFirmwareTable}`
    = note: unsafe function cannot be called generically without an unsafe block
note: required by a bound in `StaticDetour::<T>::initialize`
   --> C:\Users\Brandon\.cargo\registry\src\github.com-1285ae84e5963aae\detour-0.8.1\src\detours\statik.rs:106:8
    |
106 |     D: Fn<T::Arguments, Output = T::Output> + Send + 'static,
    |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `StaticDetour::<T>::initialize`

For more information about this error, try `rustc --explain E0277`.
warning: `redacted` (bin "redacted") generated 2 warnings
error: could not compile `redacted` due to previous error; 2 warnings emitted

Support trailing commas in function parameter lists

Currently, the following code does not work, leading to infinite recursion during macro expansion:

static_detour! {
    static Test: fn(i32,) -> i32;
}

However, it is legal for lists to have trailing commas in Rust:

type Test = fn(i32,) -> i32;

... and this is even recommended when the lists span across multiple lines. Therefore, this should be a nice-to-have feature.

Error Building on nightly-1.67.0 - Tuple trait bounds on Args for Fn-Family of traits

Rust nightly-1.67.0 changes the Fn family of traits, requiring Tuple trait bounds on Fn::Args (rust-lang/rust#99943)

So now when using detour, the following error comes up:

Error
Checking detour v0.8.1
error[E0059]: type parameter to bare `Fn` trait must be a tuple
   --> C:\Users\hpmas\.cargo\registry\src\github.com-1ecc6299db9ec823\detour-0.8.1\src\detours\statik.rs:106:8
    |
106 |     D: Fn<T::Arguments, Output = T::Output> + Send + 'static,
    |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Tuple` is not implemented for `<T as Function>::Arguments`
    |
note: required by a bound in `Fn`
   --> C:\Users\hpmas\.rustup\toolchains\nightly-x86_64-pc-windows-msvc\lib/rustlib/src/rust\library\core\src\ops\function.rs:159:20
    |
159 | pub trait Fn<Args: Tuple>: FnMut<Args> {
    |                    ^^^^^ required by this bound in `Fn`
help: consider further restricting the associated type
    |
106 |     D: Fn<T::Arguments, Output = T::Output> + Send + 'static, <T as Function>::Arguments: Tuple
    |                                                             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error[E0059]: type parameter to bare `Fn` trait must be a tuple
   --> C:\Users\hpmas\.cargo\registry\src\github.com-1ecc6299db9ec823\detour-0.8.1\src\detours\statik.rs:157:8
    |
157 |     C: Fn<T::Arguments, Output = T::Output> + Send + 'static,
    |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Tuple` is not implemented for `<T as Function>::Arguments`
    |
note: required by a bound in `Fn`
   --> C:\Users\hpmas\.rustup\toolchains\nightly-x86_64-pc-windows-msvc\lib/rustlib/src/rust\library\core\src\ops\function.rs:159:20
    |
159 | pub trait Fn<Args: Tuple>: FnMut<Args> {
    |                    ^^^^^ required by this bound in `Fn`
help: consider further restricting the associated type
    |
157 |     C: Fn<T::Arguments, Output = T::Output> + Send + 'static, <T as Function>::Arguments: Tuple
    |                                                             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
For more information about this error, try `rustc --explain E0059`.
error: could not compile `detour` due to 2 previous errors

I assume just the trait definition of Function needs to be updated. I know this repo hasn't had any activity in a while, but I saw some forks make commits to fix this. If there are any contributors still around that could look at PRs, I don't mind making the changes myself and creating a PR to fix this. Otherwise, maybe one of the other forks could make a PR.

Build errors

Im kinda new to rust so, im sorry if thats not the right place to ask. When i try to run cargo build I get the following error:

E:\Rust\rusty-cube (master -> origin)
λ cargo build
Compiling winapi v0.2.8
Compiling libudis86-sys v0.2.0
Compiling region v2.1.0
error: failed to run custom build command for libudis86-sys v0.2.0

Caused by:
process didn't exit successfully: E:\Rust\rusty-cube\target\debug\build\libudis86-sys-574469093d5e1b3f\build-script-build (exit code: 101)
--- stdout
TARGET = Some("x86_64-pc-windows-gnu")
OPT_LEVEL = Some("0")
TARGET = Some("x86_64-pc-windows-gnu")
HOST = Some("x86_64-pc-windows-gnu")
TARGET = Some("x86_64-pc-windows-gnu")
TARGET = Some("x86_64-pc-windows-gnu")
HOST = Some("x86_64-pc-windows-gnu")
CC_x86_64-pc-windows-gnu = None
CC_x86_64_pc_windows_gnu = None
HOST_CC = None
CC = None
TARGET = Some("x86_64-pc-windows-gnu")
HOST = Some("x86_64-pc-windows-gnu")
CFLAGS_x86_64-pc-windows-gnu = None
CFLAGS_x86_64_pc_windows_gnu = None
HOST_CFLAGS = None
CFLAGS = None
DEBUG = Some("true")
running: "gcc.exe" "-O0" "-ffunction-sections" "-fdata-sections" "-g" "-m64" "-I" "libudis86" "-includestring.h" "-Wall" "-Wextra" "-o" "E:\Rust\rusty-cube\target\debug\build\libudis86-sys-1a1a1859c01f0f11\out\libudis86\decode.o" "-c" "libudis86\decode.c"
cargo:warning=libudis86\decode.c:1:0: sorry, unimplemented: 64-bit mode not compiled in
cargo:warning= /* udis86 - libudis86/decode.c
cargo:warning=
exit code: 1

--- stderr
thread 'main' panicked at '

Internal error occurred: Command "gcc.exe" "-O0" "-ffunction-sections" "-fdata-sections" "-g" "-m64" "-I" "libudis86" "-includestring.h" "-Wall" "-Wextra" "-o" "E:\Rust\rusty-cube\target\debug\build\libudis86-sys-1a1a1859c01f0f11\out\libudis86\decode.o" "-c" "libudis86\decode.c" with args "gcc.exe" did not execute successfully (status code exit code: 1).

', C:\Users\J\.cargo\registry\src\github.com-1ecc6299db9ec823\gcc-0.3.55\src\lib.rs:1672:5
note: run with RUST_BACKTRACE=1 environment variable to display a backtrace.

warning: build failed, waiting for other jobs to finish...
error: build failed

Compatibility issues with lazy_static

When using detour-rs in conjunction with lazy_static in a loop something goes very wrong, to the point where nothing in the program works.

When using lazy_static in a loop, e.g. in this case:

use std::collections::HashMap;
use std::sync::Mutex;

lazy_static! {
    pub static ref HASHMAP: Mutex<HashMap<String, i32>> = Mutex::new(HashMap::new());
}

pub fn hashmap_test() {
    let mut hashmap = HASHMAP.lock().unwrap();
    for i in 0..5000 as i32 {
        hashmap.insert(format!("test{}", i).to_string(), i);
    }

    // Print out a random value from the hashmap to confirm it was successful
    println!("{:?}", hashmap.get("test4343"));
}

and then detouring a function, e.g. d3d9 EndScene:

use {
  detour::{Error, GenericDetour},
  std::mem,
  winapi::{shared::d3d9::LPDIRECT3DDEVICE9, um::winnt::HRESULT},
};

type EndScene = extern "stdcall" fn(LPDIRECT3DDEVICE9) -> HRESULT;

static mut END_SCENE_DETOUR: Result<GenericDetour<EndScene>, Error> = Err(Error::NotInitialized);

pub extern "stdcall" fn hk_end_scene(p_device: LPDIRECT3DDEVICE9) -> HRESULT {
  unsafe {
    return match &END_SCENE_DETOUR {
      Ok(f) => f.call(p_device),
      Err(_) => 80004005, //E_FAIL
    };
  }
}

pub fn hook_function(vtable: Vec<*const usize>) {
  unsafe {
    END_SCENE_DETOUR =
      GenericDetour::<EndScene>::new(mem::transmute(*vtable.get(42).unwrap()), hk_end_scene);
    println!("Created Endscene detour");
    match &END_SCENE_DETOUR {
      Ok(o) => {
        o.enable().unwrap();
        println!("Endscene detour enabled");
      }
      _ => {}
    }
  }
}

the whole program failes to execute, and dosen't even allocate a console, like it should:

#![feature(abi_thiscall)]
#![feature(core_intrinsics)]
#![cfg_attr(feature = "const", feature(const_fn, const_vec_new))]

mod d3d9_util;
mod hashmap_test;
mod hook;
mod process;

#[macro_use]
extern crate lazy_static;

use std::thread;

fn init() {
  unsafe {
    winapi::um::consoleapi::AllocConsole();

    hashmap_test::hashmap_test();

    let end_scene = d3d9_util::get_d3d9_vtable().unwrap();

    println!("d3d9Device[42]: {:p}", *end_scene.get(42).unwrap());
    hook::hook_function(end_scene);
  }
}

#[allow(non_snake_case)]
#[no_mangle]
pub unsafe extern "system" fn DllMain(
  _hinst_dll: winapi::shared::minwindef::HINSTANCE,
  reason: u32,
  _: *mut winapi::ctypes::c_void,
) {
  if reason == 1 {
    thread::spawn(init);
  }
}

Both functions function like expected when used seperately but when both are ran, it seems like it fails completely to execute. If the lazy_static HashMap is used outside a loop it functions completely normally.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.