use std::cmp::Ordering;
use std::fmt;
use std::os::raw::{c_uchar, c_ulong, c_ulonglong, c_ushort};
use std::path::PathBuf;
use std::time::SystemTime;
use num_derive::FromPrimitive;
use serde::{Deserialize, Serialize};
use wchar::wchar_t;
use windows::Win32::Foundation::{CloseHandle, GetLastError};
use windows::Win32::Storage::FileSystem::FILE_ID_INFO;
use windows::Win32::System::ProcessStatus::K32GetProcessImageFileNameA;
use windows::Win32::System::Threading::{OpenProcess, PROCESS_QUERY_INFORMATION, PROCESS_VM_READ};
#[derive(FromPrimitive)]
#[repr(C)]
pub enum FileChangeInfo {
FileChangeNotSet,
FileOpenDirectory,
FileChangeWrite,
FileChangeNewFile,
FileChangeRenameFile,
FileChangeExtensionChanged,
FileChangeDeleteFile,
FileChangeDeleteNewFile,
FileChangeOverwriteFile,
}
#[derive(FromPrimitive)]
#[repr(C)]
pub enum FileLocationInfo {
FileNotProtected,
FileProtected,
FileMovedIn,
FileMovedOut,
}
#[derive(Debug, Copy, Clone)]
#[repr(C)]
pub struct ReplyIrp {
pub data_size: c_ulonglong,
pub data: *const CDriverMsg,
pub num_ops: u64,
}
impl ReplyIrp {
#[inline]
fn unpack_drivermsg(&self) -> Vec<&CDriverMsg> {
let mut res = vec![];
unsafe {
let mut msg = &*self.data;
res.push(msg);
for _ in 0..(self.num_ops) {
if msg.next.is_null() {
break;
}
msg = &*msg.next;
res.push(msg);
}
}
res
}
}
#[derive(Debug, Copy, Clone)]
#[repr(C)]
pub struct UnicodeString {
pub length: c_ushort,
pub maximum_length: c_ushort,
pub buffer: *const wchar_t,
}
impl UnicodeString {
#[inline]
#[must_use]
pub fn to_string_ext(&self, extension: [wchar_t; 12]) -> String {
unsafe {
let str_slice = std::slice::from_raw_parts(self.buffer, self.length as usize);
let mut first_zero_index = 0;
let mut last_dot_index = 0;
let mut first_zero_index_ext = 0;
for (i, c) in str_slice.iter().enumerate() {
if *c == 46 {
last_dot_index = i + 1;
}
if *c == 0 {
first_zero_index = i;
break;
}
}
if first_zero_index_ext > 0 && last_dot_index > 0 {
for (i, c) in extension.iter().enumerate() {
if *c == 0 {
first_zero_index_ext = i;
break;
} else if *c != str_slice[last_dot_index + i] {
first_zero_index_ext = 0;
break;
}
}
String::from_utf16_lossy(
&[
&str_slice[..last_dot_index],
&extension[..first_zero_index_ext],
]
.concat(),
)
} else {
String::from_utf16_lossy(&str_slice[..first_zero_index])
}
}
}
}
impl fmt::Display for UnicodeString {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
unsafe {
let str_slice = std::slice::from_raw_parts(self.buffer, self.length as usize);
let mut first_zero_index = 0;
for (i, c) in str_slice.iter().enumerate() {
if *c == 0 {
first_zero_index = i;
break;
}
}
write!(
f,
"{}",
String::from_utf16_lossy(&str_slice[..first_zero_index])
)
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[repr(C)]
pub struct IOMessage {
pub extension: [wchar_t; 12],
pub file_id_vsn: c_ulonglong,
pub file_id_id: [u8; 16],
pub mem_sized_used: c_ulonglong,
pub entropy: f64,
pub pid: c_ulong,
pub irp_op: c_uchar,
pub is_entropy_calc: u8,
pub file_change: c_uchar,
pub file_location_info: c_uchar,
pub filepathstr: String,
pub gid: c_ulonglong,
pub runtime_features: RuntimeFeatures,
pub file_size: i64,
pub time: SystemTime,
}
impl IOMessage {
#[inline]
#[must_use]
pub fn from(c_drivermsg: &CDriverMsg) -> Self {
Self {
extension: c_drivermsg.extension,
file_id_vsn: c_drivermsg.file_id.VolumeSerialNumber,
file_id_id: c_drivermsg.file_id.FileId.Identifier,
mem_sized_used: c_drivermsg.mem_sized_used,
entropy: c_drivermsg.entropy,
pid: c_drivermsg.pid,
irp_op: c_drivermsg.irp_op,
is_entropy_calc: c_drivermsg.is_entropy_calc,
file_change: c_drivermsg.file_change,
file_location_info: c_drivermsg.file_location_info,
filepathstr: c_drivermsg.filepath.to_string_ext(c_drivermsg.extension),
gid: c_drivermsg.gid,
runtime_features: RuntimeFeatures::new(),
file_size: match PathBuf::from(
&c_drivermsg.filepath.to_string_ext(c_drivermsg.extension),
)
.metadata()
{
Ok(f) => f.len() as i64,
Err(_e) => -1,
},
time: SystemTime::now(),
}
}
#[inline]
pub fn exepath(&mut self) {
let pid = self.pid;
unsafe {
let r_handle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, pid);
if let Ok(handle) = r_handle {
if !(handle.is_invalid() || handle.0 == 0) {
let mut buffer: Vec<u8> = Vec::new();
buffer.resize(1024, 0);
let res = K32GetProcessImageFileNameA(handle, buffer.as_mut_slice());
CloseHandle(handle);
if res == 0 {
let _errorcode = GetLastError().0;
} else {
let pathbuf = PathBuf::from(
String::from_utf8_unchecked(buffer).trim_matches(char::from(0)),
);
self.runtime_features.exe_still_exists = true;
self.runtime_features.exepath = pathbuf.file_name().map_or_else(
|| PathBuf::from(r"DEFAULT"),
|filename| PathBuf::from(filename.to_string_lossy().to_string()),
);
}
}
}
}
}
}
impl Eq for IOMessage {}
impl Ord for IOMessage {
fn cmp(&self, other: &Self) -> Ordering {
self.time.cmp(&other.time)
}
}
impl PartialOrd for IOMessage {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.time.cmp(&other.time))
}
}
impl PartialEq for IOMessage {
fn eq(&self, other: &Self) -> bool {
self.time == other.time
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[repr(C)]
pub struct RuntimeFeatures {
pub exepath: PathBuf,
pub exe_still_exists: bool,
}
impl RuntimeFeatures {
#[inline]
#[must_use]
pub fn new() -> Self {
Self {
exepath: PathBuf::new(),
exe_still_exists: true,
}
}
}
impl Default for RuntimeFeatures {
#[inline]
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Copy, Clone)]
#[repr(C)]
pub struct CDriverMsg {
pub extension: [wchar_t; 12],
pub file_id: FILE_ID_INFO,
pub mem_sized_used: c_ulonglong,
pub entropy: f64,
pub pid: c_ulong,
pub irp_op: c_uchar,
pub is_entropy_calc: u8,
pub file_change: c_uchar,
pub file_location_info: c_uchar,
pub filepath: UnicodeString,
pub gid: c_ulonglong,
pub next: *const CDriverMsg,
}
#[repr(C)]
pub struct CDriverMsgs<'a> {
drivermsgs: Vec<&'a CDriverMsg>,
index: usize,
}
impl CDriverMsgs<'_> {
#[inline]
#[must_use]
pub fn new(irp: &ReplyIrp) -> CDriverMsgs {
CDriverMsgs {
drivermsgs: irp.unpack_drivermsg(),
index: 0,
}
}
}
impl Iterator for CDriverMsgs<'_> {
type Item = CDriverMsg;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
if self.index == self.drivermsgs.len() {
None
} else {
let res = *self.drivermsgs[self.index];
self.index += 1;
Some(res)
}
}
}