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)
This commit is contained in:
@@ -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<Vec<HookInfo>> {
|
||||
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::<windows::Win32::Foundation::HMODULE>()) 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::<windows::Win32::Foundation::HMODULE>();
|
||||
|
||||
// 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::<MODULEINFO>() 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<Vec<u8>> {
|
||||
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 {
|
||||
|
||||
Reference in New Issue
Block a user