ezhook 0.2.2

Function hooking for x86
Documentation
#[inline(always)]
pub unsafe fn transmute<T: Copy + 'static, U: Copy + 'static>(value: T) -> U {
    *(&value as *const _ as *const _)
}

#[cfg(test)]
pub use test::*;

#[cfg(test)]
mod test {
    use core::{mem, ptr};

    pub fn black_box<T>(value: T) -> T {
        let result = unsafe { ptr::read_volatile(&value) };
        mem::forget(value);
        result
    }

    #[cfg(unix)]
    pub use unix::*;

    #[cfg(unix)]
    mod unix {
        use core::slice;
        use libc::{
            mmap, mprotect, MAP_ANONYMOUS, MAP_FAILED, MAP_PRIVATE, PROT_EXEC, PROT_READ,
            PROT_WRITE,
        };

        const PAGE_SIZE: usize = 0x1000;

        pub fn unprotect(address: usize, size: usize) {
            let page = address & !(PAGE_SIZE - 1);

            assert_eq!(
                unsafe {
                    mprotect(
                        page as _,
                        address - page + size,
                        PROT_READ | PROT_WRITE | PROT_EXEC,
                    )
                },
                0,
            );
        }

        pub fn allocate(address: usize, size: usize) -> &'static mut [u8] {
            #[cfg(target_os = "linux")]
            let address = {
                extern crate std;

                use std::{
                    fs::File,
                    io::{BufRead, BufReader},
                    vec::Vec,
                };

                let mut address = address;

                let maps = File::open("/proc/self/maps").unwrap();

                for line in BufReader::new(maps).lines() {
                    let range = line.as_ref().unwrap().split(' ').next().unwrap();

                    if let [from, to] = &*range.split('-').collect::<Vec<_>>() {
                        let from = usize::from_str_radix(from, 16).unwrap();
                        let to = usize::from_str_radix(to, 16).unwrap();

                        if from <= address && address < to {
                            address = to;
                        }
                    }
                }

                address
            };

            let region = unsafe {
                mmap(
                    address as _,
                    size,
                    PROT_READ | PROT_WRITE | PROT_EXEC,
                    MAP_PRIVATE | MAP_ANONYMOUS,
                    -1,
                    0,
                )
            };
            assert_ne!(region, MAP_FAILED);

            unsafe { slice::from_raw_parts_mut(region as _, size) }
        }
    }

    #[cfg(windows)]
    pub use windows::*;

    #[cfg(windows)]
    mod windows {
        use core::{
            mem::{self, MaybeUninit},
            slice,
        };
        use winapi::um::{
            memoryapi::{VirtualAlloc, VirtualProtect, VirtualQuery},
            winnt::{MEM_COMMIT, MEM_FREE, MEM_RESERVE, PAGE_EXECUTE_READWRITE},
        };

        const ALLOCATION_GRANULARITY: usize = 0x10000;

        pub fn unprotect(address: usize, size: usize) {
            assert_ne!(
                unsafe { VirtualProtect(address as _, size, PAGE_EXECUTE_READWRITE, &mut 0) },
                0,
            );
        }

        pub fn allocate(address: usize, size: usize) -> &'static mut [u8] {
            let mut address = address & !(ALLOCATION_GRANULARITY - 1);

            let mut info = MaybeUninit::uninit();

            loop {
                assert_eq!(
                    unsafe {
                        VirtualQuery(address as _, info.as_mut_ptr(), mem::size_of_val(&info))
                    },
                    mem::size_of_val(&info),
                );

                if unsafe { info.assume_init().State } & MEM_FREE != 0 {
                    break;
                }

                address += ALLOCATION_GRANULARITY;
            }

            let region = unsafe {
                VirtualAlloc(
                    address as _,
                    size,
                    MEM_COMMIT | MEM_RESERVE,
                    PAGE_EXECUTE_READWRITE,
                )
            };
            assert!(!region.is_null());

            unsafe { slice::from_raw_parts_mut(region as _, size) }
        }
    }
}