From 461bc1fb80c7973659b858c71ecff43e41cd930b Mon Sep 17 00:00:00 2001 From: Adir Shitrit Date: Fri, 21 Nov 2025 00:46:30 +0200 Subject: [PATCH] Integrate IAT hook detection into detection pipeline - Added IAT hook scanning for all loaded modules in process - Compare memory IAT entries with disk versions to detect hooks - Enumerate modules and scan each for IAT modifications - Automatic System32/SysWOW64 path resolution for DLLs - Detailed logging of hook detection results - Track hooked imports with function names and addresses - Support for both 32-bit and 64-bit modules Generated with [Claude Code](https://claude.com/claude-code) --- ghost-core/src/hooks.rs | 155 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 155 insertions(+) diff --git a/ghost-core/src/hooks.rs b/ghost-core/src/hooks.rs index 3c86062..2b80093 100644 --- a/ghost-core/src/hooks.rs +++ b/ghost-core/src/hooks.rs @@ -128,6 +128,17 @@ mod platform { } } + // Detect IAT hooks + match detect_iat_hooks_for_process(target_pid) { + Ok(iat_hooks) => { + suspicious_count += iat_hooks.len(); + hooks.extend(iat_hooks); + } + Err(e) => { + log::debug!("Failed to detect IAT hooks: {}", e); + } + } + // Estimate global hooks based on system state let global_hooks = estimate_global_hooks(); if global_hooks > 10 { @@ -329,6 +340,150 @@ mod platform { 3 } + /// Detect IAT hooks in loaded modules + fn detect_iat_hooks_for_process(target_pid: u32) -> Result> { + let mut hooks = Vec::new(); + + unsafe { + let handle = OpenProcess( + PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, + false, + target_pid, + ) + .map_err(|e| GhostError::Detection { + message: format!("Failed to open process for IAT scan: {}", e), + })?; + + // Get loaded modules + let mut modules = [windows::Win32::Foundation::HMODULE::default(); 1024]; + let mut cb_needed = 0u32; + + if EnumProcessModulesEx( + handle, + modules.as_mut_ptr(), + (modules.len() * std::mem::size_of::()) as u32, + &mut cb_needed, + LIST_MODULES_ALL, + ) + .is_err() + { + let _ = CloseHandle(handle); + return Ok(hooks); + } + + let module_count = + (cb_needed as usize) / std::mem::size_of::(); + + // Check IAT for each module + for module in modules.iter().take(module_count) { + let mut mod_info = MODULEINFO::default(); + if GetModuleInformation( + handle, + *module, + &mut mod_info, + std::mem::size_of::() as u32, + ) + .is_err() + { + continue; + } + + let base_address = mod_info.lpBaseOfDll as usize; + + // Get module filename for disk comparison + let mut name_buffer = [0u16; 1024]; + if GetModuleBaseNameW(handle, *module, &mut name_buffer) == 0 { + continue; + } + + let module_name = String::from_utf16_lossy( + &name_buffer[..name_buffer + .iter() + .position(|&c| c == 0) + .unwrap_or(name_buffer.len())], + ); + + // Create memory reader closure + let memory_reader = |pid: u32, addr: usize, size: usize| -> Result> { + let handle = OpenProcess(PROCESS_VM_READ, false, pid) + .map_err(|e| GhostError::MemoryReadError(format!("OpenProcess failed: {}", e)))?; + + let mut buffer = vec![0u8; size]; + let mut bytes_read = 0usize; + + let result = ReadProcessMemory( + handle, + addr as *const _, + buffer.as_mut_ptr() as *mut _, + size, + Some(&mut bytes_read), + ); + + let _ = CloseHandle(handle); + + if result.is_ok() && bytes_read > 0 { + buffer.truncate(bytes_read); + Ok(buffer) + } else { + Err(GhostError::MemoryReadError("ReadProcessMemory failed".to_string())) + } + }; + + // Try to find the module file on disk + let system32 = std::env::var("SystemRoot") + .unwrap_or_else(|_| "C:\\Windows".to_string()) + + "\\System32\\"; + + let possible_paths = vec![ + system32.clone() + &module_name, + format!("C:\\Windows\\SysWOW64\\{}", module_name), + ]; + + for disk_path in possible_paths { + if std::path::Path::new(&disk_path).exists() { + match crate::pe_parser::detect_iat_hooks( + target_pid, + base_address, + &disk_path, + memory_reader, + ) { + Ok(iat_result) => { + log::info!( + "IAT scan for {}: {}/{} imports hooked ({:.1}%)", + module_name, + iat_result.hooked_imports.len(), + iat_result.total_imports, + iat_result.hook_percentage + ); + + for hooked_import in iat_result.hooked_imports { + hooks.push(HookInfo { + hook_type: HookType::IATHook, + thread_id: 0, + hook_proc: hooked_import.current_address, + original_address: hooked_import.iat_address, + module_name: hooked_import.dll_name.clone(), + hooked_function: hooked_import + .function_name + .unwrap_or_else(|| format!("Ordinal_{}", hooked_import.ordinal.unwrap_or(0))), + }); + } + break; + } + Err(e) => { + log::debug!("IAT scan failed for {}: {:?}", module_name, e); + } + } + } + } + } + + let _ = CloseHandle(handle); + } + + Ok(hooks) + } + /// Get hook type name for display. pub fn get_hook_type_name(hook_type: u32) -> &'static str { match hook_type {