fsfilter_rs/driver_comm.rs
1//! Low-level communication with the minifilter.
2
3use std::mem;
4use std::os::raw::{c_ulong, c_ulonglong};
5use std::ptr;
6
7use sysinfo::{get_current_pid, Pid, PidExt};
8use wchar::wchar_t;
9use widestring::U16CString;
10use windows::core::{HRESULT, PCSTR, PCWSTR};
11use windows::Win32::Foundation::{CloseHandle, HANDLE};
12use windows::Win32::Storage::FileSystem::GetDriveTypeA;
13use windows::Win32::Storage::InstallableFileSystems::{
14 FilterConnectCommunicationPort, FilterSendMessage,
15};
16
17use crate::driver_comm::DriveType::{
18 DriveCDRom, DriveFixed, DriveNoRootDir, DriveRamDisk, DriveRemote, DriveRemovable, DriveUnknown,
19};
20use crate::driver_comm::IrpMajorOp::{IrpCreate, IrpNone, IrpRead, IrpSetInfo, IrpWrite};
21use crate::shared_def;
22use crate::shared_def::ReplyIrp;
23
24type BufPath = [wchar_t; 520];
25
26/// The user-mode app (this app) can send several messages types to the driver. See [`DriverComMessageType`]
27/// for details.
28/// Depending on the message type, the *pid*, *gid* and *path* fields can be optional.
29#[derive(Debug)]
30#[repr(C)]
31struct DriverComMessage {
32 /// The type message to send. See [`DriverComMessageType`].
33 r#type: c_ulong,
34 /// The pid of the process which triggered an i/o activity;
35 pid: c_ulong,
36 /// The gid is maintained by the driver
37 gid: c_ulonglong,
38 path: BufPath,
39}
40
41/// Messages types to send directives to the minifilter, by using te [`DriverComMessage`] struct.
42#[repr(C)]
43#[allow(dead_code)]
44enum DriverComMessageType {
45 /// Not used yet. The minifilter has the ability to monitor a specific part of the fs.
46 AddScanDirectory,
47 /// Not used yet. The minifilter has the ability to monitor a specific part of the fs.
48 RemScanDirectory,
49 /// Ask for a [`ReplyIrp`], if any available.
50 GetOps,
51 /// Set this app pid to the minifilter (related IRPs will be ignored);
52 SetPid,
53 /// Instruct the minifilter to kill all pids in the family designated by a given gid.
54 KillGid,
55}
56
57/// A minifilter is identified by a port (know in advance), like a named pipe used for communication,
58/// and a handle, retrieved by [`open_kernel_driver_com`](Self::open_kernel_driver_com).
59#[derive(Debug)]
60#[repr(C)]
61pub struct Driver {
62 handle: HANDLE,
63}
64
65impl Driver {
66 /// Can be used to properly close the communication (and unregister) with the minifilter.
67 /// If this fn is not used and the program has stopped, the handle is automatically closed,
68 /// seemingly without any side-effects.
69 #[inline]
70 pub fn close_kernel_communication(&self) -> bool {
71 unsafe { CloseHandle(self.handle).as_bool() }
72 }
73
74 /// The user-mode running app (this one) has to register itself to the driver.
75 ///
76 /// # Panics
77 /// This fn panics if it is unable to get the current pid.
78 ///
79 /// # Errors
80 /// This fn returns an error if it is unable to register itself to the minifilter.
81 #[inline]
82 pub fn driver_set_app_pid(&self) -> Result<(), windows::core::Error> {
83 let buf = Self::string_to_commessage_buffer(r"\Device\harddiskVolume");
84
85 let mut get_irp_msg: DriverComMessage = DriverComMessage {
86 r#type: DriverComMessageType::SetPid as c_ulong,
87 pid: get_current_pid().unwrap().as_u32() as c_ulong,
88 gid: 140_713_315_094_899,
89 path: buf, //wch!("\0"),
90 };
91 let mut tmp: u32 = 0;
92
93 unsafe {
94 FilterSendMessage(
95 self.handle,
96 ptr::addr_of_mut!(get_irp_msg).cast::<std::ffi::c_void>(),
97 mem::size_of::<DriverComMessage>() as c_ulong,
98 None,
99 0,
100 std::ptr::addr_of_mut!(tmp),
101 )
102 }
103 }
104
105 /// Try to open a com canal with the minifilter before this app is registered.
106 ///
107 /// # Panics
108 /// This function will panic if the minifilter port has any nul value (except the last one)
109 /// in it's name.
110 ///
111 /// # Errors
112 /// This fn can fail is the minifilter is unreachable:
113 ///
114 /// * if it is not started (try `sc start snFilter` first
115 /// * if a connection is already established: it can accepts only one at a time.
116 ///
117 /// In that case the Error is raised by the OS (`windows::Error`) and is generally readable.
118 #[inline]
119 pub fn open_kernel_driver_com() -> Result<Self, windows::core::Error> {
120 let com_port_name = U16CString::from_str("\\snFilter").unwrap().into_raw();
121 let handle;
122 unsafe {
123 handle = FilterConnectCommunicationPort(PCWSTR(com_port_name), 0, None, 0, None)?;
124 }
125 let res = Self { handle };
126 Ok(res)
127 }
128
129 /// Ask the driver for a [`ReplyIrp`], if any. This is a low-level function and the returned object
130 /// uses C pointers. Managing C pointers requires a special care, because of the Rust timelines.
131 /// [`ReplyIrp`] is optional since the minifilter returns null if there is no new activity.
132 ///
133 /// # Panics
134 /// This fn panics if it is unable to get the current pid or cannot get driver message from the
135 /// minifilter.
136 #[inline]
137 pub fn get_irp(&self, vecnew: &mut Vec<u8>) -> Option<ReplyIrp> {
138 let mut get_irp_msg = Self::build_irp_msg(
139 DriverComMessageType::GetOps,
140 get_current_pid().unwrap(),
141 0,
142 "",
143 );
144 let mut tmp: u32 = 0;
145
146 unsafe {
147 FilterSendMessage(
148 self.handle,
149 ptr::addr_of_mut!(get_irp_msg).cast::<std::ffi::c_void>(),
150 mem::size_of::<DriverComMessage>() as c_ulong,
151 Option::from(vecnew.as_mut_ptr().cast::<std::ffi::c_void>()),
152 65536_u32,
153 ptr::addr_of_mut!(tmp).cast::<u32>(),
154 )
155 .expect("Cannot get driver message from driver");
156 }
157
158 if tmp != 0 {
159 let reply_irp: ReplyIrp;
160 unsafe {
161 reply_irp =
162 std::ptr::read_unaligned(vecnew.as_ptr().cast::<shared_def::ReplyIrp>());
163 }
164 return Some(reply_irp);
165 }
166 None
167 }
168
169 /// Ask the minifilter to kill all pids related to the given *gid*. Pids are killed in driver-mode
170 /// by calls to `NtClose`.
171 #[inline]
172 pub fn try_kill(&self, gid: c_ulonglong) -> Result<HRESULT, windows::core::Error> {
173 let mut killmsg = DriverComMessage {
174 r#type: DriverComMessageType::KillGid as c_ulong,
175 pid: 0, //get_current_pid().unwrap() as u32,
176 gid,
177 path: [0; 520],
178 };
179 let mut res: i32 = 0;
180 let mut res_size: u32 = 0;
181
182 unsafe {
183 FilterSendMessage(
184 self.handle,
185 ptr::addr_of_mut!(killmsg).cast::<std::ffi::c_void>(),
186 mem::size_of::<DriverComMessage>() as c_ulong,
187 Option::from(ptr::addr_of_mut!(res).cast::<std::ffi::c_void>()),
188 4_u32,
189 ptr::addr_of_mut!(res_size).cast::<u32>(),
190 )?;
191 }
192
193 Ok(HRESULT(res))
194 }
195
196 #[inline]
197 fn string_to_commessage_buffer(bufstr: &str) -> BufPath {
198 let temp = U16CString::from_str(bufstr).unwrap();
199 let mut buf: BufPath = [0; 520];
200 for (i, c) in temp.as_slice_with_nul().iter().enumerate() {
201 buf[i] = *c as wchar_t;
202 }
203 buf
204 }
205
206 // TODO: move to ComMessage?
207 #[inline]
208 fn build_irp_msg(
209 commsgtype: DriverComMessageType,
210 pid: Pid,
211 gid: u64,
212 path: &str,
213 ) -> DriverComMessage {
214 DriverComMessage {
215 r#type: commsgtype as c_ulong, // SetPid
216 pid: pid.as_u32() as c_ulong,
217 gid,
218 path: Self::string_to_commessage_buffer(path),
219 }
220 }
221}
222
223/// See [`IOMessage`](crate::shared_def::IOMessage) struct and
224/// [this doc](https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/irp-major-function-codes).
225#[repr(C)]
226pub enum IrpMajorOp {
227 /// Nothing happened
228 IrpNone,
229 /// On read, any time following the successful completion of a create request.
230 IrpRead,
231 /// On write, any time following the successful completion of a create request.
232 IrpWrite,
233 /// Set Metadata about a file or file handle. In that case, [`FileChangeInfo`](crate::shared_def::FileChangeInfo) indicates
234 /// the nature of the modification.
235 IrpSetInfo,
236 /// Open a handle to a file object or device object.
237 IrpCreate,
238 /// File object handle has been closed
239 IrpCleanUp,
240}
241
242impl IrpMajorOp {
243 #[inline]
244 #[must_use]
245 pub fn from_byte(b: u8) -> Self {
246 match b {
247 // 0 => IrpNone,
248 1 => IrpRead,
249 2 => IrpWrite,
250 3 => IrpSetInfo,
251 4 | 5 => IrpCreate,
252 _ => IrpNone,
253 }
254 }
255}
256
257/// See [`IOMessage`](crate::shared_def::IOMessage) struct and
258/// [this doc](https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getdrivetypea).
259#[repr(C)]
260pub enum DriveType {
261 /// The drive type cannot be determined.
262 DriveUnknown,
263 /// The root path is invalid; for example, there is no volume mounted at the specified path.
264 DriveNoRootDir,
265 /// The drive has removable media; for example, a floppy drive, thumb drive, or flash card reader.
266 DriveRemovable,
267 /// The drive has fixed media; for example, a hard disk drive or flash drive.
268 DriveFixed,
269 /// The drive is a remote (network) drive.
270 DriveRemote,
271 /// The drive is a CD-ROM drive.
272 DriveCDRom,
273 /// The drive is a RAM disk.
274 DriveRamDisk,
275}
276
277impl DriveType {
278 /// Determines whether a disk drive is a removable, fixed, CD-ROM, RAM disk, or network drive.
279 ///
280 /// # Panics
281 /// Will panic if drive path is invalid.
282 #[inline]
283 #[must_use]
284 pub fn from_filepath(filepath: &str) -> Self {
285 let mut drive_type = 1u32;
286 if !filepath.is_empty() {
287 let drive_path = &filepath[..=filepath.find('\\').unwrap()];
288 unsafe {
289 drive_type = GetDriveTypeA(PCSTR(String::from(drive_path).as_ptr()));
290 }
291 }
292 match drive_type {
293 0 => DriveUnknown,
294 // 1 => DriveNoRootDir,
295 2 => DriveRemovable,
296 3 => DriveFixed,
297 4 => DriveRemote,
298 5 => DriveCDRom,
299 6 => DriveRamDisk,
300 _ => DriveNoRootDir,
301 }
302 }
303}