wraith/km/
irp.rs

1//! IRP (I/O Request Packet) handling
2
3use core::ffi::c_void;
4use core::ptr::NonNull;
5
6use super::error::{status, NtStatus};
7use super::memory::MdlRaw;
8
9/// IRP major function codes
10#[repr(u8)]
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum IrpMajorFunction {
13    Create = 0x00,
14    CreateNamedPipe = 0x01,
15    Close = 0x02,
16    Read = 0x03,
17    Write = 0x04,
18    QueryInformation = 0x05,
19    SetInformation = 0x06,
20    QueryEa = 0x07,
21    SetEa = 0x08,
22    FlushBuffers = 0x09,
23    QueryVolumeInformation = 0x0a,
24    SetVolumeInformation = 0x0b,
25    DirectoryControl = 0x0c,
26    FileSystemControl = 0x0d,
27    DeviceControl = 0x0e,
28    InternalDeviceControl = 0x0f,
29    Shutdown = 0x10,
30    LockControl = 0x11,
31    Cleanup = 0x12,
32    CreateMailslot = 0x13,
33    QuerySecurity = 0x14,
34    SetSecurity = 0x15,
35    Power = 0x16,
36    SystemControl = 0x17,
37    DeviceChange = 0x18,
38    QueryQuota = 0x19,
39    SetQuota = 0x1a,
40    Pnp = 0x1b,
41}
42
43/// IRP structure (simplified)
44#[repr(C)]
45pub struct IrpRaw {
46    pub type_: i16,
47    pub size: u16,
48    pub mdl_address: *mut MdlRaw,
49    pub flags: u32,
50    pub associated_irp: IrpAssociatedUnion,
51    pub thread_list_entry: [*mut c_void; 2],
52    pub io_status: IoStatusBlock,
53    pub requestor_mode: i8,
54    pub pending_returned: u8,
55    pub stack_count: i8,
56    pub current_location: i8,
57    pub cancel: u8,
58    pub cancel_irql: u8,
59    pub apc_environment: i8,
60    pub allocation_flags: u8,
61    pub user_iosb: *mut IoStatusBlock,
62    pub user_event: *mut c_void,
63    pub overlay: IrpOverlay,
64    pub cancel_routine: *mut c_void,
65    pub user_buffer: *mut c_void,
66    pub tail: IrpTail,
67}
68
69/// IRP associated union
70#[repr(C)]
71pub union IrpAssociatedUnion {
72    pub master_irp: *mut IrpRaw,
73    pub irp_count: i32,
74    pub system_buffer: *mut c_void,
75}
76
77/// IRP overlay
78#[repr(C)]
79pub union IrpOverlay {
80    pub async_parameters: AsyncParameters,
81    pub allocation_size: u64,
82}
83
84#[repr(C)]
85#[derive(Clone, Copy)]
86pub struct AsyncParameters {
87    pub user_apc_routine: *mut c_void,
88    pub user_apc_context: *mut c_void,
89}
90
91/// IRP tail
92#[repr(C)]
93pub union IrpTail {
94    pub overlay: TailOverlay,
95    pub apc: [u8; 88],
96    pub completion_key: *mut c_void,
97}
98
99#[repr(C)]
100#[derive(Clone, Copy)]
101pub struct TailOverlay {
102    pub driver_context: [*mut c_void; 4],
103    pub thread: *mut c_void,
104    pub auxiliary_buffer: *mut c_void,
105    pub list_entry: [*mut c_void; 2],
106    pub current_stack_location: *mut IoStackLocation,
107    pub original_file_object: *mut c_void,
108}
109
110/// I/O status block
111#[repr(C)]
112#[derive(Debug, Clone, Copy)]
113pub struct IoStatusBlock {
114    pub status: NtStatus,
115    pub information: usize,
116}
117
118impl Default for IoStatusBlock {
119    fn default() -> Self {
120        Self {
121            status: status::STATUS_SUCCESS,
122            information: 0,
123        }
124    }
125}
126
127/// I/O stack location
128#[repr(C)]
129pub struct IoStackLocation {
130    pub major_function: u8,
131    pub minor_function: u8,
132    pub flags: u8,
133    pub control: u8,
134    pub parameters: IoStackParameters,
135    pub device_object: *mut c_void,
136    pub file_object: *mut c_void,
137    pub completion_routine: *mut c_void,
138    pub context: *mut c_void,
139}
140
141/// I/O stack parameters union
142#[repr(C)]
143pub union IoStackParameters {
144    pub create: CreateParameters,
145    pub read: ReadWriteParameters,
146    pub write: ReadWriteParameters,
147    pub device_io_control: DeviceIoControlParameters,
148    pub others: OtherParameters,
149}
150
151#[repr(C)]
152#[derive(Clone, Copy)]
153pub struct CreateParameters {
154    pub security_context: *mut c_void,
155    pub options: u32,
156    pub file_attributes: u16,
157    pub share_access: u16,
158    pub ea_length: u32,
159}
160
161#[repr(C)]
162#[derive(Clone, Copy)]
163pub struct ReadWriteParameters {
164    pub length: u32,
165    pub key: u32,
166    pub byte_offset: u64,
167}
168
169#[repr(C)]
170#[derive(Clone, Copy)]
171pub struct DeviceIoControlParameters {
172    pub output_buffer_length: u32,
173    pub input_buffer_length: u32,
174    pub io_control_code: u32,
175    pub type3_input_buffer: *mut c_void,
176}
177
178#[repr(C)]
179#[derive(Clone, Copy)]
180pub struct OtherParameters {
181    pub argument1: *mut c_void,
182    pub argument2: *mut c_void,
183    pub argument3: *mut c_void,
184    pub argument4: *mut c_void,
185}
186
187/// safe IRP wrapper
188pub struct Irp {
189    raw: NonNull<IrpRaw>,
190}
191
192impl Irp {
193    /// wrap raw IRP pointer
194    ///
195    /// # Safety
196    /// ptr must be a valid IRP pointer
197    pub unsafe fn from_raw(ptr: *mut c_void) -> Option<Self> {
198        NonNull::new(ptr as *mut IrpRaw).map(|raw| Self { raw })
199    }
200
201    /// get raw pointer
202    pub fn as_raw(&self) -> *mut IrpRaw {
203        self.raw.as_ptr()
204    }
205
206    /// get current I/O stack location
207    pub fn current_stack_location(&self) -> Option<&IoStackLocation> {
208        // SAFETY: IRP is valid
209        unsafe {
210            let tail = &(*self.raw.as_ptr()).tail;
211            let overlay = &tail.overlay;
212            let stack = overlay.current_stack_location;
213            if stack.is_null() {
214                None
215            } else {
216                Some(&*stack)
217            }
218        }
219    }
220
221    /// get mutable current I/O stack location
222    pub fn current_stack_location_mut(&mut self) -> Option<&mut IoStackLocation> {
223        // SAFETY: we have exclusive access
224        unsafe {
225            let tail = &mut (*self.raw.as_ptr()).tail;
226            let overlay = &mut tail.overlay;
227            let stack = overlay.current_stack_location;
228            if stack.is_null() {
229                None
230            } else {
231                Some(&mut *stack)
232            }
233        }
234    }
235
236    /// get major function code
237    pub fn major_function(&self) -> Option<IrpMajorFunction> {
238        self.current_stack_location().map(|stack| {
239            // SAFETY: we're reading a valid enum value
240            unsafe { core::mem::transmute(stack.major_function) }
241        })
242    }
243
244    /// get minor function code
245    pub fn minor_function(&self) -> Option<u8> {
246        self.current_stack_location().map(|stack| stack.minor_function)
247    }
248
249    /// get system buffer (for buffered I/O)
250    pub fn system_buffer(&self) -> *mut c_void {
251        // SAFETY: IRP is valid
252        unsafe { (*self.raw.as_ptr()).associated_irp.system_buffer }
253    }
254
255    /// get user buffer
256    pub fn user_buffer(&self) -> *mut c_void {
257        // SAFETY: IRP is valid
258        unsafe { (*self.raw.as_ptr()).user_buffer }
259    }
260
261    /// get MDL address (for direct I/O)
262    pub fn mdl_address(&self) -> *mut MdlRaw {
263        // SAFETY: IRP is valid
264        unsafe { (*self.raw.as_ptr()).mdl_address }
265    }
266
267    /// get IOCTL parameters
268    pub fn ioctl_parameters(&self) -> Option<DeviceIoControlParameters> {
269        self.current_stack_location().map(|stack| {
270            // SAFETY: assuming this is a device control request
271            unsafe { stack.parameters.device_io_control }
272        })
273    }
274
275    /// get IOCTL code
276    pub fn ioctl_code(&self) -> Option<u32> {
277        self.ioctl_parameters().map(|p| p.io_control_code)
278    }
279
280    /// get input buffer length
281    pub fn input_buffer_length(&self) -> Option<u32> {
282        self.ioctl_parameters().map(|p| p.input_buffer_length)
283    }
284
285    /// get output buffer length
286    pub fn output_buffer_length(&self) -> Option<u32> {
287        self.ioctl_parameters().map(|p| p.output_buffer_length)
288    }
289
290    /// get read/write parameters
291    pub fn rw_parameters(&self) -> Option<ReadWriteParameters> {
292        self.current_stack_location().map(|stack| {
293            // SAFETY: assuming this is a read/write request
294            unsafe { stack.parameters.read }
295        })
296    }
297
298    /// get read/write length
299    pub fn rw_length(&self) -> Option<u32> {
300        self.rw_parameters().map(|p| p.length)
301    }
302
303    /// get read/write offset
304    pub fn rw_offset(&self) -> Option<u64> {
305        self.rw_parameters().map(|p| p.byte_offset)
306    }
307
308    /// set I/O status
309    pub fn set_status(&mut self, status: NtStatus, information: usize) {
310        // SAFETY: we have exclusive access
311        unsafe {
312            (*self.raw.as_ptr()).io_status.status = status;
313            (*self.raw.as_ptr()).io_status.information = information;
314        }
315    }
316
317    /// get I/O status
318    pub fn io_status(&self) -> IoStatusBlock {
319        // SAFETY: IRP is valid
320        unsafe { (*self.raw.as_ptr()).io_status }
321    }
322
323    /// complete the IRP
324    pub fn complete(&mut self, status: NtStatus) {
325        self.complete_with_info(status, 0);
326    }
327
328    /// complete the IRP with information
329    pub fn complete_with_info(&mut self, status: NtStatus, information: usize) {
330        self.set_status(status, information);
331        // SAFETY: IRP is valid
332        unsafe {
333            IofCompleteRequest(self.raw.as_ptr() as *mut c_void, 0); // IO_NO_INCREMENT
334        }
335    }
336
337    /// complete with priority boost
338    pub fn complete_with_boost(&mut self, status: NtStatus, information: usize, boost: i8) {
339        self.set_status(status, information);
340        // SAFETY: IRP is valid
341        unsafe {
342            IofCompleteRequest(self.raw.as_ptr() as *mut c_void, boost);
343        }
344    }
345
346    /// mark IRP as pending
347    pub fn mark_pending(&mut self) {
348        // SAFETY: we have exclusive access
349        unsafe {
350            IoMarkIrpPending(self.raw.as_ptr() as *mut c_void);
351        }
352    }
353
354    /// get input buffer as typed reference
355    pub fn input_buffer<T>(&self) -> Option<&T> {
356        let buffer = self.system_buffer();
357        let len = self.input_buffer_length()?;
358
359        if buffer.is_null() || (len as usize) < core::mem::size_of::<T>() {
360            return None;
361        }
362
363        // SAFETY: buffer is valid and large enough
364        Some(unsafe { &*(buffer as *const T) })
365    }
366
367    /// get output buffer as typed mutable reference
368    pub fn output_buffer_mut<T>(&mut self) -> Option<&mut T> {
369        let buffer = self.system_buffer();
370        let len = self.output_buffer_length()?;
371
372        if buffer.is_null() || (len as usize) < core::mem::size_of::<T>() {
373            return None;
374        }
375
376        // SAFETY: buffer is valid and large enough
377        Some(unsafe { &mut *(buffer as *mut T) })
378    }
379
380    /// get input buffer as byte slice
381    pub fn input_bytes(&self) -> Option<&[u8]> {
382        let buffer = self.system_buffer();
383        let len = self.input_buffer_length()?;
384
385        if buffer.is_null() || len == 0 {
386            return None;
387        }
388
389        // SAFETY: buffer is valid for len bytes
390        Some(unsafe { core::slice::from_raw_parts(buffer as *const u8, len as usize) })
391    }
392
393    /// get output buffer as mutable byte slice
394    pub fn output_bytes_mut(&mut self) -> Option<&mut [u8]> {
395        let buffer = self.system_buffer();
396        let len = self.output_buffer_length()?;
397
398        if buffer.is_null() || len == 0 {
399            return None;
400        }
401
402        // SAFETY: buffer is valid for len bytes
403        Some(unsafe { core::slice::from_raw_parts_mut(buffer as *mut u8, len as usize) })
404    }
405}
406
407// IRP handling functions
408extern "system" {
409    fn IofCompleteRequest(Irp: *mut c_void, PriorityBoost: i8);
410    fn IoMarkIrpPending(Irp: *mut c_void);
411}