Difficulty: Beginner

Module 1: Kernel vs User-Mode Evasion

Why ring 0 access is the ultimate evasion primitive and the cost of getting there.

Module Objective

Understand the architectural separation between user mode (ring 3) and kernel mode (ring 0) on Windows, why kernel-level rootkits like Nidhogg can evade virtually all user-mode security products, what Driver Signature Enforcement (DSE) is, and the realistic attack paths for loading unsigned drivers.

1. The x86 Privilege Ring Model

Intel and AMD processors implement a hardware privilege system with four rings (0 through 3). Windows uses only two of these:

RingNameRuns WhatAccess Level
Ring 0Kernel Modentoskrnl.exe, HAL, kernel drivers (.sys files)Full hardware access, all memory, all instructions
Ring 3User ModeApplications, services, DLLs, user-mode EDR agentsRestricted: no direct hardware, isolated address space, filtered syscalls

The transition between ring 3 and ring 0 happens through system calls (the syscall instruction on x64). When a user-mode application calls a Windows API like NtOpenProcess, execution transfers to the kernel through the SSDT (System Service Descriptor Table), runs in ring 0, and returns the result.

Ring Transition via Syscall

User-Mode App
Ring 3
syscall
CPU trap
KiSystemCall64
Ring 0 entry
Nt* Handler
Kernel routine

2. Why User-Mode Evasion Is a Losing Battle

Most offensive tools (Cobalt Strike, Meterpreter, custom loaders) operate in user mode. They must evade user-mode EDR hooks, API monitoring, ETW tracing, and memory scanning — all from the same privilege level. This creates an inherent disadvantage:

Evasion ChallengeUser-Mode (Ring 3)Kernel-Mode (Ring 0)
API HookingMust unhook ntdll.dll, risk detection by hook integrity checksCan prevent hooks from being installed, or silently bypass them
ETW TelemetryCan patch EtwEventWrite in own process, but EDR gets kernel-level ETWCan disable ETW providers at the source in kernel memory
Process EnumerationCannot hide from NtQuerySystemInformationCan unlink EPROCESS from ActiveProcessLinks, invisible to all user-mode queries
Memory ScanningCannot prevent EDR kernel driver from reading process memoryCan manipulate VAD entries and PTEs to hide memory regions
File ProtectionACLs can be overridden by SYSTEM/adminCan intercept IRP_MJ_CREATE at the filesystem level, denying all access

The Core Asymmetry

A kernel driver runs at the same privilege level as the EDR kernel driver. It can read, modify, or nullify anything the EDR does. User-mode tools can never reach up to touch kernel data structures. This asymmetry is why kernel rootkits remain the most powerful evasion mechanism on Windows.

3. What a Kernel Rootkit Can Do

Nidhogg demonstrates the full range of capabilities available to a kernel-mode rootkit. Each capability maps to a specific kernel mechanism:

Nidhogg Capability Map

CapabilityKernel MechanismModule
Hide processesDKOM: unlink EPROCESS from ActiveProcessLinks3
Protect processes from terminationObRegisterCallbacks: strip PROCESS_TERMINATE from handles3, 6
Hide/protect filesIRP major function hooking on filesystem driver4
Protect registry keysCmRegisterCallbackEx: block registry operations4
Blind EDR telemetryDisable ETW trace providers in kernel memory5
Remove EDR callbacksLocate and null callback arrays (PsSetCreateProcessNotifyRoutine, etc.)6
Evade memory scannersVAD manipulation (PTE manipulation is a general kernel technique, not a core Nidhogg feature)7
Elevate process tokensDirect token pointer replacement in EPROCESS3

4. The Kernel Address Space

On 64-bit Windows, the virtual address space is divided cleanly between user mode and kernel mode:

TextVirtual Address Space (x64 Windows)
=============================================
0x0000000000000000 - 0x00007FFFFFFFFFFF   User space   (128 TB)
  - Per-process, isolated
  - Accessible from ring 3
  - Contains: EXE, DLLs, heap, stack, TEB/PEB

0xFFFF800000000000 - 0xFFFFFFFFFFFFFFFF   Kernel space (128 TB)
  - Shared across all processes
  - Accessible ONLY from ring 0
  - Contains: ntoskrnl, HAL, kernel drivers,
    pool allocations, system PTEs, EPROCESS/ETHREAD

Key insight: kernel address space is shared across all processes. When Nidhogg's driver modifies an EPROCESS structure, that change is visible system-wide because every process maps the same kernel memory. There is no per-process isolation in kernel space.

5. Driver Signature Enforcement (DSE)

Microsoft introduced Driver Signature Enforcement to prevent unauthorized kernel code from loading. Starting with Windows Vista x64, all kernel drivers must be signed with a valid certificate:

Signing LevelRequirementWho Can Get It
WHQLMicrosoft Hardware Quality Labs certificationHardware vendors through Microsoft's portal
AttestationEV code signing certificate + Microsoft attestation signatureDevelopers with EV certificates (purchased from CAs)
Test SigningSelf-signed with test certificate (requires test mode boot)Anyone (but system must boot with TESTSIGNING ON)

DSE is enforced by the Code Integrity (CI) module (ci.dll). When NtLoadDriver is called, CI validates the driver's Authenticode signature against the minimum signing level before allowing the image to load into kernel space.

C// Simplified view of what CI checks during driver load
// ci!CiValidateImageHeader is called from ntoskrnl
NTSTATUS CiValidateImageHeader(
    PVOID ImageBase,
    ULONG ImageSize,
    ULONG ImageSigningLevel,  // Minimum required level
    // ... additional parameters
);
// Returns STATUS_INVALID_IMAGE_HASH if signature check fails

6. Bypassing DSE: Attack Paths

Despite DSE, attackers have several paths to load unsigned kernel drivers. These are the realistic vectors that red teams use:

BYOVD — Bring Your Own Vulnerable Driver

Load a legitimately signed but vulnerable driver (e.g., one with an arbitrary read/write primitive), then exploit it to map unsigned code into kernel memory. The signed driver passes DSE; the payload never goes through NtLoadDriver. Tools like kdmapper automate this using drivers such as Intel's iqvw64e.sys.

Bypass MethodHow It WorksDetectability
BYOVD (kdmapper)Exploit a signed vulnerable driver to manually map unsigned codeModerate: vulnerable driver loads are logged, Microsoft maintains a blocklist
Test Signing ModeBoot with bcdedit /set testsigning onHigh: watermark on desktop, NtQuerySystemInformation exposes it
Leaked/Stolen CertificatesSign with a compromised legitimate code signing certLow initially, high once cert is revoked and added to CRL
Boot Configuration AbuseModify BCD to disable integrity checks during bootHigh: requires admin + reboot, Secure Boot blocks this
HyperV/VBS WeaknessesExploit pre-VBS systems that lack HVCI enforcementVaries: older Windows 10 builds without HVCI are vulnerable

Nidhogg and DSE

Nidhogg itself is distributed as source code. To use it operationally, the red team must compile it and find a way to load it. The project does not ship a DSE bypass — that is left to the operator. During development and testing, test signing mode or kdmapper are common choices. In a real engagement, BYOVD is the most practical path.

7. HVCI and Kernel Code Integrity

Hypervisor-protected Code Integrity (HVCI), part of Virtualization-Based Security (VBS), adds a second layer of defense beyond DSE:

TextWithout HVCI:
  Ring 0 code can allocate RWX kernel memory and execute arbitrary code

With HVCI:
  The hypervisor (ring -1) enforces W^X in kernel space
  - Pages can be writable OR executable, never both
  - New kernel code pages must be validated by Secure Kernel
  - Even a kernel driver cannot simply allocate and run shellcode

HVCI significantly raises the bar for kernel rootkits. On systems with HVCI enabled, manually mapped drivers (like kdmapper output) may fail because the hypervisor blocks execution of unsigned kernel pages. However, many enterprise environments still lack HVCI, and older Windows 10 builds do not enable it by default.

8. PatchGuard (Kernel Patch Protection)

PatchGuard (KPP) is a kernel integrity monitoring system that periodically checks critical kernel structures for unauthorized modifications:

What PatchGuard Monitors

When PatchGuard detects a modification, it triggers CRITICAL_STRUCTURE_CORRUPTION (bug check 0x109), crashing the system with a blue screen. This means Nidhogg must carefully avoid techniques that PatchGuard monitors. For example, Nidhogg uses DKOM (modifying linked list pointers) rather than SSDT hooking, because SSDT modifications are reliably detected by PatchGuard. However, DKOM is not guaranteed to be PatchGuard-safe: PatchGuard's monitoring scope varies between Windows builds and can include checks on kernel data structures like EPROCESS linked lists. Nidhogg's own documentation acknowledges that PatchGuard can potentially detect DKOM modifications. In practice, DKOM is less likely to be caught than SSDT hooking, but it is not undetectable.

9. Nidhogg Architecture Overview

Nidhogg is structured as two components that communicate via IOCTLs:

Nidhogg Two-Component Architecture

NidhoggClient.exe
User-mode C++ client
Ring 3
→ IOCTL →
Nidhogg.sys
Kernel driver
Ring 0
Kernel Structures
EPROCESS, callbacks,
ETW, IRP hooks

The user-mode client sends commands (hide process, protect file, disable ETW, etc.) through DeviceIoControl calls. The kernel driver receives these as I/O Request Packets (IRPs) with IOCTL codes, executes the requested operation using kernel APIs and data structure manipulation, and returns the result. This architecture is covered in detail in Module 2 (driver basics) and Module 8 (full IOCTL table).

Knowledge Check

Q1: Why can a kernel-mode rootkit evade user-mode EDR agents?

A) Kernel drivers run faster than user-mode processes
B) Ring 0 code has full access to all system memory and can modify kernel structures that ring 3 code cannot see or alter
C) EDR agents do not monitor kernel drivers at all
D) Kernel mode disables all security software automatically

Q2: What is BYOVD?

A) A method to sign drivers with a stolen certificate
B) A technique to disable PatchGuard at boot time
C) Loading a legitimately signed but vulnerable driver and exploiting it to map unsigned kernel code
D) A Windows API for loading test-signed drivers

Q3: Why does Nidhogg use DKOM instead of SSDT hooking to hide processes?

A) PatchGuard reliably monitors the SSDT and would trigger a BSOD; DKOM modifications to EPROCESS linked lists are less consistently monitored, though still not guaranteed to be PatchGuard-safe
B) SSDT hooking is not possible on 64-bit Windows
C) DKOM is faster than SSDT hooking
D) SSDT hooking requires a user-mode component