Files
safedv-RustiveDump/src/ntapi/process.rs
T
2024-10-06 18:01:51 +02:00

150 lines
5.3 KiB
Rust

#[cfg(feature = "verbose")]
use libc_print::libc_println;
use alloc::vec::Vec;
use core::{ffi::c_void, ptr::null_mut};
use crate::{common::utils::dbj2_hash, debug_println};
use super::{
def::{
AccessMask, ClientId, ObjectAttributes, SystemProcessInformation, OBJ_CASE_INSENSITIVE,
STATUS_INFO_LENGTH_MISMATCH,
},
g_instance::instance,
};
/// Takes a snapshot of the currently running processes.
///
/// This function utilizes the `NtQuerySystemInformation` function from the NT API to retrieve
/// information about all processes currently running on the system. It first determines the necessary
/// buffer size, then allocates memory, and finally retrieves the process information.
pub unsafe fn nt_process_snapshot(
snapshot: &mut *mut SystemProcessInformation,
size: &mut usize,
) -> i32 {
let mut length: u32 = 0;
// First call to determine the required length of the buffer for process information.
let mut status =
instance()
.ntdll
.nt_query_system_information
.run(5, null_mut(), 0, &mut length);
// Check if the call returned STATUS_INFO_LENGTH_MISMATCH (expected) or another error.
if status != STATUS_INFO_LENGTH_MISMATCH && status != 0 {
return status;
}
// Allocate memory for the SystemProcessInformation structure.
let mut buffer = Vec::new();
buffer.resize(length as usize, 0);
// Second call to actually retrieve the process information into the allocated buffer.
status = instance().ntdll.nt_query_system_information.run(
5,
buffer.as_mut_ptr() as *mut c_void,
length,
&mut length,
);
// Check if the process information retrieval was successful.
if status != 0 {
return status;
}
// Cast the buffer to the SystemProcessInformation structure.
*snapshot = buffer.as_mut_ptr() as *mut SystemProcessInformation;
*size = length as usize;
// Keep the buffer alive by preventing its deallocation.
core::mem::forget(buffer);
status
}
/// Retrieves a handle to a process with the specified PID and desired access rights using the NT API.
///
/// This function opens a handle to a target process by specifying its process ID (PID) and the desired access rights.
/// The syscall `NtOpenProcess` is used to obtain the handle, and the function initializes the required structures
/// (`OBJECT_ATTRIBUTES` and `CLIENT_ID`) needed to make the system call.
pub unsafe fn get_process_handle(pid: i32, desired_access: AccessMask) -> *mut c_void {
let mut process_handle: *mut c_void = null_mut();
// Initialize object attributes for the process, setting up the basic structure with default options.
let mut object_attributes = ObjectAttributes::new();
ObjectAttributes::initialize(
&mut object_attributes,
null_mut(), // No name for the object.
OBJ_CASE_INSENSITIVE, // Case-insensitive name comparison.
null_mut(), // No root directory.
null_mut(), // No security descriptor.
);
// Initialize client ID structure with the target process ID.
let mut client_id = ClientId::new();
client_id.unique_process = pid as _;
// Perform a system call to NtOpenProcess to obtain a handle to the specified process.
instance().ntdll.nt_open_process.run(
&mut process_handle,
desired_access,
&mut object_attributes,
&mut client_id as *mut _ as *mut c_void,
);
process_handle
}
/// Retrieves a handle to a process with the specified name hash and desired access rights using the NT API.
///
/// This function takes a process name hash, searches for its process ID (PID) using the `process_snapshot` function,
/// and then opens a handle to the process using `NtOpenProcess`.
pub unsafe fn get_process_handle_by_name(name: u32, desired_access: AccessMask) -> *mut c_void {
let mut snapshot: *mut SystemProcessInformation = null_mut();
let mut size: usize = 0;
// Take a snapshot of all currently running processes.
let status = nt_process_snapshot(&mut snapshot, &mut size);
if status != 0 {
debug_println!(
"Failed to retrieve process snapshot: NTSTATUS: 0x{:X}",
status
);
return null_mut();
}
let mut current = snapshot;
while !current.is_null() {
// Get the address of the process name.
let name_addr = (*current).image_name.buffer;
let name_len = (*current).image_name.length as usize;
// Only compare if the process has a name.
if !name_addr.is_null() && name_len > 0 {
// Convert the process name to a byte slice.
let name_slice: &[u8] = core::slice::from_raw_parts(name_addr as _, name_len);
// Calculate the hash of the process name.
let hash = dbj2_hash(name_slice);
// If the hash matches the provided hash, open a handle to the process.
if name == hash {
return get_process_handle((*current).unique_process_id as i32, desired_access);
}
}
// Move to the next process in the list.
if (*current).next_entry_offset == 0 {
break;
}
current = (current as *const u8).add((*current).next_entry_offset as usize)
as *mut SystemProcessInformation;
}
null_mut()
}