1use std::cmp::Ordering;
5use std::fmt;
6use std::os::raw::{c_uchar, c_ulong, c_ulonglong, c_ushort};
7use std::path::PathBuf;
8use std::time::SystemTime;
9
10use num_derive::FromPrimitive;
11use serde::{Deserialize, Serialize};
12use wchar::wchar_t;
13use windows::Win32::Foundation::{CloseHandle, GetLastError};
14use windows::Win32::Storage::FileSystem::FILE_ID_INFO;
15use windows::Win32::System::ProcessStatus::K32GetProcessImageFileNameA;
16use windows::Win32::System::Threading::{OpenProcess, PROCESS_QUERY_INFORMATION, PROCESS_VM_READ};
17
18#[derive(FromPrimitive)]
20#[repr(C)]
21pub enum FileChangeInfo {
22 FileChangeNotSet,
23 FileOpenDirectory,
24 FileChangeWrite,
25 FileChangeNewFile,
26 FileChangeRenameFile,
27 FileChangeExtensionChanged,
28 FileChangeDeleteFile,
29 FileChangeDeleteNewFile,
31 FileChangeOverwriteFile,
32}
33
34#[derive(FromPrimitive)]
36#[repr(C)]
37pub enum FileLocationInfo {
38 FileNotProtected,
39 FileProtected,
40 FileMovedIn,
41 FileMovedOut,
42}
43
44#[derive(Debug, Copy, Clone)]
49#[repr(C)]
50pub struct ReplyIrp {
51 pub data_size: c_ulonglong,
53 pub data: *const CDriverMsg,
55 pub num_ops: u64,
57}
58
59impl ReplyIrp {
60 #[inline]
62 fn unpack_drivermsg(&self) -> Vec<&CDriverMsg> {
63 let mut res = vec![];
64 unsafe {
65 let mut msg = &*self.data;
66 res.push(msg);
67 for _ in 0..(self.num_ops) {
68 if msg.next.is_null() {
69 break;
70 }
71 msg = &*msg.next;
72 res.push(msg);
73 }
74 }
75 res
76 }
77}
78
79#[derive(Debug, Copy, Clone)]
83#[repr(C)]
84pub struct UnicodeString {
85 pub length: c_ushort,
86 pub maximum_length: c_ushort,
87 pub buffer: *const wchar_t,
88}
89
90impl UnicodeString {
91 #[inline]
109 #[must_use]
110 pub fn to_string_ext(&self, extension: [wchar_t; 12]) -> String {
111 unsafe {
112 let str_slice = std::slice::from_raw_parts(self.buffer, self.length as usize);
113 let mut first_zero_index = 0;
114 let mut last_dot_index = 0;
115 let mut first_zero_index_ext = 0;
116
117 for (i, c) in str_slice.iter().enumerate() {
119 if *c == 46 {
120 last_dot_index = i + 1;
121 }
122 if *c == 0 {
123 first_zero_index = i;
124 break;
125 }
126 }
127
128 if first_zero_index_ext > 0 && last_dot_index > 0 {
129 for (i, c) in extension.iter().enumerate() {
131 if *c == 0 {
132 first_zero_index_ext = i;
133 break;
134 } else if *c != str_slice[last_dot_index + i] {
135 first_zero_index_ext = 0;
136 break;
137 }
138 }
139 String::from_utf16_lossy(
140 &[
141 &str_slice[..last_dot_index],
142 &extension[..first_zero_index_ext],
143 ]
144 .concat(),
145 )
146 } else {
147 String::from_utf16_lossy(&str_slice[..first_zero_index])
148 }
149 }
150 }
151}
152
153impl fmt::Display for UnicodeString {
154 #[inline]
155 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
156 unsafe {
157 let str_slice = std::slice::from_raw_parts(self.buffer, self.length as usize);
158 let mut first_zero_index = 0;
159 for (i, c) in str_slice.iter().enumerate() {
160 if *c == 0 {
161 first_zero_index = i;
162 break;
163 }
164 }
165 write!(
166 f,
167 "{}",
168 String::from_utf16_lossy(&str_slice[..first_zero_index])
169 )
170 }
171 }
172}
173
174#[derive(Debug, Clone, Serialize, Deserialize)]
176#[repr(C)]
177pub struct IOMessage {
178 pub extension: [wchar_t; 12],
180 pub file_id_vsn: c_ulonglong,
182 pub file_id_id: [u8; 16],
184 pub mem_sized_used: c_ulonglong,
186 pub entropy: f64,
188 pub pid: c_ulong,
190 pub irp_op: c_uchar,
198 pub is_entropy_calc: u8,
200 pub file_change: c_uchar,
211 pub file_location_info: c_uchar,
217 pub filepathstr: String,
219 pub gid: c_ulonglong,
221 pub runtime_features: RuntimeFeatures,
223 pub file_size: i64,
225 pub time: SystemTime,
227}
228
229impl IOMessage {
230 #[inline]
232 #[must_use]
233 pub fn from(c_drivermsg: &CDriverMsg) -> Self {
234 Self {
235 extension: c_drivermsg.extension,
236 file_id_vsn: c_drivermsg.file_id.VolumeSerialNumber,
237 file_id_id: c_drivermsg.file_id.FileId.Identifier,
238 mem_sized_used: c_drivermsg.mem_sized_used,
239 entropy: c_drivermsg.entropy,
240 pid: c_drivermsg.pid,
241 irp_op: c_drivermsg.irp_op,
242 is_entropy_calc: c_drivermsg.is_entropy_calc,
243 file_change: c_drivermsg.file_change,
244 file_location_info: c_drivermsg.file_location_info,
245 filepathstr: c_drivermsg.filepath.to_string_ext(c_drivermsg.extension),
246 gid: c_drivermsg.gid,
247 runtime_features: RuntimeFeatures::new(),
248 file_size: match PathBuf::from(
249 &c_drivermsg.filepath.to_string_ext(c_drivermsg.extension),
250 )
251 .metadata()
252 {
253 Ok(f) => f.len() as i64,
254 Err(_e) => -1,
255 },
256 time: SystemTime::now(),
257 }
258 }
259
260 #[inline]
263 pub fn exepath(&mut self) {
264 let pid = self.pid;
265 unsafe {
266 let r_handle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, pid);
267 if let Ok(handle) = r_handle {
268 if !(handle.is_invalid() || handle.0 == 0) {
269 let mut buffer: Vec<u8> = Vec::new();
270 buffer.resize(1024, 0);
271 let res = K32GetProcessImageFileNameA(handle, buffer.as_mut_slice());
272
273 CloseHandle(handle);
274 if res == 0 {
275 let _errorcode = GetLastError().0;
276 } else {
277 let pathbuf = PathBuf::from(
278 String::from_utf8_unchecked(buffer).trim_matches(char::from(0)),
279 );
280 self.runtime_features.exe_still_exists = true;
281 self.runtime_features.exepath = pathbuf.file_name().map_or_else(
282 || PathBuf::from(r"DEFAULT"),
283 |filename| PathBuf::from(filename.to_string_lossy().to_string()),
284 );
285 }
286 }
288 }
289 }
290 }
291}
292
293impl Eq for IOMessage {}
294
295impl Ord for IOMessage {
296 fn cmp(&self, other: &Self) -> Ordering {
297 self.time.cmp(&other.time)
298 }
299}
300
301impl PartialOrd for IOMessage {
302 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
303 Some(self.time.cmp(&other.time))
304 }
305}
306
307impl PartialEq for IOMessage {
308 fn eq(&self, other: &Self) -> bool {
309 self.time == other.time
310 }
311}
312
313#[derive(Debug, Clone, Serialize, Deserialize)]
315#[repr(C)]
316pub struct RuntimeFeatures {
317 pub exepath: PathBuf,
319 pub exe_still_exists: bool,
321}
322
323impl RuntimeFeatures {
324 #[inline]
326 #[must_use]
327 pub fn new() -> Self {
328 Self {
329 exepath: PathBuf::new(),
330 exe_still_exists: true,
331 }
332 }
333}
334
335impl Default for RuntimeFeatures {
336 #[inline]
337 fn default() -> Self {
338 Self::new()
339 }
340}
341
342#[derive(Debug, Copy, Clone)]
348#[repr(C)]
349pub struct CDriverMsg {
350 pub extension: [wchar_t; 12],
351 pub file_id: FILE_ID_INFO,
352 pub mem_sized_used: c_ulonglong,
353 pub entropy: f64,
354 pub pid: c_ulong,
355 pub irp_op: c_uchar,
356 pub is_entropy_calc: u8,
357 pub file_change: c_uchar,
358 pub file_location_info: c_uchar,
359 pub filepath: UnicodeString,
360 pub gid: c_ulonglong,
361 pub next: *const CDriverMsg,
363}
364
365#[repr(C)]
368pub struct CDriverMsgs<'a> {
369 drivermsgs: Vec<&'a CDriverMsg>,
370 index: usize,
371}
372
373impl CDriverMsgs<'_> {
374 #[inline]
376 #[must_use]
377 pub fn new(irp: &ReplyIrp) -> CDriverMsgs {
378 CDriverMsgs {
379 drivermsgs: irp.unpack_drivermsg(),
380 index: 0,
381 }
382 }
383}
384
385impl Iterator for CDriverMsgs<'_> {
386 type Item = CDriverMsg;
387
388 #[inline]
389 fn next(&mut self) -> Option<Self::Item> {
390 if self.index == self.drivermsgs.len() {
391 None
392 } else {
393 let res = *self.drivermsgs[self.index];
394 self.index += 1;
395 Some(res)
396 }
397 }
398}