mirror of
https://github.com/safing/portmaster.git
synced 2026-05-20 20:40:36 +00:00
feat(kext): track owner PID of connected user-space process
Add IRP_MJ_CREATE and IRP_MJ_CLEANUP handlers to capture and clear the PID of the user-space process that holds the device handle open. - wdk/ffi: expose PsGetCurrentProcessId kernel API - wdk/irp_helpers: add CreateRequest and CleanupRequest wrappers - driver/device: add owner_pid AtomicU32 field with lock-free access for callouts; add is_owner_pid() helper - driver/entry: register driver_create/driver_cleanup dispatch routines that store/clear owner_pid on connect/disconnect
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
use alloc::string::String;
|
||||
use core::sync::atomic::{AtomicU32, Ordering};
|
||||
use num_traits::FromPrimitive;
|
||||
use protocol::{command::CommandType, info::Info};
|
||||
use smoltcp::wire::{IpAddress, IpProtocol, Ipv4Address, Ipv6Address};
|
||||
@@ -34,6 +35,10 @@ pub struct Device {
|
||||
pub(crate) injector: Injector,
|
||||
pub(crate) network_allocator: NetworkAllocator,
|
||||
pub(crate) bandwidth_stats: Bandwidth,
|
||||
/// PID of the user-space process that currently holds the device handle open.
|
||||
/// Written once on IRP_MJ_CREATE, cleared on IRP_MJ_CLEANUP.
|
||||
/// AtomicU32 gives lock-free reads in callouts with zero overhead.
|
||||
pub(crate) owner_pid: AtomicU32,
|
||||
}
|
||||
|
||||
impl Device {
|
||||
@@ -57,9 +62,16 @@ impl Device {
|
||||
injector: Injector::new(),
|
||||
network_allocator: NetworkAllocator::new(),
|
||||
bandwidth_stats: Bandwidth::new(),
|
||||
owner_pid: AtomicU32::new(0),
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the PID of the process that currently has the device handle open, or 0 if none.
|
||||
pub fn is_owner_pid(&self, pid: u32) -> bool {
|
||||
let p = self.owner_pid.load(Ordering::Acquire);
|
||||
p != 0 && p == pid
|
||||
}
|
||||
|
||||
/// Cleanup is called just before drop.
|
||||
// pub fn cleanup(&mut self) {}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ use crate::common::ControlCode;
|
||||
use crate::device;
|
||||
use alloc::boxed::Box;
|
||||
use num_traits::FromPrimitive;
|
||||
use wdk::irp_helpers::{DeviceControlRequest, ReadRequest, WriteRequest};
|
||||
use wdk::irp_helpers::{CleanupRequest, CreateRequest, DeviceControlRequest, ReadRequest, WriteRequest};
|
||||
use wdk::{err, info, interface};
|
||||
use windows_sys::Wdk::Foundation::{DEVICE_OBJECT, DRIVER_OBJECT, IRP};
|
||||
use windows_sys::Win32::Foundation::{NTSTATUS, STATUS_SUCCESS};
|
||||
@@ -39,6 +39,8 @@ pub extern "system" fn driver_entry(
|
||||
|
||||
// Set driver functions.
|
||||
driver.set_driver_unload(Some(driver_unload));
|
||||
driver.set_create_fn(Some(driver_create));
|
||||
driver.set_cleanup_fn(Some(driver_cleanup));
|
||||
driver.set_read_fn(Some(driver_read));
|
||||
driver.set_write_fn(Some(driver_write));
|
||||
driver.set_device_control_fn(Some(device_control));
|
||||
@@ -68,6 +70,35 @@ unsafe extern "system" fn driver_unload(_object: *const DRIVER_OBJECT) {
|
||||
}
|
||||
}
|
||||
|
||||
/// driver_create is triggered when user-space opens a handle to the device (CreateFile).
|
||||
unsafe extern "system" fn driver_create(
|
||||
_device_object: *const DEVICE_OBJECT,
|
||||
irp: *mut IRP,
|
||||
) -> NTSTATUS {
|
||||
let mut create_request = CreateRequest::new(irp.as_mut().unwrap());
|
||||
if let Some(device) = get_device() {
|
||||
let pid = create_request.get_requestor_pid();
|
||||
device.owner_pid.store(pid, core::sync::atomic::Ordering::Release);
|
||||
info!("Device opened by PID {}", pid);
|
||||
}
|
||||
create_request.complete();
|
||||
create_request.get_status()
|
||||
}
|
||||
|
||||
/// driver_cleanup is triggered when user-space closes the last handle to the device.
|
||||
unsafe extern "system" fn driver_cleanup(
|
||||
_device_object: *const DEVICE_OBJECT,
|
||||
irp: *mut IRP,
|
||||
) -> NTSTATUS {
|
||||
let mut cleanup_request = CleanupRequest::new(irp.as_mut().unwrap());
|
||||
if let Some(device) = get_device() {
|
||||
let old_pid = device.owner_pid.swap(0, core::sync::atomic::Ordering::Release);
|
||||
info!("Device closed by PID {}", old_pid);
|
||||
}
|
||||
cleanup_request.complete();
|
||||
cleanup_request.get_status()
|
||||
}
|
||||
|
||||
// driver_read event triggered from user-space on file.Read.
|
||||
unsafe extern "system" fn driver_read(
|
||||
_device_object: *const DEVICE_OBJECT,
|
||||
|
||||
@@ -534,4 +534,8 @@ extern "C" {
|
||||
/// The KeQuerySystemTime routine obtains the current system time.
|
||||
/// System time is a count of 100-nanosecond intervals since January 1, 1601. System time is typically updated approximately every ten milliseconds. This value is computed for the GMT time zone.
|
||||
pub(crate) fn pm_QuerySystemTime() -> u64;
|
||||
|
||||
/// Returns the process identifier of the current process.
|
||||
/// This is safe to call from IRP_MJ_CREATE handlers, which always execute in the context of the initiating user-space process.
|
||||
pub(crate) fn PsGetCurrentProcessId() -> HANDLE;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,57 @@ use windows_sys::{
|
||||
},
|
||||
};
|
||||
|
||||
/// Wraps an IRP_MJ_CREATE request (triggered when user-space opens the device handle).
|
||||
pub struct CreateRequest<'a> {
|
||||
irp: &'a mut IRP,
|
||||
}
|
||||
|
||||
impl CreateRequest<'_> {
|
||||
pub fn new(irp: &mut IRP) -> CreateRequest<'_> {
|
||||
CreateRequest { irp }
|
||||
}
|
||||
|
||||
/// Returns the PID of the process that opened the device handle.
|
||||
/// Safe to call here because IRP_MJ_CREATE always runs in the context
|
||||
/// of the initiating process, never in an arbitrary system thread.
|
||||
pub fn get_requestor_pid(&self) -> u32 {
|
||||
unsafe { crate::ffi::PsGetCurrentProcessId() as u32 }
|
||||
}
|
||||
|
||||
pub fn complete(&mut self) {
|
||||
// FILE_OPENED (1): indicates the device was opened (not created/superseded).
|
||||
const FILE_OPENED: usize = 1;
|
||||
self.irp.IoStatus.Information = FILE_OPENED;
|
||||
self.irp.IoStatus.Anonymous.Status = STATUS_SUCCESS;
|
||||
unsafe { IofCompleteRequest(self.irp, IO_NO_INCREMENT as i8) };
|
||||
}
|
||||
|
||||
pub fn get_status(&self) -> NTSTATUS {
|
||||
unsafe { self.irp.IoStatus.Anonymous.Status }
|
||||
}
|
||||
}
|
||||
|
||||
/// Wraps an IRP_MJ_CLEANUP request (triggered when user-space closes the last handle).
|
||||
pub struct CleanupRequest<'a> {
|
||||
irp: &'a mut IRP,
|
||||
}
|
||||
|
||||
impl CleanupRequest<'_> {
|
||||
pub fn new(irp: &mut IRP) -> CleanupRequest<'_> {
|
||||
CleanupRequest { irp }
|
||||
}
|
||||
|
||||
pub fn complete(&mut self) {
|
||||
self.irp.IoStatus.Information = 0;
|
||||
self.irp.IoStatus.Anonymous.Status = STATUS_SUCCESS;
|
||||
unsafe { IofCompleteRequest(self.irp, IO_NO_INCREMENT as i8) };
|
||||
}
|
||||
|
||||
pub fn get_status(&self) -> NTSTATUS {
|
||||
unsafe { self.irp.IoStatus.Anonymous.Status }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ReadRequest<'a> {
|
||||
irp: &'a mut IRP,
|
||||
buffer: &'a mut [u8],
|
||||
|
||||
Reference in New Issue
Block a user