Add rust kext to the mono repo
This commit is contained in:
2
windows_kext/wdk/.cargo/config.toml
Normal file
2
windows_kext/wdk/.cargo/config.toml
Normal file
@@ -0,0 +1,2 @@
|
||||
[build]
|
||||
target = "x86_64-pc-windows-msvc"
|
||||
139
windows_kext/wdk/Cargo.lock
generated
Normal file
139
windows_kext/wdk/Cargo.lock
generated
Normal file
@@ -0,0 +1,139 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "ntstatus"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96ea8ea6a9a8cbe8fefe99b632bd45ec4b41b0bf234e4d740c516372922fb180"
|
||||
dependencies = [
|
||||
"num_enum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_enum"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "683751d591e6d81200c39fb0d1032608b77724f34114db54f571ff1317b337c0"
|
||||
dependencies = [
|
||||
"num_enum_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_enum_derive"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c11e44798ad209ccdd91fc192f0526a369a01234f7373e1b141c96d7cee4f0e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.70"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44c8b28c477cc3bf0e7966561e3460130e1255f7a1cf71931075f1c5e7a7e269"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
||||
[[package]]
|
||||
name = "wdk"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ntstatus",
|
||||
"widestring",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "widestring"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
source = "git+https://github.com/microsoft/windows-rs?rev=41ad38d8c42c92fd23fe25ba4dca76c2d861ca06#41ad38d8c42c92fd23fe25ba4dca76c2d861ca06"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.0"
|
||||
source = "git+https://github.com/microsoft/windows-rs?rev=41ad38d8c42c92fd23fe25ba4dca76c2d861ca06#41ad38d8c42c92fd23fe25ba4dca76c2d861ca06"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.0"
|
||||
source = "git+https://github.com/microsoft/windows-rs?rev=41ad38d8c42c92fd23fe25ba4dca76c2d861ca06#41ad38d8c42c92fd23fe25ba4dca76c2d861ca06"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.0"
|
||||
source = "git+https://github.com/microsoft/windows-rs?rev=41ad38d8c42c92fd23fe25ba4dca76c2d861ca06#41ad38d8c42c92fd23fe25ba4dca76c2d861ca06"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.0"
|
||||
source = "git+https://github.com/microsoft/windows-rs?rev=41ad38d8c42c92fd23fe25ba4dca76c2d861ca06#41ad38d8c42c92fd23fe25ba4dca76c2d861ca06"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.0"
|
||||
source = "git+https://github.com/microsoft/windows-rs?rev=41ad38d8c42c92fd23fe25ba4dca76c2d861ca06#41ad38d8c42c92fd23fe25ba4dca76c2d861ca06"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.0"
|
||||
source = "git+https://github.com/microsoft/windows-rs?rev=41ad38d8c42c92fd23fe25ba4dca76c2d861ca06#41ad38d8c42c92fd23fe25ba4dca76c2d861ca06"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.0"
|
||||
source = "git+https://github.com/microsoft/windows-rs?rev=41ad38d8c42c92fd23fe25ba4dca76c2d861ca06#41ad38d8c42c92fd23fe25ba4dca76c2d861ca06"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.0"
|
||||
source = "git+https://github.com/microsoft/windows-rs?rev=41ad38d8c42c92fd23fe25ba4dca76c2d861ca06#41ad38d8c42c92fd23fe25ba4dca76c2d861ca06"
|
||||
20
windows_kext/wdk/Cargo.toml
Normal file
20
windows_kext/wdk/Cargo.toml
Normal file
@@ -0,0 +1,20 @@
|
||||
[package]
|
||||
name = "wdk"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
ntstatus = { version = "0.1.2", default-features = false }
|
||||
|
||||
[dependencies.widestring]
|
||||
version = "1.0.2"
|
||||
default-features = false
|
||||
features = ["alloc"]
|
||||
|
||||
# WARNING: Do not update. The version was choosen for a reason. See wdk/README.md for more detiels.
|
||||
[dependencies.windows-sys]
|
||||
git = "https://github.com/microsoft/windows-rs"
|
||||
rev = "41ad38d8c42c92fd23fe25ba4dca76c2d861ca06"
|
||||
features = ["Wdk_Foundation", "Wdk_Storage_FileSystem", "Wdk_System_SystemServices", "Win32_Foundation", "Win32_Security", "Win32_System_IO", "Win32_System_Kernel", "Win32_System_Power", "Win32_System_WindowsProgramming", "Win32_NetworkManagement_IpHelper", "Win32_Networking_WinSock", "Win32_NetworkManagement_WindowsFilteringPlatform", "Win32_System_Rpc"]
|
||||
14
windows_kext/wdk/README.md
Normal file
14
windows_kext/wdk/README.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# WDK (Windows Driver Kit)
|
||||
|
||||
A library that interfaces with the windows kernel.
|
||||
The crate has extensive use of **unsafe** rust, be more causes when making changes.
|
||||
|
||||
Do not update `windows-sys` dependency.
|
||||
The version contains bugs that have specific workarounds in this crate. Updating without reviewing the new version can result in broken build or undefined behavior.
|
||||
|
||||
see: `wdk/src/driver.rs`
|
||||
see: `wdk/src/irp_helper.rs`
|
||||
|
||||
Open issues need to be resolved:
|
||||
https://github.com/microsoft/wdkmetadata/issues/59
|
||||
https://github.com/microsoft/windows-rs/issues/2805
|
||||
13
windows_kext/wdk/build.rs
Normal file
13
windows_kext/wdk/build.rs
Normal file
@@ -0,0 +1,13 @@
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
fn main() {
|
||||
// C Helper
|
||||
println!("cargo:rerun-if-changed=../c_helper/x64/c_helper.lib");
|
||||
println!("cargo:rustc-link-search=native=../c_helper/x64");
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
fn main() {
|
||||
// C Helper
|
||||
println!("cargo:rerun-if-changed=../c_helper/ARM64/c_helper.lib");
|
||||
println!("cargo:rustc-link-search=native=../c_helper/ARM64");
|
||||
}
|
||||
1
windows_kext/wdk/rust-analyzer.cargo.target
Normal file
1
windows_kext/wdk/rust-analyzer.cargo.target
Normal file
@@ -0,0 +1 @@
|
||||
x86_64-pc-windows-msvc
|
||||
1
windows_kext/wdk/rust-toolchain
Normal file
1
windows_kext/wdk/rust-toolchain
Normal file
@@ -0,0 +1 @@
|
||||
stable
|
||||
70
windows_kext/wdk/src/allocator.rs
Normal file
70
windows_kext/wdk/src/allocator.rs
Normal file
@@ -0,0 +1,70 @@
|
||||
extern crate alloc;
|
||||
|
||||
use core::alloc::{GlobalAlloc, Layout};
|
||||
|
||||
use alloc::alloc::handle_alloc_error;
|
||||
use windows_sys::Wdk::System::SystemServices::{ExAllocatePool2, ExFreePoolWithTag};
|
||||
|
||||
// For reference: https://learn.microsoft.com/en-us/windows-hardware/drivers/kernel/pool_flags
|
||||
#[allow(dead_code)]
|
||||
#[repr(u64)]
|
||||
enum PoolType {
|
||||
RequiredStartUseQuota = 0x0000000000000001,
|
||||
Uninitialized = 0x0000000000000002, // Don't zero-initialize allocation
|
||||
Session = 0x0000000000000004, // Use session specific pool
|
||||
CacheAligned = 0x0000000000000008, // Cache aligned allocation
|
||||
RaiseOnFailure = 0x0000000000000020, // Raise exception on failure
|
||||
NonPaged = 0x0000000000000040, // Non paged pool NX
|
||||
NonPagedExecute = 0x0000000000000080, // Non paged pool executable
|
||||
Paged = 0x0000000000000100, // Paged pool
|
||||
RequiredEnd = 0x0000000080000000,
|
||||
OptionalStart = 0x0000000100000000,
|
||||
OptionalEnd = 0x8000000000000000,
|
||||
}
|
||||
|
||||
pub struct WindowsAllocator {}
|
||||
|
||||
unsafe impl Sync for WindowsAllocator {}
|
||||
|
||||
pub(crate) const POOL_TAG: u32 = u32::from_ne_bytes(*b"PMrs");
|
||||
|
||||
unsafe impl GlobalAlloc for WindowsAllocator {
|
||||
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
||||
let pool = ExAllocatePool2(PoolType::NonPaged as u64, layout.size(), POOL_TAG);
|
||||
if pool.is_null() {
|
||||
handle_alloc_error(layout);
|
||||
}
|
||||
|
||||
pool as *mut u8
|
||||
}
|
||||
|
||||
unsafe fn dealloc(&self, ptr: *mut u8, _: Layout) {
|
||||
ExFreePoolWithTag(ptr as _, POOL_TAG);
|
||||
}
|
||||
|
||||
unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
|
||||
let pool = self.alloc(layout);
|
||||
pool
|
||||
}
|
||||
|
||||
unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
|
||||
// SAFETY: the caller must ensure that the `new_size` does not overflow.
|
||||
// `layout.align()` comes from a `Layout` and is thus guaranteed to be valid.
|
||||
let new_layout = unsafe { Layout::from_size_align_unchecked(new_size, layout.align()) };
|
||||
// SAFETY: the caller must ensure that `new_layout` is greater than zero.
|
||||
let new_ptr = unsafe { self.alloc(new_layout) };
|
||||
if !new_ptr.is_null() {
|
||||
// SAFETY: the previously allocated block cannot overlap the newly allocated block.
|
||||
// The safety contract for `dealloc` must be upheld by the caller.
|
||||
unsafe {
|
||||
core::ptr::copy_nonoverlapping(
|
||||
ptr,
|
||||
new_ptr,
|
||||
core::cmp::min(layout.size(), new_size),
|
||||
);
|
||||
self.dealloc(ptr, layout);
|
||||
}
|
||||
}
|
||||
new_ptr
|
||||
}
|
||||
}
|
||||
12
windows_kext/wdk/src/attributes.rs
Normal file
12
windows_kext/wdk/src/attributes.rs
Normal file
@@ -0,0 +1,12 @@
|
||||
extern crate proc_macro;
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
|
||||
// using proc_macro_attribute to declare an attribute like procedural macro
|
||||
|
||||
#[proc_macro_attribute]
|
||||
// _metadata is argument provided to macro call and _input is code to which attribute like macro attaches
|
||||
pub fn my_custom_attribute(_metadata: TokenStream, _input: TokenStream) -> TokenStream {
|
||||
// returning a simple TokenStream for Struct
|
||||
TokenStream::from(quote! {struct H{}})
|
||||
}
|
||||
50
windows_kext/wdk/src/consts.rs
Normal file
50
windows_kext/wdk/src/consts.rs
Normal file
@@ -0,0 +1,50 @@
|
||||
// Actions
|
||||
pub const FWP_ACTION_FLAG_TERMINATING: u32 = 0x00001000;
|
||||
pub const FWP_ACTION_FLAG_NON_TERMINATING: u32 = 0x00002000;
|
||||
pub const FWP_ACTION_FLAG_CALLOUT: u32 = 0x00004000;
|
||||
|
||||
pub const FWP_ACTION_BLOCK: u32 = 0x00000001 | FWP_ACTION_FLAG_TERMINATING;
|
||||
pub const FWP_ACTION_PERMIT: u32 = 0x00000002 | FWP_ACTION_FLAG_TERMINATING;
|
||||
pub const FWP_ACTION_CALLOUT_TERMINATING: u32 =
|
||||
0x00000003 | FWP_ACTION_FLAG_CALLOUT | FWP_ACTION_FLAG_TERMINATING;
|
||||
pub const FWP_ACTION_CALLOUT_INSPECTION: u32 =
|
||||
0x00000004 | FWP_ACTION_FLAG_CALLOUT | FWP_ACTION_FLAG_NON_TERMINATING;
|
||||
pub const FWP_ACTION_CALLOUT_UNKNOWN: u32 = 0x00000005 | FWP_ACTION_FLAG_CALLOUT;
|
||||
pub const FWP_ACTION_CONTINUE: u32 = 0x00000006 | FWP_ACTION_FLAG_NON_TERMINATING;
|
||||
pub const FWP_ACTION_NONE: u32 = 0x00000007;
|
||||
pub const FWP_ACTION_NONE_NO_MATCH: u32 = 0x00000008;
|
||||
pub const FWP_CONDITION_FLAG_IS_LOOPBACK: u32 = 0x00000001;
|
||||
pub const FWP_CONDITION_FLAG_IS_IPSEC_SECURED: u32 = 0x00000002;
|
||||
pub const FWP_CONDITION_FLAG_IS_REAUTHORIZE: u32 = 0x00000004;
|
||||
pub const FWP_CONDITION_FLAG_IS_WILDCARD_BIND: u32 = 0x00000008;
|
||||
pub const FWP_CONDITION_FLAG_IS_RAW_ENDPOINT: u32 = 0x00000010;
|
||||
pub const FWP_CONDITION_FLAG_IS_FRAGMENT: u32 = 0x00000020;
|
||||
pub const FWP_CONDITION_FLAG_IS_FRAGMENT_GROUP: u32 = 0x00000040;
|
||||
pub const FWP_CONDITION_FLAG_IS_IPSEC_NATT_RECLASSIFY: u32 = 0x00000080;
|
||||
pub const FWP_CONDITION_FLAG_REQUIRES_ALE_CLASSIFY: u32 = 0x00000100;
|
||||
pub const FWP_CONDITION_FLAG_IS_IMPLICIT_BIND: u32 = 0x00000200;
|
||||
pub const FWP_CONDITION_FLAG_IS_REASSEMBLED: u32 = 0x00000400;
|
||||
pub const FWP_CONDITION_FLAG_IS_NAME_APP_SPECIFIED: u32 = 0x00004000;
|
||||
pub const FWP_CONDITION_FLAG_IS_PROMISCUOUS: u32 = 0x00008000;
|
||||
pub const FWP_CONDITION_FLAG_IS_AUTH_FW: u32 = 0x00010000;
|
||||
pub const FWP_CONDITION_FLAG_IS_RECLASSIFY: u32 = 0x00020000;
|
||||
pub const FWP_CONDITION_FLAG_IS_OUTBOUND_PASS_THRU: u32 = 0x00040000;
|
||||
pub const FWP_CONDITION_FLAG_IS_INBOUND_PASS_THRU: u32 = 0x00080000;
|
||||
pub const FWP_CONDITION_FLAG_IS_CONNECTION_REDIRECTED: u32 = 0x00100000;
|
||||
|
||||
// Driver
|
||||
pub const METHOD_BUFFERED: u32 = 0;
|
||||
pub const METHOD_IN_DIRECT: u32 = 1;
|
||||
pub const METHOD_OUT_DIRECT: u32 = 2;
|
||||
pub const METHOD_NEITHER: u32 = 3;
|
||||
|
||||
pub const SIOCTL_TYPE: u32 = 40000;
|
||||
|
||||
pub const FILE_READ_DATA: u32 = 0x00000001;
|
||||
pub const FILE_READ_ATTRIBUTES: u32 = 0x00000080;
|
||||
pub const FILE_READ_EA: u32 = 0x00000008;
|
||||
pub const FILE_WRITE_DATA: u32 = 0x00000002;
|
||||
pub const FILE_WRITE_ATTRIBUTES: u32 = 0x00000100;
|
||||
pub const FILE_WRITE_EA: u32 = 0x00000010;
|
||||
pub const FILE_APPEND_DATA: u32 = 0x00000004;
|
||||
pub const FILE_EXECUTE: u32 = 0x00000020;
|
||||
33
windows_kext/wdk/src/debug.rs
Normal file
33
windows_kext/wdk/src/debug.rs
Normal file
@@ -0,0 +1,33 @@
|
||||
#[cfg(debug_assertions)]
|
||||
#[macro_export]
|
||||
macro_rules! log {
|
||||
($level:expr, $($arg:tt)*) => ({
|
||||
let message = alloc::format!($($arg)*);
|
||||
$crate::interface::dbg_print(alloc::format!("{} {}: {}", $level, core::module_path!(), message));
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
#[macro_export]
|
||||
macro_rules! log {
|
||||
($($arg:expr),*) => {{
|
||||
$(
|
||||
_ = $arg;
|
||||
)*
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! err {
|
||||
($($arg:tt)*) => ($crate::log!("ERROR", $($arg)*));
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! dbg {
|
||||
($($arg:tt)*) => ($crate::log!("DEBUG", $($arg)*));
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! info {
|
||||
($($arg:tt)*) => ($crate::log!("INFO", $($arg)*));
|
||||
}
|
||||
103
windows_kext/wdk/src/driver.rs
Normal file
103
windows_kext/wdk/src/driver.rs
Normal file
@@ -0,0 +1,103 @@
|
||||
use windows_sys::{
|
||||
Wdk::Foundation::{DEVICE_OBJECT, DRIVER_OBJECT, IRP},
|
||||
Win32::Foundation::{HANDLE, NTSTATUS},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
interface,
|
||||
irp_helpers::{ReadRequest, WriteRequest},
|
||||
};
|
||||
|
||||
pub trait Device {
|
||||
fn new(driver: &Driver) -> Self;
|
||||
fn cleanup(&mut self);
|
||||
fn read(&mut self, read_request: &mut ReadRequest);
|
||||
fn write(&mut self, write_request: &mut WriteRequest);
|
||||
fn shutdown(&mut self);
|
||||
}
|
||||
|
||||
pub struct Driver {
|
||||
_device_handle: HANDLE,
|
||||
driver_object: *mut DRIVER_OBJECT,
|
||||
device_object: *mut DEVICE_OBJECT,
|
||||
}
|
||||
unsafe impl Sync for Driver {}
|
||||
|
||||
// This is a workaround for current state of wdk bindings.
|
||||
// TODO: replace with official version when they are correct: https://github.com/microsoft/wdkmetadata/issues/59
|
||||
pub type UnloadFnType = unsafe extern "system" fn(driver_object: *const DRIVER_OBJECT);
|
||||
pub type MjFnType = unsafe extern "system" fn(&mut DEVICE_OBJECT, &mut IRP) -> NTSTATUS;
|
||||
|
||||
impl Driver {
|
||||
pub(crate) fn new(
|
||||
driver_object: *mut DRIVER_OBJECT,
|
||||
_driver_handle: HANDLE,
|
||||
device_handle: HANDLE,
|
||||
) -> Driver {
|
||||
return Driver {
|
||||
// driver_handle,
|
||||
_device_handle: device_handle,
|
||||
driver_object,
|
||||
device_object: interface::wdf_device_wdm_get_device_object(device_handle),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn get_device_object(&self) -> *mut DEVICE_OBJECT {
|
||||
return self.device_object;
|
||||
}
|
||||
|
||||
pub fn get_device_object_ref(&self) -> Option<&mut DEVICE_OBJECT> {
|
||||
return unsafe { self.device_object.as_mut() };
|
||||
}
|
||||
|
||||
pub fn set_driver_unload(&mut self, driver_unload: UnloadFnType) {
|
||||
if let Some(driver) = unsafe { self.driver_object.as_mut() } {
|
||||
driver.DriverUnload = Some(unsafe { core::mem::transmute(driver_unload) })
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_read_fn(&mut self, mj_fn: MjFnType) {
|
||||
self.set_major_fn(windows_sys::Wdk::System::SystemServices::IRP_MJ_READ, mj_fn);
|
||||
}
|
||||
|
||||
pub fn set_write_fn(&mut self, mj_fn: MjFnType) {
|
||||
self.set_major_fn(
|
||||
windows_sys::Wdk::System::SystemServices::IRP_MJ_WRITE,
|
||||
mj_fn,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn set_create_fn(&mut self, mj_fn: MjFnType) {
|
||||
self.set_major_fn(
|
||||
windows_sys::Wdk::System::SystemServices::IRP_MJ_CREATE,
|
||||
mj_fn,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn set_device_control_fn(&mut self, mj_fn: MjFnType) {
|
||||
self.set_major_fn(
|
||||
windows_sys::Wdk::System::SystemServices::IRP_MJ_DEVICE_CONTROL,
|
||||
mj_fn,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn set_close_fn(&mut self, mj_fn: MjFnType) {
|
||||
self.set_major_fn(
|
||||
windows_sys::Wdk::System::SystemServices::IRP_MJ_CLOSE,
|
||||
mj_fn,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn set_cleanup_fn(&mut self, mj_fn: MjFnType) {
|
||||
self.set_major_fn(
|
||||
windows_sys::Wdk::System::SystemServices::IRP_MJ_CLEANUP,
|
||||
mj_fn,
|
||||
);
|
||||
}
|
||||
|
||||
fn set_major_fn(&mut self, fn_index: u32, mj_fn: MjFnType) {
|
||||
if let Some(driver) = unsafe { self.driver_object.as_mut() } {
|
||||
driver.MajorFunction[fn_index as usize] = Some(unsafe { core::mem::transmute(mj_fn) })
|
||||
}
|
||||
}
|
||||
}
|
||||
9
windows_kext/wdk/src/error.rs
Normal file
9
windows_kext/wdk/src/error.rs
Normal file
@@ -0,0 +1,9 @@
|
||||
// use anyhow::anyhow;
|
||||
|
||||
// pub fn anyhow_ntstatus(status: i32) -> anyhow::Error {
|
||||
// if let Some(value) = ntstatus::ntstatus::NtStatus::from_u32(status as u32) {
|
||||
// return anyhow!(value);
|
||||
// }
|
||||
|
||||
// return anyhow!("UNKNOWN_NTSTATUS_CODE");
|
||||
// }
|
||||
143
windows_kext/wdk/src/fast_mutex.rs
Normal file
143
windows_kext/wdk/src/fast_mutex.rs
Normal file
@@ -0,0 +1,143 @@
|
||||
use alloc::boxed::Box;
|
||||
use core::{
|
||||
cell::UnsafeCell,
|
||||
ops::{Deref, DerefMut},
|
||||
};
|
||||
use windows_sys::{
|
||||
Wdk::{
|
||||
Foundation::{FAST_MUTEX, KEVENT},
|
||||
System::SystemServices::FM_LOCK_BIT,
|
||||
},
|
||||
Win32::System::Kernel::{SynchronizationEvent, EVENT_TYPE},
|
||||
};
|
||||
|
||||
// #[link(name = "NtosKrnl", kind = "static")]
|
||||
extern "C" {
|
||||
fn KeInitializeEvent(event: *mut KEVENT, event_type: EVENT_TYPE, state: bool);
|
||||
|
||||
/// The ExAcquireFastMutex routine acquires the given fast mutex with APCs to the current thread disabled.
|
||||
fn ExAcquireFastMutex(kmutex: *mut FAST_MUTEX);
|
||||
|
||||
/// The ExTryToAcquireFastMutex routine acquires the given fast mutex, if possible, with APCs to the current thread disabled.
|
||||
fn ExTryToAcquireFastMutex(kmutex: *mut FAST_MUTEX) -> bool;
|
||||
|
||||
// The ExReleaseFastMutex routine releases ownership of a fast mutex that was acquired with ExAcquireFastMutex or ExTryToAcquireFastMutex.
|
||||
fn ExReleaseFastMutex(kmutex: *mut FAST_MUTEX);
|
||||
}
|
||||
|
||||
/// The ExInitializeFastMutex routine initializes a fast mutex variable, used to synchronize mutually exclusive access by a set of threads to a shared resource.
|
||||
/// ExInitializeFastMutex must be called before any calls to other ExXxxFastMutex routines occur.
|
||||
#[allow(non_snake_case)]
|
||||
unsafe fn ExInitializeFastMutex(kmutex: *mut FAST_MUTEX) {
|
||||
core::ptr::write_volatile(&mut (*kmutex).Count, FM_LOCK_BIT as i32);
|
||||
// (*kmutex).Count = FM_LOCK_BIT as i32;
|
||||
|
||||
(*kmutex).Owner = core::ptr::null_mut();
|
||||
(*kmutex).Contention = 0;
|
||||
KeInitializeEvent(&mut (*kmutex).Event, SynchronizationEvent, false)
|
||||
}
|
||||
|
||||
pub struct FastMutex<T> {
|
||||
kmutex: UnsafeCell<Option<*mut FAST_MUTEX>>,
|
||||
val: UnsafeCell<T>,
|
||||
}
|
||||
|
||||
impl<T> FastMutex<T> {
|
||||
pub const fn default(val: T) -> Self {
|
||||
Self {
|
||||
kmutex: UnsafeCell::new(None),
|
||||
val: UnsafeCell::new(val),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(&self) {
|
||||
let mutex = Box::into_raw(Box::new(unsafe {
|
||||
MaybeUninit::zeroed().assume_init();
|
||||
}));
|
||||
unsafe {
|
||||
ExInitializeFastMutex(mutex);
|
||||
*self.kmutex.get() = Some(mutex);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deinit(&self) {
|
||||
unsafe {
|
||||
let opt = &mut (*self.kmutex.get());
|
||||
if let Some(mutex) = opt {
|
||||
_ = Box::from_raw(mutex);
|
||||
}
|
||||
opt.take();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lock(&self) -> Result<LockGuard<T>, ()> {
|
||||
unsafe {
|
||||
if let Some(mutex) = *self.kmutex.get() {
|
||||
ExAcquireFastMutex(mutex);
|
||||
return Ok(LockGuard::new(self));
|
||||
}
|
||||
}
|
||||
|
||||
return Err(());
|
||||
}
|
||||
|
||||
pub fn try_lock(&self) -> Option<LockGuard<T>> {
|
||||
unsafe {
|
||||
if let Some(mutex) = *self.kmutex.get() {
|
||||
ExTryToAcquireFastMutex(mutex);
|
||||
return Some(LockGuard::new(self));
|
||||
}
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
fn get<'a>(&self) -> *mut T {
|
||||
self.val.get()
|
||||
}
|
||||
|
||||
fn unlock(&self) {
|
||||
unsafe {
|
||||
if let Some(mutex) = *self.kmutex.get() {
|
||||
ExReleaseFastMutex(mutex);
|
||||
} else {
|
||||
panic!("Mutex not initialized");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for FastMutex<T> {
|
||||
fn drop(&mut self) {
|
||||
self.deinit();
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LockGuard<'a, T> {
|
||||
mutex: &'a FastMutex<T>,
|
||||
}
|
||||
|
||||
impl<'a, T> LockGuard<'a, T> {
|
||||
fn new(mutex: &'a FastMutex<T>) -> Self {
|
||||
return LockGuard { mutex };
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Drop for LockGuard<'a, T> {
|
||||
fn drop(&mut self) {
|
||||
self.mutex.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Deref for LockGuard<'a, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*self.mutex.get() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> DerefMut for LockGuard<'a, T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
unsafe { &mut *self.mutex.get() }
|
||||
}
|
||||
}
|
||||
535
windows_kext/wdk/src/ffi.rs
Normal file
535
windows_kext/wdk/src/ffi.rs
Normal file
@@ -0,0 +1,535 @@
|
||||
use core::ffi::c_void;
|
||||
|
||||
use windows_sys::{
|
||||
core::{GUID, PCWSTR},
|
||||
Wdk::Foundation::{DEVICE_OBJECT, DRIVER_OBJECT, MDL},
|
||||
Win32::{
|
||||
Foundation::{HANDLE, NTSTATUS, UNICODE_STRING},
|
||||
NetworkManagement::WindowsFilteringPlatform::{
|
||||
FWPM_PROVIDER_CONTEXT2, FWP_CONDITION_VALUE0, FWP_MATCH_TYPE, FWP_VALUE0,
|
||||
},
|
||||
Networking::WinSock::{ADDRESS_FAMILY, SCOPE_ID},
|
||||
System::Kernel::COMPARTMENT_ID,
|
||||
},
|
||||
};
|
||||
|
||||
use crate::filter_engine::{
|
||||
classify::ClassifyOut, layer::IncomingValues, metadata::FwpsIncomingMetadataValues,
|
||||
};
|
||||
|
||||
pub(crate) type FwpsCalloutClassifyFn = unsafe extern "C" fn(
|
||||
inFixedValues: *const IncomingValues,
|
||||
inMetaValues: *const FwpsIncomingMetadataValues,
|
||||
layerData: *mut c_void,
|
||||
classifyContext: *mut c_void,
|
||||
filter: *const FWPS_FILTER2,
|
||||
flowContext: u64,
|
||||
classifyOut: *mut ClassifyOut,
|
||||
);
|
||||
|
||||
pub(crate) type FwpsCalloutNotifyFn = unsafe extern "C" fn(
|
||||
notifyType: u32,
|
||||
filterKey: *const GUID,
|
||||
filter: *mut FWPS_FILTER2,
|
||||
) -> NTSTATUS;
|
||||
|
||||
pub(crate) type FwpsCalloutFlowDeleteNotifyFn =
|
||||
unsafe extern "C" fn(layerId: u16, calloutId: u32, flowContext: u64);
|
||||
|
||||
/// The FWPS_ACTION0 structure specifies the run-time action that the filter engine takes if all of the filter's filtering conditions are true.
|
||||
#[allow(non_camel_case_types, non_snake_case)]
|
||||
#[repr(C)]
|
||||
pub(crate) struct FWPS_ACTION0 {
|
||||
r#type: u32,
|
||||
calloutId: u32,
|
||||
}
|
||||
|
||||
/// The FWPS_FILTER_CONDITION0 structure defines a run-time filtering condition for a filter.
|
||||
#[allow(non_camel_case_types, non_snake_case)]
|
||||
#[repr(C)]
|
||||
pub(crate) struct FWPS_FILTER_CONDITION0 {
|
||||
fieldId: u16,
|
||||
reserved: u16,
|
||||
matchType: FWP_MATCH_TYPE,
|
||||
conditionValue: FWP_CONDITION_VALUE0,
|
||||
}
|
||||
|
||||
/// The WdfExecutionLevel enumeration type specifies the maximum IRQL at which the framework will call the event callback functions that a driver has supplied for a framework object.
|
||||
#[repr(C)]
|
||||
enum WdfExecutionLevel {
|
||||
Invalid = 0,
|
||||
InheritFromParent,
|
||||
Passive,
|
||||
Dispatch,
|
||||
}
|
||||
|
||||
/// The WDF_SYNCHRONIZATION_SCOPE enumeration type specifies how the framework will synchronize execution of an object's event callback functions.
|
||||
#[repr(C)]
|
||||
enum WdfSynchronizationScope {
|
||||
Invalid = 0x00,
|
||||
InheritFromParent,
|
||||
Device,
|
||||
Queue,
|
||||
None,
|
||||
}
|
||||
|
||||
unsafe impl Sync for WdfObjectContextTypeInfo {}
|
||||
|
||||
/// The FWPS_FILTER2 structure defines a run-time filter in the filter engine.
|
||||
#[allow(non_camel_case_types, non_snake_case)]
|
||||
#[repr(C)]
|
||||
pub(crate) struct FWPS_FILTER2 {
|
||||
pub(crate) filterId: u64,
|
||||
pub(crate) weight: FWP_VALUE0,
|
||||
pub(crate) subLayerWeight: u16,
|
||||
pub(crate) flags: u16,
|
||||
pub(crate) numFilterConditions: u32,
|
||||
pub(crate) filterCondition: *mut FWPS_FILTER_CONDITION0,
|
||||
pub(crate) action: FWPS_ACTION0,
|
||||
pub(crate) context: u64,
|
||||
pub(crate) providerContext: *mut FWPM_PROVIDER_CONTEXT2,
|
||||
}
|
||||
|
||||
/// The FWPS_CALLOUT3 structure defines the data that is required for a callout driver to register a callout with the filter engine.
|
||||
#[allow(non_camel_case_types, non_snake_case)]
|
||||
#[repr(C)]
|
||||
pub(crate) struct FWPS_CALLOUT3 {
|
||||
pub(crate) calloutKey: GUID,
|
||||
pub(crate) flags: u32,
|
||||
pub(crate) classifyFn: Option<FwpsCalloutClassifyFn>,
|
||||
pub(crate) notifyFn: Option<FwpsCalloutNotifyFn>,
|
||||
pub(crate) flowDeleteFn: Option<FwpsCalloutFlowDeleteNotifyFn>,
|
||||
}
|
||||
|
||||
/// The filter engine calls a callout's completionFn callout function whenever packet data, described by the netBufferList parameter in one of the packet injection functions, has been injected into the network stack.
|
||||
#[allow(non_camel_case_types)]
|
||||
type FWPS_INJECT_COMPLETE0 = unsafe extern "C" fn(
|
||||
context: *mut c_void,
|
||||
net_buffer_list: *mut NET_BUFFER_LIST,
|
||||
dispatch_level: bool,
|
||||
);
|
||||
|
||||
/// The FWPS_TRANSPORT_SEND_PARAMS1 structure defines properties of an outbound transport layer packet.
|
||||
#[allow(non_camel_case_types)]
|
||||
#[repr(C)]
|
||||
pub(crate) struct FWPS_TRANSPORT_SEND_PARAMS1 {
|
||||
pub(crate) remote_address: *const u8,
|
||||
pub(crate) remote_scope_id: SCOPE_ID,
|
||||
pub(crate) control_data: *mut c_void, //WSACMSGHDR,
|
||||
pub(crate) control_data_length: u32,
|
||||
pub(crate) header_include_header: *mut u8,
|
||||
pub(crate) header_include_header_length: u32,
|
||||
}
|
||||
|
||||
/// The FWPS_PACKET_INJECTION_STATE enumeration type specifies the injection state of a network buffer list.
|
||||
#[allow(non_camel_case_types)]
|
||||
#[repr(C)]
|
||||
pub(crate) enum FWPS_PACKET_INJECTION_STATE {
|
||||
FWPS_PACKET_NOT_INJECTED,
|
||||
FWPS_PACKET_INJECTED_BY_SELF,
|
||||
FWPS_PACKET_INJECTED_BY_OTHER,
|
||||
FWPS_PACKET_PREVIOUSLY_INJECTED_BY_SELF,
|
||||
FWPS_PACKET_INJECTION_STATE_MAX,
|
||||
}
|
||||
|
||||
pub(crate) const FWPS_INJECTION_TYPE_STREAM: u32 = 0x00000001;
|
||||
pub(crate) const FWPS_INJECTION_TYPE_TRANSPORT: u32 = 0x00000002;
|
||||
pub(crate) const FWPS_INJECTION_TYPE_NETWORK: u32 = 0x00000004;
|
||||
pub(crate) const FWPS_INJECTION_TYPE_FORWARD: u32 = 0x00000008;
|
||||
pub(crate) const FWPS_INJECTION_TYPE_L2: u32 = 0x00000010;
|
||||
pub(crate) const FWPS_INJECTION_TYPE_VSWITCH_TRANSPORT: u32 = 0x00000020;
|
||||
|
||||
pub(crate) const NDIS_OBJECT_TYPE_DEFAULT: u8 = 0x80; // used when object type is implicit in the API call
|
||||
pub(crate) const NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1: u8 = 1;
|
||||
|
||||
/// The NBListHeader is the header of NET_BUFFER_LIST struct.
|
||||
#[repr(C)]
|
||||
pub(crate) struct NBListHeader {
|
||||
pub(crate) next: *mut NET_BUFFER_LIST,
|
||||
pub(crate) first_net_buffer: *mut NET_BUFFER,
|
||||
}
|
||||
|
||||
/// The NET_BUFFER_LIST structure specifies a linked list of NET_BUFFER structures.
|
||||
/// This is internal struct should never be allocated from the driver. Use provided functions by microsoft.
|
||||
#[allow(non_camel_case_types, non_snake_case)]
|
||||
#[repr(C)]
|
||||
pub struct NET_BUFFER_LIST {
|
||||
pub(crate) Header: NBListHeader,
|
||||
pub(crate) Context: *mut c_void,
|
||||
pub(crate) ParentNetBufferList: *mut NET_BUFFER_LIST,
|
||||
pub(crate) NdisPoolHandle: NDIS_HANDLE,
|
||||
pub(crate) NdisReserved: [*mut c_void; 2],
|
||||
pub(crate) ProtocolReserved: [*mut c_void; 4],
|
||||
pub(crate) MiniportReserved: [*mut c_void; 2],
|
||||
pub(crate) Scratch: *mut c_void,
|
||||
pub(crate) SourceHandle: NDIS_HANDLE,
|
||||
pub(crate) NblFlags: u32,
|
||||
pub(crate) ChildRefCount: i32,
|
||||
pub(crate) Flags: u32,
|
||||
pub(crate) Status: NDIS_STATUS,
|
||||
pub(crate) NetBufferListInfo: [*mut c_void; 20], // Extra data at the end of the struct. The size of the array is not fixed.
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types, non_snake_case)]
|
||||
#[repr(C)]
|
||||
pub union NBSize {
|
||||
pub DataLength: u32,
|
||||
pub stDataLength: u64,
|
||||
}
|
||||
|
||||
/// This is internal struct should never be allocated from the driver. Use provided functions by microsoft.
|
||||
/// The NET_BUFFER structure specifies data that is transmitted or received over the network.
|
||||
#[allow(non_camel_case_types, non_snake_case)]
|
||||
#[repr(C)]
|
||||
pub struct NET_BUFFER {
|
||||
pub(crate) Next: *mut NET_BUFFER,
|
||||
pub(crate) CurrentMdl: *mut MDL,
|
||||
pub(crate) CurrentMdlOffset: u32,
|
||||
pub(crate) nbSize: NBSize,
|
||||
pub(crate) MdlChain: *mut MDL,
|
||||
pub(crate) DataOffset: u32,
|
||||
pub(crate) ChecksumBias: u16,
|
||||
pub(crate) Reserved: u16,
|
||||
pub(crate) NdisPoolHandle: NDIS_HANDLE,
|
||||
pub(crate) NdisReserved: [*mut c_void; 2],
|
||||
pub(crate) ProtocolReserved: [*mut c_void; 6],
|
||||
pub(crate) MiniportReserved: [*mut c_void; 4],
|
||||
pub(crate) DataPhysicalAddress: u64,
|
||||
pub(crate) SharedMemoryInfo: *mut c_void,
|
||||
}
|
||||
|
||||
/// This data type is used as the generic handle type in NDIS function calls.
|
||||
#[allow(non_camel_case_types)]
|
||||
pub type NDIS_HANDLE = *mut c_void;
|
||||
|
||||
/// This data type is used to indicate success and error states in numerous functions and object identifiers.
|
||||
#[allow(non_camel_case_types)]
|
||||
pub type NDIS_STATUS = i32;
|
||||
|
||||
/// The NDIS_OBJECT_HEADER structure packages the object type, version, and size information that is required in many NDIS 6.0 structures.
|
||||
#[allow(non_camel_case_types, non_snake_case)]
|
||||
#[repr(C)]
|
||||
pub(crate) struct NDIS_OBJECT_HEADER {
|
||||
pub(crate) Type: u8,
|
||||
pub(crate) Revision: u8,
|
||||
pub(crate) Size: u16,
|
||||
}
|
||||
|
||||
/// The NET_BUFFER_LIST_POOL_PARAMETERS structure defines the parameters for a pool of NET_BUFFER_LIST structures.
|
||||
#[allow(non_camel_case_types, non_snake_case)]
|
||||
#[repr(C)]
|
||||
pub(crate) struct NET_BUFFER_LIST_POOL_PARAMETERS {
|
||||
pub(crate) Header: NDIS_OBJECT_HEADER,
|
||||
pub(crate) ProtocolId: u8,
|
||||
pub(crate) fAllocateNetBuffer: bool,
|
||||
pub(crate) ContextSize: u16,
|
||||
pub(crate) PoolTag: u32,
|
||||
pub(crate) DataSize: u32,
|
||||
pub(crate) Flags: u32,
|
||||
}
|
||||
/// WdfObjectContextTypeInfo is a description of the device context.
|
||||
#[repr(C)]
|
||||
pub struct WdfObjectContextTypeInfo {
|
||||
size: u32,
|
||||
context_name: *const u8,
|
||||
context_size: usize,
|
||||
unique_type: *const WdfObjectContextTypeInfo,
|
||||
_evt_driver_get_unique_context_type: *const c_void, // Internal use
|
||||
}
|
||||
|
||||
impl WdfObjectContextTypeInfo {
|
||||
pub const fn default(null_terminated_name: &'static str) -> Self {
|
||||
Self {
|
||||
size: core::mem::size_of::<WdfObjectContextTypeInfo>() as u32,
|
||||
context_name: null_terminated_name.as_ptr(),
|
||||
context_size: 0,
|
||||
unique_type: core::ptr::null(),
|
||||
_evt_driver_get_unique_context_type: core::ptr::null(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// WdfObjectAttributes contains attributes for the device context.
|
||||
#[repr(C)]
|
||||
pub struct WdfObjectAttributes {
|
||||
size: u32,
|
||||
evt_cleanup_callback: Option<extern "system" fn(wdf_object: HANDLE)>,
|
||||
evt_destroy_callback: Option<extern "system" fn(wdf_object: HANDLE)>,
|
||||
execution_level: WdfExecutionLevel,
|
||||
synchronization_scope: WdfSynchronizationScope,
|
||||
parent_object: HANDLE,
|
||||
context_size_override: usize,
|
||||
context_type_info: *const WdfObjectContextTypeInfo,
|
||||
}
|
||||
|
||||
impl WdfObjectAttributes {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
size: core::mem::size_of::<WdfObjectAttributes>() as u32,
|
||||
evt_cleanup_callback: None,
|
||||
evt_destroy_callback: None,
|
||||
execution_level: WdfExecutionLevel::InheritFromParent,
|
||||
synchronization_scope: WdfSynchronizationScope::InheritFromParent,
|
||||
parent_object: 0,
|
||||
context_size_override: 0,
|
||||
context_type_info: core::ptr::null(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_context<T>(&mut self, context_info: &'static mut WdfObjectContextTypeInfo) {
|
||||
context_info.context_size = core::mem::size_of::<T>();
|
||||
context_info.unique_type = context_info;
|
||||
self.context_size_override = 0;
|
||||
self.context_type_info = context_info.unique_type;
|
||||
}
|
||||
|
||||
pub fn set_cleanup_fn(&mut self, callback: extern "system" fn(wdf_object: HANDLE)) {
|
||||
self.evt_cleanup_callback = Some(callback);
|
||||
}
|
||||
|
||||
pub fn set_destroy_fn(&mut self, callback: extern "system" fn(wdf_object: HANDLE)) {
|
||||
self.evt_destroy_callback = Some(callback);
|
||||
}
|
||||
}
|
||||
|
||||
// #[link(name = "Fwpkclnt", kind = "static")]
|
||||
// #[link(name = "Fwpuclnt", kind = "static")]
|
||||
// #[link(name = "WdfDriverEntry", kind = "static")]
|
||||
// #[link(name = "WdfLdr", kind = "static")]
|
||||
// #[link(name = "BufferOverflowK", kind = "static")]
|
||||
// #[link(name = "uuid", kind = "static")]
|
||||
// #[link(name = "wdmsec", kind = "static")]
|
||||
// #[link(name = "wmilib", kind = "static")]
|
||||
// #[link(name = "NtosKrnl", kind = "static")]
|
||||
// #[link(name = "ndis", kind = "static")]
|
||||
#[link(name = "c_helper", kind = "static")]
|
||||
extern "C" {
|
||||
/// The FwpsCalloutUnregisterById0 function unregisters a callout from the filter engine.
|
||||
pub(crate) fn FwpsCalloutUnregisterById0(id: u32) -> NTSTATUS;
|
||||
|
||||
/// The FwpsCalloutRegister3 function registers a callout with the filter engine.
|
||||
pub(crate) fn FwpsCalloutRegister3(
|
||||
deviceObject: *mut c_void,
|
||||
callout: *const FWPS_CALLOUT3,
|
||||
calloutId: *mut u32,
|
||||
) -> NTSTATUS;
|
||||
|
||||
/// The FwpsPendOperation0 function is called by a callout to suspend packet processing pending completion of another operation.
|
||||
pub(crate) fn FwpsPendOperation0(
|
||||
completionHandle: HANDLE,
|
||||
completionContext: *mut HANDLE,
|
||||
) -> NTSTATUS;
|
||||
|
||||
/// The FwpsCompleteOperation0 function is called by a callout to resume packet processing that was suspended pending completion of another operation.
|
||||
pub(crate) fn FwpsCompleteOperation0(completionContext: HANDLE, netBufferList: *mut c_void);
|
||||
|
||||
/// The FwpsAcquireClassifyHandle0 function generates a classification handle that is used to identify asynchronous classification operations and requests for writable layer data.
|
||||
pub(crate) fn FwpsAcquireClassifyHandle0(
|
||||
classify_context: *mut c_void,
|
||||
reserved: u32, // Must be zero.
|
||||
classify_handle: *mut u64,
|
||||
) -> NTSTATUS;
|
||||
|
||||
/// A callout driver calls FwpsReleaseClassifyHandle0 to release a classification handle that was previously acquired through a call to FwpsAcquireClassifyHandle0.
|
||||
pub(crate) fn FwpsReleaseClassifyHandle0(classify_handle: u64);
|
||||
|
||||
/// A callout's classifyFn function calls FwpsPendClassify0 to pend the current classify request. After the request is pended, the callout driver must complete the processing of the classify request asynchronously by calling FwpsCompleteClassify0.
|
||||
pub(crate) fn FwpsPendClassify0(
|
||||
classify_handle: u64,
|
||||
filterId: u64,
|
||||
flags: u32, // Must be zero.
|
||||
classifyOut: *const ClassifyOut,
|
||||
) -> NTSTATUS;
|
||||
|
||||
/// A callout driver calls FwpsCompleteClassify0 to asynchronously complete a pended classify request. The callout driver's classifyFn function must have previously called FwpsPendClassify0 to pend the classify request.
|
||||
pub(crate) fn FwpsCompleteClassify0(
|
||||
classify_handle: u64,
|
||||
flags: u32, // Must be zero.
|
||||
classifyOut: *const ClassifyOut,
|
||||
);
|
||||
|
||||
/// The FwpsAcquireWritableLayerDataPointer0 function returns layer-specific data that can be inspected and changed.
|
||||
pub(crate) fn FwpsAcquireWritableLayerDataPointer0(
|
||||
classify_handle: u64,
|
||||
filter_id: u64,
|
||||
flags: u32,
|
||||
writable_layer_data: *mut c_void,
|
||||
classify_out: *mut ClassifyOut,
|
||||
) -> NTSTATUS;
|
||||
|
||||
/// The FwpsApplyModifiedLayerData0 function applies changes to layer-specific data made after a call to FwpsAcquireWritableLayerDataPointer0.
|
||||
pub(crate) fn FwpsApplyModifiedLayerData0(
|
||||
classifyHandle: u64,
|
||||
modifiedLayerData: *mut *mut c_void,
|
||||
flags: u32,
|
||||
);
|
||||
|
||||
/// pm_InitDriverObject initialize driver object. This function initializes requerd memory for the device context.
|
||||
pub(crate) fn pm_InitDriverObject(
|
||||
driver_object: *mut DRIVER_OBJECT,
|
||||
registry_path: *mut UNICODE_STRING,
|
||||
wdf_driver: *mut HANDLE,
|
||||
wdf_device: *mut HANDLE,
|
||||
win_driver_path: PCWSTR,
|
||||
dos_driver_path: PCWSTR,
|
||||
object_attributes: *mut WdfObjectAttributes,
|
||||
wdf_driver_unload: extern "C" fn(HANDLE),
|
||||
) -> NTSTATUS;
|
||||
|
||||
/// pm_WdfObjectGetTypedContextWerker 1to1 reference to the WdfObjectGetTypedContextWorker macro. The WdfObjectGetTypedContext macro returns a pointer to an object's context space.
|
||||
pub(crate) fn pm_WdfObjectGetTypedContextWorker(
|
||||
wdf_object: HANDLE,
|
||||
type_info: *const WdfObjectContextTypeInfo,
|
||||
) -> *mut c_void;
|
||||
|
||||
/// WdfObjectGetTypedContext 1to1 reference to WdfDeviceWdmGetDeviceObject. The WdfDeviceWdmGetDeviceObject method returns the Windows Driver Model (WDM) device object that is associated with a specified framework device object.
|
||||
pub(crate) fn pm_GetDeviceObject(wdf_device: HANDLE) -> *mut DEVICE_OBJECT;
|
||||
|
||||
/// The FwpsInjectNetworkSendAsync0 function injects packet data into the send data path.
|
||||
pub(crate) fn FwpsInjectNetworkSendAsync0(
|
||||
injectionHandle: HANDLE,
|
||||
injectionContext: HANDLE,
|
||||
flags: u32,
|
||||
compartmentId: COMPARTMENT_ID,
|
||||
netBufferList: *mut NET_BUFFER_LIST,
|
||||
completionFn: FWPS_INJECT_COMPLETE0,
|
||||
completionContext: *mut c_void,
|
||||
) -> NTSTATUS;
|
||||
|
||||
/// The FwpsInjectNetworkReceiveAsync0 function injects packet data into the receive data path.
|
||||
pub(crate) fn FwpsInjectNetworkReceiveAsync0(
|
||||
injectionHandle: HANDLE,
|
||||
injectionContext: HANDLE,
|
||||
flags: u32,
|
||||
compartmentId: COMPARTMENT_ID,
|
||||
interfaceIndex: u32,
|
||||
subInterfaceIndex: u32,
|
||||
netBufferList: *mut NET_BUFFER_LIST,
|
||||
completionFn: FWPS_INJECT_COMPLETE0,
|
||||
completionContext: *mut c_void,
|
||||
) -> NTSTATUS;
|
||||
|
||||
/// The FwpsInjectTransportSendAsync1 function injects packet data from the transport, datagram data, or ICMP error layers into the send data path. This function differs from the previous version (FwpsInjectTransportSendAsync0) in that it takes an updated parameters structure as an argument.
|
||||
pub(crate) fn FwpsInjectTransportSendAsync1(
|
||||
injectionHandle: HANDLE,
|
||||
injectionContext: HANDLE,
|
||||
endpointHandle: u64,
|
||||
flags: u32,
|
||||
sendArgs: *mut FWPS_TRANSPORT_SEND_PARAMS1,
|
||||
addressFamily: ADDRESS_FAMILY,
|
||||
compartmentId: COMPARTMENT_ID,
|
||||
netBufferList: *mut NET_BUFFER_LIST,
|
||||
completionFn: FWPS_INJECT_COMPLETE0,
|
||||
completionContext: *mut c_void,
|
||||
) -> NTSTATUS;
|
||||
|
||||
/// The FwpsInjectTransportReceiveAsync0 function injects packet data from the transport, datagram data, or ICMP error layers into the receive data path.
|
||||
pub(crate) fn FwpsInjectTransportReceiveAsync0(
|
||||
injectionHandle: HANDLE,
|
||||
injectionContext: HANDLE,
|
||||
reserved: *const c_void,
|
||||
flags: u32,
|
||||
addressFamily: ADDRESS_FAMILY,
|
||||
compartmentId: COMPARTMENT_ID,
|
||||
interfaceIndex: u32,
|
||||
subInterfaceIndex: u32,
|
||||
netBufferList: *mut NET_BUFFER_LIST,
|
||||
completionFn: FWPS_INJECT_COMPLETE0,
|
||||
completionContext: *mut c_void,
|
||||
) -> NTSTATUS;
|
||||
|
||||
/// The FwpsInjectionHandleCreate0 function creates a handle that can be used by packet injection functions to inject packet or stream data into the TCP/IP network stack and by the FwpsQueryPacketInjectionState0 function to query the packet injection state.
|
||||
pub(crate) fn FwpsInjectionHandleCreate0(
|
||||
addressFamily: ADDRESS_FAMILY,
|
||||
flags: u32,
|
||||
injectionHandle: &mut HANDLE,
|
||||
) -> NTSTATUS;
|
||||
|
||||
/// The FwpsQueryPacketInjectionState0 function is called by a callout to query the injection state of packet data.
|
||||
pub(crate) fn FwpsQueryPacketInjectionState0(
|
||||
injectionHandle: HANDLE,
|
||||
netBufferList: *const NET_BUFFER_LIST,
|
||||
injectionContext: *mut HANDLE,
|
||||
) -> FWPS_PACKET_INJECTION_STATE;
|
||||
|
||||
/// The FwpsInjectionHandleDestroy0 function destroys an injection handle that was previously created by calling the FwpsInjectionHandleCreate0 function.
|
||||
pub(crate) fn FwpsInjectionHandleDestroy0(injectionHandle: HANDLE) -> NTSTATUS;
|
||||
|
||||
/// The FwpsReferenceNetBufferList0 function increments the reference count for a NET_BUFFER_LIST structure.
|
||||
pub(crate) fn FwpsReferenceNetBufferList0(
|
||||
netBufferList: *mut NET_BUFFER_LIST,
|
||||
intendToModify: bool,
|
||||
);
|
||||
|
||||
/// The FwpsDereferenceNetBufferList0 function decrements the reference count for a NET_BUFFER_LIST structure that a callout driver had acquired earlier using the FwpsReferenceNetBufferList0 function.
|
||||
pub(crate) fn FwpsDereferenceNetBufferList0(
|
||||
netBufferList: *mut NET_BUFFER_LIST,
|
||||
dispatchLevel: bool,
|
||||
);
|
||||
|
||||
/// Call the NdisGetDataBuffer function to gain access to a contiguous block of data from a NET_BUFFER structure.
|
||||
pub(crate) fn NdisGetDataBuffer(
|
||||
NetBuffer: *const NET_BUFFER,
|
||||
BytesNeeded: u32,
|
||||
Storage: *mut u8,
|
||||
AlignMultiple: u32,
|
||||
AlignOffset: u32,
|
||||
) -> *mut u8;
|
||||
|
||||
/// Call the NdisAllocateCloneNetBufferList function to create a new clone NET_BUFFER_LIST structure.
|
||||
pub(crate) fn NdisAllocateCloneNetBufferList(
|
||||
OriginalNetBufferList: *mut NET_BUFFER_LIST,
|
||||
NetBufferListPoolHandle: NDIS_HANDLE,
|
||||
NetBufferPoolHandle: NDIS_HANDLE,
|
||||
AllocateCloneFlag: u32,
|
||||
) -> *mut NET_BUFFER_LIST;
|
||||
|
||||
/// Call the NdisFreeCloneNetBufferList function to free a NET_BUFFER_LIST structure and all associated NET_BUFFER structures and MDL chains that were previously allocated by calling the NdisAllocateCloneNetBufferList function.
|
||||
pub(crate) fn NdisFreeCloneNetBufferList(
|
||||
CloneNetBufferList: *mut NET_BUFFER_LIST,
|
||||
FreeCloneFlags: u32,
|
||||
);
|
||||
|
||||
/// The FwpsAllocateNetBufferAndNetBufferList0 function allocates a new NET_BUFFER_LIST structure.
|
||||
pub(crate) fn FwpsAllocateNetBufferAndNetBufferList0(
|
||||
poolHandle: NDIS_HANDLE,
|
||||
contextSize: u16,
|
||||
contextBackFill: u16,
|
||||
mdlChain: *mut MDL,
|
||||
dataOffset: u32,
|
||||
dataLength: u64,
|
||||
netBufferList: *mut *mut NET_BUFFER_LIST,
|
||||
) -> NTSTATUS;
|
||||
|
||||
/// The FwpsFreeNetBufferList0 function frees a NET_BUFFER_LIST structure that was previously allocated by a call to the FwpsAllocateNetBufferAndNetBufferList0 function.
|
||||
pub(crate) fn FwpsFreeNetBufferList0(netBufferList: *mut NET_BUFFER_LIST);
|
||||
|
||||
/// Call the NdisAllocateNetBufferListPool function to allocate a pool of NET_BUFFER_LIST structures.
|
||||
pub(crate) fn NdisAllocateNetBufferListPool(
|
||||
NdisHandle: NDIS_HANDLE,
|
||||
Parameters: *const NET_BUFFER_LIST_POOL_PARAMETERS,
|
||||
) -> NDIS_HANDLE;
|
||||
|
||||
/// Call the NdisFreeNetBufferListPool function to free a NET_BUFFER_LIST structure pool.
|
||||
pub(crate) fn NdisFreeNetBufferListPool(PoolHandle: NDIS_HANDLE);
|
||||
|
||||
/// Call the NdisRetreatNetBufferDataStart function to access more used data space in the MDL chain of a NET_BUFFER structure.
|
||||
pub(crate) fn NdisRetreatNetBufferDataStart(
|
||||
NetBuffer: *mut NET_BUFFER,
|
||||
DataOffsetDelta: u32,
|
||||
DataBackFill: u32,
|
||||
AllocateMdlHandler: *mut c_void,
|
||||
) -> NDIS_STATUS;
|
||||
|
||||
/// Call the NdisAdvanceNetBufferDataStart function to release the used data space that was added with the NdisRetreatNetBufferDataStart function.
|
||||
pub(crate) fn NdisAdvanceNetBufferDataStart(
|
||||
NetBuffer: *mut NET_BUFFER,
|
||||
DataOffsetDelta: u32,
|
||||
FreeMdl: bool,
|
||||
FreeMdlHandler: *mut c_void,
|
||||
);
|
||||
|
||||
/// 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;
|
||||
}
|
||||
101
windows_kext/wdk/src/filter_engine/callout.rs
Normal file
101
windows_kext/wdk/src/filter_engine/callout.rs
Normal file
@@ -0,0 +1,101 @@
|
||||
use super::{callout_data::CalloutData, ffi, layer::Layer};
|
||||
use crate::ffi::FwpsCalloutClassifyFn;
|
||||
use alloc::{borrow::ToOwned, format, string::String};
|
||||
use windows_sys::Wdk::Foundation::DEVICE_OBJECT;
|
||||
|
||||
pub enum FilterType {
|
||||
Resettable,
|
||||
NonResettable,
|
||||
}
|
||||
|
||||
pub struct Callout {
|
||||
pub(crate) id: u32,
|
||||
pub(super) address: u64,
|
||||
pub(crate) name: String,
|
||||
pub(crate) description: String,
|
||||
pub(crate) guid: u128,
|
||||
pub(crate) layer: Layer,
|
||||
pub(crate) action: u32,
|
||||
pub(crate) registered: bool,
|
||||
pub(crate) filter_type: FilterType,
|
||||
pub(crate) filter_id: u64,
|
||||
pub(crate) callout_fn: fn(CalloutData),
|
||||
}
|
||||
|
||||
impl Callout {
|
||||
pub fn new(
|
||||
name: &str,
|
||||
description: &str,
|
||||
guid: u128,
|
||||
layer: Layer,
|
||||
action: u32,
|
||||
filter_type: FilterType,
|
||||
callout_fn: fn(CalloutData),
|
||||
) -> Self {
|
||||
Self {
|
||||
id: 0,
|
||||
address: 0,
|
||||
name: name.to_owned(),
|
||||
description: description.to_owned(),
|
||||
guid,
|
||||
layer,
|
||||
action,
|
||||
registered: false,
|
||||
filter_type,
|
||||
filter_id: 0,
|
||||
callout_fn,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register_filter(
|
||||
&mut self,
|
||||
filter_engine_handle: isize,
|
||||
sublayer_guid: u128,
|
||||
) -> Result<(), String> {
|
||||
match ffi::register_filter(
|
||||
filter_engine_handle,
|
||||
sublayer_guid,
|
||||
&format!("{}-filter", self.name),
|
||||
&self.description,
|
||||
self.guid,
|
||||
self.layer,
|
||||
self.action,
|
||||
self.address, // The address of the callout is passed as context.
|
||||
) {
|
||||
Ok(id) => {
|
||||
self.filter_id = id;
|
||||
}
|
||||
Err(error) => {
|
||||
return Err(format!("failed to register filter: {}", error));
|
||||
}
|
||||
};
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
pub(crate) fn register_callout(
|
||||
&mut self,
|
||||
filter_engine_handle: isize,
|
||||
device_object: *mut DEVICE_OBJECT,
|
||||
callout_fn: FwpsCalloutClassifyFn,
|
||||
) -> Result<(), String> {
|
||||
match ffi::register_callout(
|
||||
device_object,
|
||||
filter_engine_handle,
|
||||
&format!("{}-callout", self.name),
|
||||
&self.description,
|
||||
self.guid,
|
||||
self.layer,
|
||||
callout_fn,
|
||||
) {
|
||||
Ok(id) => {
|
||||
self.registered = true;
|
||||
self.id = id;
|
||||
}
|
||||
Err(code) => {
|
||||
return Err(format!("failed to register callout: {}", code));
|
||||
}
|
||||
};
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
209
windows_kext/wdk/src/filter_engine/callout_data.rs
Normal file
209
windows_kext/wdk/src/filter_engine/callout_data.rs
Normal file
@@ -0,0 +1,209 @@
|
||||
use crate::{
|
||||
ffi::{FwpsCompleteOperation0, FwpsPendOperation0},
|
||||
utils::check_ntstatus,
|
||||
};
|
||||
|
||||
use super::{
|
||||
classify::ClassifyOut,
|
||||
layer::{Layer, Value, ValueType},
|
||||
metadata::FwpsIncomingMetadataValues,
|
||||
packet::TransportPacketList,
|
||||
stream_data::StreamCalloutIoPacket,
|
||||
FilterEngine,
|
||||
};
|
||||
use alloc::string::{String, ToString};
|
||||
use core::{ffi::c_void, ptr::NonNull};
|
||||
use windows_sys::Win32::{
|
||||
Foundation::HANDLE,
|
||||
NetworkManagement::WindowsFilteringPlatform::FWP_CONDITION_FLAG_IS_REAUTHORIZE,
|
||||
Networking::WinSock::SCOPE_ID,
|
||||
};
|
||||
|
||||
pub enum ClassifyDefer {
|
||||
Initial(HANDLE, Option<TransportPacketList>),
|
||||
Reauthorization(usize, Option<TransportPacketList>),
|
||||
}
|
||||
|
||||
impl ClassifyDefer {
|
||||
pub fn complete(
|
||||
self,
|
||||
filter_engine: &mut FilterEngine,
|
||||
) -> Result<Option<TransportPacketList>, String> {
|
||||
unsafe {
|
||||
match self {
|
||||
ClassifyDefer::Initial(context, packet_list) => {
|
||||
FwpsCompleteOperation0(context, core::ptr::null_mut());
|
||||
return Ok(packet_list);
|
||||
}
|
||||
ClassifyDefer::Reauthorization(_callout_id, packet_list) => {
|
||||
// There is no way to reset single filter. If another request for filter reset is trigger at the same time it will fail.
|
||||
if let Err(err) = filter_engine.reset_all_filters() {
|
||||
return Err(err);
|
||||
}
|
||||
return Ok(packet_list);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// pub fn add_net_buffer(&mut self, nbl: NetBufferList) {
|
||||
// if let Some(packet_list) = match self {
|
||||
// ClassifyDefer::Initial(_, packet_list) => packet_list,
|
||||
// ClassifyDefer::Reauthorization(_, packet_list) => packet_list,
|
||||
// } {
|
||||
// packet_list.net_buffer_list_queue.push(nbl);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
pub struct CalloutData<'a> {
|
||||
pub layer: Layer,
|
||||
pub(crate) callout_id: usize,
|
||||
pub(crate) values: &'a [Value],
|
||||
pub(crate) metadata: *const FwpsIncomingMetadataValues,
|
||||
pub(crate) classify_out: *mut ClassifyOut,
|
||||
pub(crate) layer_data: *mut c_void,
|
||||
}
|
||||
|
||||
impl<'a> CalloutData<'a> {
|
||||
pub fn get_value_type(&self, index: usize) -> ValueType {
|
||||
self.values[index].value_type
|
||||
}
|
||||
|
||||
pub fn get_value_u8(&'a self, index: usize) -> u8 {
|
||||
unsafe {
|
||||
return self.values[index].value.uint8;
|
||||
};
|
||||
}
|
||||
|
||||
pub fn get_value_u16(&'a self, index: usize) -> u16 {
|
||||
unsafe {
|
||||
return self.values[index].value.uint16;
|
||||
};
|
||||
}
|
||||
|
||||
pub fn get_value_u32(&'a self, index: usize) -> u32 {
|
||||
unsafe {
|
||||
return self.values[index].value.uint32;
|
||||
};
|
||||
}
|
||||
|
||||
pub fn get_value_byte_array16(&'a self, index: usize) -> &[u8; 16] {
|
||||
unsafe {
|
||||
return self.values[index].value.byte_array16.as_ref().unwrap();
|
||||
};
|
||||
}
|
||||
|
||||
pub fn get_process_id(&self) -> Option<u64> {
|
||||
unsafe { (*self.metadata).get_process_id() }
|
||||
}
|
||||
|
||||
pub fn get_process_path(&self) -> Option<String> {
|
||||
unsafe {
|
||||
return (*self.metadata).get_process_path();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_transport_endpoint_handle(&self) -> Option<u64> {
|
||||
unsafe {
|
||||
return (*self.metadata).get_transport_endpoint_handle();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_remote_scope_id(&self) -> Option<SCOPE_ID> {
|
||||
unsafe {
|
||||
return (*self.metadata).get_remote_scope_id();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_control_data(&self) -> Option<NonNull<[u8]>> {
|
||||
unsafe {
|
||||
return (*self.metadata).get_control_data();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_layer_data(&self) -> *mut c_void {
|
||||
return self.layer_data;
|
||||
}
|
||||
|
||||
pub fn get_stream_callout_packet(&self) -> Option<&mut StreamCalloutIoPacket> {
|
||||
match self.layer {
|
||||
Layer::StreamV4 | Layer::StreamV4Discard | Layer::StreamV6 | Layer::StreamV6Discard => unsafe {
|
||||
(self.layer_data as *mut StreamCalloutIoPacket).as_mut()
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pend_operation(
|
||||
&mut self,
|
||||
packet_list: Option<TransportPacketList>,
|
||||
) -> Result<ClassifyDefer, String> {
|
||||
unsafe {
|
||||
let mut completion_context = 0;
|
||||
if let Some(completion_handle) = (*self.metadata).get_completion_handle() {
|
||||
let status = FwpsPendOperation0(completion_handle, &mut completion_context);
|
||||
check_ntstatus(status)?;
|
||||
|
||||
return Ok(ClassifyDefer::Initial(completion_context, packet_list));
|
||||
}
|
||||
|
||||
Err("callout not supported".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pend_filter_rest(&mut self, packet_list: Option<TransportPacketList>) -> ClassifyDefer {
|
||||
ClassifyDefer::Reauthorization(self.callout_id, packet_list)
|
||||
}
|
||||
|
||||
pub fn action_permit(&mut self) {
|
||||
unsafe {
|
||||
(*self.classify_out).action_permit();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn action_continue(&mut self) {
|
||||
unsafe {
|
||||
(*self.classify_out).action_continue();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn action_block(&mut self) {
|
||||
unsafe {
|
||||
(*self.classify_out).action_block();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn action_none(&mut self) {
|
||||
unsafe {
|
||||
(*self.classify_out).set_none();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn block_and_absorb(&mut self) {
|
||||
unsafe {
|
||||
(*self.classify_out).action_block();
|
||||
(*self.classify_out).set_absorb();
|
||||
}
|
||||
}
|
||||
pub fn clear_write_flag(&mut self) {
|
||||
unsafe {
|
||||
(*self.classify_out).clear_write_flag();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_reauthorize(&self, flags_index: usize) -> bool {
|
||||
self.get_value_u32(flags_index) & FWP_CONDITION_FLAG_IS_REAUTHORIZE > 0
|
||||
}
|
||||
|
||||
pub fn parmit_and_absorb(&mut self) {
|
||||
unsafe {
|
||||
(*self.classify_out).action_permit();
|
||||
(*self.classify_out).set_absorb();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_callout_id(&self) -> usize {
|
||||
self.callout_id
|
||||
}
|
||||
}
|
||||
87
windows_kext/wdk/src/filter_engine/classify.rs
Normal file
87
windows_kext/wdk/src/filter_engine/classify.rs
Normal file
@@ -0,0 +1,87 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use windows_sys::Win32::NetworkManagement::WindowsFilteringPlatform::FWPS_CLASSIFY_OUT_FLAG_ABSORB;
|
||||
|
||||
const FWP_ACTION_FLAG_TERMINATING: u32 = 0x00001000;
|
||||
const FWP_ACTION_FLAG_NON_TERMINATING: u32 = 0x00002000;
|
||||
const FWP_ACTION_FLAG_CALLOUT: u32 = 0x00004000;
|
||||
|
||||
const FWP_ACTION_BLOCK: u32 = 0x00000001 | FWP_ACTION_FLAG_TERMINATING;
|
||||
const FWP_ACTION_PERMIT: u32 = 0x00000002 | FWP_ACTION_FLAG_TERMINATING;
|
||||
const FWP_ACTION_CALLOUT_TERMINATING: u32 =
|
||||
0x00000003 | FWP_ACTION_FLAG_CALLOUT | FWP_ACTION_FLAG_TERMINATING;
|
||||
const FWP_ACTION_CALLOUT_INSPECTION: u32 =
|
||||
0x00000004 | FWP_ACTION_FLAG_CALLOUT | FWP_ACTION_FLAG_NON_TERMINATING;
|
||||
const FWP_ACTION_CALLOUT_UNKNOWN: u32 = 0x00000005 | FWP_ACTION_FLAG_CALLOUT;
|
||||
const FWP_ACTION_CONTINUE: u32 = 0x00000006 | FWP_ACTION_FLAG_NON_TERMINATING;
|
||||
const FWP_ACTION_NONE: u32 = 0x00000007;
|
||||
const FWP_ACTION_NONE_NO_MATCH: u32 = 0x00000008;
|
||||
|
||||
const FWP_CONDITION_FLAG_IS_LOOPBACK: u32 = 0x00000001;
|
||||
const FWP_CONDITION_FLAG_IS_IPSEC_SECURED: u32 = 0x00000002;
|
||||
const FWP_CONDITION_FLAG_IS_REAUTHORIZE: u32 = 0x00000004;
|
||||
const FWP_CONDITION_FLAG_IS_WILDCARD_BIND: u32 = 0x00000008;
|
||||
const FWP_CONDITION_FLAG_IS_RAW_ENDPOINT: u32 = 0x00000010;
|
||||
const FWP_CONDITION_FLAG_IS_FRAGMENT: u32 = 0x00000020;
|
||||
const FWP_CONDITION_FLAG_IS_FRAGMENT_GROUP: u32 = 0x00000040;
|
||||
const FWP_CONDITION_FLAG_IS_IPSEC_NATT_RECLASSIFY: u32 = 0x00000080;
|
||||
const FWP_CONDITION_FLAG_REQUIRES_ALE_CLASSIFY: u32 = 0x00000100;
|
||||
const FWP_CONDITION_FLAG_IS_IMPLICIT_BIND: u32 = 0x00000200;
|
||||
const FWP_CONDITION_FLAG_IS_REASSEMBLED: u32 = 0x00000400;
|
||||
const FWP_CONDITION_FLAG_IS_NAME_APP_SPECIFIED: u32 = 0x00004000;
|
||||
const FWP_CONDITION_FLAG_IS_PROMISCUOUS: u32 = 0x00008000;
|
||||
const FWP_CONDITION_FLAG_IS_AUTH_FW: u32 = 0x00010000;
|
||||
const FWP_CONDITION_FLAG_IS_RECLASSIFY: u32 = 0x00020000;
|
||||
const FWP_CONDITION_FLAG_IS_OUTBOUND_PASS_THRU: u32 = 0x00040000;
|
||||
const FWP_CONDITION_FLAG_IS_INBOUND_PASS_THRU: u32 = 0x00080000;
|
||||
const FWP_CONDITION_FLAG_IS_CONNECTION_REDIRECTED: u32 = 0x00100000;
|
||||
|
||||
const FWPS_RIGHT_ACTION_WRITE: u32 = 0x00000001;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct ClassifyOut {
|
||||
action_type: u32,
|
||||
_out_context: u64, // System use
|
||||
_filter_id: u64, // System use
|
||||
rights: u32,
|
||||
flags: u32,
|
||||
reserved: u32,
|
||||
}
|
||||
|
||||
impl ClassifyOut {
|
||||
// Checks if write action flag is set. Indicates if the callout can change the action.
|
||||
pub fn can_set_action(&self) -> bool {
|
||||
self.rights & FWPS_RIGHT_ACTION_WRITE > 0
|
||||
}
|
||||
|
||||
/// Set block action. Write flag should be cleared, after this.
|
||||
pub fn action_block(&mut self) {
|
||||
self.action_type = FWP_ACTION_BLOCK;
|
||||
}
|
||||
|
||||
/// Set permit action.
|
||||
pub fn action_permit(&mut self) {
|
||||
self.action_type = FWP_ACTION_PERMIT;
|
||||
}
|
||||
|
||||
// Set continue action.
|
||||
pub fn action_continue(&mut self) {
|
||||
self.action_type = FWP_ACTION_CONTINUE;
|
||||
}
|
||||
|
||||
// Set none action.
|
||||
pub fn set_none(&mut self) {
|
||||
self.action_type = FWP_ACTION_NONE;
|
||||
}
|
||||
|
||||
// Set absorb flag. This will drop the packet. Used when the packets will be reinjected in the future.
|
||||
pub fn set_absorb(&mut self) {
|
||||
self.flags |= FWPS_CLASSIFY_OUT_FLAG_ABSORB;
|
||||
}
|
||||
|
||||
// Clear the write flag permission. Next filter in the chain will not change the action.
|
||||
pub fn clear_write_flag(&mut self) {
|
||||
self.rights &= !FWPS_RIGHT_ACTION_WRITE;
|
||||
}
|
||||
}
|
||||
79
windows_kext/wdk/src/filter_engine/connect_request.rs
Normal file
79
windows_kext/wdk/src/filter_engine/connect_request.rs
Normal file
@@ -0,0 +1,79 @@
|
||||
use core::ffi::c_void;
|
||||
|
||||
use windows_sys::Win32::{
|
||||
Foundation::HANDLE,
|
||||
Networking::WinSock::{AF_INET, AF_INET6},
|
||||
};
|
||||
|
||||
use crate::info;
|
||||
|
||||
#[repr(C)]
|
||||
pub(crate) struct FwpsConnectRequest0 {
|
||||
pub(crate) local_address_and_port: [u8; 128],
|
||||
pub(crate) remote_address_and_port: [u8; 128],
|
||||
pub(crate) port_reservation_token: u64,
|
||||
pub(crate) local_redirect_target_pid: u32,
|
||||
pub(crate) previous_version: *const FwpsConnectRequest0,
|
||||
pub(crate) modifier_filter_id: u64,
|
||||
pub(crate) local_redirect_handle: HANDLE,
|
||||
pub(crate) local_redirect_context: *mut c_void,
|
||||
pub(crate) local_redirect_context_size: usize,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct SocketAddressGeneric {
|
||||
family: u16,
|
||||
padding: [u8; 128 - 2],
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct SocketAddressIPv4 {
|
||||
family: u16,
|
||||
port: u16,
|
||||
addr: [u8; 4],
|
||||
zero: [u8; 8],
|
||||
padding: [u8; 128 - 2 - 2 - 4 - 8],
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct SocketAddressIPv6 {
|
||||
family: u16,
|
||||
port: u16,
|
||||
flowinfo: u16,
|
||||
addr: [u8; 16],
|
||||
scope_id: u32,
|
||||
padding: [u8; 128 - 2 - 2 - 2 - 16 - 4],
|
||||
}
|
||||
|
||||
impl FwpsConnectRequest0 {
|
||||
pub(crate) fn set_remote(&mut self, ip: &[u8], port: u16) {
|
||||
info!("local: {:?}", self.local_address_and_port);
|
||||
info!("remote: {:?}", self.remote_address_and_port);
|
||||
unsafe {
|
||||
let generic_socket: &mut SocketAddressGeneric =
|
||||
core::mem::transmute(&mut self.remote_address_and_port);
|
||||
match generic_socket.family {
|
||||
AF_INET => {
|
||||
info!("Socket type AF_INET");
|
||||
let socket_ipv4: &mut SocketAddressIPv4 = core::mem::transmute(generic_socket);
|
||||
for i in 0..4 {
|
||||
socket_ipv4.addr[i] = ip[i];
|
||||
}
|
||||
socket_ipv4.port = u16::to_be(port);
|
||||
}
|
||||
AF_INET6 => {
|
||||
info!("Socket type AF_INET6");
|
||||
let socket_ipv6: &mut SocketAddressIPv6 = core::mem::transmute(generic_socket);
|
||||
for i in 0..16 {
|
||||
socket_ipv6.addr[i] = ip[i];
|
||||
}
|
||||
socket_ipv6.port = u16::to_be(port);
|
||||
}
|
||||
_ => {
|
||||
info!("Unsupported socket type: {}", generic_socket.family);
|
||||
}
|
||||
}
|
||||
}
|
||||
info!("after: {:?}", self.remote_address_and_port);
|
||||
}
|
||||
}
|
||||
255
windows_kext/wdk/src/filter_engine/ffi.rs
Normal file
255
windows_kext/wdk/src/filter_engine/ffi.rs
Normal file
@@ -0,0 +1,255 @@
|
||||
use crate::alloc::borrow::ToOwned;
|
||||
use crate::ffi::FwpsCalloutClassifyFn;
|
||||
use crate::ffi::{FwpsCalloutRegister3, FwpsCalloutUnregisterById0, FWPS_CALLOUT3, FWPS_FILTER2};
|
||||
use crate::utils::check_ntstatus;
|
||||
use alloc::string::String;
|
||||
|
||||
use core::mem::MaybeUninit;
|
||||
use core::ptr;
|
||||
use widestring::U16CString;
|
||||
|
||||
use windows_sys::Wdk::Foundation::DEVICE_OBJECT;
|
||||
use windows_sys::Win32::Foundation::{NTSTATUS, STATUS_SUCCESS};
|
||||
use windows_sys::Win32::NetworkManagement::WindowsFilteringPlatform::{
|
||||
FwpmCalloutAdd0, FwpmEngineClose0, FwpmEngineOpen0, FwpmFilterAdd0, FwpmFilterDeleteById0,
|
||||
FwpmSubLayerAdd0, FwpmSubLayerDeleteByKey0, FwpmTransactionAbort0, FwpmTransactionBegin0,
|
||||
FwpmTransactionCommit0, FWPM_CALLOUT0, FWPM_CALLOUT_FLAG_USES_PROVIDER_CONTEXT,
|
||||
FWPM_DISPLAY_DATA0, FWPM_FILTER0, FWPM_FILTER_FLAG_CLEAR_ACTION_RIGHT, FWPM_SESSION0,
|
||||
FWPM_SESSION_FLAG_DYNAMIC, FWPM_SUBLAYER0, FWP_UINT8,
|
||||
};
|
||||
use windows_sys::Win32::System::Rpc::RPC_C_AUTHN_WINNT;
|
||||
use windows_sys::{
|
||||
core::GUID,
|
||||
Win32::Foundation::{HANDLE, INVALID_HANDLE_VALUE},
|
||||
};
|
||||
|
||||
use super::layer::Layer;
|
||||
|
||||
pub(crate) fn create_filter_engine() -> Result<HANDLE, String> {
|
||||
unsafe {
|
||||
let mut handle: HANDLE = INVALID_HANDLE_VALUE;
|
||||
let mut wdf_session: FWPM_SESSION0 = MaybeUninit::zeroed().assume_init();
|
||||
wdf_session.flags = FWPM_SESSION_FLAG_DYNAMIC;
|
||||
let status = FwpmEngineOpen0(
|
||||
core::ptr::null(),
|
||||
RPC_C_AUTHN_WINNT,
|
||||
core::ptr::null_mut(),
|
||||
&wdf_session,
|
||||
&mut handle,
|
||||
);
|
||||
check_ntstatus(status as i32)?;
|
||||
|
||||
return Ok(handle);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn register_sublayer(
|
||||
filter_engine_handle: HANDLE,
|
||||
name: &str,
|
||||
description: &str,
|
||||
guid: u128,
|
||||
) -> Result<(), String> {
|
||||
let Ok(name) = U16CString::from_str(name) else {
|
||||
return Err("invalid argument name".to_owned());
|
||||
};
|
||||
let Ok(description) = U16CString::from_str(description) else {
|
||||
return Err("invalid argument description".to_owned());
|
||||
};
|
||||
|
||||
unsafe {
|
||||
let mut sublayer: FWPM_SUBLAYER0 = MaybeUninit::zeroed().assume_init();
|
||||
sublayer.subLayerKey = GUID::from_u128(guid);
|
||||
sublayer.displayData.name = name.as_ptr() as _;
|
||||
sublayer.displayData.description = description.as_ptr() as _;
|
||||
sublayer.flags = 0;
|
||||
sublayer.weight = 0xFFFF;
|
||||
|
||||
let status = FwpmSubLayerAdd0(filter_engine_handle, &sublayer, core::ptr::null_mut());
|
||||
check_ntstatus(status as i32)?;
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn unregister_sublayer(filter_engine_handle: HANDLE, guid: u128) -> Result<(), String> {
|
||||
let guid = GUID::from_u128(guid);
|
||||
unsafe {
|
||||
let status = FwpmSubLayerDeleteByKey0(filter_engine_handle, ptr::addr_of!(guid));
|
||||
check_ntstatus(status as i32)?;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn generic_notify(
|
||||
_notify_type: u32,
|
||||
_filter_key: *const GUID,
|
||||
_filter: *mut FWPS_FILTER2,
|
||||
) -> NTSTATUS {
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
unsafe extern "C" fn generic_delete_notify(_layer_id: u16, _callout_id: u32, _flow_context: u64) {}
|
||||
|
||||
pub(crate) fn register_callout(
|
||||
device_object: *mut DEVICE_OBJECT,
|
||||
filter_engine_handle: HANDLE,
|
||||
name: &str,
|
||||
description: &str,
|
||||
guid: u128,
|
||||
layer: Layer,
|
||||
callout_fn: FwpsCalloutClassifyFn,
|
||||
) -> Result<u32, String> {
|
||||
let s_callout = FWPS_CALLOUT3 {
|
||||
calloutKey: GUID::from_u128(guid),
|
||||
flags: 0,
|
||||
classifyFn: Some(callout_fn),
|
||||
notifyFn: Some(generic_notify),
|
||||
flowDeleteFn: Some(generic_delete_notify),
|
||||
};
|
||||
|
||||
unsafe {
|
||||
let mut callout_id: u32 = 0;
|
||||
let status = FwpsCalloutRegister3(device_object as _, &s_callout, &mut callout_id);
|
||||
|
||||
check_ntstatus(status)?;
|
||||
|
||||
if let Err(err) = callout_add(filter_engine_handle, guid, layer, name, description) {
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
return Ok(callout_id);
|
||||
}
|
||||
}
|
||||
|
||||
fn callout_add(
|
||||
filter_engine_handle: HANDLE,
|
||||
guid: u128,
|
||||
layer: Layer,
|
||||
name: &str,
|
||||
description: &str,
|
||||
) -> Result<(), String> {
|
||||
let Ok(name) = U16CString::from_str(name) else {
|
||||
return Err("invalid argument name".to_owned());
|
||||
};
|
||||
let Ok(description) = U16CString::from_str(description) else {
|
||||
return Err("invalid argument description".to_owned());
|
||||
};
|
||||
let display_data = FWPM_DISPLAY_DATA0 {
|
||||
name: name.as_ptr() as _,
|
||||
description: description.as_ptr() as _,
|
||||
};
|
||||
|
||||
unsafe {
|
||||
let mut callout: FWPM_CALLOUT0 = MaybeUninit::zeroed().assume_init();
|
||||
callout.calloutKey = GUID::from_u128(guid);
|
||||
callout.displayData = display_data;
|
||||
callout.applicableLayer = layer.get_guid();
|
||||
callout.flags = FWPM_CALLOUT_FLAG_USES_PROVIDER_CONTEXT;
|
||||
let status = FwpmCalloutAdd0(
|
||||
filter_engine_handle,
|
||||
&callout,
|
||||
core::ptr::null_mut(),
|
||||
core::ptr::null_mut(),
|
||||
);
|
||||
check_ntstatus(status as i32)?;
|
||||
};
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
pub(crate) fn unregister_callout(callout_id: u32) -> Result<(), String> {
|
||||
unsafe {
|
||||
let status = FwpsCalloutUnregisterById0(callout_id);
|
||||
|
||||
check_ntstatus(status as i32)?;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn register_filter(
|
||||
filter_engine_handle: HANDLE,
|
||||
sublayer_guid: u128,
|
||||
name: &str,
|
||||
description: &str,
|
||||
callout_guid: u128,
|
||||
layer: Layer,
|
||||
action: u32,
|
||||
context: u64,
|
||||
) -> Result<u64, String> {
|
||||
let Ok(name) = U16CString::from_str(name) else {
|
||||
return Err("invalid argument name".to_owned());
|
||||
};
|
||||
let Ok(description) = U16CString::from_str(description) else {
|
||||
return Err("invalid argument description".to_owned());
|
||||
};
|
||||
let mut filter_id: u64 = 0;
|
||||
unsafe {
|
||||
let mut filter: FWPM_FILTER0 = MaybeUninit::zeroed().assume_init();
|
||||
filter.displayData.name = name.as_ptr() as _;
|
||||
filter.displayData.description = description.as_ptr() as _;
|
||||
filter.action.r#type = action; // Says this filter's callout MUST make a block/permit decision. Also see doc excerpts below.
|
||||
filter.subLayerKey = GUID::from_u128(sublayer_guid);
|
||||
filter.weight.r#type = FWP_UINT8;
|
||||
filter.weight.Anonymous.uint8 = 15; // The weight of this filter within its sublayer
|
||||
filter.flags = FWPM_FILTER_FLAG_CLEAR_ACTION_RIGHT;
|
||||
filter.numFilterConditions = 0; // If you specify 0, this filter invokes its callout for all traffic in its layer
|
||||
filter.layerKey = layer.get_guid(); // This layer must match the layer that ExampleCallout is registered to
|
||||
filter.action.Anonymous.calloutKey = GUID::from_u128(callout_guid);
|
||||
filter.Anonymous.rawContext = context;
|
||||
let status = FwpmFilterAdd0(
|
||||
filter_engine_handle,
|
||||
&filter,
|
||||
core::ptr::null_mut(),
|
||||
&mut filter_id,
|
||||
);
|
||||
|
||||
check_ntstatus(status as i32)?;
|
||||
|
||||
return Ok(filter_id);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn unregister_filter(
|
||||
filter_engine_handle: HANDLE,
|
||||
filter_id: u64,
|
||||
) -> Result<(), String> {
|
||||
unsafe {
|
||||
let status = FwpmFilterDeleteById0(filter_engine_handle, filter_id);
|
||||
check_ntstatus(status as i32)?;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn filter_engine_close(filter_engine_handle: HANDLE) -> Result<(), String> {
|
||||
unsafe {
|
||||
let status = FwpmEngineClose0(filter_engine_handle);
|
||||
check_ntstatus(status as i32)?;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn filter_engine_transaction_begin(
|
||||
filter_engine_handle: HANDLE,
|
||||
flags: u32,
|
||||
) -> Result<(), String> {
|
||||
unsafe {
|
||||
let status = FwpmTransactionBegin0(filter_engine_handle, flags);
|
||||
check_ntstatus(status as i32)?;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn filter_engine_transaction_commit(filter_engine_handle: HANDLE) -> Result<(), String> {
|
||||
unsafe {
|
||||
let status = FwpmTransactionCommit0(filter_engine_handle);
|
||||
check_ntstatus(status as i32)?;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn filter_engine_transaction_abort(filter_engine_handle: HANDLE) -> Result<(), String> {
|
||||
unsafe {
|
||||
let status = FwpmTransactionAbort0(filter_engine_handle);
|
||||
check_ntstatus(status as i32)?;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
1519
windows_kext/wdk/src/filter_engine/layer.rs
Normal file
1519
windows_kext/wdk/src/filter_engine/layer.rs
Normal file
File diff suppressed because it is too large
Load Diff
175
windows_kext/wdk/src/filter_engine/metadata.rs
Normal file
175
windows_kext/wdk/src/filter_engine/metadata.rs
Normal file
@@ -0,0 +1,175 @@
|
||||
use core::{ffi::c_void, ptr::NonNull};
|
||||
|
||||
use alloc::string::String;
|
||||
use widestring::U16CString;
|
||||
use windows_sys::Win32::{
|
||||
Foundation::HANDLE,
|
||||
NetworkManagement::{
|
||||
IpHelper::IP_ADDRESS_PREFIX,
|
||||
WindowsFilteringPlatform::{
|
||||
FWPS_METADATA_FIELD_COMPLETION_HANDLE, FWPS_METADATA_FIELD_PROCESS_ID,
|
||||
FWPS_METADATA_FIELD_PROCESS_PATH, FWPS_METADATA_FIELD_REMOTE_SCOPE_ID,
|
||||
FWPS_METADATA_FIELD_TRANSPORT_CONTROL_DATA,
|
||||
FWPS_METADATA_FIELD_TRANSPORT_ENDPOINT_HANDLE, FWP_BYTE_BLOB, FWP_DIRECTION,
|
||||
},
|
||||
},
|
||||
Networking::WinSock::SCOPE_ID,
|
||||
};
|
||||
|
||||
#[repr(C)]
|
||||
pub(crate) struct FwpsIncomingMetadataValues {
|
||||
/// Bitmask representing which values are set.
|
||||
current_metadata_values: u32,
|
||||
/// Internal flags;
|
||||
flags: u32,
|
||||
/// Reserved for system use.
|
||||
reserved: u64,
|
||||
/// Discard module and reason.
|
||||
discard_metadata: FwpsDiscardMetadata0,
|
||||
/// Flow Handle.
|
||||
flow_handle: u64,
|
||||
/// IP Header size.
|
||||
ip_header_size: u32,
|
||||
/// Transport Header size
|
||||
transport_header_size: u32,
|
||||
/// Process Path.
|
||||
process_path: *const FWP_BYTE_BLOB,
|
||||
/// Token used for authorization.
|
||||
token: u64,
|
||||
/// Process Id.
|
||||
process_id: u64,
|
||||
/// Source and Destination interface indices for discard indications.
|
||||
source_interface_index: u32,
|
||||
destination_interface_index: u32,
|
||||
/// Compartment Id for injection APIs.
|
||||
compartment_id: u32,
|
||||
/// Fragment data for inbound fragments.
|
||||
fragment_metadata: FwpsInboundFragmentMetadata0,
|
||||
/// Path MTU for outbound packets (to enable calculation of fragments).
|
||||
path_mtu: u32,
|
||||
/// Completion handle (required in order to be able to pend at this layer).
|
||||
completion_handle: HANDLE,
|
||||
/// Endpoint handle for use in outbound transport layer injection.
|
||||
transport_endpoint_handle: u64,
|
||||
/// Remote scope id for use in outbound transport layer injection.
|
||||
remote_scope_id: SCOPE_ID,
|
||||
/// Socket control data (and length) for use in outbound transport layer injection.
|
||||
control_data: *const u8,
|
||||
control_data_length: u32,
|
||||
/// Direction for the current packet. Only specified for ALE re-authorization.
|
||||
packet_direction: FWP_DIRECTION,
|
||||
/// Raw IP header (and length) if the packet is sent with IP header from a RAW socket.
|
||||
header_include_header: *mut c_void,
|
||||
header_include_header_length: u32,
|
||||
destination_prefix: IP_ADDRESS_PREFIX,
|
||||
frame_length: u16,
|
||||
parent_endpoint_handle: u64,
|
||||
icmp_id_and_sequence: u32,
|
||||
/// PID of the process that will be accepting the redirected connection
|
||||
local_redirect_target_pid: u64,
|
||||
/// original destination of a redirected connection
|
||||
original_destination: *mut c_void,
|
||||
redirect_records: HANDLE,
|
||||
/// Bitmask representing which L2 values are set.
|
||||
current_l2_metadata_values: u32,
|
||||
/// L2 layer Flags;
|
||||
l2_flags: u32,
|
||||
ethernet_mac_header_size: u32,
|
||||
wifi_operation_mode: u32,
|
||||
padding0: u32,
|
||||
padding1: u16,
|
||||
padding2: u32,
|
||||
v_switch_packet_context: HANDLE,
|
||||
sub_process_tag: *mut c_void,
|
||||
// Reserved for system use.
|
||||
reserved1: u64,
|
||||
}
|
||||
|
||||
impl FwpsIncomingMetadataValues {
|
||||
pub(crate) fn has_field(&self, field: u32) -> bool {
|
||||
self.current_metadata_values & field > 0
|
||||
}
|
||||
|
||||
pub(crate) fn get_process_id(&self) -> Option<u64> {
|
||||
if self.has_field(FWPS_METADATA_FIELD_PROCESS_ID) {
|
||||
return Some(self.process_id);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn get_process_path(&self) -> Option<String> {
|
||||
if self.has_field(FWPS_METADATA_FIELD_PROCESS_PATH) {
|
||||
if let Ok(path16) = U16CString::from_ptr(
|
||||
core::mem::transmute((*self.process_path).data),
|
||||
(*self.process_path).size as usize / 2,
|
||||
) {
|
||||
if let Ok(path) = path16.to_string() {
|
||||
return Some(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub(crate) fn get_completion_handle(&self) -> Option<HANDLE> {
|
||||
if self.has_field(FWPS_METADATA_FIELD_COMPLETION_HANDLE) {
|
||||
return Some(self.completion_handle);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub(crate) fn get_transport_endpoint_handle(&self) -> Option<u64> {
|
||||
if self.has_field(FWPS_METADATA_FIELD_TRANSPORT_ENDPOINT_HANDLE) {
|
||||
return Some(self.transport_endpoint_handle);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub(crate) fn get_remote_scope_id(&self) -> Option<SCOPE_ID> {
|
||||
if self.has_field(FWPS_METADATA_FIELD_REMOTE_SCOPE_ID) {
|
||||
return Some(self.remote_scope_id);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn get_control_data(&self) -> Option<NonNull<[u8]>> {
|
||||
if self.has_field(FWPS_METADATA_FIELD_TRANSPORT_CONTROL_DATA) {
|
||||
if self.control_data.is_null() || self.control_data_length == 0 {
|
||||
return None;
|
||||
}
|
||||
let ptr = NonNull::new(self.control_data as *mut u8).unwrap();
|
||||
let slice = NonNull::slice_from_raw_parts(ptr, self.control_data_length as usize);
|
||||
return Some(slice);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(C)]
|
||||
enum FwpsDiscardModule0 {
|
||||
FwpsDiscardModuleNetwork = 0,
|
||||
FwpsDiscardModuleTransport = 1,
|
||||
FwpsDiscardModuleGeneral = 2,
|
||||
FwpsDiscardModuleMax = 3,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct FwpsDiscardMetadata0 {
|
||||
discard_module: FwpsDiscardModule0,
|
||||
discard_reason: u32,
|
||||
filter_id: u64,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct FwpsInboundFragmentMetadata0 {
|
||||
fragment_identification: u32,
|
||||
fragment_offset: u16,
|
||||
fragment_length: u32,
|
||||
}
|
||||
232
windows_kext/wdk/src/filter_engine/mod.rs
Normal file
232
windows_kext/wdk/src/filter_engine/mod.rs
Normal file
@@ -0,0 +1,232 @@
|
||||
use core::ffi::c_void;
|
||||
|
||||
use crate::alloc::borrow::ToOwned;
|
||||
use crate::driver::Driver;
|
||||
use crate::ffi::FWPS_FILTER2;
|
||||
use crate::filter_engine::transaction::Transaction;
|
||||
use crate::{dbg, info};
|
||||
use alloc::boxed::Box;
|
||||
use alloc::string::String;
|
||||
use alloc::{format, vec::Vec};
|
||||
use windows_sys::Wdk::Foundation::DEVICE_OBJECT;
|
||||
use windows_sys::Win32::Foundation::{HANDLE, INVALID_HANDLE_VALUE};
|
||||
|
||||
use self::callout::{Callout, FilterType};
|
||||
use self::callout_data::CalloutData;
|
||||
use self::classify::ClassifyOut;
|
||||
use self::layer::IncomingValues;
|
||||
use self::metadata::FwpsIncomingMetadataValues;
|
||||
|
||||
pub mod callout;
|
||||
pub mod callout_data;
|
||||
pub(crate) mod classify;
|
||||
#[allow(dead_code)]
|
||||
pub mod ffi;
|
||||
pub mod layer;
|
||||
pub(crate) mod metadata;
|
||||
pub mod net_buffer;
|
||||
pub mod packet;
|
||||
pub mod stream_data;
|
||||
pub mod transaction;
|
||||
// Helper functions for ALE Readirect layers. Not needed for the current implementation.
|
||||
// pub mod connect_request;
|
||||
|
||||
pub struct FilterEngine {
|
||||
device_object: *mut DEVICE_OBJECT,
|
||||
handle: HANDLE,
|
||||
sublayer_guid: u128,
|
||||
committed: bool,
|
||||
callouts: Option<Vec<Box<Callout>>>,
|
||||
}
|
||||
|
||||
impl FilterEngine {
|
||||
pub fn new(driver: &Driver, layer_guid: u128) -> Result<Self, String> {
|
||||
let filter_engine_handle: HANDLE;
|
||||
match ffi::create_filter_engine() {
|
||||
Ok(handle) => {
|
||||
filter_engine_handle = handle;
|
||||
}
|
||||
Err(code) => {
|
||||
return Err(format!("failed to initialize filter engine {}", code).to_owned());
|
||||
}
|
||||
}
|
||||
Ok(Self {
|
||||
device_object: driver.get_device_object(),
|
||||
handle: filter_engine_handle,
|
||||
sublayer_guid: layer_guid,
|
||||
committed: false,
|
||||
callouts: None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn commit(&mut self, callouts: Vec<Callout>) -> Result<(), String> {
|
||||
{
|
||||
// Begin write transaction. This is also a lock guard.
|
||||
let mut filter_engine = match Transaction::begin_write(self) {
|
||||
Ok(transaction) => transaction,
|
||||
Err(err) => {
|
||||
return Err(err);
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(err) = filter_engine.register_sublayer() {
|
||||
return Err(format!("filter_engine: {}", err));
|
||||
}
|
||||
|
||||
dbg!("Callouts count: {}", callouts.len());
|
||||
let mut boxed_callouts = Vec::new();
|
||||
// Register all callouts
|
||||
for callout in callouts {
|
||||
let mut callout = Box::new(callout);
|
||||
callout.address = callout.as_ref() as *const Callout as u64;
|
||||
|
||||
if let Err(err) = callout.register_callout(
|
||||
filter_engine.handle,
|
||||
filter_engine.device_object,
|
||||
catch_all_callout,
|
||||
) {
|
||||
// This will destroy the callout structs.
|
||||
return Err(err);
|
||||
}
|
||||
if let Err(err) =
|
||||
callout.register_filter(filter_engine.handle, filter_engine.sublayer_guid)
|
||||
{
|
||||
// This will destroy the callout structs.
|
||||
return Err(err);
|
||||
}
|
||||
dbg!(
|
||||
"registering callout: {} -> {}",
|
||||
callout.name,
|
||||
callout.filter_id
|
||||
);
|
||||
boxed_callouts.push(callout)
|
||||
}
|
||||
if let Some(callouts) = &mut filter_engine.callouts {
|
||||
callouts.append(&mut boxed_callouts);
|
||||
} else {
|
||||
filter_engine.callouts = Some(boxed_callouts);
|
||||
}
|
||||
|
||||
if let Err(err) = filter_engine.commit() {
|
||||
return Err(err);
|
||||
}
|
||||
}
|
||||
self.committed = true;
|
||||
info!("transaction committed");
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
pub fn reset_all_filters(&mut self) -> Result<(), String> {
|
||||
// Begin to write transaction. This is also a lock guard. It will abort if transaction is not committed.
|
||||
let mut filter_engine = match Transaction::begin_write(self) {
|
||||
Ok(transaction) => transaction,
|
||||
Err(err) => {
|
||||
return Err(err);
|
||||
}
|
||||
};
|
||||
let filter_engine_handle = filter_engine.handle;
|
||||
let sublayer_guid = filter_engine.sublayer_guid;
|
||||
if let Some(callouts) = &mut filter_engine.callouts {
|
||||
for callout in callouts {
|
||||
if let FilterType::Resettable = callout.filter_type {
|
||||
if callout.filter_id != 0 {
|
||||
// Remove old filter.
|
||||
if let Err(err) =
|
||||
ffi::unregister_filter(filter_engine_handle, callout.filter_id)
|
||||
{
|
||||
return Err(format!("filter_engine: {}", err));
|
||||
}
|
||||
callout.filter_id = 0;
|
||||
}
|
||||
// Create new filter.
|
||||
if let Err(err) = callout.register_filter(filter_engine_handle, sublayer_guid) {
|
||||
return Err(format!("filter_engine: {}", err));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Commit transaction.
|
||||
if let Err(err) = filter_engine.commit() {
|
||||
return Err(err);
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
fn register_sublayer(&self) -> Result<(), String> {
|
||||
let result = ffi::register_sublayer(
|
||||
self.handle,
|
||||
"PortmasterSublayer",
|
||||
"The Portmaster sublayer holds all it's filters.",
|
||||
self.sublayer_guid,
|
||||
);
|
||||
if let Err(code) = result {
|
||||
return Err(format!("failed to register sublayer: {}", code));
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for FilterEngine {
|
||||
fn drop(&mut self) {
|
||||
dbg!("Unregistering callouts");
|
||||
if let Some(callouts) = &self.callouts {
|
||||
for callout in callouts {
|
||||
if callout.registered {
|
||||
if let Err(code) = ffi::unregister_callout(callout.id) {
|
||||
dbg!("failed to unregister callout: {}", code);
|
||||
}
|
||||
if callout.filter_id != 0 {
|
||||
if let Err(code) = ffi::unregister_filter(self.handle, callout.filter_id) {
|
||||
dbg!("failed to unregister filter: {}", code)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if self.committed {
|
||||
if let Err(code) = ffi::unregister_sublayer(self.handle, self.sublayer_guid) {
|
||||
dbg!("Failed to unregister sublayer: {}", code);
|
||||
}
|
||||
}
|
||||
|
||||
if self.handle != 0 && self.handle != INVALID_HANDLE_VALUE {
|
||||
_ = ffi::filter_engine_close(self.handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn catch_all_callout(
|
||||
fixed_values: *const IncomingValues,
|
||||
meta_values: *const FwpsIncomingMetadataValues,
|
||||
layer_data: *mut c_void,
|
||||
_context: *mut c_void,
|
||||
filter: *const FWPS_FILTER2,
|
||||
_flow_context: u64,
|
||||
classify_out: *mut ClassifyOut,
|
||||
) {
|
||||
let filter = &(*filter);
|
||||
// Filter context is the address of the callout.
|
||||
let callout = filter.context as *mut Callout;
|
||||
|
||||
if let Some(callout) = callout.as_ref() {
|
||||
// Setup callout data.
|
||||
let array = core::slice::from_raw_parts(
|
||||
(*fixed_values).incoming_value_array,
|
||||
(*fixed_values).value_count as usize,
|
||||
);
|
||||
let data = CalloutData {
|
||||
layer: callout.layer,
|
||||
callout_id: filter.context as usize,
|
||||
values: array,
|
||||
metadata: meta_values,
|
||||
classify_out,
|
||||
layer_data,
|
||||
};
|
||||
// Call the defined function.
|
||||
(callout.callout_fn)(data);
|
||||
}
|
||||
}
|
||||
355
windows_kext/wdk/src/filter_engine/net_buffer.rs
Normal file
355
windows_kext/wdk/src/filter_engine/net_buffer.rs
Normal file
@@ -0,0 +1,355 @@
|
||||
use core::mem::MaybeUninit;
|
||||
|
||||
use alloc::{
|
||||
string::{String, ToString},
|
||||
vec::Vec,
|
||||
};
|
||||
use windows_sys::Wdk::System::SystemServices::{
|
||||
IoAllocateMdl, IoFreeMdl, MmBuildMdlForNonPagedPool,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
allocator::POOL_TAG,
|
||||
ffi::{
|
||||
FwpsAllocateNetBufferAndNetBufferList0, FwpsFreeNetBufferList0,
|
||||
NdisAdvanceNetBufferDataStart, NdisAllocateNetBufferListPool, NdisFreeNetBufferListPool,
|
||||
NdisGetDataBuffer, NdisRetreatNetBufferDataStart, NDIS_HANDLE, NDIS_OBJECT_TYPE_DEFAULT,
|
||||
NET_BUFFER_LIST, NET_BUFFER_LIST_POOL_PARAMETERS,
|
||||
NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1,
|
||||
},
|
||||
utils::check_ntstatus,
|
||||
};
|
||||
|
||||
pub struct NetBufferList {
|
||||
pub(crate) nbl: *mut NET_BUFFER_LIST,
|
||||
data: Option<Vec<u8>>,
|
||||
advance_on_drop: Option<u32>,
|
||||
}
|
||||
|
||||
impl NetBufferList {
|
||||
pub fn new(nbl: *mut NET_BUFFER_LIST) -> NetBufferList {
|
||||
NetBufferList {
|
||||
nbl,
|
||||
data: None,
|
||||
advance_on_drop: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> NetBufferListIter {
|
||||
NetBufferListIter(self.nbl)
|
||||
}
|
||||
|
||||
pub fn read_bytes(&self, buffer: &mut [u8]) -> Result<(), ()> {
|
||||
unsafe {
|
||||
let Some(nbl) = self.nbl.as_ref() else {
|
||||
return Err(());
|
||||
};
|
||||
let nb = nbl.Header.first_net_buffer;
|
||||
if let Some(nb) = nb.as_ref() {
|
||||
let data_length = nb.nbSize.DataLength;
|
||||
if data_length == 0 {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
if buffer.len() > data_length as usize {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
let mut ptr =
|
||||
NdisGetDataBuffer(nb, buffer.len() as u32, core::ptr::null_mut(), 1, 0);
|
||||
if !ptr.is_null() {
|
||||
buffer.copy_from_slice(core::slice::from_raw_parts(ptr, buffer.len()));
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
ptr = NdisGetDataBuffer(nb, buffer.len() as u32, buffer.as_mut_ptr(), 1, 0);
|
||||
if !ptr.is_null() {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
return Err(());
|
||||
}
|
||||
|
||||
pub fn clone(&self, net_allocator: &NetworkAllocator) -> Result<NetBufferList, String> {
|
||||
unsafe {
|
||||
let Some(nbl) = self.nbl.as_ref() else {
|
||||
return Err("net buffer list is null".to_string());
|
||||
};
|
||||
|
||||
let nb = nbl.Header.first_net_buffer;
|
||||
if let Some(nb) = nb.as_ref() {
|
||||
let data_length = nb.nbSize.DataLength;
|
||||
if data_length == 0 {
|
||||
return Err("can't clone empty packet".to_string());
|
||||
}
|
||||
|
||||
// Allocate space in buffer, if buffer is too small.
|
||||
let mut buffer = alloc::vec![0 as u8; data_length as usize];
|
||||
|
||||
let ptr = NdisGetDataBuffer(nb, data_length, buffer.as_mut_ptr(), 1, 0);
|
||||
|
||||
if !ptr.is_null() {
|
||||
buffer.copy_from_slice(core::slice::from_raw_parts(ptr, data_length as usize));
|
||||
} else {
|
||||
let ptr = NdisGetDataBuffer(nb, data_length, buffer.as_mut_ptr(), 1, 0);
|
||||
if ptr.is_null() {
|
||||
return Err("failed to copy packet buffer".to_string());
|
||||
}
|
||||
}
|
||||
|
||||
let new_nbl = net_allocator.wrap_packet_in_nbl(&buffer)?;
|
||||
|
||||
return Ok(NetBufferList {
|
||||
nbl: new_nbl,
|
||||
data: Some(buffer),
|
||||
advance_on_drop: None,
|
||||
});
|
||||
} else {
|
||||
return Err("net buffer is null".to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_data_mut(&mut self) -> Option<&mut [u8]> {
|
||||
if let Some(data) = &mut self.data {
|
||||
return Some(data.as_mut_slice());
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
pub fn get_data(&self) -> Option<&[u8]> {
|
||||
if let Some(data) = &self.data {
|
||||
return Some(data.as_slice());
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
pub fn get_data_length(&self) -> u32 {
|
||||
unsafe {
|
||||
if let Some(nbl) = self.nbl.as_ref() {
|
||||
let mut nb = nbl.Header.first_net_buffer;
|
||||
let mut data_length = 0;
|
||||
while !nb.is_null() {
|
||||
let mut next = core::ptr::null_mut();
|
||||
if let Some(nb) = nb.as_ref() {
|
||||
data_length += nb.nbSize.DataLength;
|
||||
next = nb.Next;
|
||||
}
|
||||
nb = next;
|
||||
}
|
||||
|
||||
data_length
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Retreats the mnl of the buffer. Does not auto advance multiple retreats.
|
||||
pub fn retreat(&mut self, size: u32, auto_advance: bool) {
|
||||
unsafe {
|
||||
if let Some(nbl) = self.nbl.as_mut() {
|
||||
if let Some(nb) = nbl.Header.first_net_buffer.as_mut() {
|
||||
NdisRetreatNetBufferDataStart(nb as _, size, 0, core::ptr::null_mut());
|
||||
if auto_advance {
|
||||
self.advance_on_drop = Some(size);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Advances the MDL of the buffer.
|
||||
pub fn advance(&self, size: u32) {
|
||||
unsafe {
|
||||
if let Some(nbl) = self.nbl.as_mut() {
|
||||
if let Some(nb) = nbl.Header.first_net_buffer.as_mut() {
|
||||
NdisAdvanceNetBufferDataStart(nb as _, size, false, core::ptr::null_mut());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for NetBufferList {
|
||||
fn drop(&mut self) {
|
||||
if let Some(advance_amount) = self.advance_on_drop {
|
||||
self.advance(advance_amount);
|
||||
}
|
||||
if self.data.is_some() {
|
||||
NetworkAllocator::free_net_buffer(self.nbl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NetBufferListIter(*mut NET_BUFFER_LIST);
|
||||
|
||||
impl NetBufferListIter {
|
||||
pub fn new(nbl: *mut NET_BUFFER_LIST) -> Self {
|
||||
Self(nbl)
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for NetBufferListIter {
|
||||
type Item = NetBufferList;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
unsafe {
|
||||
if let Some(nbl) = self.0.as_mut() {
|
||||
self.0 = nbl.Header.next as _;
|
||||
return Some(NetBufferList {
|
||||
nbl,
|
||||
data: None,
|
||||
advance_on_drop: None,
|
||||
});
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_packet_partial<'a>(nbl: *mut NET_BUFFER_LIST, buffer: &'a mut [u8]) -> Result<(), ()> {
|
||||
unsafe {
|
||||
let Some(nbl) = nbl.as_ref() else {
|
||||
return Err(());
|
||||
};
|
||||
let nb = nbl.Header.first_net_buffer;
|
||||
if let Some(nb) = nb.as_ref() {
|
||||
let data_length = nb.nbSize.DataLength;
|
||||
if data_length == 0 {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
if buffer.len() > data_length as usize {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
let ptr = NdisGetDataBuffer(nb, buffer.len() as u32, buffer.as_mut_ptr(), 1, 0);
|
||||
if !ptr.is_null() {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
return Err(());
|
||||
}
|
||||
|
||||
pub struct RetreatGuard {
|
||||
size: u32,
|
||||
nbl: *mut NET_BUFFER_LIST,
|
||||
}
|
||||
|
||||
impl Drop for RetreatGuard {
|
||||
fn drop(&mut self) {
|
||||
NetworkAllocator::advance_net_buffer(self.nbl, self.size);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NetworkAllocator {
|
||||
pool_handle: NDIS_HANDLE,
|
||||
}
|
||||
|
||||
impl NetworkAllocator {
|
||||
pub fn new() -> Self {
|
||||
unsafe {
|
||||
let mut params: NET_BUFFER_LIST_POOL_PARAMETERS = MaybeUninit::zeroed().assume_init();
|
||||
params.Header.Type = NDIS_OBJECT_TYPE_DEFAULT;
|
||||
params.Header.Revision = NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1;
|
||||
params.Header.Size = core::mem::size_of::<NET_BUFFER_LIST_POOL_PARAMETERS>() as u16;
|
||||
params.fAllocateNetBuffer = true;
|
||||
params.PoolTag = POOL_TAG;
|
||||
params.DataSize = 0;
|
||||
|
||||
let pool_handle = NdisAllocateNetBufferListPool(core::ptr::null_mut(), ¶ms);
|
||||
Self { pool_handle }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn wrap_packet_in_nbl(&self, packet_data: &[u8]) -> Result<*mut NET_BUFFER_LIST, String> {
|
||||
if self.pool_handle.is_null() {
|
||||
return Err("allocator not initialized".to_string());
|
||||
}
|
||||
unsafe {
|
||||
// Create MDL struct that will hold the buffer.
|
||||
let mdl = IoAllocateMdl(
|
||||
packet_data.as_ptr() as _,
|
||||
packet_data.len() as u32,
|
||||
0,
|
||||
0,
|
||||
core::ptr::null_mut(),
|
||||
);
|
||||
if mdl.is_null() {
|
||||
return Err("failed to allocate mdl".to_string());
|
||||
}
|
||||
|
||||
// Build mdl with packet_data buffer.
|
||||
MmBuildMdlForNonPagedPool(mdl);
|
||||
|
||||
// Initialize NBL structure.
|
||||
let mut nbl = core::ptr::null_mut();
|
||||
let status = FwpsAllocateNetBufferAndNetBufferList0(
|
||||
self.pool_handle,
|
||||
0,
|
||||
0,
|
||||
mdl,
|
||||
0,
|
||||
packet_data.len() as u64,
|
||||
&mut nbl,
|
||||
);
|
||||
if let Err(err) = check_ntstatus(status) {
|
||||
IoFreeMdl(mdl);
|
||||
return Err(err);
|
||||
}
|
||||
return Ok(nbl);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn free_net_buffer(nbl: *mut NET_BUFFER_LIST) {
|
||||
NetBufferListIter::new(nbl).for_each(|nbl| unsafe {
|
||||
if let Some(nbl) = nbl.nbl.as_mut() {
|
||||
if let Some(nb) = nbl.Header.first_net_buffer.as_mut() {
|
||||
IoFreeMdl(nb.MdlChain);
|
||||
}
|
||||
FwpsFreeNetBufferList0(nbl);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn retreat_net_buffer(
|
||||
nbl: *mut NET_BUFFER_LIST,
|
||||
size: u32,
|
||||
auto_advance: bool,
|
||||
) -> Option<RetreatGuard> {
|
||||
unsafe {
|
||||
if let Some(nbl) = nbl.as_mut() {
|
||||
if let Some(nb) = nbl.Header.first_net_buffer.as_mut() {
|
||||
NdisRetreatNetBufferDataStart(nb as _, size, 0, core::ptr::null_mut());
|
||||
if auto_advance {
|
||||
return Some(RetreatGuard { size, nbl });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return None;
|
||||
}
|
||||
pub fn advance_net_buffer(nbl: *mut NET_BUFFER_LIST, size: u32) {
|
||||
unsafe {
|
||||
if let Some(nbl) = nbl.as_mut() {
|
||||
if let Some(nb) = nbl.Header.first_net_buffer.as_mut() {
|
||||
NdisAdvanceNetBufferDataStart(nb as _, size, false, core::ptr::null_mut());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for NetworkAllocator {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
if !self.pool_handle.is_null() {
|
||||
NdisFreeNetBufferListPool(self.pool_handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
344
windows_kext/wdk/src/filter_engine/packet.rs
Normal file
344
windows_kext/wdk/src/filter_engine/packet.rs
Normal file
@@ -0,0 +1,344 @@
|
||||
use alloc::{
|
||||
boxed::Box,
|
||||
string::{String, ToString},
|
||||
};
|
||||
use core::{ffi::c_void, mem::MaybeUninit, ptr::NonNull};
|
||||
use windows_sys::Win32::{
|
||||
Foundation::{HANDLE, INVALID_HANDLE_VALUE},
|
||||
Networking::WinSock::{AF_INET, AF_INET6, AF_UNSPEC, SCOPE_ID},
|
||||
System::Kernel::UNSPECIFIED_COMPARTMENT_ID,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
ffi::{
|
||||
FwpsInjectNetworkReceiveAsync0, FwpsInjectNetworkSendAsync0,
|
||||
FwpsInjectTransportReceiveAsync0, FwpsInjectTransportSendAsync1,
|
||||
FwpsInjectionHandleCreate0, FwpsInjectionHandleDestroy0, FwpsQueryPacketInjectionState0,
|
||||
FWPS_INJECTION_TYPE_NETWORK, FWPS_INJECTION_TYPE_TRANSPORT, FWPS_PACKET_INJECTION_STATE,
|
||||
FWPS_TRANSPORT_SEND_PARAMS1, NET_BUFFER_LIST,
|
||||
},
|
||||
utils::check_ntstatus,
|
||||
};
|
||||
|
||||
use super::{callout_data::CalloutData, net_buffer::NetBufferList};
|
||||
|
||||
pub struct TransportPacketList {
|
||||
ipv6: bool,
|
||||
pub net_buffer_list_queue: NetBufferList,
|
||||
remote_ip: [u8; 16],
|
||||
endpoint_handle: u64,
|
||||
remote_scope_id: SCOPE_ID,
|
||||
control_data: Option<NonNull<[u8]>>,
|
||||
inbound: bool,
|
||||
interface_index: u32,
|
||||
sub_interface_index: u32,
|
||||
}
|
||||
|
||||
pub struct InjectInfo {
|
||||
pub ipv6: bool,
|
||||
pub inbound: bool,
|
||||
pub loopback: bool,
|
||||
pub interface_index: u32,
|
||||
pub sub_interface_index: u32,
|
||||
}
|
||||
|
||||
pub struct Injector {
|
||||
transport_inject_handle: HANDLE,
|
||||
packet_inject_handle_v4: HANDLE,
|
||||
packet_inject_handle_v6: HANDLE,
|
||||
}
|
||||
|
||||
// TODO: Implement custom allocator for the packet buffers for reusing memory and reducing allocations. This should improve latency.
|
||||
impl Injector {
|
||||
pub fn new() -> Self {
|
||||
let mut transport_inject_handle: HANDLE = INVALID_HANDLE_VALUE;
|
||||
let mut packet_inject_handle_v4: HANDLE = INVALID_HANDLE_VALUE;
|
||||
let mut packet_inject_handle_v6: HANDLE = INVALID_HANDLE_VALUE;
|
||||
unsafe {
|
||||
let status = FwpsInjectionHandleCreate0(
|
||||
AF_UNSPEC,
|
||||
FWPS_INJECTION_TYPE_TRANSPORT,
|
||||
&mut transport_inject_handle,
|
||||
);
|
||||
if let Err(err) = check_ntstatus(status) {
|
||||
crate::err!("error allocating transport inject handle: {}", err);
|
||||
}
|
||||
let status = FwpsInjectionHandleCreate0(
|
||||
AF_INET,
|
||||
FWPS_INJECTION_TYPE_NETWORK,
|
||||
&mut packet_inject_handle_v4,
|
||||
);
|
||||
|
||||
if let Err(err) = check_ntstatus(status) {
|
||||
crate::err!("error allocating network inject handle: {}", err);
|
||||
}
|
||||
let status = FwpsInjectionHandleCreate0(
|
||||
AF_INET6,
|
||||
FWPS_INJECTION_TYPE_NETWORK,
|
||||
&mut packet_inject_handle_v6,
|
||||
);
|
||||
|
||||
if let Err(err) = check_ntstatus(status) {
|
||||
crate::err!("error allocating network inject handle: {}", err);
|
||||
}
|
||||
}
|
||||
Self {
|
||||
transport_inject_handle,
|
||||
packet_inject_handle_v4,
|
||||
packet_inject_handle_v6,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: pick a better name
|
||||
pub fn from_ale_callout(
|
||||
ipv6: bool,
|
||||
callout_data: &CalloutData,
|
||||
net_buffer_list: NetBufferList,
|
||||
remote_ip_slice: &[u8],
|
||||
inbound: bool,
|
||||
interface_index: u32,
|
||||
sub_interface_index: u32,
|
||||
) -> TransportPacketList {
|
||||
let mut control_data = None;
|
||||
if let Some(cd) = callout_data.get_control_data() {
|
||||
control_data = Some(cd);
|
||||
}
|
||||
let mut remote_ip: [u8; 16] = [0; 16];
|
||||
if ipv6 {
|
||||
remote_ip[0..16].copy_from_slice(&remote_ip_slice);
|
||||
} else {
|
||||
remote_ip[0..4].copy_from_slice(&remote_ip_slice);
|
||||
}
|
||||
|
||||
TransportPacketList {
|
||||
ipv6,
|
||||
net_buffer_list_queue: net_buffer_list,
|
||||
remote_ip,
|
||||
endpoint_handle: callout_data.get_transport_endpoint_handle().unwrap_or(0),
|
||||
remote_scope_id: callout_data
|
||||
.get_remote_scope_id()
|
||||
.unwrap_or(unsafe { MaybeUninit::zeroed().assume_init() }),
|
||||
control_data,
|
||||
inbound,
|
||||
interface_index,
|
||||
sub_interface_index,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: pick a better name. This is not transport
|
||||
pub fn inject_packet_list_transport(
|
||||
&self,
|
||||
packet_list: TransportPacketList,
|
||||
) -> Result<(), String> {
|
||||
if self.transport_inject_handle == INVALID_HANDLE_VALUE {
|
||||
return Err("failed to inject packet: invalid handle value".to_string());
|
||||
}
|
||||
unsafe {
|
||||
let mut control_data_length = 0;
|
||||
let control_data = match &packet_list.control_data {
|
||||
Some(cd) => {
|
||||
control_data_length = cd.len();
|
||||
cd.as_ptr().cast()
|
||||
}
|
||||
None => core::ptr::null_mut(),
|
||||
};
|
||||
|
||||
let mut send_params = FWPS_TRANSPORT_SEND_PARAMS1 {
|
||||
remote_address: &packet_list.remote_ip as _,
|
||||
remote_scope_id: packet_list.remote_scope_id,
|
||||
control_data: control_data as _,
|
||||
control_data_length: control_data_length as u32,
|
||||
header_include_header: core::ptr::null_mut(),
|
||||
header_include_header_length: 0,
|
||||
};
|
||||
let address_family = if packet_list.ipv6 { AF_INET6 } else { AF_INET };
|
||||
|
||||
let net_buffer_list = packet_list.net_buffer_list_queue;
|
||||
// Escape the stack. Packet buffer should be valid until the packet is injected.
|
||||
let boxed_nbl = Box::new(net_buffer_list);
|
||||
let raw_nbl = boxed_nbl.nbl;
|
||||
let raw_ptr = Box::into_raw(boxed_nbl);
|
||||
|
||||
// Inject
|
||||
let status = if packet_list.inbound {
|
||||
FwpsInjectTransportReceiveAsync0(
|
||||
self.transport_inject_handle,
|
||||
0,
|
||||
core::ptr::null_mut(),
|
||||
0,
|
||||
address_family,
|
||||
UNSPECIFIED_COMPARTMENT_ID,
|
||||
packet_list.interface_index,
|
||||
packet_list.sub_interface_index,
|
||||
raw_nbl,
|
||||
free_packet,
|
||||
raw_ptr as _,
|
||||
)
|
||||
} else {
|
||||
FwpsInjectTransportSendAsync1(
|
||||
self.transport_inject_handle,
|
||||
0,
|
||||
packet_list.endpoint_handle,
|
||||
0,
|
||||
&mut send_params,
|
||||
address_family,
|
||||
UNSPECIFIED_COMPARTMENT_ID,
|
||||
raw_nbl,
|
||||
free_packet,
|
||||
raw_ptr as _,
|
||||
)
|
||||
};
|
||||
// Check for success
|
||||
if let Err(err) = check_ntstatus(status) {
|
||||
_ = Box::from_raw(raw_ptr);
|
||||
return Err(err);
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
pub fn inject_net_buffer_list(
|
||||
&self,
|
||||
net_buffer_list: NetBufferList,
|
||||
inject_info: InjectInfo,
|
||||
) -> Result<(), String> {
|
||||
if self.packet_inject_handle_v4 == INVALID_HANDLE_VALUE {
|
||||
return Err("failed to inject packet: invalid handle value".to_string());
|
||||
}
|
||||
// Escape the stack, so the data can be freed after inject is complete.
|
||||
let packet_boxed = Box::new(net_buffer_list);
|
||||
let nbl = packet_boxed.nbl;
|
||||
let packet_pointer = Box::into_raw(packet_boxed);
|
||||
|
||||
let inject_handle = if inject_info.ipv6 {
|
||||
self.packet_inject_handle_v6
|
||||
} else {
|
||||
self.packet_inject_handle_v4
|
||||
};
|
||||
|
||||
let status = if inject_info.inbound && !inject_info.loopback {
|
||||
// Inject inbound.
|
||||
unsafe {
|
||||
FwpsInjectNetworkReceiveAsync0(
|
||||
inject_handle,
|
||||
0,
|
||||
0,
|
||||
UNSPECIFIED_COMPARTMENT_ID,
|
||||
inject_info.interface_index,
|
||||
inject_info.sub_interface_index,
|
||||
nbl,
|
||||
free_packet,
|
||||
(packet_pointer as *mut NetBufferList) as _,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
// Inject outbound.
|
||||
unsafe {
|
||||
FwpsInjectNetworkSendAsync0(
|
||||
inject_handle,
|
||||
0,
|
||||
0,
|
||||
UNSPECIFIED_COMPARTMENT_ID,
|
||||
nbl,
|
||||
free_packet,
|
||||
(packet_pointer as *mut NetBufferList) as _,
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
// Check for error.
|
||||
if let Err(err) = check_ntstatus(status) {
|
||||
unsafe {
|
||||
// Get back ownership for data.
|
||||
_ = Box::from_raw(packet_pointer);
|
||||
}
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
pub fn was_network_packet_injected_by_self(
|
||||
&self,
|
||||
nbl: *const NET_BUFFER_LIST,
|
||||
ipv6: bool,
|
||||
) -> bool {
|
||||
let inject_handle = if ipv6 {
|
||||
self.packet_inject_handle_v6
|
||||
} else {
|
||||
self.packet_inject_handle_v4
|
||||
};
|
||||
if inject_handle == INVALID_HANDLE_VALUE || inject_handle == 0 {
|
||||
return false;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let state = FwpsQueryPacketInjectionState0(inject_handle, nbl, core::ptr::null_mut());
|
||||
|
||||
match state {
|
||||
FWPS_PACKET_INJECTION_STATE::FWPS_PACKET_NOT_INJECTED => false,
|
||||
FWPS_PACKET_INJECTION_STATE::FWPS_PACKET_INJECTED_BY_SELF => true,
|
||||
FWPS_PACKET_INJECTION_STATE::FWPS_PACKET_INJECTED_BY_OTHER => false,
|
||||
FWPS_PACKET_INJECTION_STATE::FWPS_PACKET_PREVIOUSLY_INJECTED_BY_SELF => true,
|
||||
FWPS_PACKET_INJECTION_STATE::FWPS_PACKET_INJECTION_STATE_MAX => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn was_network_packet_injected_by_self_ale(&self, nbl: *const NET_BUFFER_LIST) -> bool {
|
||||
unsafe {
|
||||
let state = FwpsQueryPacketInjectionState0(
|
||||
self.transport_inject_handle,
|
||||
nbl,
|
||||
core::ptr::null_mut(),
|
||||
);
|
||||
|
||||
match state {
|
||||
FWPS_PACKET_INJECTION_STATE::FWPS_PACKET_NOT_INJECTED => false,
|
||||
FWPS_PACKET_INJECTION_STATE::FWPS_PACKET_INJECTED_BY_SELF => true,
|
||||
FWPS_PACKET_INJECTION_STATE::FWPS_PACKET_INJECTED_BY_OTHER => false,
|
||||
FWPS_PACKET_INJECTION_STATE::FWPS_PACKET_PREVIOUSLY_INJECTED_BY_SELF => true,
|
||||
FWPS_PACKET_INJECTION_STATE::FWPS_PACKET_INJECTION_STATE_MAX => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Injector {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
if self.transport_inject_handle != INVALID_HANDLE_VALUE
|
||||
&& self.transport_inject_handle != 0
|
||||
{
|
||||
FwpsInjectionHandleDestroy0(self.transport_inject_handle);
|
||||
self.transport_inject_handle = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
if self.packet_inject_handle_v4 != INVALID_HANDLE_VALUE
|
||||
&& self.packet_inject_handle_v4 != 0
|
||||
{
|
||||
FwpsInjectionHandleDestroy0(self.packet_inject_handle_v4);
|
||||
self.packet_inject_handle_v4 = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
if self.packet_inject_handle_v6 != INVALID_HANDLE_VALUE
|
||||
&& self.packet_inject_handle_v6 != 0
|
||||
{
|
||||
FwpsInjectionHandleDestroy0(self.packet_inject_handle_v6);
|
||||
self.packet_inject_handle_v6 = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn free_packet(
|
||||
context: *mut c_void,
|
||||
net_buffer_list: *mut NET_BUFFER_LIST,
|
||||
_dispatch_level: bool,
|
||||
) {
|
||||
if let Some(nbl) = net_buffer_list.as_ref() {
|
||||
if let Err(err) = check_ntstatus(nbl.Status) {
|
||||
crate::err!("inject status: {}", err);
|
||||
}
|
||||
}
|
||||
_ = Box::from_raw(context as *mut NetBufferList);
|
||||
}
|
||||
67
windows_kext/wdk/src/filter_engine/stream_data.rs
Normal file
67
windows_kext/wdk/src/filter_engine/stream_data.rs
Normal file
@@ -0,0 +1,67 @@
|
||||
use crate::ffi::{NET_BUFFER, NET_BUFFER_LIST};
|
||||
use windows_sys::Wdk::Foundation::MDL;
|
||||
|
||||
const FWPS_STREAM_FLAG_RECEIVE: u32 = 0x00000001;
|
||||
|
||||
#[repr(C)]
|
||||
pub enum StreamActionType {
|
||||
None,
|
||||
NeedMoreData,
|
||||
DropConnection,
|
||||
Defer,
|
||||
AllowConnection,
|
||||
TypeMax,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct StreamCalloutIoPacket {
|
||||
stream_data: *mut StreamData,
|
||||
missed_bytes: usize,
|
||||
count_bytes_required: usize,
|
||||
count_bytes_enforced: usize,
|
||||
stream_action: StreamActionType,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct StreamDataOffset {
|
||||
// NET_BUFFER_LIST in which offset lies.
|
||||
net_buffer_list: *mut NET_BUFFER_LIST,
|
||||
// NET_BUFFER in which offset lies.
|
||||
net_buffer: *mut NET_BUFFER,
|
||||
// MDL in which offset lies.
|
||||
mdl: *mut MDL,
|
||||
// Byte offset from the beginning of the MDL in which data lies.
|
||||
mdl_offset: u32,
|
||||
// Offset relative to the DataOffset of the NET_BUFFER.
|
||||
net_buffer_offset: u32,
|
||||
// Offset from the beginning of the entire stream buffer.
|
||||
stream_data_offset: usize,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct StreamData {
|
||||
flags: u32,
|
||||
data_offset: StreamDataOffset,
|
||||
data_length: usize,
|
||||
net_buffer_list_chain: *mut NET_BUFFER_LIST,
|
||||
}
|
||||
|
||||
impl StreamCalloutIoPacket {
|
||||
pub fn get_data_len(&self) -> usize {
|
||||
unsafe {
|
||||
if let Some(stream_data) = self.stream_data.as_ref() {
|
||||
return stream_data.data_length;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
pub fn is_receive(&self) -> bool {
|
||||
unsafe {
|
||||
if let Some(stream_data) = self.stream_data.as_ref() {
|
||||
return stream_data.flags & FWPS_STREAM_FLAG_RECEIVE > 0;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
74
windows_kext/wdk/src/filter_engine/transaction.rs
Normal file
74
windows_kext/wdk/src/filter_engine/transaction.rs
Normal file
@@ -0,0 +1,74 @@
|
||||
use core::ops::{Deref, DerefMut};
|
||||
|
||||
use super::{ffi, FilterEngine};
|
||||
use alloc::{format, string::String};
|
||||
use windows_sys::Win32::NetworkManagement::WindowsFilteringPlatform::FWPM_TXN_READ_ONLY;
|
||||
|
||||
/// Transaction guard for Filter Engine. Internally useses a lock. DO NOT USE WITH OTHER LOCKS.
|
||||
pub(super) struct Transaction<'a> {
|
||||
filter_engine: &'a mut FilterEngine,
|
||||
committed: bool,
|
||||
}
|
||||
|
||||
impl<'a> Transaction<'a> {
|
||||
fn begin(filter_engine: &'a mut FilterEngine, flags: u32) -> Result<Self, String> {
|
||||
if let Err(code) = ffi::filter_engine_transaction_begin(filter_engine.handle, flags) {
|
||||
return Err(format!(
|
||||
"filter-engine: failed to begin transaction: {}",
|
||||
code
|
||||
));
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
filter_engine,
|
||||
committed: false,
|
||||
})
|
||||
}
|
||||
|
||||
/// Creates a read only guard for filter engine transaction.
|
||||
#[allow(dead_code)]
|
||||
pub(super) fn begin_read(filter_engine: &'a mut FilterEngine) -> Result<Self, String> {
|
||||
return Self::begin(filter_engine, FWPM_TXN_READ_ONLY);
|
||||
}
|
||||
|
||||
/// Creates a read/write guard for filter engine transaction.
|
||||
pub(super) fn begin_write(filter_engine: &'a mut FilterEngine) -> Result<Self, String> {
|
||||
return Self::begin(filter_engine, 0);
|
||||
}
|
||||
|
||||
/// Applying all the changes and releases the lock.
|
||||
pub(super) fn commit(&mut self) -> Result<(), String> {
|
||||
if let Err(code) = ffi::filter_engine_transaction_commit(self.filter_engine.handle) {
|
||||
return Err(format!(
|
||||
"filter-engine: failed to commit transaction: {}",
|
||||
code
|
||||
));
|
||||
}
|
||||
self.committed = true;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Deref for Transaction<'a> {
|
||||
type Target = FilterEngine;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.filter_engine
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> DerefMut for Transaction<'a> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.filter_engine
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Drop for Transaction<'a> {
|
||||
/// Releases the lock of transaction was not committed.
|
||||
fn drop(&mut self) {
|
||||
if !self.committed {
|
||||
_ = ffi::filter_engine_transaction_abort(self.filter_engine.handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
100
windows_kext/wdk/src/interface.rs
Normal file
100
windows_kext/wdk/src/interface.rs
Normal file
@@ -0,0 +1,100 @@
|
||||
use crate::{
|
||||
alloc::borrow::ToOwned,
|
||||
driver::Driver,
|
||||
ffi::{
|
||||
pm_GetDeviceObject, pm_InitDriverObject, pm_WdfObjectGetTypedContextWorker,
|
||||
WdfObjectAttributes, WdfObjectContextTypeInfo,
|
||||
},
|
||||
utils::check_ntstatus,
|
||||
};
|
||||
use alloc::ffi::CString;
|
||||
use alloc::format;
|
||||
use alloc::string::String;
|
||||
use widestring::U16CString;
|
||||
use windows_sys::{
|
||||
Wdk::{
|
||||
Foundation::{DEVICE_OBJECT, DRIVER_OBJECT},
|
||||
System::SystemServices::DbgPrint,
|
||||
},
|
||||
Win32::Foundation::{HANDLE, INVALID_HANDLE_VALUE, UNICODE_STRING},
|
||||
};
|
||||
|
||||
// Debug
|
||||
pub fn dbg_print(str: String) {
|
||||
if let Ok(c_str) = CString::new(str) {
|
||||
unsafe {
|
||||
DbgPrint(c_str.as_ptr() as _);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init_driver_object(
|
||||
driver_object: *mut DRIVER_OBJECT,
|
||||
registry_path: *mut UNICODE_STRING,
|
||||
driver_name: &str,
|
||||
object_attributes: *mut WdfObjectAttributes,
|
||||
) -> Result<Driver, String> {
|
||||
let win_driver_path = format!("\\Device\\{}", driver_name);
|
||||
let dos_driver_path = format!("\\??\\{}", driver_name);
|
||||
|
||||
let mut wdf_driver_handle = INVALID_HANDLE_VALUE;
|
||||
let mut wdf_device_handle = INVALID_HANDLE_VALUE;
|
||||
|
||||
let Ok(win_driver) = U16CString::from_str(win_driver_path) else {
|
||||
return Err("Invalid argument win_driver_path".to_owned());
|
||||
};
|
||||
let Ok(dos_driver) = U16CString::from_str(dos_driver_path) else {
|
||||
return Err("Invalid argument dos_driver_path".to_owned());
|
||||
};
|
||||
|
||||
unsafe {
|
||||
let status = pm_InitDriverObject(
|
||||
driver_object,
|
||||
registry_path,
|
||||
&mut wdf_driver_handle,
|
||||
&mut wdf_device_handle,
|
||||
win_driver.as_ptr(),
|
||||
dos_driver.as_ptr(),
|
||||
object_attributes,
|
||||
empty_wdf_driver_unload,
|
||||
);
|
||||
|
||||
check_ntstatus(status)?;
|
||||
|
||||
return Ok(Driver::new(
|
||||
driver_object,
|
||||
wdf_driver_handle,
|
||||
wdf_device_handle,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_device_context_from_wdf_device<T>(
|
||||
wdf_device: HANDLE,
|
||||
type_info: &'static WdfObjectContextTypeInfo,
|
||||
) -> *mut T {
|
||||
unsafe {
|
||||
return core::mem::transmute(pm_WdfObjectGetTypedContextWorker(wdf_device, type_info));
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn wdf_device_wdm_get_device_object(wdf_device: HANDLE) -> *mut DEVICE_OBJECT {
|
||||
unsafe {
|
||||
return pm_GetDeviceObject(wdf_device);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_device_context_from_device_object<'a, T>(
|
||||
device_object: &mut DEVICE_OBJECT,
|
||||
) -> Result<&'a mut T, ()> {
|
||||
unsafe {
|
||||
if let Some(context) = device_object.DeviceExtension.as_mut() {
|
||||
return Ok(core::mem::transmute(context));
|
||||
}
|
||||
}
|
||||
|
||||
return Err(());
|
||||
}
|
||||
|
||||
/// Empty unload event
|
||||
extern "C" fn empty_wdf_driver_unload(_driver: HANDLE) {}
|
||||
216
windows_kext/wdk/src/ioqueue.rs
Normal file
216
windows_kext/wdk/src/ioqueue.rs
Normal file
@@ -0,0 +1,216 @@
|
||||
use core::{
|
||||
cell::UnsafeCell,
|
||||
ffi::c_void,
|
||||
fmt::Display,
|
||||
marker::PhantomData,
|
||||
mem::MaybeUninit,
|
||||
pin::Pin,
|
||||
sync::atomic::{AtomicBool, Ordering},
|
||||
};
|
||||
|
||||
use crate::dbg;
|
||||
use alloc::boxed::Box;
|
||||
use ntstatus::ntstatus::NtStatus;
|
||||
use windows_sys::{Wdk::Foundation::KQUEUE, Win32::System::Kernel::LIST_ENTRY};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Status {
|
||||
Uninitialized,
|
||||
Timeout,
|
||||
UserAPC,
|
||||
Abandoned,
|
||||
}
|
||||
|
||||
impl Display for Status {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
match self {
|
||||
Status::Uninitialized => write!(f, "Uninitialized"),
|
||||
Status::Timeout => write!(f, "Timeout"),
|
||||
Status::UserAPC => write!(f, "UserAPC"),
|
||||
Status::Abandoned => write!(f, "Abandoned"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(i8)]
|
||||
pub enum KprocessorMode {
|
||||
KernelMode = 0,
|
||||
UserMode = 1,
|
||||
}
|
||||
|
||||
// #[link(name = "NtosKrnl", kind = "static")]
|
||||
extern "C" {
|
||||
/*
|
||||
KeInitializeQueue
|
||||
[out] Queue
|
||||
Pointer to a KQUEUE structure for which the caller must provide resident storage in nonpaged pool. This structure is defined as follows:
|
||||
|
||||
[in] Count
|
||||
The maximum number of threads for which the waits on the queue object can be satisfied concurrently. If this parameter is not supplied, the number of processors in the machine is used.
|
||||
*/
|
||||
fn KeInitializeQueue(queue: *mut KQUEUE, count: u64);
|
||||
/*
|
||||
KeInsertQueue returns the previous signal state of the given Queue. If it was set to zero (that is, not signaled) before KeInsertQueue was called, KeInsertQueue returns zero, meaning that no entries were queued. If it was nonzero (signaled), KeInsertQueue returns the number of entries that were queued before KeInsertQueue was called.
|
||||
*/
|
||||
fn KeInsertQueue(queue: *mut KQUEUE, list_entry: *mut c_void) -> i32;
|
||||
/*
|
||||
KeRemoveQueue returns one of the following:
|
||||
A pointer to a dequeued entry from the given queue object, if one is available
|
||||
STATUS_TIMEOUT, if the given Timeout interval expired before an entry became available
|
||||
STATUS_USER_APC, if a user-mode APC was delivered in the context of the calling thread
|
||||
STATUS_ABANDONED, if the queue has been run down
|
||||
*/
|
||||
fn KeRemoveQueue(
|
||||
queue: *mut KQUEUE,
|
||||
waitmode: KprocessorMode,
|
||||
timeout: *const i64,
|
||||
) -> *mut LIST_ENTRY;
|
||||
|
||||
// If the queue is empty, KeRundownQueue returns NULL; otherwise, it returns the address of the first entry in the queue.
|
||||
fn KeRundownQueue(queue: *mut KQUEUE) -> *mut LIST_ENTRY;
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct Entry<T> {
|
||||
list: LIST_ENTRY, // Internal use
|
||||
entry: T,
|
||||
}
|
||||
|
||||
pub struct IOQueue<T> {
|
||||
// The address of the value should not change.
|
||||
kernel_queue: Pin<Box<UnsafeCell<KQUEUE>>>,
|
||||
initialized: AtomicBool,
|
||||
_type: PhantomData<T>, // 0 size variable. Required for the generic to work properly. Compiler limitation.
|
||||
}
|
||||
|
||||
unsafe impl<T> Sync for IOQueue<T> {}
|
||||
|
||||
impl<T> IOQueue<T> {
|
||||
/// Make sure `rundown` is called on exit, if `drop()` is not called for queue.
|
||||
pub fn new() -> Self {
|
||||
unsafe {
|
||||
let kernel_queue = Box::pin(UnsafeCell::new(MaybeUninit::zeroed().assume_init()));
|
||||
KeInitializeQueue(kernel_queue.get(), 1);
|
||||
|
||||
Self {
|
||||
kernel_queue,
|
||||
initialized: AtomicBool::new(true),
|
||||
_type: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Pushes new entry of any type.
|
||||
pub fn push(&self, entry: T) -> Result<(), Status> {
|
||||
let kqueue = self.kernel_queue.get();
|
||||
// Allocate entry.
|
||||
let list_entry = Box::new(Entry {
|
||||
list: LIST_ENTRY {
|
||||
Flink: core::ptr::null_mut(),
|
||||
Blink: core::ptr::null_mut(),
|
||||
},
|
||||
entry,
|
||||
});
|
||||
let raw_ptr = Box::into_raw(list_entry);
|
||||
|
||||
// Check if initialized.
|
||||
let result = if self.initialized.load(Ordering::Acquire) {
|
||||
unsafe { KeInsertQueue(kqueue, raw_ptr as *mut c_void) }
|
||||
} else {
|
||||
-1
|
||||
};
|
||||
// There is no documentation that rundown queue will return error. This is here just for good measures.
|
||||
// It is unlikely to happen and not critical.
|
||||
if result >= 0 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
_ = unsafe { Box::from_raw(raw_ptr) };
|
||||
return Err(Status::Uninitialized);
|
||||
}
|
||||
|
||||
/// Returns an Element or a status.
|
||||
fn pop_internal(&self, timeout: *const i64) -> Result<T, Status> {
|
||||
unsafe {
|
||||
let kqueue = self.kernel_queue.get();
|
||||
// Check if initialized.
|
||||
if self.initialized.load(Ordering::Acquire) {
|
||||
// Pop and check the return value.
|
||||
let list_entry =
|
||||
KeRemoveQueue(kqueue, KprocessorMode::KernelMode, timeout) as *mut Entry<T>;
|
||||
let error_code = NtStatus::try_from(list_entry as u32);
|
||||
match error_code {
|
||||
Ok(NtStatus::STATUS_TIMEOUT) => return Err(Status::Timeout),
|
||||
Ok(NtStatus::STATUS_USER_APC) => return Err(Status::UserAPC),
|
||||
Ok(NtStatus::STATUS_ABANDONED) => return Err(Status::Abandoned),
|
||||
_ => {
|
||||
// The return value is a pointer.
|
||||
let list_entry = Box::from_raw(list_entry);
|
||||
let entry = list_entry.entry;
|
||||
return Ok(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(Status::Uninitialized)
|
||||
}
|
||||
|
||||
/// Returns element or a status. Waits until element is pushed or the queue is interrupted.
|
||||
pub fn wait_and_pop(&self) -> Result<T, Status> {
|
||||
// No timeout.
|
||||
self.pop_internal(core::ptr::null())
|
||||
}
|
||||
|
||||
/// Returns element or a status. Does not wait.
|
||||
pub fn pop(&self) -> Result<T, Status> {
|
||||
let timeout: i64 = 0;
|
||||
self.pop_internal(&timeout)
|
||||
}
|
||||
|
||||
/// Returns element or a status. Waits the specified timeout.
|
||||
pub fn pop_timeout(&self, timeout: i64) -> Result<T, Status> {
|
||||
let timeout_ptr: i64 = timeout * -10000;
|
||||
self.pop_internal(&timeout_ptr)
|
||||
}
|
||||
|
||||
/// Removes all elements and frees all the memory. The object can't be used after this function is called.
|
||||
pub fn rundown(&self) {
|
||||
unsafe {
|
||||
let kqueue = self.kernel_queue.get();
|
||||
if kqueue.is_null() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if initialized.
|
||||
if self.initialized.swap(false, Ordering::Acquire) {
|
||||
// Remove and free all elements from the queue.
|
||||
let list_entries: *mut LIST_ENTRY = KeRundownQueue(kqueue);
|
||||
if !list_entries.is_null() {
|
||||
let mut entry = list_entries;
|
||||
while !core::ptr::eq((*entry).Flink, list_entries) {
|
||||
let next = (*entry).Flink;
|
||||
dbg!("discarding entry");
|
||||
let _ = Box::from_raw(entry as *mut Entry<T>);
|
||||
entry = next;
|
||||
}
|
||||
dbg!("discarding last entry");
|
||||
let _ = Box::from_raw(entry as *mut Entry<T>);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for IOQueue<T> {
|
||||
fn drop(&mut self) {
|
||||
// Reinitialize queue.
|
||||
self.rundown();
|
||||
unsafe {
|
||||
let ptr = self.kernel_queue.get();
|
||||
if !ptr.is_null() {
|
||||
*ptr = MaybeUninit::zeroed().assume_init();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
198
windows_kext/wdk/src/irp_helpers.rs
Normal file
198
windows_kext/wdk/src/irp_helpers.rs
Normal file
@@ -0,0 +1,198 @@
|
||||
use windows_sys::{
|
||||
Wdk::{
|
||||
Foundation::{IO_STACK_LOCATION_0_4, IRP},
|
||||
Storage::FileSystem::IO_NO_INCREMENT,
|
||||
System::SystemServices::IofCompleteRequest,
|
||||
},
|
||||
Win32::Foundation::{
|
||||
NTSTATUS, STATUS_END_OF_FILE, STATUS_NOT_IMPLEMENTED, STATUS_SUCCESS, STATUS_TIMEOUT,
|
||||
},
|
||||
};
|
||||
|
||||
pub struct ReadRequest<'a> {
|
||||
irp: &'a mut IRP,
|
||||
buffer: &'a mut [u8],
|
||||
fill_index: usize,
|
||||
}
|
||||
|
||||
impl ReadRequest<'_> {
|
||||
pub fn new(irp: &mut IRP) -> ReadRequest {
|
||||
unsafe {
|
||||
let irp_sp = irp.Tail.Overlay.Anonymous2.Anonymous.CurrentStackLocation;
|
||||
let device_io = (*irp_sp).Parameters.Read;
|
||||
|
||||
let system_buffer = irp.AssociatedIrp.SystemBuffer;
|
||||
let buffer = core::slice::from_raw_parts_mut(
|
||||
system_buffer as *mut u8,
|
||||
device_io.Length as usize,
|
||||
);
|
||||
ReadRequest {
|
||||
irp,
|
||||
buffer,
|
||||
fill_index: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn free_space(&self) -> usize {
|
||||
self.buffer.len() - self.fill_index
|
||||
}
|
||||
|
||||
pub fn complete(&mut self) {
|
||||
self.irp.IoStatus.Information = self.fill_index;
|
||||
self.irp.IoStatus.Anonymous.Status = STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
pub fn end_of_file(&mut self) {
|
||||
self.irp.IoStatus.Information = self.fill_index;
|
||||
self.irp.IoStatus.Anonymous.Status = STATUS_END_OF_FILE;
|
||||
}
|
||||
|
||||
pub fn timeout(&mut self) {
|
||||
self.irp.IoStatus.Anonymous.Status = STATUS_TIMEOUT;
|
||||
}
|
||||
|
||||
pub fn get_status(&self) -> NTSTATUS {
|
||||
unsafe { self.irp.IoStatus.Anonymous.Status }
|
||||
}
|
||||
|
||||
pub fn write(&mut self, bytes: &[u8]) -> usize {
|
||||
let mut bytes_to_write: usize = bytes.len();
|
||||
|
||||
// Check if we have enough space
|
||||
if bytes_to_write > self.free_space() {
|
||||
bytes_to_write = self.free_space();
|
||||
}
|
||||
|
||||
for i in 0..bytes_to_write {
|
||||
self.buffer[self.fill_index + i] = bytes[i];
|
||||
}
|
||||
self.fill_index = self.fill_index + bytes_to_write;
|
||||
|
||||
bytes_to_write
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WriteRequest<'a> {
|
||||
irp: &'a mut IRP,
|
||||
buffer: &'a mut [u8],
|
||||
}
|
||||
|
||||
impl WriteRequest<'_> {
|
||||
pub fn new(irp: &mut IRP) -> WriteRequest {
|
||||
unsafe {
|
||||
let irp_sp = irp.Tail.Overlay.Anonymous2.Anonymous.CurrentStackLocation;
|
||||
let device_io = (*irp_sp).Parameters.Write;
|
||||
|
||||
let system_buffer = irp.AssociatedIrp.SystemBuffer;
|
||||
let buffer = core::slice::from_raw_parts_mut(
|
||||
system_buffer as *mut u8,
|
||||
device_io.Length as usize,
|
||||
);
|
||||
WriteRequest { irp, buffer }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_buffer(&self) -> &[u8] {
|
||||
&self.buffer
|
||||
}
|
||||
|
||||
pub fn mark_all_as_read(&mut self) {
|
||||
self.irp.IoStatus.Information = self.buffer.len();
|
||||
}
|
||||
|
||||
pub fn complete(&mut self) {
|
||||
self.irp.IoStatus.Anonymous.Status = STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
pub fn get_status(&self) -> NTSTATUS {
|
||||
unsafe { self.irp.IoStatus.Anonymous.Status }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DeviceControlRequest<'a> {
|
||||
irp: &'a mut IRP,
|
||||
buffer: &'a mut [u8],
|
||||
fill_index: usize,
|
||||
control_code: u32,
|
||||
}
|
||||
|
||||
// Windows-rs version of the struct is incorrect (18.01.2024).
|
||||
// TODO: Use the official version when fixed.
|
||||
// For future reference: https://github.com/microsoft/windows-rs/issues/2805
|
||||
#[repr(C)]
|
||||
struct DeviceIOControlParams {
|
||||
output_buffer_length: u32,
|
||||
_padding1: u32,
|
||||
input_buffer_length: u32,
|
||||
_padding2: u32,
|
||||
io_control_code: u32,
|
||||
_padding3: u32,
|
||||
}
|
||||
|
||||
impl DeviceControlRequest<'_> {
|
||||
pub fn new(irp: &mut IRP) -> DeviceControlRequest {
|
||||
unsafe {
|
||||
let irp_sp = irp.Tail.Overlay.Anonymous2.Anonymous.CurrentStackLocation;
|
||||
// Use the struct directly when replaced with proper version.
|
||||
let device_io: DeviceIOControlParams =
|
||||
core::mem::transmute::<IO_STACK_LOCATION_0_4, DeviceIOControlParams>(
|
||||
(*irp_sp).Parameters.DeviceIoControl,
|
||||
);
|
||||
|
||||
let system_buffer = irp.AssociatedIrp.SystemBuffer;
|
||||
let buffer = core::slice::from_raw_parts_mut(
|
||||
system_buffer as *mut u8,
|
||||
device_io.output_buffer_length as usize,
|
||||
);
|
||||
DeviceControlRequest {
|
||||
irp,
|
||||
buffer,
|
||||
fill_index: 0,
|
||||
control_code: device_io.io_control_code,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_buffer(&self) -> &[u8] {
|
||||
&self.buffer
|
||||
}
|
||||
pub fn write(&mut self, bytes: &[u8]) -> usize {
|
||||
let mut bytes_to_write: usize = bytes.len();
|
||||
|
||||
// Check if we have enough space
|
||||
if bytes_to_write > self.free_space() {
|
||||
bytes_to_write = self.free_space();
|
||||
}
|
||||
|
||||
for i in 0..bytes_to_write {
|
||||
self.buffer[self.fill_index + i] = bytes[i];
|
||||
}
|
||||
self.fill_index = self.fill_index + bytes_to_write;
|
||||
|
||||
bytes_to_write
|
||||
}
|
||||
|
||||
pub fn complete(&mut self) {
|
||||
self.irp.IoStatus.Information = self.buffer.len();
|
||||
self.irp.IoStatus.Anonymous.Status = STATUS_SUCCESS;
|
||||
unsafe { IofCompleteRequest(self.irp, IO_NO_INCREMENT as i8) };
|
||||
}
|
||||
|
||||
pub fn not_implemented(&mut self) {
|
||||
self.irp.IoStatus.Anonymous.Status = STATUS_NOT_IMPLEMENTED;
|
||||
unsafe { IofCompleteRequest(self.irp, IO_NO_INCREMENT as i8) };
|
||||
}
|
||||
|
||||
pub fn get_status(&self) -> NTSTATUS {
|
||||
unsafe { self.irp.IoStatus.Anonymous.Status }
|
||||
}
|
||||
|
||||
pub fn get_control_code(&self) -> u32 {
|
||||
self.control_code
|
||||
}
|
||||
|
||||
pub fn free_space(&self) -> usize {
|
||||
self.buffer.len() - self.fill_index
|
||||
}
|
||||
}
|
||||
32
windows_kext/wdk/src/lib.rs
Normal file
32
windows_kext/wdk/src/lib.rs
Normal file
@@ -0,0 +1,32 @@
|
||||
#![cfg_attr(not(test), no_std)]
|
||||
#![allow(clippy::needless_return)]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
pub mod allocator;
|
||||
pub mod consts;
|
||||
pub mod debug;
|
||||
pub mod driver;
|
||||
pub mod error;
|
||||
pub mod filter_engine;
|
||||
pub mod interface;
|
||||
pub mod ioqueue;
|
||||
pub mod irp_helpers;
|
||||
pub mod rw_spin_lock;
|
||||
pub mod spin_lock;
|
||||
pub mod utils;
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub mod ffi;
|
||||
|
||||
// Needed by the linker for legacy reasons. Not important for rust.
|
||||
#[cfg(not(test))]
|
||||
#[export_name = "_fltused"]
|
||||
static _FLTUSED: i32 = 0;
|
||||
|
||||
// Needed by the compiler but not used.
|
||||
#[cfg(not(test))]
|
||||
#[no_mangle]
|
||||
pub extern "system" fn __CxxFrameHandler3(_: *mut u8, _: *mut u8, _: *mut u8, _: *mut u8) -> i32 {
|
||||
0
|
||||
}
|
||||
73
windows_kext/wdk/src/rw_spin_lock.rs
Normal file
73
windows_kext/wdk/src/rw_spin_lock.rs
Normal file
@@ -0,0 +1,73 @@
|
||||
use core::cell::UnsafeCell;
|
||||
|
||||
use windows_sys::Wdk::System::SystemServices::{
|
||||
ExAcquireSpinLockExclusive, ExAcquireSpinLockShared, ExReleaseSpinLockExclusive,
|
||||
ExReleaseSpinLockShared,
|
||||
};
|
||||
|
||||
/// A reader-writer spin lock implementation.
|
||||
///
|
||||
/// This lock allows multiple readers to access the data simultaneously,
|
||||
/// but only one writer can access the data at a time. It uses a spin loop
|
||||
/// to wait for the lock to become available.
|
||||
pub struct RwSpinLock {
|
||||
data: UnsafeCell<i32>,
|
||||
}
|
||||
|
||||
impl RwSpinLock {
|
||||
/// Creates a new `RwSpinLock` with the default initial value.
|
||||
pub const fn default() -> Self {
|
||||
Self {
|
||||
data: UnsafeCell::new(0),
|
||||
}
|
||||
}
|
||||
|
||||
/// Acquires a read lock on the `RwSpinLock`.
|
||||
///
|
||||
/// This method blocks until a read lock can be acquired.
|
||||
/// Returns a `RwLockGuard` that represents the acquired read lock.
|
||||
pub fn read_lock(&self) -> RwLockGuard {
|
||||
let irq = unsafe { ExAcquireSpinLockShared(self.data.get()) };
|
||||
RwLockGuard {
|
||||
data: &self.data,
|
||||
exclusive: false,
|
||||
old_irq: irq,
|
||||
}
|
||||
}
|
||||
|
||||
/// Acquires a write lock on the `RwSpinLock`.
|
||||
///
|
||||
/// This method blocks until a write lock can be acquired.
|
||||
/// Returns a `RwLockGuard` that represents the acquired write lock.
|
||||
pub fn write_lock(&self) -> RwLockGuard {
|
||||
let irq = unsafe { ExAcquireSpinLockExclusive(self.data.get()) };
|
||||
RwLockGuard {
|
||||
data: &self.data,
|
||||
exclusive: true,
|
||||
old_irq: irq,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a guard for a read-write lock.
|
||||
pub struct RwLockGuard<'a> {
|
||||
data: &'a UnsafeCell<i32>,
|
||||
exclusive: bool,
|
||||
old_irq: u8,
|
||||
}
|
||||
|
||||
impl<'a> Drop for RwLockGuard<'a> {
|
||||
/// Releases the acquired spin lock when the `RwLockGuard` goes out of scope.
|
||||
///
|
||||
/// If the lock was acquired exclusively, it releases the spin lock using `ExReleaseSpinLockExclusive`.
|
||||
/// If the lock was acquired shared, it releases the spin lock using `ExReleaseSpinLockShared`.
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
if self.exclusive {
|
||||
ExReleaseSpinLockExclusive(self.data.get(), self.old_irq);
|
||||
} else {
|
||||
ExReleaseSpinLockShared(self.data.get(), self.old_irq);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
53
windows_kext/wdk/src/spin_lock.rs
Normal file
53
windows_kext/wdk/src/spin_lock.rs
Normal file
@@ -0,0 +1,53 @@
|
||||
use core::{ffi::c_void, mem::MaybeUninit, ptr};
|
||||
|
||||
use windows_sys::Wdk::System::SystemServices::{
|
||||
KeAcquireInStackQueuedSpinLock, KeInitializeSpinLock, KeReleaseInStackQueuedSpinLock,
|
||||
KLOCK_QUEUE_HANDLE,
|
||||
};
|
||||
|
||||
// Copy of KSPIN_LOCK_QUEUE WDK C struct
|
||||
#[repr(C)]
|
||||
#[allow(dead_code)]
|
||||
struct KSpinLockQueue {
|
||||
next: *mut c_void, // struct _KSPIN_LOCK_QUEUE * volatile Next;
|
||||
lock: *mut c_void, // PKSPIN_LOCK volatile Lock;
|
||||
}
|
||||
|
||||
// Copy of KLOCK_QUEUE_HANDLE WDK C struct
|
||||
pub struct KLockQueueHandle {
|
||||
lock: KLOCK_QUEUE_HANDLE,
|
||||
}
|
||||
|
||||
// Copy of KSpinLock WDK C struct
|
||||
#[repr(C)]
|
||||
pub struct KSpinLock {
|
||||
ptr: *mut usize,
|
||||
}
|
||||
|
||||
impl KSpinLock {
|
||||
pub fn create() -> Self {
|
||||
unsafe {
|
||||
let p: KSpinLock = KSpinLock {
|
||||
ptr: ptr::null_mut(),
|
||||
};
|
||||
KeInitializeSpinLock(p.ptr);
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lock(&mut self) -> KLockQueueHandle {
|
||||
unsafe {
|
||||
let mut handle = MaybeUninit::zeroed().assume_init();
|
||||
KeAcquireInStackQueuedSpinLock(self.ptr, &mut handle);
|
||||
KLockQueueHandle { lock: handle }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for KLockQueueHandle {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
KeReleaseInStackQueuedSpinLock(&mut self.lock);
|
||||
}
|
||||
}
|
||||
}
|
||||
22
windows_kext/wdk/src/utils.rs
Normal file
22
windows_kext/wdk/src/utils.rs
Normal file
@@ -0,0 +1,22 @@
|
||||
use alloc::string::{String, ToString};
|
||||
use ntstatus::ntstatus::NtStatus;
|
||||
use windows_sys::Win32::Foundation::STATUS_SUCCESS;
|
||||
|
||||
use crate::ffi;
|
||||
|
||||
pub fn check_ntstatus(status: i32) -> Result<(), String> {
|
||||
if status == STATUS_SUCCESS {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let Some(status) = NtStatus::from_u32(status as u32) else {
|
||||
return Err("UNKNOWN_ERROR_CODE".to_string());
|
||||
};
|
||||
|
||||
return Err(status.to_string());
|
||||
}
|
||||
|
||||
pub fn get_system_timestamp_ms() -> u64 {
|
||||
// 100 nano seconds units -> device by 10 -> micro seconds -> divide by 1000 -> milliseconds
|
||||
unsafe { ffi::pm_QuerySystemTime() / 10_000 }
|
||||
}
|
||||
Reference in New Issue
Block a user