diff --git a/Cargo.lock b/Cargo.lock index 4329777..136976b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -187,11 +187,11 @@ dependencies = [ "indexmap", "log", "mlua", + "ntapi", "retour", "semver", "serde", "serde_plain", - "tempfile", "thiserror", "toml", "winapi", @@ -407,15 +407,6 @@ dependencies = [ "libc", ] -[[package]] -name = "fastrand" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] - [[package]] name = "flate2" version = "1.0.25" @@ -499,15 +490,6 @@ dependencies = [ "hashbrown", ] -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - [[package]] name = "io-lifetimes" version = "1.0.10" @@ -656,6 +638,15 @@ version = "1.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5b8c256fd9471521bcb84c3cdba98921497f1a331cbc15b8030fc63b82050ce" +[[package]] +name = "ntapi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" +dependencies = [ + "winapi", +] + [[package]] name = "num-traits" version = "0.2.15" @@ -789,15 +780,6 @@ dependencies = [ "bitflags", ] -[[package]] -name = "redox_syscall" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" -dependencies = [ - "bitflags", -] - [[package]] name = "redox_users" version = "0.4.3" @@ -805,7 +787,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ "getrandom", - "redox_syscall 0.2.16", + "redox_syscall", "thiserror", ] @@ -986,19 +968,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "tempfile" -version = "3.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" -dependencies = [ - "cfg-if", - "fastrand", - "redox_syscall 0.3.5", - "rustix", - "windows-sys 0.45.0", -] - [[package]] name = "termcolor" version = "1.2.0" diff --git a/chaudloader/Cargo.toml b/chaudloader/Cargo.toml index 96930bd..db374f5 100644 --- a/chaudloader/Cargo.toml +++ b/chaudloader/Cargo.toml @@ -9,12 +9,11 @@ crate-type = ["cdylib"] [dependencies] retour = "0.1" anyhow = "1" -winapi = { version = "0.3.9", features = ["dxgi"] } +winapi = "0.3.9" log = "0.4" env_logger = "0.9.0" thiserror = "1" zip = "0.6" -tempfile = "3" clean-path = "0.2" windows-libloader = { path = "../windows-libloader" } const_format = "0.2" @@ -26,3 +25,4 @@ indexmap = "1" semver = { version = "1", features = ["serde"] } serde_plain = "1" crc32fast = "1" +ntapi = "0.4" diff --git a/chaudloader/src/assets.rs b/chaudloader/src/assets.rs index 5321ae6..c764cda 100644 --- a/chaudloader/src/assets.rs +++ b/chaudloader/src/assets.rs @@ -1,5 +1,3 @@ -use crate::hooks; - pub mod exedat; pub mod mpak; pub mod msg; @@ -55,42 +53,52 @@ impl Replacer { self.replacers.insert(path.to_path_buf(), Box::new(pack_cb)); } + pub fn purge( + &mut self, + path: &std::path::Path, + ) -> Result, std::io::Error> { + let replacement_path = if let Some(path) = self.replacement_paths.remove(path) { + path + } else { + return Ok(None); + }; + std::fs::remove_file(&replacement_path)?; + Ok(Some(replacement_path)) + } + pub fn get<'a>( &'a mut self, - path: &'a std::path::Path, - ) -> Result<(&'a std::path::Path, bool), std::io::Error> { - Ok(( - match self.replacement_paths.entry(path.to_path_buf()) { - std::collections::hash_map::Entry::Occupied(entry) => entry.into_mut().as_path(), - std::collections::hash_map::Entry::Vacant(entry) => { - let replacer = if let Some(replacer) = self.replacers.get(path) { - replacer - } else { - return Ok((path, false)); - }; - - // Unwrap these hook guards because there's not much we can do if they fail. - let _create_file_a_hook_guard = - unsafe { hooks::HookDisableGuard::new(&hooks::stage1::CreateFileAHook) } - .unwrap(); - let _create_file_w_hook_guard = - unsafe { hooks::HookDisableGuard::new(&hooks::stage1::CreateFileWHook) } - .unwrap(); + path: &std::path::Path, + ) -> Result, std::io::Error> { + Ok(match self.replacement_paths.entry(path.to_path_buf()) { + std::collections::hash_map::Entry::Occupied(entry) => Some(entry.into_mut().as_path()), + std::collections::hash_map::Entry::Vacant(entry) => { + let replacer = if let Some(replacer) = self.replacers.get(path) { + replacer + } else { + return Ok(None); + }; - let dest_f = tempfile::NamedTempFile::new_in(&self.temp_dir)?; - log::info!( - "replacing {} -> {}", - path.display(), - dest_f.path().display() - ); - let (mut dest_f, dest_path) = dest_f.keep()?; + let dest_path = self.temp_dir.join(std::path::Path::new( + &path + .as_os_str() + .to_string_lossy() + .replace("_", "__") + .replace("../", "_DOTDOTSLASH_") + .replace("./", "_DOTSLASH_") + .replace("/", "_SLASH_") + .replace("..\\", "_DOTDOTSLASH_") + .replace(".\\", "_DOTSLASH_") + .replace("\\", "_SLASH_"), + )); + { + let mut dest_f = std::fs::File::create(&dest_path)?; + log::info!("replacing {} -> {}", path.display(), dest_path.display()); replacer(&mut dest_f)?; - - entry.insert(dest_path).as_path() } - }, - true, - )) + Some(entry.insert(dest_path).as_path()) + } + }) } } diff --git a/chaudloader/src/hooks/stage0.rs b/chaudloader/src/hooks/stage0.rs index 2e109eb..a93a1ed 100644 --- a/chaudloader/src/hooks/stage0.rs +++ b/chaudloader/src/hooks/stage0.rs @@ -230,15 +230,14 @@ unsafe fn init(game_volume: crate::GameVolume) -> Result<(), anyhow::Error> { .unwrap() .into_inner(); - // TODO: This path is a little wobbly, since it relies on BNLC specifying this weird relative path. - // We should canonicalize this path instead. - let dat_path = std::path::Path::new("..\\exe\\data").join(&dat_filename); - if !overlay.has_overlaid_files() { continue; } let overlay = std::cell::RefCell::new(overlay); + + // TODO: This path is a little wobbly, since it relies on BNLC specifying this exact path. + let dat_path = std::path::Path::new(&format!("data\\{}", &dat_filename)).to_path_buf(); assets_replacer.add(&dat_path, move |writer| { let mut overlay = overlay.borrow_mut(); Ok(overlay.pack_into(writer)?) diff --git a/chaudloader/src/hooks/stage1.rs b/chaudloader/src/hooks/stage1.rs index e6d7f82..2aef25e 100644 --- a/chaudloader/src/hooks/stage1.rs +++ b/chaudloader/src/hooks/stage1.rs @@ -1,73 +1,353 @@ -use crate::assets; +use crate::{assets, hooks}; use retour::static_detour; use std::os::windows::ffi::OsStrExt; use std::os::windows::ffi::OsStringExt; +type NtCreateFileFunc = unsafe extern "system" fn( + file_handle: winapi::shared::ntdef::PHANDLE, + desired_access: winapi::um::winnt::ACCESS_MASK, + object_attributes: winapi::shared::ntdef::POBJECT_ATTRIBUTES, + io_status_block: ntapi::ntioapi::PIO_STATUS_BLOCK, + allocation_size: winapi::shared::ntdef::PLARGE_INTEGER, + file_attributes: winapi::shared::minwindef::ULONG, + share_access: winapi::shared::ntdef::ULONG, + create_disposition: winapi::shared::minwindef::ULONG, + create_options: winapi::shared::minwindef::ULONG, + ea_buffer: winapi::shared::ntdef::PVOID, + ea_length: winapi::shared::minwindef::ULONG, +) -> winapi::shared::ntdef::NTSTATUS; + +type DuplicateHandleFunc = unsafe extern "system" fn( + h_source_process_handle: winapi::shared::ntdef::HANDLE, + h_source_handle: winapi::shared::ntdef::HANDLE, + h_target_process_handle: winapi::shared::ntdef::HANDLE, + lp_target_handle: winapi::shared::minwindef::LPHANDLE, + dw_desired_access: winapi::shared::minwindef::DWORD, + b_inherit_handle: winapi::shared::minwindef::BOOL, + dw_options: winapi::shared::minwindef::DWORD, +) -> winapi::shared::minwindef::BOOL; + +type CloseHandleFunc = unsafe extern "system" fn( + h_object: winapi::shared::ntdef::HANDLE, +) -> winapi::shared::minwindef::BOOL; + static_detour! { - pub static CreateFileWHook: unsafe extern "system" fn( - /* lp_file_name: */ winapi::shared::ntdef::LPCWSTR, - /* dw_desired_access: */ winapi::shared::minwindef::DWORD, - /* dw_share_mode: */ winapi::shared::minwindef::DWORD, - /* lp_security_attributes: */ winapi::um::minwinbase::LPSECURITY_ATTRIBUTES, - /* dw_creation_disposition: */ winapi::shared::minwindef::DWORD, - /* dw_flags_and_attributes: */ winapi::shared::minwindef::DWORD, - /* handle: */ winapi::shared::ntdef::HANDLE - ) -> winapi::shared::ntdef::HANDLE; - - pub static CreateFileAHook: unsafe extern "system" fn( - /* lp_file_name: */ winapi::shared::ntdef::LPCSTR, + static NtCreateFileHook: unsafe extern "system" fn( + /* file_handle: */ winapi::shared::ntdef::PHANDLE, + /* desired_access: */ winapi::um::winnt::ACCESS_MASK, + /* object_attributes: */ winapi::shared::ntdef::POBJECT_ATTRIBUTES, + /* io_status_block: */ ntapi::ntioapi::PIO_STATUS_BLOCK, + /* allocation_size: */ winapi::shared::ntdef::PLARGE_INTEGER, + /* file_attributes: */ winapi::shared::minwindef::ULONG, + /* share_access: */ winapi::shared::ntdef::ULONG, + /* create_disposition: */ winapi::shared::minwindef::ULONG, + /* create_options: */ winapi::shared::minwindef::ULONG, + /* ea_buffer: */ winapi::shared::ntdef::PVOID, + /* ea_length: */ winapi::shared::minwindef::ULONG + ) -> winapi::shared::ntdef::NTSTATUS; + + static DuplicateHandleHook: unsafe extern "system" fn( + /* h_source_process_handle: */ winapi::shared::ntdef::HANDLE, + /* h_source_handle: */ winapi::shared::ntdef::HANDLE, + /* h_target_process_handle: */ winapi::shared::ntdef::HANDLE, + /* lp_target_handle: */ winapi::shared::minwindef::LPHANDLE, /* dw_desired_access: */ winapi::shared::minwindef::DWORD, - /* dw_share_mode: */ winapi::shared::minwindef::DWORD, - /* lp_security_attributes: */ winapi::um::minwinbase::LPSECURITY_ATTRIBUTES, - /* dw_creation_disposition: */ winapi::shared::minwindef::DWORD, - /* dw_flags_and_attributes: */ winapi::shared::minwindef::DWORD, - /* handle: */ winapi::shared::ntdef::HANDLE - ) -> winapi::shared::ntdef::HANDLE; - - pub static CloseHandle: unsafe extern "system" fn( + /* b_inherit_handle: */ winapi::shared::minwindef::BOOL, + /* dw_options: */ winapi::shared::minwindef::DWORD + ) -> winapi::shared::minwindef::BOOL; + + static CloseHandleHook: unsafe extern "system" fn( /* h_object: */ winapi::shared::ntdef::HANDLE ) -> winapi::shared::minwindef::BOOL; } -unsafe fn on_create_file( - path: &std::path::Path, - dw_desired_access: winapi::shared::minwindef::DWORD, - dw_share_mode: winapi::shared::minwindef::DWORD, - lp_security_attributes: winapi::um::minwinbase::LPSECURITY_ATTRIBUTES, - dw_creation_disposition: winapi::shared::minwindef::DWORD, - dw_flags_and_attributes: winapi::shared::minwindef::DWORD, - handle: winapi::shared::ntdef::HANDLE, -) -> winapi::shared::ntdef::HANDLE { - // FIXME: This path is relative to the exe folder, but is sometimes something like ..\exe\data\exe1.dat. We should canonicalize it in all cases to intercept all reads. - let path = clean_path::clean(path); +struct HooksDisableGuard { + _nt_create_file_guard: hooks::HookDisableGuard, + _duplicate_handle_guard: hooks::HookDisableGuard, + _close_handle_guard: hooks::HookDisableGuard, +} + +impl HooksDisableGuard { + unsafe fn new() -> Result { + Ok(Self { + _nt_create_file_guard: hooks::HookDisableGuard::new(&NtCreateFileHook)?, + _duplicate_handle_guard: hooks::HookDisableGuard::new(&DuplicateHandleHook)?, + _close_handle_guard: hooks::HookDisableGuard::new(&CloseHandleHook)?, + }) + } +} + +static HANDLE_TRACKER: std::sync::LazyLock> = + std::sync::LazyLock::new(|| { + std::sync::Mutex::new(HandleTracker { + handle_to_path: std::collections::HashMap::new(), + path_to_handles: std::collections::HashMap::new(), + }) + }); + +struct HandleTracker { + handle_to_path: std::collections::HashMap, + path_to_handles: + std::collections::HashMap>, +} + +impl HandleTracker { + fn insert(&mut self, path: &std::path::Path, handle: winapi::shared::ntdef::HANDLE) -> usize { + let handle = unsafe { std::mem::transmute(handle) }; + self.handle_to_path.insert(handle, path.to_path_buf()); + let handles = self + .path_to_handles + .entry(path.to_path_buf()) + .or_insert_with(|| std::collections::HashSet::new()); + handles.insert(handle); + handles.len() + } + fn dupe( + &mut self, + src_handle: winapi::shared::ntdef::HANDLE, + dest_handle: winapi::shared::ntdef::HANDLE, + ) -> usize { + let src_handle = unsafe { std::mem::transmute(src_handle) }; + let path = if let Some(path) = self.handle_to_path.get(&src_handle).cloned() { + path + } else { + return 0; + }; + + let dest_handle = unsafe { std::mem::transmute(dest_handle) }; + + self.handle_to_path.insert(dest_handle, path.clone()); + let handles = self.path_to_handles.get_mut(&path).unwrap(); + handles.insert(dest_handle); + handles.len() + } + + fn remove( + &mut self, + handle: winapi::shared::ntdef::HANDLE, + ) -> Option<(std::path::PathBuf, usize)> { + let handle = unsafe { std::mem::transmute(handle) }; + let path = if let Some(path) = self.handle_to_path.remove(&handle) { + path + } else { + return None; + }; + + let mut handles_entry = match self.path_to_handles.entry(path) { + std::collections::hash_map::Entry::Occupied(entry) => entry, + std::collections::hash_map::Entry::Vacant(_) => unreachable!(), + }; + + handles_entry.get_mut().remove(&handle); + let path = handles_entry.key().to_path_buf(); + + Some(( + path, + if handles_entry.get().is_empty() { + handles_entry.remove(); + 0 + } else { + handles_entry.get().len() + }, + )) + } +} + +fn as_ntapi_unicode_string(buf: &mut [u16]) -> winapi::shared::ntdef::UNICODE_STRING { + let byte_len = buf.len() * std::mem::size_of::(); + winapi::shared::ntdef::UNICODE_STRING { + Buffer: buf.as_mut_ptr(), + Length: byte_len as winapi::shared::ntdef::USHORT, + MaximumLength: byte_len as winapi::shared::ntdef::USHORT, + } +} + +unsafe fn osstring_from_ntapi_unicode_string( + ustr: &winapi::shared::ntdef::UNICODE_STRING, +) -> std::ffi::OsString { + std::ffi::OsString::from_wide(std::slice::from_raw_parts( + ustr.Buffer, + ustr.Length as usize / std::mem::size_of::(), + )) +} + +unsafe fn on_nt_create_file( + file_handle: winapi::shared::ntdef::PHANDLE, + desired_access: winapi::um::winnt::ACCESS_MASK, + object_attributes: winapi::shared::ntdef::POBJECT_ATTRIBUTES, + io_status_block: ntapi::ntioapi::PIO_STATUS_BLOCK, + allocation_size: winapi::shared::ntdef::PLARGE_INTEGER, + file_attributes: winapi::shared::minwindef::ULONG, + share_access: winapi::shared::ntdef::ULONG, + create_disposition: winapi::shared::minwindef::ULONG, + create_options: winapi::shared::minwindef::ULONG, + ea_buffer: winapi::shared::ntdef::PVOID, + ea_length: winapi::shared::minwindef::ULONG, +) -> winapi::shared::ntdef::NTSTATUS { + let _hook_disable_guard: HooksDisableGuard = HooksDisableGuard::new().unwrap(); + + if (*(*object_attributes).ObjectName).Length == 0 { + // We are not even opening a named file. + return NtCreateFileHook.call( + file_handle, + desired_access, + object_attributes, + io_status_block, + allocation_size, + file_attributes, + share_access, + create_disposition, + create_options, + ea_buffer, + ea_length, + ); + } + + let object_name = osstring_from_ntapi_unicode_string(&*(*object_attributes).ObjectName); + let path = std::path::Path::new(&object_name); + + // Handle tracker must be locked before asset replacer to avoid lock inversion. + let mut handle_tracker = HANDLE_TRACKER.lock().unwrap(); let mut assets_replacer = assets::REPLACER.get().unwrap().lock().unwrap(); - let (new_path, is_replaced) = assets_replacer.get(&path).unwrap(); - if is_replaced { + let new_path = if let Some(new_path) = assets_replacer.get(path).unwrap() { + new_path + } else { + // There is no appropriate replacement for this file. + return NtCreateFileHook.call( + file_handle, + desired_access, + object_attributes, + io_status_block, + allocation_size, + file_attributes, + share_access, + create_disposition, + create_options, + ea_buffer, + ea_length, + ); + }; + + let status = NtCreateFileHook.call( + file_handle, + desired_access, + &mut winapi::shared::ntdef::OBJECT_ATTRIBUTES { + RootDirectory: std::ptr::null_mut(), + ObjectName: &mut as_ntapi_unicode_string(&mut { + // Path needs to be converted into an NT Object Manager path (\??\...). + // + // This is not the same as a UNC path (\\?\...), confusingly. + let mut oss = std::ffi::OsString::from("\\??\\"); + oss.push(new_path.as_os_str()); + oss.as_os_str().encode_wide().collect::>() + }), + ..*object_attributes + }, + io_status_block, + allocation_size, + file_attributes, + share_access, + create_disposition, + create_options, + ea_buffer, + ea_length, + ); + + // If we failed to open the file we created ourselves, just abort. + assert_eq!(status, winapi::shared::ntstatus::STATUS_SUCCESS); + + let handle = *file_handle; + + let refcount = handle_tracker.insert(path, handle); + log::info!( + "NtCreateFile: read to {} was redirected -> {} (handle: {:p}, open handles: {})", + path.display(), + new_path.display(), + handle, + refcount + ); + + winapi::shared::ntstatus::STATUS_SUCCESS +} + +unsafe fn on_duplicate_handle( + h_source_process_handle: winapi::shared::ntdef::HANDLE, + h_source_handle: winapi::shared::ntdef::HANDLE, + h_target_process_handle: winapi::shared::ntdef::HANDLE, + lp_target_handle: winapi::shared::minwindef::LPHANDLE, + dw_desired_access: winapi::shared::minwindef::DWORD, + b_inherit_handle: winapi::shared::minwindef::BOOL, + dw_options: winapi::shared::minwindef::DWORD, +) -> winapi::shared::minwindef::BOOL { + let _hook_disable_guard: HooksDisableGuard = HooksDisableGuard::new().unwrap(); + + if DuplicateHandleHook.call( + h_source_process_handle, + h_source_handle, + h_target_process_handle, + lp_target_handle, + dw_desired_access, + b_inherit_handle, + dw_options, + ) == winapi::shared::minwindef::FALSE + { + return winapi::shared::minwindef::FALSE; + } + + let mut handle_tracker: std::sync::MutexGuard = HANDLE_TRACKER.lock().unwrap(); + + let target_handle = *lp_target_handle; + + if handle_tracker.dupe(h_source_handle, target_handle) > 0 { log::info!( - "read to {} was redirected -> {}", - path.display(), - new_path.display() + "DuplicateHandle: tracked handle duped: {:p} -> {:p}", + h_source_handle, + target_handle ); } - let path_wstr = new_path - .as_os_str() - .encode_wide() - .chain(std::iter::once(0)) - .collect::>(); + winapi::shared::minwindef::TRUE +} - CreateFileWHook.call( - path_wstr[..].as_ptr(), - dw_desired_access, - dw_share_mode, - lp_security_attributes, - dw_creation_disposition, - dw_flags_and_attributes, - handle, - ) +unsafe fn on_close_handle( + h_object: winapi::shared::ntdef::HANDLE, +) -> winapi::shared::minwindef::BOOL { + let _hook_disable_guard: HooksDisableGuard = HooksDisableGuard::new().unwrap(); + + if CloseHandleHook.call(h_object) == winapi::shared::minwindef::FALSE { + return winapi::shared::minwindef::FALSE; + } + + // Handle tracker must be locked before asset replacer to avoid lock inversion. + let mut handle_tracker: std::sync::MutexGuard = HANDLE_TRACKER.lock().unwrap(); + // let mut assets_replacer = assets::REPLACER.get().unwrap().lock().unwrap(); + + if let Some((_path, _)) = handle_tracker + .remove(h_object) + .filter(|(_, refcount)| *refcount == 0) + { + // FIXME: We would like to purge assets once the last handle is closed, but unfortunately BNLC leaks a handle to dat archives. + // I'm not sure if there is really a way around this, unfortunately. + + // let purged_path = assets_replacer.purge(&path).unwrap(); + // if let Some(purged_path) = purged_path { + // log::info!( + // "CloseHandle: last handle to {} was closed, purged: {}", + // path.display(), + // purged_path.display() + // ); + // } else { + // log::warn!( + // "CloseHandle: last handle to {} was closed, but path was not found in asset replacer!", + // path.display(), + // ); + // } + } + + winapi::shared::minwindef::TRUE } /// Install hooks into the process. @@ -77,68 +357,72 @@ pub unsafe fn install() -> Result<(), anyhow::Error> { windows_libloader::ModuleHandle::get("kernel32.dll").unwrap() }); - // BNLC actually uses both CreateFileA and CreateFileW... It seems like the third-party code uses CreateFileW but the BNLC code itself uses CreateFileA... - // - // Since we don't really care about the distincton, let's just normalize it here and hook it all via on_create_file. + static NTDLL: std::sync::LazyLock = + std::sync::LazyLock::new(|| unsafe { + windows_libloader::ModuleHandle::get("ntdll.dll").unwrap() + }); + unsafe { - CreateFileWHook + NtCreateFileHook .initialize( - std::mem::transmute(KERNEL32.get_symbol_address("CreateFileW").unwrap()), - { - move |lp_file_name, - dw_desired_access, - dw_share_mode, - lp_security_attributes, - dw_creation_disposition, - dw_flags_and_attributes, - handle| { - on_create_file( - &std::path::PathBuf::from(std::ffi::OsString::from_wide( - std::slice::from_raw_parts( - lp_file_name, - winapi::um::winbase::lstrlenW(lp_file_name) as usize, - ), - )), - dw_desired_access, - dw_share_mode, - lp_security_attributes, - dw_creation_disposition, - dw_flags_and_attributes, - handle, - ) - } + std::mem::transmute(NTDLL.get_symbol_address("NtCreateFile").unwrap()), + |file_handle, + desired_access, + object_attributes, + io_status_block, + allocation_size, + file_attributes, + share_access, + create_disposition, + create_options, + ea_buffer, + ea_length| { + on_nt_create_file( + file_handle, + desired_access, + object_attributes, + io_status_block, + allocation_size, + file_attributes, + share_access, + create_disposition, + create_options, + ea_buffer, + ea_length, + ) }, )? .enable()?; - CreateFileAHook + DuplicateHandleHook .initialize( - std::mem::transmute(KERNEL32.get_symbol_address("CreateFileA").unwrap()), - { - move |lp_file_name, - dw_desired_access, - dw_share_mode, - lp_security_attributes, - dw_creation_disposition, - dw_flags_and_attributes, - handle| { - on_create_file( - std::path::Path::new(std::ffi::OsStr::new( - &std::ffi::CStr::from_ptr(lp_file_name) - .to_string_lossy() - .to_string(), - )), - dw_desired_access, - dw_share_mode, - lp_security_attributes, - dw_creation_disposition, - dw_flags_and_attributes, - handle, - ) - } + std::mem::transmute(KERNEL32.get_symbol_address("DuplicateHandle").unwrap()), + |h_source_process_handle, + h_source_handle, + h_target_process_handle, + lp_target_handle, + dw_desired_access, + b_inherit_handle, + dw_options| { + on_duplicate_handle( + h_source_process_handle, + h_source_handle, + h_target_process_handle, + lp_target_handle, + dw_desired_access, + b_inherit_handle, + dw_options, + ) }, )? .enable()?; + + CloseHandleHook + .initialize( + std::mem::transmute(KERNEL32.get_symbol_address("CloseHandle").unwrap()), + |h_object| on_close_handle(h_object), + )? + .enable()?; } Ok(())