pub use self::patcher::Patcher;
pub use self::trampoline::Trampoline;
pub mod meta;
mod patcher;
mod thunk;
mod trampoline;
#[cfg(all(feature = "nightly", test))]
mod tests {
use crate::error::{Error, Result};
use crate::RawDetour;
use matches::assert_matches;
use std::arch::asm;
use std::mem;
type CRet = unsafe extern "C" fn() -> i32;
#[inline(never)]
unsafe fn detour_test(target: CRet, result: i32) -> Result<()> {
let hook = RawDetour::new(target as *const (), ret10 as *const ())?;
assert_eq!(target(), result);
hook.enable()?;
{
assert_eq!(target(), 10);
let original: CRet = mem::transmute(hook.trampoline());
assert_eq!(original(), result);
}
hook.disable()?;
assert_eq!(target(), result);
Ok(())
}
#[test]
fn detour_relative_branch() -> Result<()> {
#[naked]
unsafe extern "C" fn branch_ret5() -> i32 {
asm!(
"
xor eax, eax
je 5f
mov eax, 2
jmp 2f
5:
mov eax, 5
2:
ret",
options(noreturn)
);
}
unsafe { detour_test(mem::transmute(branch_ret5 as usize), 5) }
}
#[test]
fn detour_hotpatch() -> Result<()> {
#[naked]
unsafe extern "C" fn hotpatch_ret0() -> i32 {
asm!(
"
nop
nop
nop
nop
nop
xor eax, eax
ret
mov eax, 5",
options(noreturn)
);
}
unsafe { detour_test(mem::transmute(hotpatch_ret0 as usize + 5), 0) }
}
#[test]
fn detour_padding_after() -> Result<()> {
#[naked]
unsafe extern "C" fn padding_after_ret0() -> i32 {
asm!(
"
mov edi, edi
xor eax, eax
ret
nop
nop",
options(noreturn)
);
}
unsafe { detour_test(mem::transmute(padding_after_ret0 as usize + 2), 0) }
}
#[test]
fn detour_external_loop() {
#[naked]
unsafe extern "C" fn external_loop() -> i32 {
asm!(
"
loop 2f
nop
nop
nop
2:",
options(noreturn)
);
}
let error =
unsafe { RawDetour::new(external_loop as *const (), ret10 as *const ()) }.unwrap_err();
assert_matches!(error, Error::UnsupportedInstruction);
}
#[test]
#[cfg(target_arch = "x86_64")]
fn detour_rip_relative_pos() -> Result<()> {
#[naked]
unsafe extern "C" fn rip_relative_ret195() -> i32 {
asm!(
"
xor eax, eax
mov al, [rip+0x3]
nop
nop
nop
ret",
options(noreturn)
);
}
unsafe { detour_test(rip_relative_ret195, 195) }
}
#[test]
#[cfg(target_arch = "x86_64")]
fn detour_rip_relative_neg() -> Result<()> {
#[naked]
unsafe extern "C" fn rip_relative_prolog_ret49() -> i32 {
asm!(
"
xor eax, eax
mov al, [rip-0x8]
ret",
options(noreturn)
);
}
unsafe { detour_test(rip_relative_prolog_ret49, 49) }
}
unsafe extern "C" fn ret10() -> i32 {
10
}
}