use alloc::collections::BTreeSet;
use alloc::vec::Vec;
use core::ptr;
use wdk::println;
use wdk_sys::{
ntddk::{
PsLookupProcessByProcessId,
},
NTSTATUS, PEPROCESS, HANDLE, STATUS_SUCCESS,
};
pub const SYSTEM_PROCESS_INFORMATION: u32 = 5;
pub unsafe fn ps_get_current_process() -> PEPROCESS {
unsafe { wdk_sys::ntddk::IoGetCurrentProcess() }
}
pub unsafe fn ps_get_process_image_filename(process: PEPROCESS) -> *mut i8 {
let _ = process;
ptr::null_mut()
}
#[derive(Debug, Clone)]
pub struct ProcessInfo {
pub pid: usize,
pub ppid: usize,
pub name: [u8; 16],
pub eprocess: usize,
pub session_id: u32,
pub is_terminated: bool,
pub found_by: EnumerationMethods,
}
#[derive(Debug, Clone, Copy, Default)]
pub struct EnumerationMethods {
pub active_process_links: bool,
pub cid_table: bool,
pub thread_process: bool,
pub pool_scan: bool,
pub system_info: bool,
}
impl EnumerationMethods {
pub fn found_by_all(&self) -> bool {
self.active_process_links
&& self.cid_table
&& self.thread_process
&& self.pool_scan
&& self.system_info
}
pub fn possibly_hidden(&self) -> bool {
let count = [
self.active_process_links,
self.cid_table,
self.thread_process,
self.pool_scan,
self.system_info,
]
.iter()
.filter(|&&x| x)
.count();
count > 0 && count < 5
}
}
#[derive(Debug, Clone)]
pub struct ThreadInfo {
pub tid: usize,
pub pid: usize,
pub ethread: usize,
pub state: u8,
pub start_address: usize,
pub is_orphan: bool,
}
pub struct ProcessEnumerator {
processes: Vec<ProcessInfo>,
seen_pids: BTreeSet<usize>,
}
impl ProcessEnumerator {
pub fn new() -> Self {
Self {
processes: Vec::new(),
seen_pids: BTreeSet::new(),
}
}
pub unsafe fn enumerate_all(&mut self) -> Result<(), NTSTATUS> {
println!("[Leviathan] Starting multi-method process enumeration");
unsafe { self.enumerate_via_system_info()? };
unsafe { self.enumerate_via_active_links()? };
unsafe { self.enumerate_via_cid_table()? };
unsafe { self.enumerate_via_threads()? };
unsafe { self.enumerate_via_pool_scan()? };
println!(
"[Leviathan] Enumeration complete: {} processes found",
self.processes.len()
);
Ok(())
}
unsafe fn enumerate_via_system_info(&mut self) -> Result<(), NTSTATUS> {
println!("[Leviathan] Enumeration via SystemProcessInformation");
Ok(())
}
unsafe fn enumerate_via_active_links(&mut self) -> Result<(), NTSTATUS> {
println!("[Leviathan] Enumeration via ActiveProcessLinks");
Ok(())
}
unsafe fn enumerate_via_cid_table(&mut self) -> Result<(), NTSTATUS> {
println!("[Leviathan] Enumeration via PspCidTable");
Ok(())
}
unsafe fn enumerate_via_threads(&mut self) -> Result<(), NTSTATUS> {
println!("[Leviathan] Enumeration via thread->process links");
Ok(())
}
unsafe fn enumerate_via_pool_scan(&mut self) -> Result<(), NTSTATUS> {
println!("[Leviathan] Enumeration via pool tag scanning");
Ok(())
}
pub fn get_processes(&self) -> &[ProcessInfo] {
&self.processes
}
pub fn find_hidden(&self) -> Vec<&ProcessInfo> {
self.processes
.iter()
.filter(|p| p.found_by.possibly_hidden())
.collect()
}
pub fn find_orphan_threads(&self) -> Vec<ThreadInfo> {
Vec::new()
}
}
pub unsafe fn lookup_process(pid: usize) -> Option<PEPROCESS> {
let mut process: PEPROCESS = ptr::null_mut();
let status = unsafe {
PsLookupProcessByProcessId(pid as HANDLE, &mut process)
};
if status == STATUS_SUCCESS && !process.is_null() {
Some(process)
} else {
None
}
}
pub unsafe fn get_process_name(process: PEPROCESS) -> [u8; 16] {
let mut name = [0u8; 16];
let name_ptr = unsafe { ps_get_process_image_filename(process) };
if !name_ptr.is_null() {
for i in 0..15 {
let byte = unsafe { *name_ptr.add(i) };
if byte == 0 {
break;
}
name[i] = byte as u8;
}
}
name
}
pub mod offsets {
pub const ACTIVE_PROCESS_LINKS: usize = 0x448;
pub const UNIQUE_PROCESS_ID: usize = 0x440;
pub const INHERITED_FROM_UNIQUE_PROCESS_ID: usize = 0x540;
pub const IMAGE_FILE_NAME: usize = 0x5A8;
pub const THREAD_LIST_HEAD: usize = 0x5E0;
pub const THREAD_LIST_ENTRY: usize = 0x4E8;
pub const ETHREAD_CID: usize = 0x478;
}
pub fn detect_dkom(enumerator: &ProcessEnumerator) -> Vec<&ProcessInfo> {
let hidden = enumerator.find_hidden();
for process in &hidden {
println!(
"[Leviathan] Potentially hidden process: PID={} Name={}",
process.pid,
core::str::from_utf8(&process.name).unwrap_or("<invalid>")
);
}
hidden
}