ShadowStrikePhantom
Back to Research
FeaturedKernelProcess InjectionDetectionEngineering

How PhantomSensor Detects Process Injection in the Kernel

February 24, 2026
12 min read
ShadowStrike

Process injection is one of the most prevalent techniques in modern malware. From APT campaigns to commodity ransomware, adversaries rely on injecting code into legitimate processes to blend in, evade process-based detections, and inherit elevated privileges.

User-space EDR hooks — patching NtWriteVirtualMemory in every process — are effective but bypassable. The real answer is detecting injection from the kernel before user-space ever sees it.

The Core Primitive: Handle Callbacks

Windows provides ObRegisterCallbacks — a kernel API that fires whenever a process handle is opened or duplicated. PhantomSensor registers a pre-operation callback for PsProcessType that intercepts every OpenProcess call system-wide.

HandleProtection.cC · Kernel
OB_PREOP_CALLBACK_STATUS
PhHandlePreCallback(
    PVOID RegistrationContext,
    POB_PRE_OPERATION_INFORMATION OpInfo
) {
    // Strip PROCESS_VM_WRITE access from handles
    // opened by non-trusted callers
    if (OpInfo->Operation == OB_OPERATION_HANDLE_CREATE) {
        if (IsTargetProtectedProcess(OpInfo->Object)) {
            OpInfo->Parameters->CreateHandleInformation
                .DesiredAccess &= ~PROCESS_VM_WRITE;
            OpInfo->Parameters->CreateHandleInformation
                .DesiredAccess &= ~PROCESS_VM_OPERATION;
        }
    }
    return OB_PREOP_SUCCESS;
}

This alone doesn't detect injection — it prevents it for protected processes. For detection across all processes, PhantomSensor combines handle tracking with VAD tree monitoring.

VAD Tree as a Detection Signal

Every time memory is committed in a process, the kernel adds an entry to the Virtual Address Descriptor (VAD) tree, which tracks the purpose and permissions of each virtual memory region. A classic injection pattern —VirtualAllocExfollowed byWriteProcessMemory— leaves a distinctive VAD signature: a region that is both writable and executable (or later made executable via VirtualProtectEx).

PhantomSensor's VadTracker monitors VAD insertions from the kernel and flags any allocation with PAGE_EXECUTE_READWRITE that originates from a process other than the target.

Correlating into the BehaviorEngine

A single VAD flag isn't a conviction. The real power comes from the BehaviorEngine correlating multiple signals across a temporal window:

  • 01.OpenProcess with PROCESS_VM_WRITE from process A targeting process B
  • 02.VirtualAllocEx in process B by process A (PAGE_EXECUTE_READWRITE or later flipped to executable)
  • 03.WriteProcessMemory call from process A into process B
  • 04.CreateRemoteThread or QueueUserAPC initiating execution in process B

When all four are observed within a configurable time window from the same source PID, the BehaviorEngine fires with a threat score of 90+ and maps to MITRE ATT&CK T1055 (Process Injection).

What About Reflective DLL Injection?

Reflective injection doesn't use CreateRemoteThread — the DLL maps and executes itself. PhantomSensor handles this through ShellcodeDetector, which scans executable non-backed memory regions (memory with no file mapping — pure private commit) for PE headers and known shellcode patterns. Reflective loaders almost always contain a PE header at offset 0 or a well-known bootstrap stub.

Source Code

The full implementation is available in ShadowSensor/Memory/InjectionDetector.c, VadTracker.c, and ShadowSensor/SelfProtection/HandleProtection.c in the GitHub repository.

View on GitHub