From 5fcab0cb73e52d6ddbe82ad39096336c79d86fec Mon Sep 17 00:00:00 2001 From: dragon-zhang Date: Mon, 28 Oct 2024 20:23:18 +0800 Subject: [PATCH] add hook for windows --- .github/workflows/ci-preemptive.sh | 0 .github/workflows/ci.sh | 0 core/Cargo.toml | 2 + core/src/common/constants.rs | 6 + core/src/syscall/common.rs | 10 ++ core/src/syscall/unix/mod.rs | 10 +- .../syscall/unix/pthread_cond_timedwait.rs | 6 +- core/src/syscall/windows/CreateFileW.rs | 91 +++++++++++++++ core/src/syscall/windows/SetFilePointerEx.rs | 46 ++++++++ core/src/syscall/windows/WaitOnAddress.rs | 105 ++++++++++++++++++ core/src/syscall/windows/mod.rs | 40 +++---- hook/Cargo.toml | 7 +- hook/src/syscall/windows.rs | 33 +++++- 13 files changed, 317 insertions(+), 39 deletions(-) mode change 100755 => 100644 .github/workflows/ci-preemptive.sh mode change 100755 => 100644 .github/workflows/ci.sh create mode 100644 core/src/syscall/windows/CreateFileW.rs create mode 100644 core/src/syscall/windows/SetFilePointerEx.rs create mode 100644 core/src/syscall/windows/WaitOnAddress.rs diff --git a/.github/workflows/ci-preemptive.sh b/.github/workflows/ci-preemptive.sh old mode 100755 new mode 100644 diff --git a/.github/workflows/ci.sh b/.github/workflows/ci.sh old mode 100755 new mode 100644 diff --git a/core/Cargo.toml b/core/Cargo.toml index 39e20026..6f8b255b 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -48,10 +48,12 @@ io-uring = { workspace = true, optional = true } [target.'cfg(windows)'.dependencies] windows-sys = { workspace = true, features = [ + "Win32_Security", "Win32_System_IO", "Win32_Foundation", "Win32_System_Kernel", "Win32_System_Threading", + "Win32_Storage_FileSystem", "Win32_Networking_WinSock", "Win32_System_SystemInformation", "Win32_System_Diagnostics_Debug", diff --git a/core/src/common/constants.rs b/core/src/common/constants.rs index b8b5d12e..dcbcbcf3 100644 --- a/core/src/common/constants.rs +++ b/core/src/common/constants.rs @@ -118,6 +118,12 @@ pub enum Syscall { pthread_mutex_trylock, pthread_mutex_lock, pthread_mutex_unlock, + #[cfg(windows)] + CreateFileW, + #[cfg(windows)] + SetFilePointerEx, + #[cfg(windows)] + WaitOnAddress, } impl Syscall { diff --git a/core/src/syscall/common.rs b/core/src/syscall/common.rs index 24f084e1..090457d2 100644 --- a/core/src/syscall/common.rs +++ b/core/src/syscall/common.rs @@ -4,6 +4,16 @@ pub extern "C" fn reset_errno() { set_errno(0); } +#[macro_export] +macro_rules! syscall_mod { + ($($mod_name: ident);*) => { + $( + pub use $mod_name::$mod_name; + mod $mod_name; + )* + } +} + #[macro_export] macro_rules! log_syscall { ( $socket:expr, $done:expr, $once_result:expr ) => { diff --git a/core/src/syscall/unix/mod.rs b/core/src/syscall/unix/mod.rs index 261fd129..be4290bf 100644 --- a/core/src/syscall/unix/mod.rs +++ b/core/src/syscall/unix/mod.rs @@ -1,14 +1,6 @@ +use crate::syscall_mod; use std::ffi::c_int; -macro_rules! syscall_mod { - ($($mod_name: ident);*) => { - $( - pub use $mod_name::$mod_name; - mod $mod_name; - )* - } -} - macro_rules! impl_facade { ( $struct_name:ident, $trait_name: ident, $syscall: ident($($arg: ident : $arg_type: ty),*) -> $result: ty ) => { #[repr(C)] diff --git a/core/src/syscall/unix/pthread_cond_timedwait.rs b/core/src/syscall/unix/pthread_cond_timedwait.rs index 975c843f..f060737b 100644 --- a/core/src/syscall/unix/pthread_cond_timedwait.rs +++ b/core/src/syscall/unix/pthread_cond_timedwait.rs @@ -71,6 +71,10 @@ impl PthreadCondTimedwaitSyscall .unwrap_or(u64::MAX) }; loop { + let mut left_time = abstimeout.saturating_sub(now()); + if 0 == left_time { + return libc::ETIMEDOUT; + } let r = self.inner.pthread_cond_timedwait( fn_ptr, cond, @@ -83,7 +87,7 @@ impl PthreadCondTimedwaitSyscall if libc::ETIMEDOUT != r { return r; } - let left_time = abstimeout.saturating_sub(now()); + left_time = abstimeout.saturating_sub(now()); if 0 == left_time { return libc::ETIMEDOUT; } diff --git a/core/src/syscall/windows/CreateFileW.rs b/core/src/syscall/windows/CreateFileW.rs new file mode 100644 index 00000000..40774f81 --- /dev/null +++ b/core/src/syscall/windows/CreateFileW.rs @@ -0,0 +1,91 @@ +use std::ffi::c_uint; +use once_cell::sync::Lazy; +use windows_sys::core::PCWSTR; +use windows_sys::Win32::Foundation::HANDLE; +use windows_sys::Win32::Security::SECURITY_ATTRIBUTES; +use windows_sys::Win32::Storage::FileSystem::{ + FILE_CREATION_DISPOSITION, FILE_FLAGS_AND_ATTRIBUTES, FILE_SHARE_MODE, +}; + +#[must_use] +pub extern "system" fn CreateFileW( + fn_ptr: Option< + &extern "system" fn( + PCWSTR, + c_uint, + FILE_SHARE_MODE, + *const SECURITY_ATTRIBUTES, + FILE_CREATION_DISPOSITION, + FILE_FLAGS_AND_ATTRIBUTES, + HANDLE, + ) -> HANDLE, + >, + lpfilename: PCWSTR, + dwdesiredaccess: c_uint, + dwsharemode: FILE_SHARE_MODE, + lpsecurityattributes: *const SECURITY_ATTRIBUTES, + dwcreationdisposition: FILE_CREATION_DISPOSITION, + dwflagsandattributes: FILE_FLAGS_AND_ATTRIBUTES, + htemplatefile: HANDLE, +) -> HANDLE { + static CHAIN: Lazy> = + Lazy::new(Default::default); + CHAIN.CreateFileW( + fn_ptr, + lpfilename, + dwdesiredaccess, + dwsharemode, + lpsecurityattributes, + dwcreationdisposition, + dwflagsandattributes, + htemplatefile + ) +} + +trait CreateFileWSyscall { + extern "system" fn CreateFileW( + &self, + fn_ptr: Option< + &extern "system" fn( + PCWSTR, + c_uint, + FILE_SHARE_MODE, + *const SECURITY_ATTRIBUTES, + FILE_CREATION_DISPOSITION, + FILE_FLAGS_AND_ATTRIBUTES, + HANDLE, + ) -> HANDLE, + >, + lpfilename: PCWSTR, + dwdesiredaccess: c_uint, + dwsharemode: FILE_SHARE_MODE, + lpsecurityattributes: *const SECURITY_ATTRIBUTES, + dwcreationdisposition: FILE_CREATION_DISPOSITION, + dwflagsandattributes: FILE_FLAGS_AND_ATTRIBUTES, + htemplatefile: HANDLE, + ) -> HANDLE; +} + +impl_facade!(CreateFileWSyscallFacade, CreateFileWSyscall, + CreateFileW( + lpfilename: PCWSTR, + dwdesiredaccess: c_uint, + dwsharemode: FILE_SHARE_MODE, + lpsecurityattributes: *const SECURITY_ATTRIBUTES, + dwcreationdisposition: FILE_CREATION_DISPOSITION, + dwflagsandattributes: FILE_FLAGS_AND_ATTRIBUTES, + htemplatefile: HANDLE + ) -> HANDLE +); + +impl_raw!(RawCreateFileWSyscall, CreateFileWSyscall, windows_sys::Win32::Storage::FileSystem, + CreateFileW( + lpfilename: PCWSTR, + dwdesiredaccess: c_uint, + dwsharemode: FILE_SHARE_MODE, + lpsecurityattributes: *const SECURITY_ATTRIBUTES, + dwcreationdisposition: FILE_CREATION_DISPOSITION, + dwflagsandattributes: FILE_FLAGS_AND_ATTRIBUTES, + htemplatefile: HANDLE + ) -> HANDLE +); \ No newline at end of file diff --git a/core/src/syscall/windows/SetFilePointerEx.rs b/core/src/syscall/windows/SetFilePointerEx.rs new file mode 100644 index 00000000..abb9737d --- /dev/null +++ b/core/src/syscall/windows/SetFilePointerEx.rs @@ -0,0 +1,46 @@ +use std::ffi::c_longlong; +use once_cell::sync::Lazy; +use windows_sys::Win32::Foundation::{BOOL, HANDLE}; +use windows_sys::Win32::Storage::FileSystem::SET_FILE_POINTER_MOVE_METHOD; + +#[must_use] +pub extern "system" fn SetFilePointerEx( + fn_ptr: Option<&extern "system" fn(HANDLE, c_longlong, *mut c_longlong, SET_FILE_POINTER_MOVE_METHOD) -> BOOL>, + hfile: HANDLE, + lidistancetomove: c_longlong, + lpnewfilepointer : *mut c_longlong, + dwmovemethod : SET_FILE_POINTER_MOVE_METHOD +) -> BOOL { + static CHAIN: Lazy> = + Lazy::new(Default::default); + CHAIN.SetFilePointerEx(fn_ptr, hfile, lidistancetomove, lpnewfilepointer, dwmovemethod) +} + +trait SetFilePointerExSyscall { + extern "system" fn SetFilePointerEx( + &self, + fn_ptr: Option<&extern "system" fn(HANDLE, c_longlong, *mut c_longlong, SET_FILE_POINTER_MOVE_METHOD) -> BOOL>, + hfile: HANDLE, + lidistancetomove: c_longlong, + lpnewfilepointer : *mut c_longlong, + dwmovemethod : SET_FILE_POINTER_MOVE_METHOD + ) -> BOOL; +} + +impl_facade!(SetFilePointerExSyscallFacade, SetFilePointerExSyscall, + SetFilePointerEx( + hfile: HANDLE, + lidistancetomove: c_longlong, + lpnewfilepointer : *mut c_longlong, + dwmovemethod : SET_FILE_POINTER_MOVE_METHOD + ) -> BOOL +); + +impl_raw!(RawSetFilePointerExSyscall, SetFilePointerExSyscall, windows_sys::Win32::Storage::FileSystem, + SetFilePointerEx( + hfile: HANDLE, + lidistancetomove: c_longlong, + lpnewfilepointer : *mut c_longlong, + dwmovemethod : SET_FILE_POINTER_MOVE_METHOD + ) -> BOOL +); \ No newline at end of file diff --git a/core/src/syscall/windows/WaitOnAddress.rs b/core/src/syscall/windows/WaitOnAddress.rs new file mode 100644 index 00000000..bbf97c6a --- /dev/null +++ b/core/src/syscall/windows/WaitOnAddress.rs @@ -0,0 +1,105 @@ +use std::ffi::{c_uint, c_void}; +use std::time::Duration; +use once_cell::sync::Lazy; +use windows_sys::Win32::Foundation::{BOOL, ERROR_TIMEOUT, FALSE, TRUE}; +use crate::common::{get_timeout_time, now}; +use crate::net::EventLoops; +use crate::syscall::common::reset_errno; +use crate::syscall::set_errno; + +#[must_use] +pub extern "system" fn WaitOnAddress( + fn_ptr: Option<&extern "system" fn(*const c_void, *const c_void, usize, c_uint) -> BOOL>, + address: *const c_void, + compareaddress: *const c_void, + addresssize: usize, + dwmilliseconds: c_uint +) -> BOOL { + static CHAIN: Lazy>> = + Lazy::new(Default::default); + CHAIN.WaitOnAddress(fn_ptr, address, compareaddress, addresssize, dwmilliseconds) +} + +trait WaitOnAddressSyscall { + extern "system" fn WaitOnAddress( + &self, + fn_ptr: Option<&extern "system" fn(*const c_void, *const c_void, usize, c_uint) -> BOOL>, + address: *const c_void, + compareaddress: *const c_void, + addresssize: usize, + dwmilliseconds: c_uint + ) -> BOOL; +} + +impl_facade!(WaitOnAddressSyscallFacade, WaitOnAddressSyscall, + WaitOnAddress( + address: *const c_void, + compareaddress: *const c_void, + addresssize: usize, + dwmilliseconds: c_uint + ) -> BOOL +); + +#[repr(C)] +#[derive(Debug, Default)] +struct NioWaitOnAddressSyscall { + inner: I, +} + +impl WaitOnAddressSyscall for NioWaitOnAddressSyscall { + extern "system" fn WaitOnAddress( + &self, + fn_ptr: Option<&extern "system" fn(*const c_void, *const c_void, usize, c_uint) -> BOOL>, + address: *const c_void, + compareaddress: *const c_void, + addresssize: usize, + dwmilliseconds: c_uint + ) -> BOOL { + let timeout = get_timeout_time(Duration::from_millis(dwmilliseconds.into())); + loop { + let mut left_time = timeout.saturating_sub(now()); + if 0 == left_time { + set_errno(ERROR_TIMEOUT); + return FALSE; + } + let r = self.inner.WaitOnAddress( + fn_ptr, + address, + compareaddress, + addresssize, + (left_time / 1_000_000).min(1).try_into().expect("overflow"), + ); + if TRUE == r { + reset_errno(); + return r; + } + left_time = timeout.saturating_sub(now()); + if 0 == left_time { + set_errno(ERROR_TIMEOUT); + return FALSE; + } + let wait_time = if left_time > 10_000_000 { + 10_000_000 + } else { + left_time + }; + if EventLoops::wait_event(Some(Duration::new( + wait_time / 1_000_000_000, + (wait_time % 1_000_000_000) as _, + ))) + .is_err() + { + return r; + } + } + } +} + +impl_raw!(RawWaitOnAddressSyscall, WaitOnAddressSyscall, windows_sys::Win32::System::Threading, + WaitOnAddress( + address: *const c_void, + compareaddress: *const c_void, + addresssize: usize, + dwmilliseconds: c_uint + ) -> BOOL +); \ No newline at end of file diff --git a/core/src/syscall/windows/mod.rs b/core/src/syscall/windows/mod.rs index b406c00b..2dfcfef1 100644 --- a/core/src/syscall/windows/mod.rs +++ b/core/src/syscall/windows/mod.rs @@ -1,19 +1,8 @@ +use crate::syscall_mod; use dashmap::DashSet; use once_cell::sync::Lazy; use windows_sys::Win32::Networking::WinSock::SOCKET; -pub use accept::accept; -pub use ioctlsocket::ioctlsocket; -pub use listen::listen; -pub use recv::recv; -pub use send::send; -pub use shutdown::shutdown; -pub use socket::socket; -pub use Sleep::Sleep; -pub use WSARecv::WSARecv; -pub use WSASend::WSASend; -pub use WSASocketW::WSASocketW; - macro_rules! impl_facade { ( $struct_name:ident, $trait_name: ident, $syscall: ident($($arg: ident : $arg_type: ty),*) -> $result: ty ) => { #[repr(C)] @@ -450,17 +439,22 @@ macro_rules! impl_raw { } } -mod Sleep; -mod WSARecv; -mod WSASend; -mod WSASocketW; -mod accept; -mod ioctlsocket; -mod listen; -mod recv; -mod send; -mod shutdown; -mod socket; +syscall_mod!( + Sleep; + WSARecv; + WSASend; + WSASocketW; + accept; + ioctlsocket; + listen; + recv; + send; + shutdown; + socket; + CreateFileW; + SetFilePointerEx; + WaitOnAddress +); static NON_BLOCKING: Lazy> = Lazy::new(Default::default); diff --git a/hook/Cargo.toml b/hook/Cargo.toml index 89eaae6b..47c4f4a5 100644 --- a/hook/Cargo.toml +++ b/hook/Cargo.toml @@ -19,12 +19,13 @@ libc.workspace = true [target.'cfg(windows)'.dependencies] windows-sys = { workspace = true, features = [ + "Win32_Security", "Win32_Foundation", - "Win32_System_Diagnostics_Debug", "Win32_System_Threading", - "Win32_Security", + "Win32_Storage_FileSystem", "Win32_System_LibraryLoader", - "Win32_System_SystemServices" + "Win32_System_SystemServices", + "Win32_System_Diagnostics_Debug" ] } minhook.workspace = true diff --git a/hook/src/syscall/windows.rs b/hook/src/syscall/windows.rs index e4cbc8af..6cb4d99b 100644 --- a/hook/src/syscall/windows.rs +++ b/hook/src/syscall/windows.rs @@ -1,11 +1,16 @@ -use std::ffi::{c_int, c_uint, c_void}; +use std::ffi::{c_int, c_longlong, c_uint, c_void}; use std::io::{Error, ErrorKind}; -use windows_sys::core::{PCSTR, PSTR}; -use windows_sys::Win32::Foundation::{BOOL, TRUE}; +use windows_sys::core::{PCSTR, PCWSTR, PSTR}; +use windows_sys::Win32::Foundation::{BOOL, HANDLE, TRUE}; use windows_sys::Win32::Networking::WinSock::{ IPPROTO, LPWSAOVERLAPPED_COMPLETION_ROUTINE, SEND_RECV_FLAGS, SOCKADDR, SOCKET, WINSOCK_SHUTDOWN_HOW, WINSOCK_SOCKET_TYPE, WSABUF, WSAPROTOCOL_INFOW, }; +use windows_sys::Win32::Security::SECURITY_ATTRIBUTES; +use windows_sys::Win32::Storage::FileSystem::{ + FILE_CREATION_DISPOSITION, FILE_FLAGS_AND_ATTRIBUTES, FILE_SHARE_MODE, + SET_FILE_POINTER_MOVE_METHOD, +}; use windows_sys::Win32::System::SystemServices::{DLL_PROCESS_ATTACH, DLL_PROCESS_DETACH}; use windows_sys::Win32::System::IO::OVERLAPPED; @@ -115,6 +120,28 @@ unsafe fn attach() -> std::io::Result<()> { g: c_uint, dw_flags: c_uint ) -> SOCKET); + impl_hook!("kernel32.dll", CREATEFILEW, CreateFileW( + lpfilename: PCWSTR, + dwdesiredaccess: c_uint, + dwsharemode: FILE_SHARE_MODE, + lpsecurityattributes: *const SECURITY_ATTRIBUTES, + dwcreationdisposition: FILE_CREATION_DISPOSITION, + dwflagsandattributes: FILE_FLAGS_AND_ATTRIBUTES, + htemplatefile: HANDLE + ) -> HANDLE); + impl_hook!("kernel32.dll", SETFILEPOINTEREX, SetFilePointerEx( + hfile: HANDLE, + lidistancetomove: c_longlong, + lpnewfilepointer : *mut c_longlong, + dwmovemethod : SET_FILE_POINTER_MOVE_METHOD + ) -> BOOL); + // NOTE: unhook WaitOnAddress due to stack overflow or bug + // impl_hook!("api-ms-win-core-synch-l1-2-0.dll", WAITONADDRESS, WaitOnAddress( + // address: *const c_void, + // compareaddress: *const c_void, + // addresssize: usize, + // dwmilliseconds: c_uint + // ) -> BOOL); // Enable the hook minhook::MinHook::enable_all_hooks() .map_err(|_| Error::new(ErrorKind::Other, "init all hooks failed !"))