use alloc::vec::Vec;
use wdk::println;
use wdk_sys::NTSTATUS;
#[derive(Debug, Clone)]
pub struct SsdtEntry {
pub index: u32,
pub address: usize,
pub expected_base: usize,
pub expected_end: usize,
pub is_hooked: bool,
pub hook_module: Option<[u8; 256]>,
}
#[derive(Debug, Clone)]
pub struct IdtEntry {
pub vector: u8,
pub handler: usize,
pub expected_start: usize,
pub expected_end: usize,
pub is_hooked: bool,
pub dpl: u8,
pub gate_type: u8,
}
#[derive(Debug, Clone)]
pub struct InlineHook {
pub address: usize,
pub function_name: [u8; 64],
pub original_bytes: [u8; 16],
pub current_bytes: [u8; 16],
pub jump_target: usize,
pub hook_type: InlineHookType,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum InlineHookType {
JmpRel32,
JmpRipRelative,
PushRet,
MovJmp,
Int3,
Unknown,
}
#[derive(Debug, Clone)]
pub struct MsrHook {
pub msr: u32,
pub current_value: u64,
pub expected_value: u64,
pub is_hooked: bool,
}
pub mod msr_indices {
pub const IA32_LSTAR: u32 = 0xC0000082;
pub const IA32_CSTAR: u32 = 0xC0000083;
pub const IA32_SFMASK: u32 = 0xC0000084;
pub const IA32_SYSENTER_EIP: u32 = 0x176;
pub const IA32_EFER: u32 = 0xC0000080;
}
pub struct HookScanner {
kernel_base: usize,
kernel_size: usize,
scan_ssdt: bool,
scan_idt: bool,
scan_inline: bool,
scan_msr: bool,
}
impl HookScanner {
pub fn new() -> Self {
Self {
kernel_base: 0,
kernel_size: 0,
scan_ssdt: true,
scan_idt: true,
scan_inline: true,
scan_msr: true,
}
}
pub unsafe fn initialize(&mut self) -> Result<(), NTSTATUS> {
println!("[Leviathan] Hook scanner initialized");
Ok(())
}
pub unsafe fn scan_ssdt(&self) -> Vec<SsdtEntry> {
let hooks = Vec::new();
println!("[Leviathan] SSDT scan: {} potential hooks found", hooks.len());
hooks
}
pub unsafe fn scan_idt(&self) -> Vec<IdtEntry> {
let hooks = Vec::new();
println!("[Leviathan] IDT scan: {} potential hooks found", hooks.len());
hooks
}
pub unsafe fn scan_inline_hooks(&self) -> Vec<InlineHook> {
let mut hooks = Vec::new();
let critical_functions = [
"NtCreateProcess",
"NtCreateThread",
"NtOpenProcess",
"NtReadVirtualMemory",
"NtWriteVirtualMemory",
"NtAllocateVirtualMemory",
"NtProtectVirtualMemory",
"NtCreateFile",
"NtSetInformationFile",
"NtQuerySystemInformation",
"NtLoadDriver",
"NtSetSystemInformation",
];
for func in &critical_functions {
if let Some(hook) = unsafe { self.check_function_hook(func) } {
hooks.push(hook);
}
}
println!("[Leviathan] Inline hook scan: {} hooks found", hooks.len());
hooks
}
unsafe fn check_function_hook(&self, _func_name: &str) -> Option<InlineHook> {
None
}
pub unsafe fn scan_msr(&self) -> Vec<MsrHook> {
let hooks = Vec::new();
println!("[Leviathan] MSR scan: {} potential hooks found", hooks.len());
hooks
}
pub unsafe fn full_scan(&self) -> HookScanResult {
println!("[Leviathan] Starting full hook scan...");
let ssdt_hooks = if self.scan_ssdt {
unsafe { self.scan_ssdt() }
} else {
Vec::new()
};
let idt_hooks = if self.scan_idt {
unsafe { self.scan_idt() }
} else {
Vec::new()
};
let inline_hooks = if self.scan_inline {
unsafe { self.scan_inline_hooks() }
} else {
Vec::new()
};
let msr_hooks = if self.scan_msr {
unsafe { self.scan_msr() }
} else {
Vec::new()
};
let result = HookScanResult {
ssdt_hooks,
idt_hooks,
inline_hooks,
msr_hooks,
scan_time: 0, };
println!(
"[Leviathan] Hook scan complete: SSDT={}, IDT={}, Inline={}, MSR={}",
result.ssdt_hooks.len(),
result.idt_hooks.len(),
result.inline_hooks.len(),
result.msr_hooks.len()
);
result
}
}
#[derive(Debug)]
pub struct HookScanResult {
pub ssdt_hooks: Vec<SsdtEntry>,
pub idt_hooks: Vec<IdtEntry>,
pub inline_hooks: Vec<InlineHook>,
pub msr_hooks: Vec<MsrHook>,
pub scan_time: u64,
}
impl HookScanResult {
pub fn has_hooks(&self) -> bool {
!self.ssdt_hooks.is_empty()
|| !self.idt_hooks.is_empty()
|| !self.inline_hooks.is_empty()
|| !self.msr_hooks.is_empty()
}
pub fn total_hooks(&self) -> usize {
self.ssdt_hooks.len()
+ self.idt_hooks.len()
+ self.inline_hooks.len()
+ self.msr_hooks.len()
}
}
pub fn detect_hook_type(bytes: &[u8]) -> InlineHookType {
if bytes.len() < 5 {
return InlineHookType::Unknown;
}
if bytes[0] == 0xE9 {
return InlineHookType::JmpRel32;
}
if bytes.len() >= 6 && bytes[0] == 0xFF && bytes[1] == 0x25 {
return InlineHookType::JmpRipRelative;
}
if bytes.len() >= 6 && bytes[0] == 0x68 && bytes[5] == 0xC3 {
return InlineHookType::PushRet;
}
if bytes.len() >= 12
&& bytes[0] == 0x48
&& bytes[1] == 0xB8
&& bytes[10] == 0xFF
&& bytes[11] == 0xE0
{
return InlineHookType::MovJmp;
}
if bytes[0] == 0xCC {
return InlineHookType::Int3;
}
InlineHookType::Unknown
}
pub fn calculate_jmp_target(hook_addr: usize, bytes: &[u8]) -> Option<usize> {
if bytes.len() < 5 || bytes[0] != 0xE9 {
return None;
}
let rel32 = i32::from_le_bytes([bytes[1], bytes[2], bytes[3], bytes[4]]);
let target = (hook_addr as i64 + 5 + rel32 as i64) as usize;
Some(target)
}
#[derive(Debug, Clone)]
pub struct KernelModule {
pub base: usize,
pub size: usize,
pub name: [u8; 256],
}
pub fn find_module_for_address(_address: usize, _modules: &[KernelModule]) -> Option<&KernelModule> {
None
}
pub struct HookMonitor {
scanner: HookScanner,
baseline: Option<HookScanResult>,
alert_callback: Option<fn(&HookScanResult)>,
}
impl HookMonitor {
pub fn new() -> Self {
Self {
scanner: HookScanner::new(),
baseline: None,
alert_callback: None,
}
}
pub fn set_alert_callback(&mut self, callback: fn(&HookScanResult)) {
self.alert_callback = Some(callback);
}
pub unsafe fn capture_baseline(&mut self) -> Result<(), NTSTATUS> {
self.baseline = Some(unsafe { self.scanner.full_scan() });
println!("[Leviathan] Hook baseline captured");
Ok(())
}
pub unsafe fn check_for_changes(&self) -> Option<HookScanResult> {
let current = unsafe { self.scanner.full_scan() };
if let Some(baseline) = &self.baseline {
if current.total_hooks() > baseline.total_hooks() {
println!(
"[Leviathan] WARNING: New hooks detected! Baseline={}, Current={}",
baseline.total_hooks(),
current.total_hooks()
);
if let Some(callback) = self.alert_callback {
callback(¤t);
}
return Some(current);
}
}
None
}
}