diff options
| author | pravic <[email protected]> | 2016-04-12 16:39:37 +0300 |
|---|---|---|
| committer | pravic <[email protected]> | 2016-04-12 16:39:37 +0300 |
| commit | 28ff216899e95a6a9756bcbe580f28ed8ce61228 (patch) | |
| tree | bbf00e3c5f3b440db5ddb3f86b6d3a893349cee0 | |
| parent | git ignore (diff) | |
| download | winapi-kmd-rs-28ff216899e95a6a9756bcbe580f28ed8ce61228.tar.xz winapi-kmd-rs-28ff216899e95a6a9756bcbe580f28ed8ce61228.zip | |
Windows Kernel-Mode library
| -rw-r--r-- | Cargo.toml | 27 | ||||
| -rw-r--r-- | README.md | 2 | ||||
| -rw-r--r-- | src/alloc/Cargo.toml | 15 | ||||
| -rw-r--r-- | src/alloc/alloc.rs | 77 | ||||
| -rw-r--r-- | src/alloc/pool.rs | 13 | ||||
| -rw-r--r-- | src/basedef.rs | 91 | ||||
| -rw-r--r-- | src/crt.rs | 86 | ||||
| -rw-r--r-- | src/debug.rs | 46 | ||||
| -rw-r--r-- | src/device_object.rs | 99 | ||||
| -rw-r--r-- | src/dpc.rs | 41 | ||||
| -rw-r--r-- | src/driver_object.rs | 34 | ||||
| -rw-r--r-- | src/event.rs | 31 | ||||
| -rw-r--r-- | src/file_object.rs | 15 | ||||
| -rw-r--r-- | src/irp.rs | 184 | ||||
| -rw-r--r-- | src/irql.rs | 61 | ||||
| -rw-r--r-- | src/lang.rs | 7 | ||||
| -rw-r--r-- | src/lib.rs | 55 | ||||
| -rw-r--r-- | src/macros.rs | 34 | ||||
| -rw-r--r-- | src/object.rs | 44 | ||||
| -rw-r--r-- | src/pool.rs | 42 | ||||
| -rw-r--r-- | src/rtl.rs | 11 | ||||
| -rw-r--r-- | src/status.rs | 59 | ||||
| -rw-r--r-- | src/string.rs | 95 | ||||
| -rw-r--r-- | src/thread.rs | 0 | ||||
| -rw-r--r-- | src/time.rs | 26 | ||||
| -rw-r--r-- | src/types.rs | 1 |
26 files changed, 1196 insertions, 0 deletions
diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..65634df --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "winapi-km" +version = "0.1.0" +description = "Windows Kernel Mode support library." + +authors = ["pravic <[email protected]>"] +readme = "README.md" +license = "MIT" + +# libc = { version = "*", default_features = false } + +[lib] +name = "km" +crate-type = ["rlib"] + +[dependencies] +core = { path= "../libcore", version="*" } +alloc_system = { path="src/alloc" } +collections = { path = "../libcollections" } + +[profile.release] +opt-level = 3 +debug = true +rpath = false +lto = true +debug-assertions = false +codegen-units = 4 diff --git a/README.md b/README.md new file mode 100644 index 0000000..d8361cf --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# Windows Kernel-Mode Drivers written in Rust + diff --git a/src/alloc/Cargo.toml b/src/alloc/Cargo.toml new file mode 100644 index 0000000..2c7f4b8 --- /dev/null +++ b/src/alloc/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "alloc_system" +version = "0.1.0" +description = "Windows Kernel Mode support library." + +authors = ["pravic <[email protected]>"] +license = "MIT" + +[lib] +name = "alloc_system" +path = "alloc.rs" +test = false + +[dependencies] +core = { path= "../../../libcore" } diff --git a/src/alloc/alloc.rs b/src/alloc/alloc.rs new file mode 100644 index 0000000..963c3e3 --- /dev/null +++ b/src/alloc/alloc.rs @@ -0,0 +1,77 @@ +//! Kernel Mode Allocator + +// The compiler needs to be instructed that this crate is an allocator in order +// to realize that when this is linked in another allocator like jemalloc should +// not be linked in +#![feature(allocator)] +#![allocator] + +// Allocators are not allowed to depend on the standard library which in turn +// requires an allocator in order to avoid circular dependencies. This crate, +// however, can use all of libcore. +#![no_std] + +#![crate_name = "alloc_system"] +#![crate_type = "rlib"] + +mod pool; + +// Listed below are the five allocation functions currently required by custom +// allocators. Their signatures and symbol names are not currently typechecked +// by the compiler, but this is a future extension and are required to match +// what is found below. +// +// Note that the standard `malloc` and `realloc` functions do not provide a way +// to communicate alignment so this implementation would need to be improved +// with respect to alignment in that aspect. + +const KMRS_TAG: u32 = 0x4B4D5253; // 'KMRS' +use pool::{ExAllocatePoolWithTag, ExFreePoolWithTag, POOL_TYPE}; + +extern "C" +{ + // from ntoskrnl + pub fn memcpy(dest: *mut u8, src: *const u8, n: usize) -> *mut u8; +} + +#[no_mangle] +pub extern "C" fn __rust_allocate(size: usize, _align: usize) -> *mut u8 +{ + unsafe { ExAllocatePoolWithTag(POOL_TYPE::PagedPool, size, KMRS_TAG) } +} + +#[no_mangle] +pub extern "C" fn __rust_deallocate(ptr: *mut u8, _old_size: usize, _align: usize) +{ + unsafe { ExFreePoolWithTag(ptr, KMRS_TAG) }; +} + +#[no_mangle] +pub extern "C" fn __rust_reallocate(old: *mut u8, old_size: usize, size: usize, align: usize) -> *mut u8 +{ + unsafe { + // http://en.cppreference.com/w/c/memory/realloc + let minsize = if size < old_size { size } else { old_size }; + let new = __rust_allocate(size, align); + if new.is_null() { + return new; + } + if !old.is_null() && old_size > 0 { + memcpy(new, old, minsize); + __rust_deallocate(old, old_size, align); + } + return new; + } +} + +#[no_mangle] +pub extern "C" fn __rust_reallocate_inplace(_ptr: *mut u8, old_size: usize, _size: usize, _align: usize) -> usize +{ + old_size // this api is not supported +} + +#[no_mangle] +pub extern "C" fn __rust_usable_size(size: usize, _align: usize) -> usize +{ + size +} diff --git a/src/alloc/pool.rs b/src/alloc/pool.rs new file mode 100644 index 0000000..5b120ac --- /dev/null +++ b/src/alloc/pool.rs @@ -0,0 +1,13 @@ +#[repr(C)] +pub enum POOL_TYPE +{ + PagedPool, +} + +pub type PVOID = *mut u8; + +extern "system" +{ + pub fn ExAllocatePoolWithTag(PoolType: POOL_TYPE, NumberOfBytes: usize, Tag: u32) -> PVOID; + pub fn ExFreePoolWithTag(P: PVOID, Tag: u32); +} diff --git a/src/basedef.rs b/src/basedef.rs new file mode 100644 index 0000000..8638b85 --- /dev/null +++ b/src/basedef.rs @@ -0,0 +1,91 @@ +//! Kernel-Mode Types. + +use ::KIRQL; + + +// Use repr(u8) as LLVM expects `void*` to be the same as `i8*` to help enable +// more optimization opportunities around it recognizing things like +// malloc/free. +#[repr(u8)] +#[doc(hidden)] +pub enum km_void { + // Two dummy variants so the #[repr] attribute can be used. + #[doc(hidden)] + __variant1, + #[doc(hidden)] + __variant2, +} + +pub type VOID = km_void; +pub type PVOID = *mut VOID; +pub type PCVOID = *const VOID; + + +pub type PETHREAD = PVOID; +pub type PIO_APC_ROUTINE = Option<extern "system" fn (ApcContext: PCVOID, IoStatusBlock: *const IO_STATUS_BLOCK, Reserved: u32)>; + + +extern "system" +{ + pub fn KeGetCurrentIrql() -> KIRQL; + pub fn KeRaiseIrqlToDpcLevel() -> KIRQL; + pub fn KfLowerIrql(NewIrql: KIRQL) -> KIRQL; + pub fn KfRaiseIrql(NewIrql: KIRQL) -> KIRQL; +} + + +/// Doubly linked list structure. +#[repr(C)] +pub struct LIST_ENTRY +{ + pub next: *mut LIST_ENTRY, + pub prev: *mut LIST_ENTRY, +} + +/// Spin Lock. +#[repr(C)] +pub struct KSPIN_LOCK +{ + pub lock: usize, +} + +/// Common dispatcher object header. +#[repr(C)] +pub struct DISPATCHER_HEADER +{ + pub Type: u8, + pub Absolute: u8, + pub Size: u8, + pub Inserted: u8, + pub SignalState: i32, + pub WaitListHead: LIST_ENTRY, +} + +/// An I/O status block. +#[repr(C)] +pub struct IO_STATUS_BLOCK +{ + pub Status: ::NTSTATUS, + pub Information: usize, +} + +pub type PIO_STATUS_BLOCK = *mut IO_STATUS_BLOCK; + + +/// Processor modes. +#[repr(C)] +pub enum KPROCESSOR_MODE +{ + KernelMode, + UserMode, +} + +/// I/O Request priority. +pub mod IO_PRIORITY { + /// I/O Request priority type. + pub type KPRIORITY_BOOST = u8; + + pub const IO_NO_INCREMENT: KPRIORITY_BOOST = 0; + pub const IO_DISK_INCREMENT: KPRIORITY_BOOST = 1; + pub const EVENT_INCREMENT: KPRIORITY_BOOST = 1; +} diff --git a/src/crt.rs b/src/crt.rs new file mode 100644 index 0000000..ad316e2 --- /dev/null +++ b/src/crt.rs @@ -0,0 +1,86 @@ +//! C runtime library. +//! +//! Functions imported from `ntoskrnl.exe`. + +extern "C" +{ + pub fn memcmp(s1: *const u8, s2: *const u8, n: usize) -> i32; + pub fn memcpy(dest: *mut u8, src: *const u8, n: usize) -> *mut u8; + + pub fn strlen(s: *const u8) -> usize; + pub fn strcmp(s1: *const u8, s2: *const u8) -> i32; + pub fn strcpy(dest: *mut u8, src: *const u8) -> *mut u8; + pub fn strcat(dest: *mut u8, src: *const u8) -> *mut u8; + pub fn strncpy(dest: *mut u8, src: *const u8, n: usize) -> *mut u8; + + pub fn wcslen(s: *const u16) -> usize; + pub fn wcscpy(dest: *mut u16, src: *const u16) -> *mut u16; + pub fn wcsncpy(dest: *mut u16, src: *const u16, n: usize) -> *mut u16; +} + + +#[no_mangle] +#[allow(non_upper_case_globals)] +#[cfg(target_arch="x86")] +#[doc(hidden)] +pub static __security_cookie: usize = 0xBB40E64E; + +#[no_mangle] +#[allow(non_upper_case_globals)] +#[cfg(target_arch="x86_64")] +#[doc(hidden)] +pub static __security_cookie: usize = 0x00002B992DDFA232; + + +#[doc(hidden)] +pub mod rust_intrinsics +{ + // Idk why, but linker cannot find `_memcmp` for llvm intrinsics. So lets make forward one. + #[no_mangle] + pub unsafe extern fn memcmp(s1: *const u8, s2: *const u8, n: usize) -> i32 { + return ::crt::memcmp(s1, s2, n); + } + + // ported from compiler-rt + #[no_mangle] + pub unsafe extern fn __mulodi4(a: i64, b: i64, overflow: &mut i32) -> i64 { + const N: i32 = 64; + const MIN: i64 = 1 << (N-1); + const MAX: i64 = !MIN; + *overflow = 0; + + let result = a * b; + if a == MIN { + if b != 0 && b != 1 { + *overflow = 1; + } + return result; + } + + if b == MIN { + if a != 0 && a != 1 { + *overflow = 1; + } + return result; + } + + let sa = a >> (N-1); + let sb = b >> (N-1); + let abs_a = (a ^ sa) - sa; + let abs_b = (b ^ sb) - sb; + + if abs_a < 2 || abs_b < 2 { + return result; + } + if sa == sb { + if abs_a > MAX / abs_b { + *overflow = 1; + } + } else { + if abs_a > MIN / -abs_b { + *overflow = 1; + } + } + return result; + } +} diff --git a/src/debug.rs b/src/debug.rs new file mode 100644 index 0000000..66e46ab --- /dev/null +++ b/src/debug.rs @@ -0,0 +1,46 @@ +//! Debugger support. + +use ::status::NTSTATUS; + +extern "C" +{ + /// `DbgPrint` routine sends a message to the kernel debugger. + pub fn DbgPrint(Format: *const u8, ...) -> NTSTATUS; + /// The `DbgPrintEx` routine sends a string to the kernel debugger if certain conditions are met. + pub fn DbgPrintEx(ComponentId: u32, Level: u32, Format: *const u8, ...) -> NTSTATUS; +} + +extern "system" +{ + /// Breaks into the kernel debugger. + pub fn DbgBreakPoint(); + /// Breaks into the kernel debugger and sends the value of `Status` to the debugger. + pub fn DbgBreakPointWithStatus(Status: NTSTATUS); +} + +/// `DbgPrintEx` Message severity. +#[repr(C)] +pub enum DPFLTR_LEVEL { + ERROR = 0, + WARNING, + TRACE, + INFO, +} + +/// `DbgPrintEx` Component name. +#[repr(C)] +pub enum DPFLTR_ID { + SYSTEM = 0, + SMSS, + SETUP, + NTFS, + // ... + IHVDRIVER = 77, + IHVVIDEO, + IHVAUDIO, + IHVNETWORK, + IHVSTREAMING, + IHVBUS, + + DEFAULT = 99, +} diff --git a/src/device_object.rs b/src/device_object.rs new file mode 100644 index 0000000..f4cbf39 --- /dev/null +++ b/src/device_object.rs @@ -0,0 +1,99 @@ +//! Device Object. + +use ::{NTSTATUS, UNICODE_STRING}; +use ::driver_object::DRIVER_OBJECT; +use ::irp::{IRP, PIRP}; +use ::dpc::KDPC; +use ::event::KEVENT; +use ::object::*; +use ::PVOID; + +extern "system" +{ + pub fn IoCreateDevice(DriverObject: &mut DRIVER_OBJECT, DeviceExtensionSize: u32, DeviceName: *const UNICODE_STRING, + DeviceType: u32, DeviceCharacteristics: u32, Exclusive: bool, DeviceObject: *mut*mut DEVICE_OBJECT) -> NTSTATUS; + + pub fn IoDeleteDevice(DeviceObject: &mut DEVICE_OBJECT) -> NTSTATUS; + pub fn IoCreateSymbolicLink(SymbolicLinkName: &UNICODE_STRING, DeviceName: &UNICODE_STRING) -> NTSTATUS; + pub fn IoDeleteSymbolicLink(SymbolicLinkName: &UNICODE_STRING) -> NTSTATUS; +} + +/// Device object flags. +#[repr(C)] +pub enum DEVICE_FLAGS { + NONE = 0, + DO_VERIFY_VOLUME = 0x00000002, + DO_BUFFERED_IO = 0x00000004, + DO_EXCLUSIVE = 0x00000008, + DO_DIRECT_IO = 0x00000010, + DO_MAP_IO_BUFFER = 0x00000020, + DO_DEVICE_HAS_NAME = 0x00000040, + DO_DEVICE_INITIALIZING = 0x00000080, + DO_SYSTEM_BOOT_PARTITION = 0x00000100, + DO_LONG_TERM_REQUESTS = 0x00000200, + DO_NEVER_LAST_DEVICE = 0x00000400, + DO_SHUTDOWN_REGISTERED = 0x00000800, + DO_BUS_ENUMERATED_DEVICE = 0x00001000, + DO_POWER_PAGABLE = 0x00002000, + DO_POWER_INRUSH = 0x00004000, + DO_POWER_NOOP = 0x00008000, + DO_LOW_PRIORITY_FILESYSTEM = 0x00010000, + DO_XIP = 0x00020000 +} + +/// The `DEVICE_OBJECT` structure is used by the operating system to represent a device object. +#[repr(C)] +pub struct DEVICE_OBJECT +{ + pub Type: u16, + pub Size: u16, + pub ReferenceCount: i32, + pub DriverObject: *const DRIVER_OBJECT, + pub NextDevice: *mut DEVICE_OBJECT, + pub AttachedDevice: *mut DEVICE_OBJECT, + pub CurrentIrp: *const IRP, + pub Timer: *mut u8, + pub Flags: u32, + pub Characteristics: u32, + pub Vpb: *mut u8, + pub DeviceExtension: *mut u8, + pub DeviceType: u32, + pub StackSize: u8, + pub Queue: *mut WAIT_CONTEXT_BLOCK, + pub AlignmentRequirement: u32, + pub DeviceQueue: KDEVICE_QUEUE, + pub Dpc: KDPC, + pub ActiveThreadCount: u32, + pub SecurityDescriptor: *const u8, + pub DeviceLock: KEVENT, + pub SectorSize: u16, + pub Spare1: u16, + pub DeviceObjectExtension: *mut DEVOBJ_EXTENSION, + pub Reserved: *const u8, +} + +/// Device object extension structure. +#[repr(C)] +pub struct DEVOBJ_EXTENSION +{ + Type: u16, + Size: u16, + DeviceObject: *mut DEVICE_OBJECT, + PowerFlags: u32, + Dope: *mut u8, + ExtensionFlags: u32, + DeviceNode: *mut u8, + AttachedTo: *mut DEVICE_OBJECT, + StartIoCount: i32, + StartIoKey: i32, + StartIoFlags: u32, + Vpb: *mut u8, +} + +pub type PDEVICE_OBJECT = *mut DEVICE_OBJECT; + +pub type PDRIVER_CANCEL = Option<extern "system" fn (DeviceObject: PDEVICE_OBJECT, Irp: PIRP)>; + +pub type PDRIVER_DISPATCH = Option<extern "system" fn (DeviceObject: &mut DEVICE_OBJECT, Irp: &mut IRP) -> NTSTATUS>; + +pub type PIO_COMPLETION_ROUTINE = Option<extern "system" fn (DeviceObject: PDEVICE_OBJECT, Irp: PIRP, Context: PVOID) -> NTSTATUS>; diff --git a/src/dpc.rs b/src/dpc.rs new file mode 100644 index 0000000..de37e1a --- /dev/null +++ b/src/dpc.rs @@ -0,0 +1,41 @@ +//! Deferred Procedure Calls (DPC). + +use ::basedef::*; + +extern "system" +{ + pub fn KeInitializeDpc(Dpc: *mut KDPC, DeferredRoutine: PDEFERRED_ROUTINE, DeferredContext: *mut u8); + pub fn KeInsertQueueDpc(Dpc: *mut KDPC, SystemArgument1: *const u8, SystemArgument2: *const u8) -> bool; + pub fn KeRemoveQueueDpc(Dpc: *mut KDPC) -> bool; + pub fn KeFlushQueuedDpcs(); + pub fn KeGenericCallDpc(DeferredRoutine: PDEFERRED_ROUTINE, DeferredContext: *mut u8); +} + +pub type PDEFERRED_ROUTINE = extern "system" fn (Dpc: *const KDPC, DeferredContext: *mut u8, SystemArgument1: *const u8, SystemArgument2: *const u8); + +/// Deferred Procedure Call object. +#[repr(C)] +pub struct KDPC +{ + Type: u8, + Number: u8, + Importance: u8, + + DpcListEntry: LIST_ENTRY, + DeferredRoutine: PDEFERRED_ROUTINE, + DeferredContext: *mut u8, + SystemArgument1: *mut u8, + SystemArgument2: *mut u8, + + DpcData: *mut KDPC_DATA, +} + +/// DPC data structure definition. +#[repr(C)] +pub struct KDPC_DATA +{ + DpcListHead: LIST_ENTRY, + DpcLock: KSPIN_LOCK, + DpcQueueDepth: i32, + DpcCount: u32, +} diff --git a/src/driver_object.rs b/src/driver_object.rs new file mode 100644 index 0000000..82a87e7 --- /dev/null +++ b/src/driver_object.rs @@ -0,0 +1,34 @@ +//! Driver Object. + +use ::{NTSTATUS, UNICODE_STRING}; +use ::device_object::*; +use ::irp::IRP; + + +pub type PDRIVER_INITIALIZE = Option<extern "system" fn (_self: &mut DRIVER_OBJECT, &UNICODE_STRING) -> NTSTATUS>; +pub type PDRIVER_STARTIO = Option<extern "system" fn (_self: &mut DRIVER_OBJECT, &IRP)>; +pub type PDRIVER_UNLOAD = Option<extern "system" fn (_self: &mut DRIVER_OBJECT)>; + + +/// Represents the image of a loaded kernel-mode driver. +#[repr(C)] +pub struct DRIVER_OBJECT +{ + pub Type: u16, + pub Size: u16, + pub DeviceObject: *mut DEVICE_OBJECT, + pub Flags: u32, + pub DriverStart: *const u8, + pub DriverSize: u32, + pub DriverSection: *const u8, + pub DriverExtension: *mut u8, + pub DriverName: UNICODE_STRING, + pub HardwareDatabase: *const UNICODE_STRING, + pub FastIoDispatch: *mut u8, + pub DriverInit: PDRIVER_INITIALIZE, + pub DriverStartIo: PDRIVER_STARTIO, + /// The entry point for the driver's Unload routine, if any. + pub DriverUnload: PDRIVER_UNLOAD, + /// A dispatch table consisting of an array of entry points for the driver's `DispatchXxx` routines. + pub MajorFunction: [PDRIVER_DISPATCH; 28], +} diff --git a/src/event.rs b/src/event.rs new file mode 100644 index 0000000..2f00bb1 --- /dev/null +++ b/src/event.rs @@ -0,0 +1,31 @@ +//! Event Objects. + +use ::basedef::DISPATCHER_HEADER; + +extern "system" +{ + pub fn KeInitializeEvent(Event: PKEVENT, Type: EVENT_TYPE, State: bool); + pub fn KeSetEvent(Event: PKEVENT, Increment: i32, Wait: bool) -> i32; + pub fn KeReadStateEvent(Event: PKEVENT) -> i32; + pub fn KeResetEvent(Event: PKEVENT) -> i32; + pub fn KeClearEvent(Event: PKEVENT); +} + +pub type PKEVENT = *mut KEVENT; + +/// Specifies the event type. +#[repr(C)] +pub enum EVENT_TYPE +{ + /// Manual-reset event. + NotificationEvent = 0, + /// Auto-clearing event. + SynchronizationEvent, +} + +/// Event object. +#[repr(C)] +pub struct KEVENT +{ + Header: DISPATCHER_HEADER, +} diff --git a/src/file_object.rs b/src/file_object.rs new file mode 100644 index 0000000..b0d24a2 --- /dev/null +++ b/src/file_object.rs @@ -0,0 +1,15 @@ +//! File Object. + +use ::device_object::PDEVICE_OBJECT; + +pub type PFILE_OBJECT = *mut FILE_OBJECT; + +/// The `FILE_OBJECT` structure is used by the system to represent a file object. +#[repr(C)] +pub struct FILE_OBJECT +{ + Type: u16, + Size: u16, + DeviceObject: PDEVICE_OBJECT, + // ... +} diff --git a/src/irp.rs b/src/irp.rs new file mode 100644 index 0000000..8d1b390 --- /dev/null +++ b/src/irp.rs @@ -0,0 +1,184 @@ +//! I/O request packets (IRP). + +use ::NTSTATUS; +use ::basedef::*; +use ::event::PKEVENT; +use ::device_object::*; +use ::file_object::*; +use ::basedef::IO_PRIORITY::*; +use ::KIRQL; + + +pub type PIRP = *mut IRP; +pub type PIO_STACK_LOCATION = *mut IO_STACK_LOCATION; + +// NOTE: fastcall is broken: https://github.com/rust-lang/rust/issues/18086 +//extern "fastcall" { fn IofCompleteRequest(Irp: PIRP, PriorityBoost: KPRIORITY_BOOST); } +extern "system" +{ + fn IoCompleteRequest(Irp: PIRP, PriorityBoost: KPRIORITY_BOOST); + + // unfortunately following are macro + // fn IoGetCurrentIrpStackLocation(Irp: PIRP) -> PIO_STACK_LOCATION; + // fn IoGetNextIrpStackLocation(Irp: PIRP) -> PIO_STACK_LOCATION; + // fn IoSetNextIrpStackLocation(Irp: PIRP); + // fn IoSkipCurrentIrpStackLocation(Irp: PIRP); +} + +/// `IRP` Major Function Codes. +#[repr(u8)] +pub enum IRP_MJ +{ + CREATE, + CREATE_NAMED_PIPE, + CLOSE, + READ, + WRITE, + QUERY_INFORMATION, + SET_INFORMATION, + QUERY_EA, + SET_EA, + FLUSH_BUFFERS, + QUERY_VOLUME_INFORMATION, + SET_VOLUME_INFORMATION, + DIRECTORY_CONTROL, + FILE_SYSTEM_CONTROL, + DEVICE_CONTROL, + INTERNAL_DEVICE_CONTROL, + SHUTDOWN, + LOCK_CONTROL, + CLEANUP, + CREATE_MAILSLOT, + QUERY_SECURITY, + SET_SECURITY, + POWER, + SYSTEM_CONTROL, + DEVICE_CHANGE, + QUERY_QUOTA, + SET_QUOTA, + PNP, + MAXIMUM_FUNCTION, +} + +/// The `IRP` structure is a partial opaque structure that represents an I/O request packet. +#[repr(C)] +pub struct IRP +{ + pub Type: u16, + pub Size: u16, + /// Pointer to an `MDL` describing a user buffer, if the driver is using direct I/O. + pub MdlAddress: PVOID, + /// Flags word - used to remember various flags. + pub Flags: u32, + /// Pointer to a system-space buffer if the driver is using buffered I/O. + pub SystemBuffer: PVOID, + pub ThreadListEntry: LIST_ENTRY, + /// I/O status - final status of operation. + pub IoStatus: IO_STATUS_BLOCK, + /// Indicates the execution mode of the original requester of the operation. + pub RequestorMode: KPROCESSOR_MODE, + /// If set to `TRUE`, a driver has marked the IRP pending. + pub PendingReturned: bool, + /// Stack state information. + pub StackCount: i8, + /// Stack state information. + pub CurrentLocation: i8, + /// If set to `TRUE`, the IRP either is or should be canceled. + pub Cancel: bool, + /// Irql at which the cancel spinlock was acquired. + pub CancelIrql: KIRQL, + pub ApcEnvironment: u8, + /// Allocation control flags. + pub AllocationFlags: u8, + /// User parameters. + pub UserIosb: PIO_STATUS_BLOCK, + pub UserEvent: PKEVENT, + + // union { + pub UserApcRoutine: PIO_APC_ROUTINE, + pub UserApcContext: PVOID, + // } Overlay + + /// Contains the entry point for a driver-supplied `Cancel` routine to be called if the IRP is canceled. + pub CancelRoutine: PDRIVER_CANCEL, + /// Contains the address of an output buffer for `IRP_MJ_DEVICE_CONTROL`. + pub UserBuffer: PVOID, + + /// Kernel structures. + // union { + pub Overlay: _IRP_OVERLAY, + // } Tail +} + +/// Kernel structures for IRP. +#[repr(C)] +pub struct _IRP_OVERLAY +{ + pub DriverContext: [PVOID; 4], + pub Thread: PETHREAD, + pub AuxiliaryBuffer: PVOID, + pub ListEntry: LIST_ENTRY, + /// Current stack location. + pub CurrentStackLocation: PIO_STACK_LOCATION, + pub OriginalFileObject: PFILE_OBJECT, +} + +/// I/O Stack Locations. +#[repr(C)] +pub struct IO_STACK_LOCATION +{ + /// The IRP major function code indicating the type of I/O operation to be performed. + pub MajorFunction: u8, + /// A subfunction code for `MajorFunction`. + pub MinorFunction: u8, + /// Request-type-specific values (see [DEVICE_FLAGS](../device_object/enum.DEVICE_FLAGS.html)). + pub Flags: u8, + pub Control: u8, + + /// A union that depends on the major and minor IRP function code values + /// contained in `MajorFunction` and `MinorFunction`. + // union Parameters + pub Parameters: [PVOID; 4], + + /// A pointer to the driver-created `DEVICE_OBJECT` structure + /// representing the target physical, logical, or virtual device for which this driver is to handle the IRP. + pub DeviceObject: PDEVICE_OBJECT, + /// A pointer to a `FILE_OBJECT` structure that represents the file object, if any, that is associated with `DeviceObject` pointer. + pub FileObject: PFILE_OBJECT, + /// The following routine is invoked depending on the flags in the above `Flags` field. + pub CompletionRoutine: PIO_COMPLETION_ROUTINE, + /// The following is used to store the address of the context parameter that should be passed to the `CompletionRoutine`. + pub Context: PVOID, +} + +/// Parameters for `IRP_MJ_READ`. +#[repr(C)] +pub struct _IO_STACK_LOCATION_READ +{ + pub Length: u32, + pub Key: u32, + pub ByteOffset: i64, +} + + +impl IRP { + /// Returns a pointer to the caller's stack location in the given `IRP`. + pub fn get_current_stack_location(&mut self) -> &mut IO_STACK_LOCATION { + unsafe { &mut *self.Overlay.CurrentStackLocation } + } + + /// Indicates that the caller has completed all processing for a given I/O request + /// and is returning the given IRP to the I/O manager. + pub fn complete_request(&mut self, Status: NTSTATUS) -> NTSTATUS { + self.IoStatus.Status = Status; + unsafe { IoCompleteRequest(self, IO_NO_INCREMENT) }; + return Status; + } +} + +impl IO_STACK_LOCATION { + /// Access parameters for `IRP_MJ_READ`. + pub fn ParametersRead(&mut self) -> &mut _IO_STACK_LOCATION_READ { + unsafe { ::core::mem::transmute(&mut self.Parameters) } + } +} diff --git a/src/irql.rs b/src/irql.rs new file mode 100644 index 0000000..e6e60bb --- /dev/null +++ b/src/irql.rs @@ -0,0 +1,61 @@ +//! Interrupt Request Level (IRQL). + +/// IRQL type. +pub type KIRQL = u8; + +/// Passive release level, no interrupt vectors are masked. +pub const PASSIVE_LEVEL: KIRQL = 0; +/// The lowest IRQL level, no interrupt vectors are masked. +pub const LOW_LEVEL: KIRQL = 0; +/// APC interrupt level. +pub const APC_LEVEL: KIRQL = 1; +/// Dispatcher level +pub const DISPATCH_LEVEL: KIRQL = 2; + +/// Timer used for profiling. +#[cfg(target_arch = "x86")] +pub const PROFILE_LEVEL: KIRQL = 27; + +/// Interval clock level. +#[cfg(target_arch = "x86")] +pub const CLOCK_LEVEL: KIRQL = 28; + +/// Interprocessor interrupt level. +#[cfg(target_arch = "x86")] +pub const IPI_LEVEL: KIRQL = 29; + +/// Power failure level. +#[cfg(target_arch = "x86")] +pub const POWER_LEVEL: KIRQL = 30; + +/// Highest interrupt level. +#[cfg(target_arch = "x86")] +pub const HIGH_LEVEL: KIRQL = 31; + +/// Synchronization level. +#[cfg(target_arch = "x86")] +pub const SYNCH_LEVEL: KIRQL = 29 - 2; + +/// Interval clock level. +#[cfg(target_arch = "x86_64")] +pub const CLOCK_LEVEL: KIRQL = 13; + +/// Interprocessor interrupt level. +#[cfg(target_arch = "x86_64")] +pub const IPI_LEVEL: KIRQL = 14; + +/// Power failure level. +#[cfg(target_arch = "x86_64")] +pub const POWER_LEVEL: KIRQL = 15; + +/// Timer used for profiling. +#[cfg(target_arch = "x86_64")] +pub const PROFILE_LEVEL: KIRQL = 16; + +/// Highest interrupt level. +#[cfg(target_arch = "x86_64")] +pub const HIGH_LEVEL: KIRQL = 17; + +/// Synchronization level. +#[cfg(target_arch = "x86_64")] +pub const SYNCH_LEVEL: KIRQL = 14- 2; diff --git a/src/lang.rs b/src/lang.rs new file mode 100644 index 0000000..5d6bd2b --- /dev/null +++ b/src/lang.rs @@ -0,0 +1,7 @@ +#[lang = "eh_personality"] extern fn eh_personality() {} +#[lang = "eh_unwind_resume"] extern fn rust_eh_unwind_resume() {} + +#[lang = "panic_fmt"] extern fn panic_fmt() -> ! { + KdPrint!("panic_fmt() -> !"); + loop{} +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..aaad51d --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,55 @@ +//! Windows Kernel Mode library. + +#![feature(lang_items)] +#![feature(on_unimplemented, fundamental)] +#![feature(no_core)] + +#![no_std] +#![allow(bad_style)] + + +#[macro_use] pub mod macros; + +mod lang; + +pub mod status; + +pub mod basedef; +pub mod crt; +pub mod debug; +pub mod device_object; +pub mod dpc; +pub mod driver_object; +pub mod event; +pub mod file_object; +pub mod irp; +pub mod irql; +pub mod object; +pub mod pool; +pub mod rtl; +pub mod string; +pub mod time; + +#[doc(hidden)] +pub use irql::KIRQL; + +#[doc(hidden)] +pub use status::*; + +#[doc(hidden)] +pub use debug::DbgPrint; + +#[doc(hidden)] +pub use string::*; + +#[doc(hidden)] +pub use driver_object::*; + +#[doc(hidden)] +pub use device_object::*; + +#[doc(hidden)] +pub use irp::IRP; + +#[doc(hidden)] +pub use basedef::{PVOID}; diff --git a/src/macros.rs b/src/macros.rs new file mode 100644 index 0000000..ba3b24d --- /dev/null +++ b/src/macros.rs @@ -0,0 +1,34 @@ +//! Macros for Kernel-Mode drivers. + +/// Macro to send a message to the kernel debugger. +/// +/// # Example +/// +/// ```no_run +/// KdPrint!("NTSTATUS is 0x%X\n", status); +/// ``` +#[macro_export] +macro_rules! KdPrint { + ($msg:expr $(, $arg:expr)*) => { unsafe { $crate::debug::DbgPrint( concat!($msg, "\0").as_ptr() $(, $arg )* )} }; +} + +/// Macro to send a message to the kernel debugger for unsafe blocks. +/// +/// Used in `unsafe {}` blocks. +#[macro_export] +macro_rules! KdPrint_u { + ($msg:expr $(, $arg:expr)*) => { $crate::debug::DbgPrint( concat!($msg, "\0").as_ptr() $(, $arg )* ) }; +} + +#[macro_export] +macro_rules! check_unsafe { + ($expr:expr) => {{ + let st: $crate::status::Status = unsafe { $expr }; + if st.is_err() { + KdPrint!("[km] error: status 0x%X\n", st); + return st; + } else { + st + } + }} +} diff --git a/src/object.rs b/src/object.rs new file mode 100644 index 0000000..ab34aeb --- /dev/null +++ b/src/object.rs @@ -0,0 +1,44 @@ +//! Kernel Objects. + +use ::basedef::*; +use ::device_object::PDEVICE_OBJECT; +use ::irp::IRP; + + +#[repr(C)] +pub struct WAIT_CONTEXT_BLOCK +{ + WaitQueueEntry: *mut KDEVICE_QUEUE_ENTRY, + DeviceRoutine: extern "system" fn (_obj: PDEVICE_OBJECT, _irp: *mut IRP, *mut u8, *mut u8) -> IO_ALLOCATION_ACTION, + DeviceContext: *mut u8, + NumberOfMapRegisters: u32, + DeviceObject: *mut u8, + CurrentIrp: *mut u8, + BufferChainingDpc: * mut u8, +} + +#[repr(C)] +pub enum IO_ALLOCATION_ACTION +{ + KeepObject = 0x01, + DeallocateObject = 0x02, + DeallocateObjectKeepRegisters = 0x03, +} + +#[repr(C)] +pub struct KDEVICE_QUEUE_ENTRY +{ + DeviceListEntry: LIST_ENTRY, + SortKey: u32, + Inserted: bool, +} + +#[repr(C)] +pub struct KDEVICE_QUEUE +{ + Type: u16, + Size: u16, + DeviceListHead: LIST_ENTRY, + Lock: KSPIN_LOCK, + Busy: bool, +} diff --git a/src/pool.rs b/src/pool.rs new file mode 100644 index 0000000..31598d5 --- /dev/null +++ b/src/pool.rs @@ -0,0 +1,42 @@ +//! Kernel Mode pools. + +use ::PVOID; + +extern "system" +{ + /// Allocates pool memory of the specified type and tag. + pub fn ExAllocatePoolWithTag(PoolType: POOL_TYPE, NumberOfBytes: usize, Tag: u32) -> PVOID; + /// Deallocates a block of pool memory allocated with the specified tag. + pub fn ExFreePoolWithTag(P: PVOID, Tag: u32); + + /// Allocates pool memory of the specified type. + pub fn ExAllocatePool(PoolType: POOL_TYPE, NumberOfBytes: usize) -> PVOID; + /// Deallocates a block of pool memory. + pub fn ExFreePool(P: PVOID); +} + + +/// Specifies the type of system memory to allocate. +#[repr(C)] +pub enum POOL_TYPE +{ + /// Nonpageable system memory, can be accessed from any IRQL. + NonPagedPool = 0, + /// Pageable system memory, can only be allocated and accessed at IRQL < DISPATCH_LEVEL. + PagedPool, + NonPagedPoolMustSucceed, + DontUseThisType, + /// Nonpaged pool, aligned on processor cache boundaries. + NonPagedPoolCacheAligned, + /// Paged pool, aligned on processor cache boundaries. + PagedPoolCacheAligned, + NonPagedPoolCacheAlignedMustS, + MaxPoolType, + NonPagedPoolSession = 32, + PagedPoolSession, + NonPagedPoolMustSucceedSession, + DontUseThisTypeSession, + NonPagedPoolCacheAlignedSession, + PagedPoolCacheAlignedSession, + NonPagedPoolCacheAlignedMustSSession, +} diff --git a/src/rtl.rs b/src/rtl.rs new file mode 100644 index 0000000..eedcbe5 --- /dev/null +++ b/src/rtl.rs @@ -0,0 +1,11 @@ +//! NT runtime routines. + +extern "system" +{ + /// Returns a random number that was generated from a given `seed` value in the range `[0..MAXLONG-1]`. + pub fn RtlRandom(Seed: *mut u32) -> u32; + /// Returns a random number that was generated from a given `seed` value in the range `[0..MAXLONG-1]`. + pub fn RtlRandomEx(Seed: *mut u32) -> u32; + /// A simple uniform random number generator, based on D.H. Lehmer's 1948 alrogithm. + pub fn RtlUniform(Seed: *mut u32) -> u32; +} diff --git a/src/status.rs b/src/status.rs new file mode 100644 index 0000000..aae9ec5 --- /dev/null +++ b/src/status.rs @@ -0,0 +1,59 @@ +//! NT Status codes. +#![allow(non_camel_case_types)] +#![allow(overflowing_literals)] + +/// NT Status type. +pub type NTSTATUS = Status; + +/// A specialized `Result` type for NT operations. +pub type Result<T> = ::core::result::Result<T, Status>; + + +/// NT Status code. +#[repr(C)] +#[derive(Clone, Copy)] +pub enum Status { + success = 0, + unsuccessful = 0xC0000001, +} + +impl Status { + /// Evaluates to `true` if the `Status` is a success type (`0..0x3FFFFFFF`) + /// or an informational type (`0x40000000..0x7FFFFFFF`). + pub fn is_ok(&self) -> bool { + (*self as i32) >= 0 + } + /// Status is a warning or error type. + pub fn is_err(&self) -> bool { + (*self as i32) < 0 + } + /// Status is a success type. + pub fn is_success(&self) -> bool { + let c = *self as u32; + c > 0 && c < 0x3FFF_FFFF + } + /// Status is a information type. + pub fn is_information(&self) -> bool { + let c = *self as u32; + c > 0x4000_0000 && c < 0x7FFF_FFFF + } + /// Status is a warning type. + pub fn is_warning(&self) -> bool { + let c = *self as u32; + c > 0x8000_0000 && c < 0xBFFF_FFFF + } + /// Status is a error type. + pub fn is_error(&self) -> bool { + let c = *self as u32; + c > 0xC000_0000 && c < 0xFFFF_FFFF + } +} + +/// Convert `Status` to `Result<()>`. +pub fn check(st: Status) -> Result<()> { + if st.is_err() { + Err(st) + } else { + Ok(()) + } +} diff --git a/src/string.rs b/src/string.rs new file mode 100644 index 0000000..252dbe6 --- /dev/null +++ b/src/string.rs @@ -0,0 +1,95 @@ +//! Kernel mode string types. + +use ::NTSTATUS; + +/// NT native string types. +pub trait NativeString { + /// Size of string in bytes. + fn size(&self) -> u16; + /// Size of buffer in bytes. + fn max_size(&self) -> u16; + + /// Check is the string is empty. + fn is_empty(&self) -> bool { + self.size() == 0u16 + } +} + +/// A counted Unicode string. +#[repr(C)] +pub struct UNICODE_STRING +{ + /// The length in **bytes** of the string stored in `Buffer`. + pub Length: u16, + /// The length in **bytes** of `Buffer`. + pub MaximumLength: u16, + /// Pointer to a buffer used to contain a string of wide characters. + pub Buffer: *const u16, +} + +/// A counted string used for ANSI strings. +#[repr(C)] +pub struct ANSI_STRING +{ + /// The length in *bytes* of the string stored in `Buffer`. + pub Length: u16, + /// The length in bytes of `Buffer`. + pub MaximumLength: u16, + /// Pointer to a buffer used to contain a string of characters. + pub Buffer: *const u8, +} + +impl NativeString for UNICODE_STRING +{ + fn size(&self) -> u16 { self.Length } + fn max_size(&self) -> u16 { self.MaximumLength } +} + +impl UNICODE_STRING +{ +} + +/// Initializes a counted Unicode string. +impl Default for UNICODE_STRING { + fn default() -> Self { + UNICODE_STRING { Length: 0, MaximumLength: 0, Buffer: ::core::ptr::null() } + } +} + +impl<'a> From<&'a [u8]> for ANSI_STRING { + fn from(s: &'a [u8]) -> Self { + let len = s.len(); + let n = if len > 0 && s[len-1] == 0 { len - 1 } else { len }; + ANSI_STRING { Length: n as u16, MaximumLength: len as u16, Buffer: s.as_ptr() } + } +} + + +pub type AnsiString = ANSI_STRING; +pub type UnicodeString = UNICODE_STRING; +pub type CONST_UNICODE_STRING = UNICODE_STRING; +pub type CONST_ANSI_STRING = ANSI_STRING; + + + +extern "system" +{ + pub fn RtlIntegerToUnicodeString(Value: u32, Base: u32, String: &mut UNICODE_STRING) -> NTSTATUS; + pub fn RtlInt64ToUnicodeString(Value: u64, Base: u32, String: &mut UNICODE_STRING) -> NTSTATUS; + pub fn RtlUnicodeStringToInteger(String: &CONST_UNICODE_STRING, Base: u32, Value: &mut u32) -> NTSTATUS; + + pub fn RtlUnicodeStringToAnsiString(DestinationString: &mut ANSI_STRING, SourceString: &CONST_UNICODE_STRING, AllocateDestination: bool) -> NTSTATUS; + pub fn RtlUnicodeStringToAnsiSize(SourceString: &CONST_UNICODE_STRING) -> u32; + + pub fn RtlAnsiStringToUnicodeString(DestinationString: &mut UNICODE_STRING, SourceString: &CONST_ANSI_STRING, AllocateDestination: bool) -> NTSTATUS; + pub fn RtlAnsiStringToUnicodeSize(SourceString: &CONST_ANSI_STRING) -> u32; + + pub fn RtlCompareUnicodeString (String1: &CONST_UNICODE_STRING, String2: &CONST_UNICODE_STRING, CaseInSensitive: bool) -> i32; + pub fn RtlCompareString (String1: &CONST_ANSI_STRING, String2: &CONST_ANSI_STRING, CaseInSensitive: bool) -> i32; + + pub fn RtlEqualUnicodeString(String1: &CONST_UNICODE_STRING, String2: &CONST_UNICODE_STRING) -> bool; + pub fn RtlEqualString(String1: &CONST_ANSI_STRING, String2: &CONST_ANSI_STRING) -> bool; + + pub fn RtlFreeAnsiString(UnicodeString: &mut ANSI_STRING); + pub fn RtlFreeUnicodeString(UnicodeString: &mut UNICODE_STRING); +} diff --git a/src/thread.rs b/src/thread.rs new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/src/thread.rs diff --git a/src/time.rs b/src/time.rs new file mode 100644 index 0000000..b4d0e99 --- /dev/null +++ b/src/time.rs @@ -0,0 +1,26 @@ +//! NT Time routines. + +/// System time is a count of 100-nanosecond intervals since January 1, 1601. +pub type SYSTEMTIME = i64; + +extern "system" +{ + fn KeQuerySystemTime(CurrentTime: *mut SYSTEMTIME); + fn KeQueryTickCount(TickCount: *mut i64); + /// Converts a GMT system time value to the local system time for the current time zone. + pub fn ExSystemTimeToLocalTime(SystemTime: *const SYSTEMTIME, LocalTime: *mut SYSTEMTIME); +} + +/// Obtains the current system time. +pub fn QuerySystemTime() -> SYSTEMTIME { + let mut t = 0i64; + unsafe { KeQuerySystemTime(&mut t) }; + return t; +} + +/// A count of the interval timer interrupts that have occurred since the system was booted. +pub fn QueryTickCount() -> i64 { + let mut t = 0i64; + unsafe { KeQueryTickCount(&mut t) }; + return t; +} diff --git a/src/types.rs b/src/types.rs new file mode 100644 index 0000000..f7e3838 --- /dev/null +++ b/src/types.rs @@ -0,0 +1 @@ +//! Kernel types. |