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
|
// Estimate global hooks based on system state
|
||||||
let global_hooks = estimate_global_hooks();
|
let global_hooks = estimate_global_hooks();
|
||||||
if global_hooks > 10 {
|
if global_hooks > 10 {
|
||||||
@@ -329,6 +340,150 @@ mod platform {
|
|||||||
3
|
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.
|
/// Get hook type name for display.
|
||||||
pub fn get_hook_type_name(hook_type: u32) -> &'static str {
|
pub fn get_hook_type_name(hook_type: u32) -> &'static str {
|
||||||
match hook_type {
|
match hook_type {
|
||||||
|
|||||||
Reference in New Issue
Block a user