1use core::ffi::c_void;
4
5use super::error::{status, KmError, KmResult, NtStatus};
6use super::irp::Irp;
7
8#[repr(u32)]
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum IoctlMethod {
12 Buffered = 0,
14 InDirect = 1,
16 OutDirect = 2,
18 Neither = 3,
20}
21
22#[derive(Debug, Clone, Copy)]
24pub struct IoctlCode(pub u32);
25
26impl IoctlCode {
27 pub const fn new(
29 device_type: u32,
30 function: u32,
31 method: IoctlMethod,
32 access: u32,
33 ) -> Self {
34 let code = (device_type << 16)
35 | (access << 14)
36 | (function << 2)
37 | (method as u32);
38 Self(code)
39 }
40
41 pub const fn custom(function: u32, method: IoctlMethod, access: IoctlAccess) -> Self {
43 Self::new(0x8000, function, method, access as u32)
44 }
45
46 pub const fn buffered(function: u32, access: IoctlAccess) -> Self {
48 Self::custom(function, IoctlMethod::Buffered, access)
49 }
50
51 pub const fn device_type(&self) -> u32 {
53 (self.0 >> 16) & 0xFFFF
54 }
55
56 pub const fn function(&self) -> u32 {
58 (self.0 >> 2) & 0xFFF
59 }
60
61 pub const fn method(&self) -> IoctlMethod {
63 match self.0 & 3 {
64 0 => IoctlMethod::Buffered,
65 1 => IoctlMethod::InDirect,
66 2 => IoctlMethod::OutDirect,
67 _ => IoctlMethod::Neither,
68 }
69 }
70
71 pub const fn access(&self) -> u32 {
73 (self.0 >> 14) & 3
74 }
75
76 pub const fn code(&self) -> u32 {
78 self.0
79 }
80}
81
82#[repr(u32)]
84#[derive(Debug, Clone, Copy, PartialEq, Eq)]
85pub enum IoctlAccess {
86 Any = 0,
88 Read = 1,
90 Write = 2,
92 ReadWrite = 3,
94}
95
96pub struct Ioctl<'a> {
98 irp: &'a mut Irp,
99 code: IoctlCode,
100}
101
102impl<'a> Ioctl<'a> {
103 pub fn from_irp(irp: &'a mut Irp) -> Option<Self> {
105 let code = irp.ioctl_code()?;
106 Some(Self {
107 irp,
108 code: IoctlCode(code),
109 })
110 }
111
112 pub fn code(&self) -> IoctlCode {
114 self.code
115 }
116
117 pub fn function(&self) -> u32 {
119 self.code.function()
120 }
121
122 pub fn method(&self) -> IoctlMethod {
124 self.code.method()
125 }
126
127 pub fn input_length(&self) -> usize {
129 self.irp.input_buffer_length().unwrap_or(0) as usize
130 }
131
132 pub fn output_length(&self) -> usize {
134 self.irp.output_buffer_length().unwrap_or(0) as usize
135 }
136
137 pub fn input<T>(&self) -> Option<&T> {
139 if self.input_length() < core::mem::size_of::<T>() {
140 return None;
141 }
142 self.irp.input_buffer()
143 }
144
145 pub fn output_mut<T>(&mut self) -> Option<&mut T> {
147 if self.output_length() < core::mem::size_of::<T>() {
148 return None;
149 }
150 self.irp.output_buffer_mut()
151 }
152
153 pub fn input_bytes(&self) -> Option<&[u8]> {
155 self.irp.input_bytes()
156 }
157
158 pub fn output_bytes_mut(&mut self) -> Option<&mut [u8]> {
160 self.irp.output_bytes_mut()
161 }
162
163 pub fn complete_success(self) {
165 self.complete_with_info(status::STATUS_SUCCESS, 0);
166 }
167
168 pub fn complete_with_output(self, output_size: usize) {
170 self.complete_with_info(status::STATUS_SUCCESS, output_size);
171 }
172
173 pub fn complete_error(self, error: KmError) {
175 self.complete_with_info(error.to_ntstatus(), 0);
176 }
177
178 pub fn complete_with_info(mut self, status: NtStatus, information: usize) {
180 self.irp.complete_with_info(status, information);
181 }
182}
183
184pub trait IoctlHandler {
186 fn handle(&self, ioctl: Ioctl) -> NtStatus;
188}
189
190pub struct IoctlDispatcher {
192 handlers: &'static [(u32, &'static dyn IoctlHandler)],
193 default_handler: Option<&'static dyn IoctlHandler>,
194}
195
196impl IoctlDispatcher {
197 pub const fn new(handlers: &'static [(u32, &'static dyn IoctlHandler)]) -> Self {
199 Self {
200 handlers,
201 default_handler: None,
202 }
203 }
204
205 pub const fn with_default(
207 handlers: &'static [(u32, &'static dyn IoctlHandler)],
208 default: &'static dyn IoctlHandler,
209 ) -> Self {
210 Self {
211 handlers,
212 default_handler: Some(default),
213 }
214 }
215
216 pub fn dispatch(&self, irp: &mut Irp) -> NtStatus {
218 let Some(ioctl) = Ioctl::from_irp(irp) else {
219 return status::STATUS_INVALID_PARAMETER;
220 };
221
222 let function = ioctl.function();
223
224 for (code, handler) in self.handlers {
226 if *code == function {
227 return handler.handle(ioctl);
228 }
229 }
230
231 if let Some(handler) = self.default_handler {
233 return handler.handle(ioctl);
234 }
235
236 ioctl.complete_with_info(status::STATUS_INVALID_DEVICE_REQUEST, 0);
238 status::STATUS_INVALID_DEVICE_REQUEST
239 }
240}
241
242#[repr(C)]
246#[derive(Debug, Clone, Copy)]
247pub struct ReadMemoryRequest {
248 pub process_id: u32,
249 pub address: u64,
250 pub size: u32,
251}
252
253#[repr(C)]
255#[derive(Debug, Clone, Copy)]
256pub struct WriteMemoryRequest {
257 pub process_id: u32,
258 pub address: u64,
259 pub size: u32,
260 }
262
263#[repr(C)]
265#[derive(Debug, Clone, Copy)]
266pub struct GetModuleBaseRequest {
267 pub process_id: u32,
268 pub module_name_offset: u32, pub module_name_length: u32,
270}
271
272#[repr(C)]
274#[derive(Debug, Clone, Copy)]
275pub struct GetModuleBaseResponse {
276 pub base_address: u64,
277 pub size: u64,
278}
279
280#[repr(C)]
282#[derive(Debug, Clone, Copy)]
283pub struct AllocateMemoryRequest {
284 pub process_id: u32,
285 pub size: u64,
286 pub protection: u32,
287 pub preferred_address: u64,
288}
289
290#[repr(C)]
292#[derive(Debug, Clone, Copy)]
293pub struct AllocateMemoryResponse {
294 pub allocated_address: u64,
295 pub actual_size: u64,
296}
297
298#[repr(C)]
300#[derive(Debug, Clone, Copy)]
301pub struct FreeMemoryRequest {
302 pub process_id: u32,
303 pub address: u64,
304}
305
306#[repr(C)]
308#[derive(Debug, Clone, Copy)]
309pub struct ProtectMemoryRequest {
310 pub process_id: u32,
311 pub address: u64,
312 pub size: u64,
313 pub new_protection: u32,
314}
315
316#[repr(C)]
318#[derive(Debug, Clone, Copy)]
319pub struct ProtectMemoryResponse {
320 pub old_protection: u32,
321}
322
323pub mod codes {
325 use super::{IoctlAccess, IoctlCode};
326
327 pub const READ_MEMORY: IoctlCode = IoctlCode::buffered(0x800, IoctlAccess::ReadWrite);
329
330 pub const WRITE_MEMORY: IoctlCode = IoctlCode::buffered(0x801, IoctlAccess::ReadWrite);
332
333 pub const GET_MODULE_BASE: IoctlCode = IoctlCode::buffered(0x802, IoctlAccess::Read);
335
336 pub const ALLOCATE_MEMORY: IoctlCode = IoctlCode::buffered(0x803, IoctlAccess::ReadWrite);
338
339 pub const FREE_MEMORY: IoctlCode = IoctlCode::buffered(0x804, IoctlAccess::ReadWrite);
341
342 pub const PROTECT_MEMORY: IoctlCode = IoctlCode::buffered(0x805, IoctlAccess::ReadWrite);
344
345 pub const QUERY_PROCESS: IoctlCode = IoctlCode::buffered(0x806, IoctlAccess::Read);
347
348 pub const COPY_PHYSICAL: IoctlCode = IoctlCode::buffered(0x807, IoctlAccess::ReadWrite);
350
351 pub const MAP_PHYSICAL: IoctlCode = IoctlCode::buffered(0x808, IoctlAccess::ReadWrite);
353
354 pub const UNMAP_PHYSICAL: IoctlCode = IoctlCode::buffered(0x809, IoctlAccess::ReadWrite);
356}
357
358#[macro_export]
360macro_rules! define_ioctl_handler {
361 ($name:ident, |$ioctl:ident| $body:block) => {
362 struct $name;
363
364 impl $crate::km::ioctl::IoctlHandler for $name {
365 fn handle(&self, $ioctl: $crate::km::ioctl::Ioctl) -> $crate::km::error::NtStatus {
366 $body
367 }
368 }
369 };
370}
371
372#[macro_export]
374macro_rules! ioctl_dispatcher {
375 ($($code:expr => $handler:expr),* $(,)?) => {
376 $crate::km::ioctl::IoctlDispatcher::new(&[
377 $(($code, &$handler as &dyn $crate::km::ioctl::IoctlHandler)),*
378 ])
379 };
380}