Exploring DKOM for Process Hiding on Windows
In this post we will explore the DKOM (Direct Kernel Object Manipulation) technique, a well-known stealth method used by advanced malware and rootkits on Windows systems to hide processes from standard system monitoring tools.
What is DKOM
?
DKOM
stands for Direct Kernel Object Manipulation.
It is a technique that operates directly on kernel data structures in memory. Instead of hooking APIs or patching the kernel, DKOM modifies kernel objects in place, leaving no hooks that can be detected with integrity checks.
One common use case:
- Hiding a process by directly altering the kernel’s doubly-linked list of
EPROCESS
structures.
When a process is created in Windows, the kernel allocates an EPROCESS
structure that describes it.
Each EPROCESS
contains a member called ActiveProcessLinks
, which is a doubly-linked list (FLINK/BLINK) pointing to the previous and next process.
The Windows API functions and tools like Task Manager
, Process Explorer
or pslist
traverse this list to enumerate active processes.
By manipulating these pointers, an attacker can:
- Unlink the target process from the
ActiveProcessLinks
list:- Adjust
Flink
andBlink
pointers of the neighboring nodes so they skip the target. - The
EPROCESS
object itself remains allocated in memory, but is no longer part of the list.
- Adjust
- As a result:
- Standard enumeration APIs (
NtQuerySystemInformation
, WMI, etc.) will not report the hidden process. - Security tools relying only on this list will not see the process, although it continues to execute.
- Standard enumeration APIs (
- The hidden process can still be detected by:
- Direct memory scanning (e.g.,
Volatility psscan
) because the structure still exists in RAM. - Kernel callbacks or ETW events that do not depend on list walking.
- Direct memory scanning (e.g.,
This unlinking technique is the essence of DKOM-based process hiding. No hooks, no patching, just surgical manipulation of in-memory linked lists inside the kernel.
Hiding a Process with WinDbg
Preparing the Environment
Before experimenting with DKOM on Windows, it is crucial to work in a controlled lab environment to avoid damaging a production system.
For this PoC you will need:
- A Windows test machine (physical or virtual) dedicated to research and malware analysis.
- A VM (e.g., Hyper-V, VMware, VirtualBox) is recommended for snapshot/rollback capabilities.
- Kernel debugging must be enabled on the target machine if you plan to attach remotely.
Tools Required
WinDbg
(Preview or Classic)- Obtain from the Microsoft Store or the Windows SDK.
Administrator Privileges
- Run
WinDbg
with elevated privileges to access live kernel memory.
- Run
Target Process
- Launch a simple process (e.g.,
notepad.exe
) that will be used as a target to demonstrate hiding.
- Launch a simple process (e.g.,
✏️ Note: Recent versions of
WinDbg
(Preview) automatically configure and download symbols from the Microsoft symbol server. Manual configuration is usually not required unless you need a custom symbol path or offline cache.
Kernel Debugging Setup
If your PoC involves live kernel debugging (recommended for DKOM analysis):
- Configure debugging transport (COM, TCP, or local):
- For VMs, named pipe (COM) or network KD is most convenient.
- Enable kernel debugging on the target:
# Administrator
bcdedit /debug on
- Reboot the machine with debugging enabled.
💡 Tip: For local debugging, use
WinDbg
->File
->Start Debugging
->Attach to kernel
->Local
.
Verification
After attaching WinDbg
to the live kernel or memory dump verify the debugger connection:
!process 0 0
This should list all active processes.
With the environment ready, you will be able to inspect, modify and unlink EPROCESS
structures manually
Identify addresses to manipulate
Follow these steps to hide a process using DKOM in a controlled lab environment:
- Identify the Target Process
- Use the
!process 0 0
command in WinDbg to list all active processes. - Find the entry for your target process (e.g.,
notepad.exe
) and note itsEPROCESS
address.
- Use the
Process Name | PID | EPROCESS Address |
---|---|---|
Notepad.exe | 0x358c | ffffa00df22e60c0 |
- Locate the ActiveProcessLinks Field
- Display the structure of the
EPROCESS
object using:
- Display the structure of the
dt _EPROCESS <EPROCESS_address>
- Locate the
UniqueProcessId
field, inWindows 11 24h2
offset is+0x1d0
- Locate the
ActiveProcessLinks
field, which is part of the doubly linked list connecting all processes, inWindows 11 24h2
the offset is+0x1d8
. - Locate the
ImgageFileName
field, inWindows 11 24h2
offset is+0x338
Field | Offset (Windows 11 24h2) | Description |
---|---|---|
UniqueProcessId | EPROCESS + 0x1d0 | Unique identifier for the process |
ActiveProcessLinks | EPROCESS + 0x1d8 | Pointer to the doubly-linked list of processes |
ImageFileName | EPROCESS + 0x338 | Executable file name of the process |
- Read the
Flink
andBlink
pointers from theActiveProcessLinks
field.
# Abstract
dt nt!_EPROCESS <EPROCESS_address> ActiveProcessLinks
dq <EPROCESS_address> + <ActiveProcessLinks_offset> L2
# Our case
dt nt!_EPROCESS ffffa00df22e60c0 ActiveProcessLinks
dq ffffa00df22e60c0 + 0x1d8 L2
Process Name | PID | EPROCESS Address | ActiveProcessLinks | FLINK | BLINK |
---|---|---|---|---|---|
Notepad.exe | 0x358c | ffffa00df22e60c0 | ffffa00df22e6298 | ffffa00df7ecc258 | ffffa00dfa0e2258 |
- Gather Information on Neighboring Processes (PID and ImageFileName)
- Identify the processes
ImageFileName
immediately before and after your target in the linked list by examining theFlink
andBlink
pointers, use their respective +/- offsets (see the above table) to inspect their details:
- Identify the processes
# -0x1d8 is EPROCESS base address offset from ActiveProcessLinks member
# +0x338 is the `ImageFileName` member offset from EPROCESS base address
da ffffa00d`f7ecc258 - 0x1d8 + 0x338
da ffffa00d`fa0e2258 - 0x1d8 + 0x338
- Identify the
UniqueProcessId
andEPROCESS
addresses of the neighboring processes. - For each neighboring process, use their respective
ActiveProcessLinks
+/- offsets (see the above table) to inspect their details:
# --- Get forward process Pid - WidgetBoard.exe
dd ffffa00df7ecc258 - 0x1d8 + 0x1d0 L1
# --- Get forward process EPROCESS Address - WidgetBoard.exe
!process 2ba0 0
# --- Get forward process ActiveProcessLinks, FLINK, BLINK - WidgetBoard.exe
dt nt!_EPROCESS ffffa00df7ecc080 ActiveProcessLinks
dq ffffa00df7ecc080 + 0x1d8 L2
Position | Process Name | PID | EPROCESS Address | ActiveProcessLinks | FLINK Value +0 | BLINK Value +8 |
---|---|---|---|---|---|---|
Forward | WidgetBoard.exe | 2ba0 | ffffa00df7ecc080 | ffffa00df7ecc258 | ffffa00df1aad258 | ffffa00df22e6298 |
# --- Get backward process Pid - EngHost.exe
dd ffffa00d`fa0e2258 - 0x1d8 + 0x1d0 L1
# --- Get backward process EPROCESS Address - EngHost.exe
!process 3660 0
# --- Get backward process ActiveProcessLinks, FLINK, BLINK - EngHost.exe
dt nt!_EPROCESS ffffa00dfa0e2080 ActiveProcessLinks
dq ffffa00dfa0e2080 + 0x1d8 L2
Position | Process Name | PID | EPROCESS Address | ActiveProcessLinks | FLINK Value +0 | BLINK Value +8 |
---|---|---|---|---|---|---|
Backward | EngHost.exe | 3660 | ffffa00dfa0e2080 | ffffa00dfa0e2258 | ffffa00df22e6298 | ffffa00df3ea2258 |
- Note the
Process ID (PID)
,ImageFileName
,EPROCESS
,ActiveProcessLinks
,FLINK
,BLINK
for both neighboring processes. This ensures you are correctly identifying the links you need to update when unlinking the target process.
Position | Process Name | PID | EPROCESS Address | ActiveProcessLinks | FLINK Value +0 | BLINK Value +8 |
---|---|---|---|---|---|---|
Backward | EngHost.exe | 3660 | ffffa00dfa0e2080 | ffffa00dfa0e2258 | ffffa00df22e6298 | ffffa00df3ea2258 |
Notepad.exe | 358c | ffffa00df22e60c0 | ffffa00df22e6298 | ffffa00df7ecc258 | ffffa00dfa0e2258 | |
Forward | WidgetBoard.exe | 2ba0 | ffffa00df7ecc080 | ffffa00df7ecc258 | ffffa00df1aad258 | ffffa00df22e6298 |
Unlink the Process To manipulate these links and remove the
Notepad.exe
process from the active list, update the following pointers usingActiveProcessLinks
address:- Point
EngHost.exe
->FLINK
in ffffa00dfa0e2258
toWidgetBoard.exe
->FLINK
in ffffa00df7ecc258
eq ffffa00d`fa0e2258 ffffa00d`f7ecc258
- Point
WidgetBoard.exe
->BLINK
at ffffa00df7ecc258 + 8
toEngHost.exe
->FLINK
at ffffa00dfa0e2258
.
# ActiveProcessLinks+8 because LIST_ENTRY has two FLINK/BLINK fields and each is 8 bytes eq ffffa00d`f7ecc258 + 8 ffffa00d`fa0e2258
- Point
Verify the Process is Hidden
- Run
ps | findstr -i notepad
in teminal or usingTaskManager
. The target process (Notepad.exe
) should no longer appear in the list, even though it is still running.
- Run
⚠️ Warning:
Modifying kernel memory can destabilize or crash the system. Always work in a disposable test environment and take snapshots before making changes.
This procedure demonstrates how DKOM
can be used to hide a process by manipulating kernel data structures directly.
How to detect DKOM for Process Hiding on Windows
In order to detect unlinked processes exhibited by malware on systems without PatchGuard, explore psscan
and psxview
from Volatility
.
You need to acquire a memory image. One of the most popular tools for this on Windows is winpmem
.
Download winpmem
Get the latest release from the official GitHub repository.
Run as Administrator
Open a command prompt with administrative privileges.
Acquire the Memory Dump
Use the following command to dump memory from the target host to a file:
.\winpmem_mini_x64_rc2.exe dump.raw
Check the Image
Install Volatility
on linux forensic machine and use windows.info
module
python vol.py -f ../dump.raw windows.info
Analyze with Volatility
As we can see, running the windows.pslist
module and filtering for the process name notepad
returns no results, meaning the process is not visible to standard enumeration:
python vol.py -f ../dump.raw windows.pslist | grep -i notepad
No output is produced, confirming that notepad.exe
is hidden from pslist
due to DKOM unlinking. However, by using psscan
or psxview
, you can still detect the hidden process because these modules scan memory for EPROCESS
structures directly, rather than relying on the linked list.
To detect hidden processes using psscan
in Volatility and redirect the output to a file, run:
python vol.py -f ../dump.raw windows.psscan > psscan.txt
cat psscan.txt | grep -i notepad
This command scans memory for EPROCESS
structures, revealing processes that have been unlinked from the active process list.
Here, notepad.exe
is found in memory with PID 10188
(different test from the previous with Windbg
), confirming that the process exists even though it was hidden from standard process listings.
You can also use the psxview
module in Volatility to compare multiple process enumeration techniques in a single output. This module shows which processes are visible to each method, making DKOM
-based hiding immediately apparent.
python vol.py -f ../dump.raw windows.psxview > psxview.txt
cat psxview.txt | grep -i notepad
The output will display columns for each enumeration method (such as pslist
, psscan
, thrdproc
, etc.). If a process is hidden via DKOM
, you will see False
under pslist
but True
under psscan
, confirming the discrepancy:
This makes psxview
a powerful module for quickly identifying processes hidden by DKOM
, as it highlights inconsistencies across different detection techniques in a single table.
Conclusion
In this post, we explored the fundamentals of DKOM (Direct Kernel Object Manipulation)
for process hiding
on Windows, including a practical walkthrough using WinDbg
and detection strategies with Volatility
. DKOM
remains a powerful and stealthy technique for evading standard process enumeration, but it is not foolproof—memory forensics and advanced detection tools can still reveal hidden processes. By learning how DKOM works and how to detect it, you can better protect systems against sophisticated threats.
Thank you for your attention! 😊
If you have questions or want to share your experience with DKOM
or process hiding techniques, feel free to leave a comment or reach out on GitHub.