darfink / detour-rs Goto Github PK
View Code? Open in Web Editor NEWA cross-platform detour library written in Rust
License: Other
A cross-platform detour library written in Rust
License: Other
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
}
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.
For those just seeing this - I would recommend moving to https://github.com/Hpmason/retour-rs - this seems better maintained.
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...
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.
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
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?
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...
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)
|
It would be nice to add pub(restriction)
support for static_detour
macro.
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?
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
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 forlibudis86-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 withRUST_BACKTRACE=1
environment variable to display a backtrace.
warning: build failed, waiting for other jobs to finish...
error: build failed
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:
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.
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.
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!
It appears to be something with the build script of libudis86-sys v0.2.1
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
From reading through the source I'm primarily seeing 2 things which rely on this assumption:
region::protect_with_handle
- Being used to rwx map existing r-x memory temporarily in order to enable patching existing code
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> {}
mmap
- Being used to rwx map entirely new memory to a specific address
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;
// ...
}
.lock()
method to return a RwGuard
or something similar, in order to properly lock/unlock/protect/unprotect memoryuse 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
Just a friendly heads up - I ran into a broken build when trying to include this crate in another project.
I see that it's fixed in 3b6f17a but that commit is unreleased on crates.
It would be nice to implement std::error::Error
for Error
.
error[E0658]: trait bounds other than Sized
on const fn parameters are unstable
--> src\detours\statik.rs:69:6
|
69 | impl<T: Function> StaticDetour {
| ^
|
= note: see issue #57563 rust-lang/rust#57563 for more information
= help: add #![feature(const_fn_trait_bound)]
to the crate attributes to enable
add Did not resolve
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
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?
Any tutorial? Can I hook d3d11 with this crate?
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.
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)
kernel32-sys is no longer used with winapi 0.3: https://github.com/retep998/winapi-rs#should-i-still-use-those--sys-crates-such-as-kernel32-sys
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+
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!
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
I have a use-case where I want to start an arbitrary program and detour file IO in order to create a virtual fs (similar to usvfs). I think, DetourCreateProcessWithDlls from MS Detours is the correct function to use for that case. Does this library support such a use-case? If not, would it be possible to implement it? Or am I on an entirely wrong track?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.