use core::ffi::c_void;
use super::error::{status, KmError, KmResult, NtStatus};
use super::irp::Irp;
#[repr(u32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum IoctlMethod {
Buffered = 0,
InDirect = 1,
OutDirect = 2,
Neither = 3,
}
#[derive(Debug, Clone, Copy)]
pub struct IoctlCode(pub u32);
impl IoctlCode {
pub const fn new(
device_type: u32,
function: u32,
method: IoctlMethod,
access: u32,
) -> Self {
let code = (device_type << 16)
| (access << 14)
| (function << 2)
| (method as u32);
Self(code)
}
pub const fn custom(function: u32, method: IoctlMethod, access: IoctlAccess) -> Self {
Self::new(0x8000, function, method, access as u32)
}
pub const fn buffered(function: u32, access: IoctlAccess) -> Self {
Self::custom(function, IoctlMethod::Buffered, access)
}
pub const fn device_type(&self) -> u32 {
(self.0 >> 16) & 0xFFFF
}
pub const fn function(&self) -> u32 {
(self.0 >> 2) & 0xFFF
}
pub const fn method(&self) -> IoctlMethod {
match self.0 & 3 {
0 => IoctlMethod::Buffered,
1 => IoctlMethod::InDirect,
2 => IoctlMethod::OutDirect,
_ => IoctlMethod::Neither,
}
}
pub const fn access(&self) -> u32 {
(self.0 >> 14) & 3
}
pub const fn code(&self) -> u32 {
self.0
}
}
#[repr(u32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum IoctlAccess {
Any = 0,
Read = 1,
Write = 2,
ReadWrite = 3,
}
pub struct Ioctl<'a> {
irp: &'a mut Irp,
code: IoctlCode,
}
impl<'a> Ioctl<'a> {
pub fn from_irp(irp: &'a mut Irp) -> Option<Self> {
let code = irp.ioctl_code()?;
Some(Self {
irp,
code: IoctlCode(code),
})
}
pub fn code(&self) -> IoctlCode {
self.code
}
pub fn function(&self) -> u32 {
self.code.function()
}
pub fn method(&self) -> IoctlMethod {
self.code.method()
}
pub fn input_length(&self) -> usize {
self.irp.input_buffer_length().unwrap_or(0) as usize
}
pub fn output_length(&self) -> usize {
self.irp.output_buffer_length().unwrap_or(0) as usize
}
pub fn input<T>(&self) -> Option<&T> {
if self.input_length() < core::mem::size_of::<T>() {
return None;
}
self.irp.input_buffer()
}
pub fn output_mut<T>(&mut self) -> Option<&mut T> {
if self.output_length() < core::mem::size_of::<T>() {
return None;
}
self.irp.output_buffer_mut()
}
pub fn input_bytes(&self) -> Option<&[u8]> {
self.irp.input_bytes()
}
pub fn output_bytes_mut(&mut self) -> Option<&mut [u8]> {
self.irp.output_bytes_mut()
}
pub fn complete_success(self) {
self.complete_with_info(status::STATUS_SUCCESS, 0);
}
pub fn complete_with_output(self, output_size: usize) {
self.complete_with_info(status::STATUS_SUCCESS, output_size);
}
pub fn complete_error(self, error: KmError) {
self.complete_with_info(error.to_ntstatus(), 0);
}
pub fn complete_with_info(mut self, status: NtStatus, information: usize) {
self.irp.complete_with_info(status, information);
}
}
pub trait IoctlHandler {
fn handle(&self, ioctl: Ioctl) -> NtStatus;
}
pub struct IoctlDispatcher {
handlers: &'static [(u32, &'static dyn IoctlHandler)],
default_handler: Option<&'static dyn IoctlHandler>,
}
impl IoctlDispatcher {
pub const fn new(handlers: &'static [(u32, &'static dyn IoctlHandler)]) -> Self {
Self {
handlers,
default_handler: None,
}
}
pub const fn with_default(
handlers: &'static [(u32, &'static dyn IoctlHandler)],
default: &'static dyn IoctlHandler,
) -> Self {
Self {
handlers,
default_handler: Some(default),
}
}
pub fn dispatch(&self, irp: &mut Irp) -> NtStatus {
let Some(ioctl) = Ioctl::from_irp(irp) else {
return status::STATUS_INVALID_PARAMETER;
};
let function = ioctl.function();
for (code, handler) in self.handlers {
if *code == function {
return handler.handle(ioctl);
}
}
if let Some(handler) = self.default_handler {
return handler.handle(ioctl);
}
ioctl.complete_with_info(status::STATUS_INVALID_DEVICE_REQUEST, 0);
status::STATUS_INVALID_DEVICE_REQUEST
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct ReadMemoryRequest {
pub process_id: u32,
pub address: u64,
pub size: u32,
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct WriteMemoryRequest {
pub process_id: u32,
pub address: u64,
pub size: u32,
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct GetModuleBaseRequest {
pub process_id: u32,
pub module_name_offset: u32, pub module_name_length: u32,
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct GetModuleBaseResponse {
pub base_address: u64,
pub size: u64,
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct AllocateMemoryRequest {
pub process_id: u32,
pub size: u64,
pub protection: u32,
pub preferred_address: u64,
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct AllocateMemoryResponse {
pub allocated_address: u64,
pub actual_size: u64,
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct FreeMemoryRequest {
pub process_id: u32,
pub address: u64,
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct ProtectMemoryRequest {
pub process_id: u32,
pub address: u64,
pub size: u64,
pub new_protection: u32,
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct ProtectMemoryResponse {
pub old_protection: u32,
}
pub mod codes {
use super::{IoctlAccess, IoctlCode};
pub const READ_MEMORY: IoctlCode = IoctlCode::buffered(0x800, IoctlAccess::ReadWrite);
pub const WRITE_MEMORY: IoctlCode = IoctlCode::buffered(0x801, IoctlAccess::ReadWrite);
pub const GET_MODULE_BASE: IoctlCode = IoctlCode::buffered(0x802, IoctlAccess::Read);
pub const ALLOCATE_MEMORY: IoctlCode = IoctlCode::buffered(0x803, IoctlAccess::ReadWrite);
pub const FREE_MEMORY: IoctlCode = IoctlCode::buffered(0x804, IoctlAccess::ReadWrite);
pub const PROTECT_MEMORY: IoctlCode = IoctlCode::buffered(0x805, IoctlAccess::ReadWrite);
pub const QUERY_PROCESS: IoctlCode = IoctlCode::buffered(0x806, IoctlAccess::Read);
pub const COPY_PHYSICAL: IoctlCode = IoctlCode::buffered(0x807, IoctlAccess::ReadWrite);
pub const MAP_PHYSICAL: IoctlCode = IoctlCode::buffered(0x808, IoctlAccess::ReadWrite);
pub const UNMAP_PHYSICAL: IoctlCode = IoctlCode::buffered(0x809, IoctlAccess::ReadWrite);
}
#[macro_export]
macro_rules! define_ioctl_handler {
($name:ident, |$ioctl:ident| $body:block) => {
struct $name;
impl $crate::km::ioctl::IoctlHandler for $name {
fn handle(&self, $ioctl: $crate::km::ioctl::Ioctl) -> $crate::km::error::NtStatus {
$body
}
}
};
}
#[macro_export]
macro_rules! ioctl_dispatcher {
($($code:expr => $handler:expr),* $(,)?) => {
$crate::km::ioctl::IoctlDispatcher::new(&[
$(($code, &$handler as &dyn $crate::km::ioctl::IoctlHandler)),*
])
};
}