lazy_importer 0.1.1

Rust port of Justas Masiulis's lazy_importer for Windows module and export resolution.
Documentation
#![no_std]
#![deny(unsafe_op_in_unsafe_fn)]
#![doc = include_str!("../README.md")]

#[cfg(all(not(windows), not(docsrs)))]
compile_error!("lazy_importer supports Windows targets only.");

#[cfg(test)]
extern crate std;

mod hash;
mod pe;
mod peb;
mod resolver;
mod types;

#[doc(hidden)]
pub mod __private {
    pub use const_random::const_random;

    pub use crate::hash::khash;
    pub use crate::resolver::Cache;
}

pub use resolver::{LazyFunction, LazyModule, ModuleHandle};

mod macros;

#[cfg(test)]
mod windows_tests {
    use core::ffi::{c_char, c_int, c_void};

    #[allow(non_snake_case)]
    #[link(name = "kernel32")]
    unsafe extern "system" {
        fn GetCurrentProcessId() -> u32;
        fn GetModuleHandleA(module_name: *const c_char) -> *mut c_void;
        fn GetProcAddress(module: *mut c_void, proc_name: *const c_char) -> *mut c_void;
    }

    #[allow(non_snake_case)]
    #[link(name = "user32")]
    unsafe extern "system" {
        fn MessageBoxA(
            window: *mut c_void,
            text: *const c_char,
            caption: *const c_char,
            typ: u32,
        ) -> c_int;
    }

    #[test]
    fn resolves_loaded_module() {
        let module = crate::li_module!("KERNEL32.DLL").get();
        assert!(module.is_some());
    }

    #[cfg(feature = "case-insensitive")]
    #[test]
    fn resolves_loaded_module_case_insensitively() {
        let module = crate::li_module!("kernel32.dll").get();
        assert!(module.is_some());
    }

    #[test]
    fn resolves_and_invokes_export() {
        type GetCurrentProcessIdFn = unsafe extern "system" fn() -> u32;

        let function = unsafe {
            crate::li_fn!(GetCurrentProcessId)
                .get::<GetCurrentProcessIdFn>()
                .expect("GetCurrentProcessId should resolve")
        };

        let direct = unsafe { GetCurrentProcessId() };
        let resolved = unsafe { function() };

        assert_eq!(resolved, direct);
    }

    #[test]
    fn resolves_export_in_module() {
        type GetCurrentProcessIdFn = unsafe extern "system" fn() -> u32;

        let kernel32 = crate::li_module!("KERNEL32.DLL")
            .get()
            .expect("kernel32 should resolve");
        let function = unsafe {
            crate::li_fn!(GetCurrentProcessId)
                .get_in::<GetCurrentProcessIdFn>(kernel32)
                .expect("GetCurrentProcessId should resolve in kernel32")
        };

        assert_eq!(unsafe { function() }, unsafe { GetCurrentProcessId() });
    }

    #[test]
    fn resolves_message_box_a() {
        type MessageBoxAFn =
            unsafe extern "system" fn(*mut c_void, *const c_char, *const c_char, u32) -> c_int;

        core::hint::black_box(MessageBoxA as *const ());

        let lazy_message_box = crate::li_fn!(MessageBoxA);
        let address = lazy_message_box
            .address()
            .expect("MessageBoxA address should resolve");
        let function = unsafe {
            lazy_message_box
                .get::<MessageBoxAFn>()
                .expect("MessageBoxA should resolve")
        };

        assert_eq!(address.as_ptr(), function as *const () as *mut c_void);
    }

    #[test]
    fn resolves_api_set_forwarded_export() {
        let kernel32 = crate::li_module!("KERNEL32.DLL")
            .get()
            .expect("kernel32 should resolve");
        let export = crate::li_fn!("SetProcessMitigationPolicy");
        let raw = export
            .raw_address_in(kernel32)
            .expect("kernel32!SetProcessMitigationPolicy should exist");
        let exports = unsafe {
            crate::pe::ExportsDirectory::new(kernel32.as_non_null())
                .expect("kernel32 export directory should parse")
        };

        assert!(
            exports.is_forwarded(raw),
            "SetProcessMitigationPolicy should be a forwarded export"
        );

        let resolved_in_module = export
            .address_in(kernel32)
            .expect("API-set forwarded export should resolve to its host DLL");
        let resolved_global = export
            .address()
            .expect("API-set forwarded export should resolve globally");
        let kernel32_direct = unsafe { GetModuleHandleA(c"kernel32.dll".as_ptr()) };
        assert!(!kernel32_direct.is_null());

        let expected =
            unsafe { GetProcAddress(kernel32_direct, c"SetProcessMitigationPolicy".as_ptr()) };
        assert!(!expected.is_null());
        assert_eq!(resolved_in_module.as_ptr(), expected);
        assert_eq!(resolved_global.as_ptr(), expected);
    }
}