use alloc::vec::Vec;
use wdk::println;
#[repr(C)]
#[derive(Clone, Copy)]
pub struct PoolHeader {
pub previous_size: u8,
pub pool_index: u8,
pub block_size: u8,
pub pool_type: u8,
pub pool_tag: [u8; 4],
}
pub const POOL_HEADER_SIZE: usize = 16;
pub mod pool_tags {
pub const PROCESS: [u8; 4] = *b"Proc";
pub const THREAD: [u8; 4] = *b"Thre";
pub const MODULE: [u8; 4] = *b"MmLd";
pub const FILE: [u8; 4] = *b"File";
pub const DRIVER: [u8; 4] = *b"Driv";
pub const HANDLE: [u8; 4] = *b"ObHd";
pub const REGKEY: [u8; 4] = *b"CM10";
pub const SOCKET: [u8; 4] = *b"TcpE";
pub const RAW_SOCKET: [u8; 4] = *b"RawE";
pub const TOKEN: [u8; 4] = *b"Toke";
}
#[derive(Debug)]
pub struct PoolScanResult {
pub address: usize,
pub pool_tag: [u8; 4],
pub block_size: usize,
pub is_linked: bool,
}
pub struct PoolScanner {
pub start_address: usize,
pub end_address: usize,
pub target_tag: [u8; 4],
pub max_results: usize,
}
impl PoolScanner {
pub fn new(target_tag: [u8; 4]) -> Self {
Self {
start_address: 0xFFFF_8000_0000_0000,
end_address: 0xFFFF_FFFF_FFFF_FFFF,
target_tag,
max_results: 1000,
}
}
pub fn with_range(mut self, start: usize, end: usize) -> Self {
self.start_address = start;
self.end_address = end;
self
}
pub fn with_max_results(mut self, max: usize) -> Self {
self.max_results = max;
self
}
pub unsafe fn scan(&self) -> Vec<PoolScanResult> {
let results = Vec::new();
println!(
"[Leviathan] Pool scan starting for tag '{}'",
core::str::from_utf8(&self.target_tag).unwrap_or("????")
);
println!(
"[Leviathan] Pool scan complete: {} results",
results.len()
);
results
}
}
pub fn scan_for_processes() -> Vec<usize> {
let scanner = PoolScanner::new(pool_tags::PROCESS);
let results = unsafe { scanner.scan() };
results.iter().map(|r| r.address).collect()
}
pub fn scan_for_threads() -> Vec<usize> {
let scanner = PoolScanner::new(pool_tags::THREAD);
let results = unsafe { scanner.scan() };
results.iter().map(|r| r.address).collect()
}
pub fn scan_for_modules() -> Vec<usize> {
let scanner = PoolScanner::new(pool_tags::MODULE);
let results = unsafe { scanner.scan() };
results.iter().map(|r| r.address).collect()
}
pub fn scan_for_drivers() -> Vec<usize> {
let scanner = PoolScanner::new(pool_tags::DRIVER);
let results = unsafe { scanner.scan() };
results.iter().map(|r| r.address).collect()
}
pub fn scan_for_network_connections() -> Vec<usize> {
let scanner = PoolScanner::new(pool_tags::SOCKET);
let results = unsafe { scanner.scan() };
results.iter().map(|r| r.address).collect()
}
pub struct PoolTagDatabase {
entries: Vec<(u32, &'static str, &'static str)>,
}
impl PoolTagDatabase {
pub fn new() -> Self {
let mut entries = Vec::new();
entries.push((u32::from_le_bytes(*b"Proc"), "nt", "EPROCESS structure"));
entries.push((u32::from_le_bytes(*b"Thre"), "nt", "ETHREAD structure"));
entries.push((u32::from_le_bytes(*b"MmLd"), "nt", "Loaded module entry"));
entries.push((u32::from_le_bytes(*b"File"), "nt", "FILE_OBJECT"));
entries.push((u32::from_le_bytes(*b"Driv"), "nt", "DRIVER_OBJECT"));
entries.push((u32::from_le_bytes(*b"ObHd"), "nt", "Object handle table"));
entries.push((u32::from_le_bytes(*b"CM10"), "nt", "Registry key object"));
entries.push((u32::from_le_bytes(*b"TcpE"), "tcpip", "TCP endpoint"));
entries.push((u32::from_le_bytes(*b"UdpA"), "tcpip", "UDP endpoint"));
entries.push((u32::from_le_bytes(*b"RawE"), "tcpip", "Raw socket"));
entries.push((u32::from_le_bytes(*b"Toke"), "nt", "Token object"));
entries.push((u32::from_le_bytes(*b"Sect"), "nt", "Section object"));
entries.push((u32::from_le_bytes(*b"Sema"), "nt", "Semaphore object"));
entries.push((u32::from_le_bytes(*b"Even"), "nt", "Event object"));
entries.push((u32::from_le_bytes(*b"Muta"), "nt", "Mutant object"));
Self { entries }
}
pub fn lookup(&self, tag: [u8; 4]) -> Option<(&str, &str)> {
let tag_u32 = u32::from_le_bytes(tag);
for (entry_tag, owner, desc) in &self.entries {
if *entry_tag == tag_u32 {
return Some((owner, desc));
}
}
None
}
pub fn all_tags(&self) -> &[(u32, &'static str, &'static str)] {
&self.entries
}
}
pub mod quick_scan {
use super::*;
#[repr(C)]
pub struct PoolTrackerBigPages {
pub va: usize,
pub key: u32,
pub pattern: u32,
pub number_of_bytes: usize,
}
#[allow(dead_code)]
pub fn scan_big_pool_table(_target_tag: [u8; 4]) -> Vec<usize> {
let results = Vec::new();
results
}
}
pub mod validation {
use super::*;
pub fn is_valid_pool_header(header: &PoolHeader) -> bool {
if header.block_size == 0 {
return false;
}
if header.pool_type > 0x7F {
return false;
}
for byte in &header.pool_tag {
if *byte != 0 && (*byte < 0x20 || *byte > 0x7E) {
return false;
}
}
true
}
pub fn is_valid_eprocess(_address: usize) -> bool {
true }
pub fn is_valid_ethread(_address: usize) -> bool {
true }
}