pub mod utils;
use std::ffi::NulError;
use utils::ForeignLibrary;
use windows_sys::Win32::Foundation::HINSTANCE;
pub use forward_dll_derive::ForwardModule;
pub trait ForwardModule {
fn init(&self) -> ForwardResult<()>;
}
#[doc(hidden)]
#[macro_export]
macro_rules! count {
() => (0usize);
( $x:tt $($xs:tt)* ) => (1usize + $crate::count!($($xs)*));
}
#[macro_export]
macro_rules! forward_dll {
($lib:expr, $name:ident, $($proc:ident)*) => {
static mut $name: $crate::DllForwarder<{ $crate::count!($($proc)*) }> = $crate::DllForwarder {
initialized: false,
module_handle: 0,
lib_name: $lib,
target_functions_address: [
0;
$crate::count!($($proc)*)
],
target_function_names: [
$(stringify!($proc),)*
]
};
$crate::define_function!($lib, $name, 0, $($proc)*);
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! define_function {
($lib:expr, $name:ident, $index:expr, ) => {};
($lib:expr, $name:ident, $index:expr, $export_name:ident = $proc:ident $($procs:tt)*) => {
const _: () = {
fn default_jumper(original_fn_addr: *const ()) -> usize {
if original_fn_addr as usize != 0 {
return original_fn_addr as usize;
}
match $crate::utils::ForeignLibrary::new($lib) {
Ok(lib) => match lib.get_proc_address(std::stringify!($proc)) {
Ok(addr) => return addr as usize,
Err(err) => eprintln!("Error: {}", err)
}
Err(err) => eprintln!("Error: {}", err)
}
exit_fn as usize
}
fn exit_fn() {
std::process::exit(1);
}
#[no_mangle]
pub extern "system" fn $export_name() -> u32 {
unsafe {
std::arch::asm!(
"push rcx",
"push rdx",
"push r8",
"push r9",
"push r10",
"push r11",
options(nostack)
);
std::arch::asm!(
"sub rsp, 28h",
"call rax",
"add rsp, 28h",
in("rax") default_jumper,
in("rcx") $name.target_functions_address[$index],
options(nostack)
);
std::arch::asm!(
"pop r11",
"pop r10",
"pop r9",
"pop r8",
"pop rdx",
"pop rcx",
"jmp rax",
options(nostack)
);
}
1
}
};
$crate::define_function!($lib, $name, ($index + 1), $($procs)*);
};
($lib:expr, $name:ident, $index:expr, $proc:ident $($procs:tt)*) => {
$crate::define_function!($lib, $name, $index, $proc=$proc $($procs)*);
};
}
#[derive(Debug)]
pub enum ForwardError {
Win32Error(&'static str, u32),
StringError(NulError),
AlreadyInitialized,
}
impl std::fmt::Display for ForwardError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match *self {
ForwardError::Win32Error(func_name, err_code) => {
write!(f, "Win32Error: {} {}", func_name, err_code)
}
ForwardError::StringError(ref err) => write!(f, "StringError: {}", err),
ForwardError::AlreadyInitialized => write!(f, "AlreadyInitialized"),
}
}
}
impl std::error::Error for ForwardError {}
pub type ForwardResult<T> = std::result::Result<T, ForwardError>;
pub struct DllForwarder<const N: usize> {
pub initialized: bool,
pub module_handle: HINSTANCE,
pub target_functions_address: [usize; N],
pub target_function_names: [&'static str; N],
pub lib_name: &'static str,
}
impl<const N: usize> DllForwarder<N> {
pub fn forward_all(&mut self) -> ForwardResult<()> {
if self.initialized {
return Err(ForwardError::AlreadyInitialized);
}
let lib = ForeignLibrary::new(self.lib_name)?;
for index in 0..self.target_functions_address.len() {
let addr_in_remote_module = lib.get_proc_address(self.target_function_names[index])?;
self.target_functions_address[index] = addr_in_remote_module as *const usize as usize;
}
self.module_handle = lib.into_raw();
self.initialized = true;
Ok(())
}
}