D
DioProcess

PspCidTable Enumeration

Ring 0

Enumerate all processes and threads via the kernel's CID (Client ID) handle table, bypassing usermode hiding techniques.

Hidden Process Detection

PspCidTable enumeration can detect processes hidden via DKOM (Direct Kernel Object Manipulation) because it uses a different data structure than the ActiveProcessLinks list.

Overview

The PspCidTable is a kernel handle table that maintains references to all processes and threads by their Client ID (PID/TID). Unlike the ActiveProcessLinks list, which is commonly manipulated by rootkits, the CID table is harder to tamper with and provides a more complete view.

CidEntry Structure

crates/callback/src/pspcidtable.rs
pub struct CidEntry {
    pub id: u32,                    // PID (for processes) or TID (for threads)
    pub object_address: u64,        // EPROCESS or ETHREAD kernel address
    pub object_type: CidObjectType, // Process or Thread
    pub parent_pid: u32,            // Parent PID (processes) or owning PID (threads)
    pub process_name: [u8; 16],     // ImageFileName from EPROCESS (15 chars + null)
}

pub enum CidObjectType {
    Process,
    Thread,
}

Implementation

The driver uses signature scanning to dynamically locate PspCidTable, avoiding hardcoded offsets that break across Windows versions:

Algorithm
// 1. Find PspCidTable via signature scanning
//    Search ntoskrnl.exe for pattern that references PspCidTable
PVOID PspCidTable = FindPattern(ntoskrnl, pattern, mask);

// 2. Walk the handle table
//    PspCidTable is an HANDLE_TABLE structure
for (each handle in table) {
    // 3. Decode handle entry to get EPROCESS/ETHREAD pointer
    PVOID Object = DecodeHandleEntry(entry);
    
    // 4. Determine object type
    POBJECT_TYPE ObjectType = ObGetObjectType(Object);
    
    if (ObjectType == *PsProcessType) {
        // 5. Read EPROCESS fields
        entry.id = PsGetProcessId(Object);
        entry.parent_pid = PsGetProcessInheritedFromUniqueProcessId(Object);
        memcpy(entry.process_name, Object + ImageFileNameOffset, 16);
        entry.object_address = (ULONG64)Object;
        entry.object_type = Process;
    }
    else if (ObjectType == *PsThreadType) {
        // 6. Read ETHREAD fields
        entry.id = PsGetThreadId(Object);
        entry.parent_pid = PsGetProcessId(PsGetThreadProcess(Object));
        entry.object_address = (ULONG64)Object;
        entry.object_type = Thread;
    }
}

API

Usage
use callback::enumerate_pspcidtable;

// Enumerate all CID entries
let entries: Vec<CidEntry> = enumerate_pspcidtable()?;

// Filter processes only
let processes: Vec<_> = entries.iter()
    .filter(|e| e.object_type == CidObjectType::Process)
    .collect();

// Find hidden processes (compare to ToolHelp32 enumeration)
let toolhelp_pids: HashSet<u32> = get_toolhelp_processes();
let hidden: Vec<_> = processes.iter()
    .filter(|p| !toolhelp_pids.contains(&p.id))
    .collect();

IOCTL

IOCTLCodeInputOutput
ENUM_PSPCIDTABLE0x0022203CNoneArray of CidEntry

UI Features

Access via Kernel Utilities tab → PspCidTable sub-tab:

  • Type filter — All, Processes, or Threads buttons
  • Columns — Type, ID, Process Name, Object Address, Parent/Owner PID
  • Sorting — Click column headers to sort
  • Search filter — Filter by name, ID, address, or parent PID
  • CSV export — Export to pspcidtable.csv
  • Context menu — Copy ID, Copy Process Name, Copy Object Address
  • Color coding — Green "Process" label, blue "Thread" label

Detecting Hidden Processes

To detect DKOM-hidden processes, compare results with standard enumeration:

  1. Enumerate processes via ToolHelp32 (CreateToolhelp32Snapshot)
  2. Enumerate processes via PspCidTable
  3. Processes in PspCidTable but NOT in ToolHelp32 are likely hidden via DKOM

Note: The DioProcess UI shows both enumerations for easy comparison.

Use Cases

  • • Detect rootkits hiding processes via ActiveProcessLinks unlinking
  • • View raw EPROCESS/ETHREAD kernel addresses
  • • Forensic analysis of running system
  • • Security research on process hiding techniques
  • • Enumerate system threads (PID 4)

Limitations

  • • Cannot detect processes hidden via PspCidTable manipulation (very rare)
  • • Signature scanning may need updates for new Windows versions
  • • Read-only: cannot unhide or manipulate entries