Skip to main content
Xsec

EDR Internals: Telemetry Architecture and Evasion Boundaries

Published on 32 min read

A source-based analysis of Windows Endpoint Detection and Response (EDR) telemetry paths, their documented semantics, enforcement limits, and red-team validation methodology.

Consider one short sequence: a process starts, maps an image, opens a handle to another process, creates a thread, writes a file, and establishes a network connection. From an operator’s perspective, this may be one action. Windows exposes it as separate state transitions, observed at different layers, at different times, and with different guarantees. An EDR does not receive an “attack” from the operating system. It reconstructs one from incomplete events.

That distinction is the central thread of this article. The useful question is not merely whether a callback or hook exists, but what the mechanism can know at that instant, what it can change, what can be lost before analysis, and which independent signals remain if it is bypassed.

Endpoint Detection and Response products combine multiple observation and enforcement mechanisms. No single architecture describes every product: sensor placement, event schemas, buffering, cloud dependencies, and response capabilities remain implementation-specific. The Windows interfaces on which many products rely are nevertheless documented well enough to establish a precise baseline.

This article separates three evidence classes:

  1. Documented contracts are public Windows application programming interfaces (API), device driver interfaces (DDI), callback signatures, and ordering rules. These are the strongest basis for architectural claims.
  2. Observed implementation details include private symbols, internal arrays, structure offsets, and reverse-engineered product routines. They are valid only for the examined Windows and product build.
  3. Product behavior includes the telemetry actually retained, enriched, transmitted, and evaluated by a specific EDR version and policy. It must be measured rather than inferred from callback availability.

The sixth edition of Windows Internals supplied with the research material describes Windows 7 and Windows Server 2008 R2. It remains useful for architectural concepts, but it is not a current implementation contract. The current Windows Driver Kit (WDK) documentation and the seventh edition of Windows Internals take precedence for supported interfaces. The Evasion Lab manual was used as a secondary source for WinDbg and reverse-engineering methodology; its private symbols, offsets, and lab observations are explicitly treated as build-specific.

WarningEvidence boundary

An undocumented structure observed in WinDbg establishes behavior for the examined build only. A supported DDI establishes a contract; a vendor implementation still requires direct measurement.

Layered EDR Architecture

An EDR architecture is easier to understand when the places where behavior occurs are separated from the places where it can be observed and interpreted. A product commonly combines a user-mode service with one or more kernel-mode drivers, then sends some of their events to a local or remote analysis engine. This is a recurring design model, not a shared specification for every vendor.

Two execution domains

User mode hosts ordinary applications and most services. Each process has its own address space and cannot directly access kernel memory. A user-mode sensor can observe functions traversed by a process, inspect selected memory regions, or receive events published by Windows. Its visibility still depends on which process is instrumented and which execution path is actually taken.

Kernel mode hosts the Windows core and device drivers. It arbitrates operations such as process creation, object access, file-system requests, and parts of network processing. An EDR driver can register with documented facilities to be called when those transitions occur. This position is closer to operating-system state, but it provides neither automatic knowledge of intent nor universal authority to block every operation.

The two domains communicate through system calls, input/output (I/O) requests, kernel-controlled objects, and event queues. They are therefore not two complete copies of the same reality: each layer exposes different context and different guarantees.

What each component does

ComponentWhat it representsTechnical purposeImportant limitation
Observed applicationThe process in which behavior runs: a browser, script host, administration tool, or native binaryProduces the calls, image mappings, memory accesses, files, and connections that may become signalsAn application does not describe operator intent by itself
User-mode sensorA module loaded or injected into selected processes, or a component consuming their interfacesInstruments API paths, captures call context, and may inspect content or memoryA call that does not cross the instrumented path can evade that specific sensor
Local EDR serviceThe durable process coordinating the endpoint agentReceives events, normalizes fields, enriches identities, applies local policy, maintains queues, and communicates with the backendA driver notification is not necessarily retained, enriched, or transmitted
EDR driverA privileged module loaded into the Windows kernelRegisters callbacks, file filters, or network components and can make selected documented synchronous decisionsA driver observes only the facilities it registered with and the operations those facilities expose
Event Tracing for Windows (ETW)The Windows tracing infrastructure built from providers, sessions, controllers, and consumersCarries structured kernel or application events to a real-time consumer or trace fileA registered provider is not necessarily enabled; an active session does not guarantee retention of every event
Antimalware Scan Interface (AMSI)A content-inspection interface integrated by selected applications, including script enginesSubmits decrypted or reconstructed content to an antimalware provider at a point chosen by the host applicationAMSI is neither an ETW provider nor a universal sensor for every process
File-system minifilterA driver attached to Windows Filter Manager for selected file-system operationsObserves or influences create, read, write, rename, or delete operations according to its registrationAltitude orders filters; it does not prove that an event is analyzed before every other layer
Windows Filtering Platform (WFP)The filtering platform in the Windows networking stackClassifies traffic at defined layers and can invoke an EDR callout selected by a filterAvailable metadata and blocking authority depend on the invoking WFP layer
Analysis backendA local or remote engine receiving normalized eventsCorrelates sensors, maintains state, enriches reputation, and produces detections or responsesTransport, latency, retention, and policy can change what an analyst ultimately sees

ETW, AMSI, kernel callbacks, minifilters, and WFP are therefore not synonyms for “EDR.” They are sources and control points that a product may combine. The local service and backend turn those observations into entities, relationships, alerts, and actions.

Technical diagram
LAYERED EDR ARCHITECTURE: Independent sensors become useful when a service correlates their partial observations.ENDPOINT ARCHITECTURE · TELEMETRY PIPELINELAYERED EDR ARCHITECTUREIndependent sensors become useful when a service correlates their partial observations.ENDPOINT · USER MODEENDPOINT · KERNEL MODELOCAL AGENTANALYSIS / CONTROLuserkerneleventsbufferretrydecisionconfiguration / policyBEHAVIORApplicationsProcesses · scriptsUSER SIGNALSAPI hooks · AMSIETWCalls · content · eventsWINDOWSSystemmanagersProcess · object · I/OKERNEL SENSORSCallbacksminifiltersWFP calloutsEDR SERVICENormalize+ enrichIdentity · policy · healthLOCAL QUEUEBuffer + retryBounded retentionANALYTICSCorrelatebehaviorDetect · investigateRESPONSEPolicy + actionAlert · isolate · containONE BEHAVIOR · MULTIPLE OBSERVATION POINTS · LOCAL NORMALIZATION · BUFFERED TRANSPORT · BACKEND CORRELATION
Conceptual architecture. Sensor placement, local processing, transport, backend analysis, and response remain product- and policy-specific.How to read the diagramRead the two endpoint lanes from left to right: applications expose user-mode signals while Windows managers expose kernel transitions. Both feed the local EDR service. The service normalizes and buffers records before analytics correlate them; policy returns through a separate control path rather than through every telemetry event.

A concrete event path

Consider a script host starting, loading a library, opening a file, and initiating a connection. Windows can notify the driver about process creation, publish ETW events, report the image mapping, and present file operations to a minifilter. If the script host integrates AMSI, script content may also be submitted through that interface. The connection then reaches one or more WFP layers.

These notifications do not necessarily arrive in the same format, with the same clock, or in the order later displayed by the backend. The local service must associate them with a process identity, copy fields while they remain valid, resolve what can be enriched without blocking the system, and decide what to retain or transmit. The backend can finally connect the script content, mapped image, affected file, and network destination to one entity.

The kernel driver is therefore one sensor among several, not the universal owner of endpoint telemetry. Callback delivery also does not prove that an event is retained or transmitted. Queue pressure, provider configuration, sampling, exclusions, local policy, and backend ingestion can each change the final evidence set.

SummaryA graph, not a stream

The endpoint is best modeled as a time-ordered graph. Processes, threads, files, registry keys, handles, images, sockets, and identities are nodes; observed operations are edges. Each sensor contributes only part of that graph. Removing one edge can reduce confidence, but it does not erase the other nodes, their prior state, or the edges reported by independent sensors.

Kernel Callback Telemetry

Process Creation

PsSetCreateProcessNotifyRoutineEx registers a PCREATE_PROCESS_NOTIFY_ROUTINE_EX callback. On creation, the callback receives a PEPROCESS pointer, a process identifier, and a PS_CREATE_NOTIFY_INFO structure. Documented fields include the parent process identifier, creator process and thread identifiers, executable file object, image name, and command line when available.

Hereafter, process identifier (PID) and thread identifier (TID) are used as event-correlation keys. Their values can be reused after the corresponding object terminates.

The registration call and the later event-delivery path are distinct. Private symbols such as an internal callback array can help with build-specific debugging, but they should not appear as stable architectural interfaces.

Technical diagram
PROCESS CREATION CALLBACK: Registration and event delivery are separate paths.KERNEL TELEMETRY · PROCESS MANAGERPROCESS CREATION CALLBACKRegistration and event delivery are separate paths.USER MODEKERNEL MODEregisterstored routineTRIGGERCreate processCreator threadDRIVER INITEDR driverSigned kernel imageREGISTRATIONPsSetCreateProcessNotifyRoutineExCallback added onceEVENTProcess ManagerCreation pathDISPATCHNotify routinesPASSIVE_LEVELEDR CALLBACKCorrelate telemetryObserve or setCreationStatusDELIVERED DATA · PEPROCESS · PROCESS PID · PARENT PID · CREATOR TID · IMAGE · COMMAND LINE
Documented process-notify path. Unlike a passive notification, the Ex callback can veto creation through CreationStatus.How to read the diagramRead the dashed upper path as one-time driver registration and the lower path as delivery for each process event. PASSIVE_LEVEL describes execution constraints, while creator-thread context identifies where the callback runs; neither establishes intent without correlated evidence.

The Ex callback runs at PASSIVE_LEVEL in the context of the thread creating the process. It is not purely observational: setting CreationStatus to an error can prevent creation. Whether an EDR uses this enforcement capability is a product-policy question. A PEPROCESS pointer is not equivalent to a serialized EPROCESS structure, and telemetry fields should not be described as present unless the sensor explicitly reads and records them.

Detection relevance. Parent-child relationships, creator identity, image provenance, and command-line semantics can support process-chain analytics. None is independently conclusive; spoofed parentage, brokered creation, renamed images, and incomplete command lines require correlation with token, handle, image, and execution telemetry.

The following WDK excerpt is intentionally limited to the documented callback contract. It distinguishes an exit notification (CreateInfo == NULL) from a creation notification and treats optional strings as optional. DbgPrintEx is suitable for a probe in a controlled lab, not for a production telemetry transport.

process-notify.c
#include <ntddk.h>
static VOID ProcessNotify(
PEPROCESS Process,
HANDLE ProcessId,
PPS_CREATE_NOTIFY_INFO CreateInfo)
{
UNREFERENCED_PARAMETER(Process);
// A NULL CreateInfo denotes process exit.
if (CreateInfo == NULL) {
return;
}
// ImageFileName is documented as optional.
if (CreateInfo->ImageFileName != NULL) {
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,
"create pid=%p image=%wZ\n", ProcessId, CreateInfo->ImageFileName);
}
}
NTSTATUS StartProcessProbe(VOID)
{
return PsSetCreateProcessNotifyRoutineEx(ProcessNotify, FALSE);
}
VOID StopProcessProbe(VOID)
{
(VOID)PsSetCreateProcessNotifyRoutineEx(ProcessNotify, TRUE);
}

Thread Creation and Exit

PsSetCreateThreadNotifyRoutine registers a callback whose classic signature contains only ProcessId, ThreadId, and a Create Boolean. The documentation does not define an ETHREAD argument. On thread creation, the routine executes in the context of the thread that created the new thread, at an interrupt request level (IRQL) no higher than APC_LEVEL, the level associated with asynchronous procedure calls (APC).

Technical diagram
THREAD LIFECYCLE SIGNAL: A notify callback is a correlation primitive, not an injection verdict.KERNEL TELEMETRY · THREAD MANAGERTHREAD LIFECYCLE SIGNALA notify callback is a correlation primitive, not an injection verdict.USER MODEKERNEL MODEregistered routinecontextTRIGGERCreate threadLocal or remoteREGISTRATIONPsSetCreateThreadNotifyRoutine[Ex]Driver initializationEVENTThread ManagerCreate · exitCALLBACK DATAProcessId · ThreadIdCreate = TRUE/FALSEEXECUTION CONTEXTCreator threadOn thread creationEDR CORRELATIONInjection hypothesisNeeds other signalsHIGH-FIDELITY DETECTION REQUIRES CORRELATION · CREATOR CONTEXT · HANDLE EVENTS · MEMORY · CALL STACK
The classic callback receives ProcessId, ThreadId and Create. It does not receive an ETHREAD structure or a source-process field.How to read the diagramThe callback identifies the target process, the new thread, and whether the event is creation or exit. The creator context can add provenance, but remote injection remains a hypothesis until handle, memory, start-address, or call-stack evidence supports it.

The event identifies the target process and new thread but does not by itself establish remote-thread injection. A reliable conclusion requires other evidence, such as the creator context, preceding process-handle access, virtual-memory operations, start-address provenance, or call-stack information. Extended callback variants alter the available execution contract, so the exact registration API must be recorded during testing.

Image Mapping

PsSetLoadImageNotifyRoutine registers a PLOAD_IMAGE_NOTIFY_ROUTINE. Windows invokes it after an executable image has been mapped into virtual memory and before its entry point runs. The callback receives an optional full image name, the target process identifier, and IMAGE_INFO data that includes mapping properties such as the image base and size.

Technical diagram
IMAGE LOAD NOTIFICATION: Notification occurs after mapping and before the image entry point runs.KERNEL TELEMETRY · IMAGE MAPPINGIMAGE LOAD NOTIFICATIONNotification occurs after mapping and before the image entry point runs.USER MODEKERNEL MODEREQUESTLoadLibraryEXE · DLLLOADERMap imageSection mappingSTATEImage mappedEntrypoint not runNOTIFYLoad-imagecallbacksPASSIVE_LEVELEDREnrich + scoreHash · signer · pathSEMANTIC LIMITVOID callbackno Allow/Deny returnBlocking requires another enforcement controlCALLBACK DATA · FULL IMAGE NAME (OPTIONAL) · PROCESS ID · IMAGE BASE · IMAGE SIZE · FLAGS
PLOAD_IMAGE_NOTIFY_ROUTINE returns void. It can observe the mapping but cannot directly return Allow or Deny.How to read the diagramThe important boundary is temporal: the image mapping already exists when notification is delivered, but its entry point has not run. The callback reports that state transition; reputation enrichment and any later process termination belong to separate controls.

The routine returns VOID; it cannot directly return an allow-or-deny decision. Signature status, hash reputation, catalog information, and prevalence are not intrinsic callback fields. A sensor may enrich the mapping with those properties through other facilities. Preventive action requires a separate control or a product-specific response path.

Registry Operations

For current Windows versions, a registry filter driver registers through CmRegisterCallbackEx. The Configuration Manager invokes the callback for operations identified by REG_NOTIFY_CLASS. User-mode registry APIs and kernel-mode Zw* routines can both reach this path.

Technical diagram
REGISTRY FILTERING PATH: User-mode and kernel-mode callers converge on the Configuration Manager.KERNEL TELEMETRY · CONFIGURATION MANAGERREGISTRY FILTERING PATHUser-mode and kernel-mode callers converge on the Configuration Manager.CALLERSKERNEL REGISTRY PATHstatusUSER MODEReg* APIsCreate · set · queryKERNEL MODEZw* routinesDriver callerDISPATCHConfigurationManagerTyped operationPRE-NOTIFYRegistryCallbackInspect · block · modifyPOST-NOTIFYRegistryCallbackObserve resultOPERATIONRegistry hiveExecute if allowedEDRCorrelateAlertOPERATION-SPECIFIC DATA · KEY OBJECT · VALUE NAME · DATA · PROCESS/THREAD CONTEXT · RETURN STATUS
CmRegisterCallbackEx registers one routine that receives typed pre/post notifications through REG_NOTIFY_CLASS.How to read the diagramBoth caller paths converge on the same manager. Pre-notifications can affect supported operations before execution; post-notifications report the resulting status. Available fields and permitted changes depend on the exact REG_NOTIFY_CLASS rather than on one universal registry-event schema.

Most operation classes expose pre-notifications and post-notifications with operation-specific structures. A pre-callback can inspect, block, or in supported cases modify an operation; a post-callback observes the resulting status and output. The available fields depend on the notification class. A generic claim that every registry event contains a full path, process identifier, thread identifier, value data, and permissions is therefore too broad. Name resolution and caller attribution may require additional work by the sensor.

Detection relevance. Persistence keys, security-provider configuration, service definitions, and policy changes become meaningful when correlated with the initiating identity, process lineage, image trust, and temporal sequence. Registry access alone does not identify intent.

Process and Thread Object Operations

ObRegisterCallbacks registers pre-operation and post-operation routines for supported process, thread, and desktop handle operations. OB_OPERATION_HANDLE_CREATE and OB_OPERATION_HANDLE_DUPLICATE distinguish new and duplicated handles.

Technical diagram
PROCESS / THREAD HANDLE GATE: Object callbacks mediate handle create and duplicate operations.KERNEL TELEMETRY · OBJECT MANAGERPROCESS / THREAD HANDLE GATEObject callbacks mediate handle create and duplicate operations.USER MODEKERNEL MODErestrictSOURCEOpenProcess /DuplicateHandleRequested accessSYSTEM CALLObject ManagerHandle operationPRE-OPEDR callbackInspect requestSUPPORTED ENFORCEMENTDesiredAccess &= allowed_maskRights may be removed, never addedRESULTHandleReduced rightsPOST-OPTelemetryFinal statusPRE-OP · OBJECT TYPE · CREATE/DUPLICATE · ORIGINAL ACCESS · MUTABLE DESIRED ACCESS · KERNEL-HANDLE FLAG
ObjectPreCallback must return OB_PREOP_SUCCESS. Protection is implemented by removing supported rights from DesiredAccess, not by returning Deny.How to read the diagramTreat DesiredAccess as the set of capabilities requested for the future handle. The pre-callback can remove supported bits before issuance, so an open call may succeed while the resulting handle cannot write memory, create a thread, or perform another protected action.

An ObjectPreCallback must return OB_PREOP_SUCCESS; it does not return an arbitrary deny status. For supported process and thread rights, the callback can remove bits from DesiredAccess, but it cannot add rights beyond the original request. A post-callback can observe the final status and granted access but cannot retroactively change the completed operation.

This mechanism supports protection of sensitive processes by constraining rights such as process-memory access, thread creation, context modification, and handle duplication. Kernel handles and operations originating from trusted or protected components require careful interpretation. A blocked or reduced handle also does not demonstrate credential-dumping intent without surrounding behavior.

The policy core below shows the supported enforcement model. Registration, altitude selection, target lifetime handling, and policy synchronization are omitted, but the access-mask operation uses the documented pre-operation structures. The target predicate must be narrow; applying this mask to every process would break legitimate software.

object-pre-callback.c
static OB_PREOP_CALLBACK_STATUS ProcessHandlePreOperation(
PVOID RegistrationContext,
POB_PRE_OPERATION_INFORMATION Info)
{
UNREFERENCED_PARAMETER(RegistrationContext);
if (Info->ObjectType != *PsProcessType ||
!IsProtectedTarget((PEPROCESS)Info->Object)) {
return OB_PREOP_SUCCESS;
}
ACCESS_MASK *desired = NULL;
if (Info->Operation == OB_OPERATION_HANDLE_CREATE) {
desired = &Info->Parameters->CreateHandleInformation.DesiredAccess;
} else if (Info->Operation == OB_OPERATION_HANDLE_DUPLICATE) {
desired = &Info->Parameters->DuplicateHandleInformation.DesiredAccess;
}
if (desired != NULL) {
*desired &= ~(PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_VM_WRITE);
}
return OB_PREOP_SUCCESS;
}

File-System Minifilters

FltMgr.sys manages file-system minifilter instances attached to volumes. Each minifilter registers only for selected input/output (I/O) operations. In the Windows driver model, those operations are carried by input/output request packets (IRP). Its assigned altitude determines its relative position in the stack; product names do not establish a fixed EDR-before-antivirus order.

Technical diagram
MINIFILTER I/O STACK: Altitude, not product name, determines callback order.KERNEL TELEMETRY · FILTER MANAGERMINIFILTER I/O STACKAltitude, not product name, determines callback order.ONE VOLUME · ORDERED MINIFILTER INSTANCESPOST-OP · LOW → HIGHPRE-OP · HIGH → LOWORIGINI/O requestCreate · read · writeROUTERFltMgr.sysRegistered operationsHIGH ALTITUDEMinifilter APre first · post lastLOW ALTITUDEMinifilter BPre later · post firstSTORAGEFile systemComplete operationCOMPLETIONStatus · bytesMetadataReturn pathPRE-OP CAN PASS · REQUEST POST-OP · PEND · SYNCHRONIZE · OR COMPLETE THE I/O WITH A FINAL STATUS
FltMgr sends pre-operation callbacks from highest to lowest altitude; completion returns through post-operation callbacks in reverse order.How to read the diagramFollow the upper direction for the request: high-altitude instances run before lower ones. Follow the lower return for completion: post-operation callbacks unwind in reverse. Only operations registered by each minifilter participate in this path.

For a given operation, pre-operation callbacks run from the highest altitude toward the file system. Completion returns through post-operation callbacks from the lowest altitude upward. A pre-callback can pass the request, request a post-callback, pend it, synchronize it, or complete it with a final status. Post-operation availability and execution context depend on the pre-operation result and operation type.

Detection relevance. Create, write, rename, disposition, section synchronization, and metadata operations can support ransomware, staging, tampering, and collection analytics. Path, content, entropy, volume context, and process attribution are product enrichments rather than a universal minifilter event schema.

A minifilter declares the operations it wishes to receive. This static table creates no fixed relationship with another vendor’s filter; FltMgr combines the table with the instance altitude at runtime.

minifilter-operations.c
CONST FLT_OPERATION_REGISTRATION Operations[] = {
{ IRP_MJ_CREATE, 0, PreCreate, PostCreate },
{ IRP_MJ_WRITE, 0, PreWrite, PostWrite },
{ IRP_MJ_SET_INFORMATION, 0, PreSetInformation, PostSetInformation },
{ IRP_MJ_OPERATION_END }
};

Event Tracing for Windows

ETW consists of providers, sessions, controllers, and consumers. A controller starts and configures a session and enables providers. Enabled providers write event records into session buffers. A consumer reads those records in real time or from an Event Trace Log (ETL) file. An EDR service can implement both controller and consumer roles, but the controller is not an intermediate event hop.

Technical diagram
ETW SESSION DATA FLOW: Controllers configure; providers write; consumers read.SYSTEM TELEMETRY · EVENT TRACING FOR WINDOWSETW SESSION DATA FLOWControllers configure; providers write; consumers read.CONTROL PLANEEVENT DATA PLANEconfigureenableownswritereadCONTROLLEREDR serviceStart session · enable providersTRACE SESSIONPolicy + buffer poolLevel · keywords · modePROVIDERSApplicationsSystem componentsUser mode · kernelSESSION BUFFERSOrdered event streamReal time and/or ETLCONSUMEREDR serviceDecode · filterANALYTICSCorrelateDetection · alertEVENT RECORD · PROVIDER / EVENT / ACTIVITY IDENTIFIERS · LEVEL · KEYWORD · TIMESTAMP · PID/TID · PAYLOAD
The controller is control plane, not an event hop. Providers write to session buffers consumed in real time or from ETL files.How to read the diagramThe upper plane configures which providers and event classes feed a session, plus buffer and delivery policy. The lower plane carries data: providers write records into session buffers and consumers read them. A controller can share a process with a consumer without becoming an event relay.

Event records can include a provider identifier, event identifier, version, level, keyword, timestamp, process and thread identifiers, activity identifiers, and provider-defined payload. The exact schema comes from the provider manifest, TraceLogging metadata, Managed Object Format (MOF), or a Trace Message Format (TMF) file. ETW is a transport and instrumentation framework; it does not guarantee that a provider is enabled, that a consumer retains every event, or that the resulting event is security-relevant.

PowerShell, .NET, kernel, and Microsoft-Windows-Threat-Intelligence events are distinct provider families with different enablement and access conditions. AMSI is a separate content-inspection interface. Its signals may be correlated with ETW, but it is not an ETW provider category.

Network Telemetry with WFP

Windows Filtering Platform exposes filtering layers at selected points in the networking stack. Shims extract classifiable values and invoke the filter engine. Filters evaluate conditions at a layer and may select a built-in action or invoke a registered callout for specialized processing. The Base Filtering Engine (BFE) manages platform configuration; Application Layer Enforcement (ALE) layers enable classification using properties such as application and user identity.

Technical diagram
WFP CLASSIFICATION PATH: A callout is invoked by a matching filter at a filtering layer.NETWORK TELEMETRY · WINDOWS FILTERING PLATFORMWFP CLASSIFICATION PATHA callout is invoked by a matching filter at a filtering layer.POLICY / MANAGEMENTKERNEL CLASSIFICATIONmanagefiltersmatchclassifyEDR SERVICEPolicy providerAdd filters + contextBFEFilter policyPersistent configurationTRAFFICSocket / flowPacket · streamALESHIMTCP/IP stackExtract conditionsLAYERFilter engineClassify + arbitrateOPTIONALEDR calloutInspect · annotateACTIONPermitor blockDecisionPATHNetworkContinueALE METADATA CAN BIND FLOW TO PROCESS · APPLICATION · USER · LOCAL/REMOTE ADDRESSES · PORTS · PROTOCOL
Traffic reaches a WFP layer through a shim; the filter engine evaluates conditions and may invoke an EDR callout before enforcing the action.How to read the diagramA layer is an observation point, a filter expresses policy at that point, and a callout is optional specialized code selected by a matching filter. The metadata and authority available to the callout are therefore bounded by the invoking layer and WFP arbitration rules.

The BFE coordinates policy and persistent configuration in user mode; classification for network and transport layers occurs in kernel mode. At ALE layers, metadata can associate a flow with application identity, user identity, protocol, and local or remote endpoints. Availability varies by layer and event type.

An EDR callout is therefore not placed before WFP. It is invoked because a matching filter at a layer selects that callout. The classify function can inspect metadata and contribute an action under the WFP arbitration rules. Packet payload inspection, stream reassembly, and final blocking semantics depend on the selected layer, callout implementation, and rights granted to the action.

User-Mode API Instrumentation

Some EDR products inject or load a user-mode sensor and detour selected exports in modules such as ntdll.dll. A common inline-hook design replaces the function prologue with a branch to sensor code, emits telemetry, and uses a trampoline to execute displaced instructions before resuming the original path.

Technical diagram
HOOKED NTDLL CALL PATH: A detour adds an observation hop before the original syscall path.USER-MODE TELEMETRY · INLINE DETOURHOOKED NTDLL CALL PATHA detour adds an observation hop before the original syscall path.USER MODEKERNEL MODEeventCALLERApplicationNtCreateThreadExEXPORTntdll entryPatched prologueDETOUREDR hookInspect + emitTRAMPOLINEOriginalbytesResume executionTRANSITIONSyscall stubBuild-specificidentifierSENSOR OUTPUTLocal telemetry queue → EDR serviceImplementation-specificSYSTEM SERVICEKernel executionPOSSIBLE USER-MODE SIGNALS · PARAMETERS · RETURN VALUE · CALL STACK · CALLER MODULE · MEMORY INTEGRITY
This is one common implementation pattern, not a universal EDR contract. A direct syscall skips this user-mode detour only; kernel and cross-process sensors remain.How to read the diagramThe detour observes calls that cross the patched export, then the trampoline restores displaced instructions and resumes the ordinary stub. Bypassing that export removes this observation hop, not the kernel operation or the independent state changes it may produce.

This pattern is not universal. Products can use different injection strategies, instrumentation frameworks, event sources, and protected-process policies. A direct system call can bypass an inline detour on a user-mode export, but it does not bypass the system call itself or independent kernel callbacks, ETW providers, WFP layers, minifilters, handle controls, memory scanners, or cross-process correlation.

System-call identifiers and stubs are architecture- and build-dependent. Hard-coding one identifier as a general Windows technique is technically unsound. Microsoft Visual C++ also does not support inline assembly for the 64-bit x86 architecture (x64) in the form commonly shown in simplified examples. Research tooling must resolve and validate the target build rather than assume a fixed stub.

Sensor Semantics and Data Quality

Identity and object lifetime

Windows identifiers are convenient correlation keys but are not permanent identities. Process and thread identifiers can be reused after object termination. A robust event model therefore combines the identifier with creation time, boot or sensor epoch, and preferably a product-generated entity identifier. Correlating a late network or file event to the current owner of a reused PID can otherwise create a false lineage.

Kernel callbacks also expose objects at different lifecycle stages. A process-create callback runs before the initial thread begins executing, while an image callback occurs after an image mapping has been created but before its entry point executes. A post-operation object callback observes a completed handle operation. These events describe different state transitions and should not be forced into a single generic timestamp such as “process started.”

Object pointers are valid only under the contract and lifetime rules of the callback receiving them. Retaining a raw pointer for asynchronous processing without taking an appropriate reference is unsafe. Production sensors generally copy stable fields into a bounded record and defer expensive enrichment to a worker or user-mode service.

Event time and causal order

Callback invocation order is not equivalent to backend arrival order. Events can traverse per-central-processing-unit (CPU) buffers, kernel-to-user queues, local persistence, compression, batching, transport, and cloud ingestion before becoming queryable. Two records generated in a known kernel order may be observed in reverse order after independent buffering paths.

Several timestamps can coexist: system time, interrupt time, performance-counter time, ETW timestamps, local service receipt time, and backend ingestion time. A defensible correlation model preserves the original clock domain and records conversion uncertainty. Backend time should not replace event-generation time when reconstructing sub-second process, thread, handle, or network sequences.

Optional fields, name resolution, and races

Documented optionality has analytical consequences. ImageFileName and CommandLine in PS_CREATE_NOTIFY_INFO can be absent. FullImageName in an image-load callback can be null. Registry callbacks receive operation-specific structures rather than a universal fully resolved path. Network metadata differs across WFP layers. Missing data is therefore not proof of tampering.

Names are also mutable views of objects. A file can be renamed after a create event; a path can involve reparse points, device paths, hard links, or network redirectors; a process image name can differ from later on-disk state. Sensors that enrich asynchronously must distinguish values captured at event time from values resolved later.

Throughput, backpressure, and event loss

Every collection path has a performance budget. Kernel callbacks execute in constrained contexts, minifilters sit on latency-sensitive I/O paths, WFP callouts can affect network throughput, and ETW sessions use finite buffers. A sensor may sample, aggregate, suppress repeated events, drop low-priority records, or fail open when a queue is saturated.

ETW controllers can expose buffer and event-loss statistics. Product queues may expose health counters, diagnostic logs, or heartbeat status. A negative test result is uninterpretable unless sensor health and loss indicators are collected alongside security events. “No event found” can mean absence of instrumentation, filtering, loss, delayed ingestion, retention expiry, query error, or a successful bypass.

Observation and Enforcement Boundaries

The native interface determines what a callback can do synchronously. Product response features can add later actions, but they should not be attributed to the collection API itself.

MechanismNative observationNative synchronous influenceImportant boundary
Process notify ExCreation and exit; documented creation metadataCreationStatus can fail creationProduct may use the callback only for telemetry
Thread notifyTarget PID, TID, create/delete stateNo documented allow/deny returnInjection requires correlation beyond the callback
Image-load notifyImage mapping metadataNo allow/deny return; callback is VOIDMapping already exists when notification is delivered
Registry callbackTyped pre/post registry operation dataPre-callback can block or modify supported operationsFields and permitted changes depend on REG_NOTIFY_CLASS
Object callbackProcess/thread handle create or duplicateSupported access bits can be removedPre-callback returns success; it is not a generic deny hook
Minifilter pre-opRegistered file-system operationsPass, pend, modify, synchronize, or completeOrdering is determined by altitude and operation registration
ETW consumerEvents emitted to an enabled sessionNone through the consumer roleProvider enablement and retention are separate control decisions
WFP calloutLayer-specific flow, stream, packet, or ALE metadataClassification can contribute permit/block behaviorRights, arbitration, and available data vary by layer
User-mode detourArguments, caller context, return path, product-defined dataHook code can alter or deny the user-mode callOnly calls traversing the instrumented path are observed

Asynchronous response must be modeled separately. A sensor may observe an image mapping and terminate the process milliseconds later, or record a network flow and isolate the host after backend correlation. Such behavior is preventive at the product level but is not synchronous prevention by the original image or network event source.

Platform Trust, Protected Execution, and Self-Protection

Kernel registration APIs impose trust conditions of their own. For example, process Ex callbacks can fail registration when the callback image lacks the required integrity characteristic, and object callback registration can be denied when routines are not located in a signed kernel image. These checks raise the cost of arbitrary registration but do not establish that every signed driver is secure.

Protected Process Light restricts access to protected processes according to signer level and requested operation. It can limit user-mode inspection or handle acquisition by an EDR component that lacks a sufficient trust level. Some security products use protected services and protected antimalware-light processes; exact configuration remains vendor- and deployment-specific.

Virtualization-based security (VBS) and hypervisor-protected code integrity (HVCI) move or enforce selected integrity decisions through an isolated environment. The vulnerable-driver blocklist and App Control add policy over which kernel images may load. These mechanisms reduce exposure to known or untrusted drivers but do not convert undocumented kernel data into a supported security boundary.

Kernel Patch Protection monitors selected critical kernel structures and code on supported 64-bit systems. It should not be described as a complete, immediate, or publicly specified detector for every callback or ETW manipulation. Its coverage and timing are implementation details. A crash after an unsupported modification is evidence of platform instability, not a reliable EDR detection result.

Product self-protection adds another layer: service and driver-device access control lists (ACL), protected services, watchdogs, callback health checks, module integrity, configuration signing, and backend health state can each contribute. Anti-tamper and behavior detection are distinct. Preventing a service stop does not prove visibility into an attack, and detecting a memory modification does not prove the original behavior was collected.

From Telemetry to Detection

Collection is only the first stage of an EDR pipeline. Detection quality depends on normalization, enrichment, state, and correlation.

StageTypical functionPrincipal failure modes
CollectionReceive callbacks, events, I/O, flow, and user-mode signalsSensor disabled, callback absent, provider not enabled, event loss
NormalizationConvert product-specific records into a stable internal schemaMissing fields, ambiguous identity, clock skew, schema drift
EnrichmentResolve signer, hash, reputation, token, path, ancestry, and asset contextNetwork dependency, cache staleness, path races, inaccessible objects
CorrelationRelate events across processes, threads, sessions, and hostsIdentifier reuse, incomplete windows, sampling, backend delay
DecisionScore, alert, block, isolate, or collect evidencePolicy differences, model threshold, suppression, response latency

The presence of a callback should not be equated with full visibility. Conversely, bypassing one callback or user-mode detour should not be equated with loss of detection. The relevant unit of analysis is the complete behavior graph and the set of independent sensors that can describe it.

Evasion Boundaries

Evasion techniques should be described by the exact visibility path they affect. Terms such as “disable EDR” or “become invisible” are too broad unless the product, version, policy, sensor state, and measured outcome are specified.

ImportantInterpretation rule

A bypass result is scoped to one collection or enforcement path. Loss of one signal does not establish loss of the behavior graph, prevention capability, or backend detection.

Technical diagram
EVASION ≠ INVISIBILITY: Each bypass targets a collection path; independent controls still observe the behavior.RED TEAM MODEL · SENSOR COVERAGEEVASION ≠ INVISIBILITYEach bypass targets a collection path; independent controls still observe the behavior.TARGETED VISIBILITYATTEMPTED GAPRESIDUAL VISIBILITY / CONTROLUSER HOOKSntdll detoursBYPASSUnhook / direct syscallSTILL VISIBLECallbacks · ETW · WFP · memoryLOCAL ETW PATHProvider writesBYPASSPatch one process pathSTILL VISIBLEKernel / other providers · integrityKERNEL CALLBACKNotify routineBYPASSKernel write primitiveRISK / CONTROLIntegrity checks · crash · telemetryKERNEL TRUSTSigned driver policyATTACKVulnerable signed driverMITIGATIONHVCI · blocklist · App ControlVALIDATION RULE · NAME THE BYPASSED SENSOR · TEST THE TARGET BUILD · MEASURE RESIDUAL TELEMETRY · EXPECT INTEGRITY CONTROLS
The useful research model maps technique to the exact sensor it affects, then enumerates residual visibility and platform mitigations.How to read the diagramRead each row from left to right: identify the targeted sensor, state the attempted gap precisely, then measure the remaining signals and controls. The middle column alone never establishes endpoint-wide invisibility.

User-mode unhooking and direct system calls

These techniques can remove or bypass selected user-mode detours. Residual evidence can include process and thread callbacks, handle operations, image mappings, ETW, WFP, minifilter activity, remote-memory state, and backend correlations. Hook restoration can itself create integrity or memory-protection anomalies.

ETW tampering

Patching EtwEventWrite in one process affects calls that traverse that copy and code path. It does not disable kernel providers, other processes, other ETW entry points, or non-ETW sensors. Modifying executable pages can also become an observable integrity event. Kernel ETW state is implementation-specific and must not be represented as a stable offset or structure layout.

Kernel callback manipulation

Removing a callback through undocumented internal structures requires a kernel write primitive and build-specific discovery. The operation is not represented by a generic input/output control (IOCTL) code for “remove callback.” A vulnerable driver exposes its own device protocol and primitives; the exploit must implement the driver-specific read/write semantics and resolve the target kernel state for the exact build. Kernel integrity mechanisms, product self-protection, callback re-registration, crashes, and remaining sensors affect the outcome.

Vulnerable signed drivers

A valid signature establishes a trust relationship for loading; it does not establish that the driver is free of exploitable defects. Modern controls include Hypervisor-Protected Code Integrity, the Microsoft vulnerable-driver blocklist, App Control policies, and the Attack Surface Reduction rule for abused vulnerable signed drivers. Coverage is not absolute, and blocklist state must be verified on the tested endpoint.

Build-Specific Internals and Reverse Engineering

Kernel debugging and reverse engineering are necessary when the research question concerns actual implementation rather than the documented contract. They require a stricter evidence format.

The supplied lab manual enumerates process, thread, and image notification routines through private kernel symbols, examines registry and object callback registrations, walks minifilter callback nodes, and resolves Event Tracing for Windows Threat Intelligence (ETW-TI) state. It also masks encoded callback entries and uses a vulnerable-driver primitive to modify kernel memory. These techniques demonstrate that the lab build can be inspected and altered; they do not make the internal symbol names, array sizes, pointer encoding, list layout, or field offsets portable.

For each internal claim, a research record should include:

  • the exact kernel and driver file versions, hashes, timestamps, and loaded base addresses;
  • the symbol-server source, Program Database (PDB) identity, symbol load status, and whether private type information was available;
  • the disassembly or pseudocode location establishing how the internal object is referenced;
  • the relevant structure layout derived from symbols or code, not copied from another build;
  • the synchronization and lifetime assumptions required before reading or modifying the object;
  • the before-and-after state, including callback re-registration, sensor health, system stability, and residual telemetry.

Security-driver ownership should be established by resolving each routine address to the loaded module and then examining the registration call or routine implementation. Labels such as WdFilter.sys, MsSecFlt.sys, or a third-party driver are observations from one system, not mandatory members of a Windows callback list. Load order, product version, enabled features, and platform security components change the population.

Removal experiments are particularly easy to overstate. Clearing an entry, unlinking a node, disabling an enable flag, or restoring bytes can produce a temporary state while leaving cached pointers, rundown references, worker queues, watchdogs, alternative callbacks, and backend evidence intact. A successful write is not equivalent to a clean or durable sensor bypass.

WarningLab material scope

The supplied manual is valuable as an experimental workflow. Its private symbols and offsets are not reused as current facts; each must be rediscovered and evidenced on the target build.

Common Analytical Errors

  1. Combining registration with event flow. A setup API called during driver initialization should not be drawn as a step traversed by every event.
  2. Presenting private symbols as public architecture. Internal names and offsets require an explicit build qualifier and evidence source.
  3. Inventing universal event fields. Callback arguments, product enrichment, and backend schema are separate layers.
  4. Equating notification with prevention. VOID callbacks cannot return block decisions; asynchronous product response should be described separately.
  5. Equating one bypass with EDR disablement. The affected hook, provider, callback, callout, or queue must be named precisely.
  6. Using an alert as the only ground truth. Raw telemetry, prevention outcome, sensor health, and backend ingestion must also be inspected.
  7. Ignoring negative controls. A test is incomplete without a comparable baseline and a benign operation of the same API shape.
  8. Ignoring recovery behavior. Watchdogs, callback re-registration, reboot, policy refresh, and updates can reverse a transient change.
  9. Assuming a fixed system-call number. Architecture and Windows build determine the stub; hard-coded examples age immediately.
  10. Treating a signed driver as trusted behavior. Signature, vulnerability state, blocklist coverage, and runtime policy are distinct properties.

Experimental Coverage Matrix

An exhaustive test plan should vary behavior and collection path independently. The table below describes minimum evidence, not a product-specific expected alert.

Test familyPrimary source under testIndependent residual sourcesRequired outcome evidence
Process creationProcess Ex callbackImage map, ETW, handle lineage, service auditCreation status, raw event, entity lineage, policy action
Remote thread behaviorThread callback or user hookObject callbacks, memory telemetry, ETW-TI, target stateSource/target identity, rights, start address, thread result
Image introductionImage-load callbackMinifilter, code integrity, memory scan, ETWMapping state, signer/hash enrichment, execution outcome
Registry persistenceRegistry callbackProcess lineage, service/task telemetry, later activationPre/post status, resolved key/value, initiating identity
Sensitive-process accessObject callbackAudit events, memory reads, thread behavior, protection stateOriginal/reduced access, granted handle, follow-on operation
File transformationMinifilterProcess/thread, entropy/content analysis, volume telemetryPre/post result, bytes/path identity, rate and scope
Script or managed codeETW/AMSI pathProcess, image, file, network, memoryProvider enablement, content availability, downstream behavior
Network connectionWFP/ALEDomain Name System (DNS), process lineage, proxy, Transport Layer Security (TLS), remote-side logsLayer, direction, endpoints, app identity, action
User-hook bypassInstrumented exportKernel callbacks, ETW, WFP, minifilter, memoryHook integrity, actual call path, residual event graph
Driver load or abuseCode integrity and driver policyImage callback, service registry, minifilter, kernel telemetryLoad decision, policy state, driver hash/version, device access

The strongest conclusion is rarely binary. Coverage can be complete, partial, delayed, sampled, enriched only in the backend, or sufficient for detection despite a missing primary event. Each dimension should be recorded separately.

Research Methodology

A defensible EDR-internals experiment records enough state for another researcher to reproduce or challenge the conclusion.

  1. Pin the environment. Record the Windows edition, build and servicing level, virtualization and VBS state, EDR product and sensor versions, policy identifier, and network connectivity.
  2. Define the behavior. Specify the process, thread, image, registry, object, file, ETW, or network operation being generated. Avoid using an alert name as the behavior definition.
  3. Inventory the expected sensors. Record documented callbacks, loaded drivers, minifilter instances and altitudes, ETW sessions and enabled providers, WFP filters and callouts, and user-mode modules.
  4. Capture a baseline. Execute a benign control with the same API shape and collect local and backend records before introducing any evasion condition.
  5. Change one variable. Disable, bypass, or alter one collection path while holding the behavior and policy constant.
  6. Measure residual visibility. Compare raw events, normalized records, alerts, prevention, backend arrival time, and endpoint side effects.
  7. Repeat after reboot and update. Callback state, provider sessions, blocklists, and self-protection can be restored or changed by servicing and sensor health checks.

Results should distinguish at least four outcomes: the targeted event disappeared; the event remained but lost fields; telemetry remained while the alert disappeared; or prevention changed while telemetry remained. These outcomes describe different layers of the pipeline and should not be collapsed into a single “bypass succeeded” statement.

The following read-only inventory provides a reproducible starting point on a Windows lab host. It does not identify every EDR sensor, but it captures three frequently misunderstood surfaces before a test begins.

Collect-EdrSurface.ps1
$EvidenceRoot = Join-Path $env:TEMP ("edr-surface-" + (Get-Date -Format "yyyyMMdd-HHmmss"))
New-Item -ItemType Directory -Path $EvidenceRoot | Out-Null
fltmc filters | Out-File (Join-Path $EvidenceRoot "minifilters.txt")
logman query -ets | Out-File (Join-Path $EvidenceRoot "etw-sessions.txt")
logman query providers | Out-File (Join-Path $EvidenceRoot "etw-providers.txt")
$WfpState = Join-Path $EvidenceRoot "wfp-state.xml"
netsh wfp show state "file=$WfpState"
Get-CimInstance Win32_SystemDriver |
Select-Object Name, State, StartMode, PathName |
Export-Csv (Join-Path $EvidenceRoot "drivers.csv") -NoTypeInformation

The snapshot should be paired with operating system (OS) and sensor versions, policy state, timestamps, and backend evidence. fltmc confirms minifilter instances and altitudes, logman distinguishes registered providers from active sessions, and netsh wfp exports the current WFP and Internet Protocol Security (IPsec) state.

A Durable Mental Model

Return to the opening sequence. Process creation establishes identity and lineage. Image notifications describe executable mappings, but do not decide whether those mappings are malicious. An object callback can observe or reduce requested access to another process, while a later thread notification says only that a thread appeared. File and WFP telemetry add effects outside the process. ETW may contribute runtime context. The analytical value emerges from their temporal relationship, not from any single event.

A claim about EDR visibility can therefore be tested with four questions:

  1. Which Windows state transition occurred? A behavior should be described independently of an alert or tool name.
  2. Which documented mechanism can observe or influence it? Registration, delivery, normalization, detection, and response are different stages.
  3. Which fields and ordering guarantees actually exist? Optional data, PID reuse, asynchronous transport, and event loss constrain every conclusion.
  4. Which independent evidence survives if that mechanism is absent? Residual process, handle, image, file, ETW, or network signals determine whether an evasion removes one event or genuinely breaks the analytical chain.
ImportantThe transferable insight

EDR internals are most usefully understood as a partially observable distributed system. The endpoint produces fragmented evidence, local components transform it, transport can delay or lose it, and a backend reconstructs behavior under policy. This model remains useful across products and Windows builds because it separates stable operating-system semantics from vendor-specific implementation.

Source Hierarchy and Limitations

The public WDK defines supported interfaces but not every internal dispatch structure used by a particular Windows build. Windows Internals explains implementation concepts but explicitly warns that undocumented internals can change. Kernel-debugger symbols and reverse engineering can establish what a specific build does; they cannot establish a forward-compatible contract. Vendor behavior requires direct observation because the existence of an OS facility does not prove how a product configures or consumes it.

The Evasion Lab manual sections on callback enumeration, minifilter manipulation, and ETW Threat Intelligence demonstrate a lab workflow based on WinDbg symbols and a vulnerable-driver read/write primitive. Those exercises are evidence of a method on the supplied lab build, not evidence that offsets, internal field layouts, callback ownership, or provider state remain identical on another system.

References

Use with an AI

Actions