# EDR Internals: Telemetry Architecture and Evasion Boundaries > A source-based analysis of Windows EDR telemetry paths, their documented semantics, enforcement limits, and red-team validation methodology. Published on 2026-07-21 | Tags: edr, windows, internals, red team, research https://xsec.fr/en/evasion/edr-internals-technical-review/ --- import EdrTechnicalInfographic from '@shared/components/EdrTechnicalInfographic.astro' import Callout from '@shared/components/Callout.astro' > 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. 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 | Component | What it represents | Technical purpose | Important limitation | |---|---|---|---| | Observed application | The process in which behavior runs: a browser, script host, administration tool, or native binary | Produces the calls, image mappings, memory accesses, files, and connections that may become signals | An application does not describe operator intent by itself | | User-mode sensor | A module loaded or injected into selected processes, or a component consuming their interfaces | Instruments API paths, captures call context, and may inspect content or memory | A call that does not cross the instrumented path can evade that specific sensor | | Local EDR service | The durable process coordinating the endpoint agent | Receives events, normalizes fields, enriches identities, applies local policy, maintains queues, and communicates with the backend | A driver notification is not necessarily retained, enriched, or transmitted | | EDR driver | A privileged module loaded into the Windows kernel | Registers callbacks, file filters, or network components and can make selected documented synchronous decisions | A 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 consumers | Carries structured kernel or application events to a real-time consumer or trace file | A 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 engines | Submits decrypted or reconstructed content to an antimalware provider at a point chosen by the host application | AMSI is neither an ETW provider nor a universal sensor for every process | | File-system minifilter | A driver attached to Windows Filter Manager for selected file-system operations | Observes or influences create, read, write, rename, or delete operations according to its registration | Altitude 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 stack | Classifies traffic at defined layers and can invoke an EDR callout selected by a filter | Available metadata and blocking authority depend on the invoking WFP layer | | Analysis backend | A local or remote engine receiving normalized events | Correlates sensors, maintains state, enriches reputation, and produces detections or responses | Transport, 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. ### 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. 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. 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. ```c title="process-notify.c" {8,12-17,21,25} #include 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). 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. 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. 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. 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. ```c title="object-pre-callback.c" {7-9,12-18,20} 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. 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. ```c title="minifilter-operations.c" {2-4,6} 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. 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. 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. 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. | Mechanism | Native observation | Native synchronous influence | Important boundary | |---|---|---|---| | Process notify Ex | Creation and exit; documented creation metadata | `CreationStatus` can fail creation | Product may use the callback only for telemetry | | Thread notify | Target PID, TID, create/delete state | No documented allow/deny return | Injection requires correlation beyond the callback | | Image-load notify | Image mapping metadata | No allow/deny return; callback is `VOID` | Mapping already exists when notification is delivered | | Registry callback | Typed pre/post registry operation data | Pre-callback can block or modify supported operations | Fields and permitted changes depend on `REG_NOTIFY_CLASS` | | Object callback | Process/thread handle create or duplicate | Supported access bits can be removed | Pre-callback returns success; it is not a generic deny hook | | Minifilter pre-op | Registered file-system operations | Pass, pend, modify, synchronize, or complete | Ordering is determined by altitude and operation registration | | ETW consumer | Events emitted to an enabled session | None through the consumer role | Provider enablement and retention are separate control decisions | | WFP callout | Layer-specific flow, stream, packet, or ALE metadata | Classification can contribute permit/block behavior | Rights, arbitration, and available data vary by layer | | User-mode detour | Arguments, caller context, return path, product-defined data | Hook code can alter or deny the user-mode call | Only 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. | Stage | Typical function | Principal failure modes | |---|---|---| | Collection | Receive callbacks, events, I/O, flow, and user-mode signals | Sensor disabled, callback absent, provider not enabled, event loss | | Normalization | Convert product-specific records into a stable internal schema | Missing fields, ambiguous identity, clock skew, schema drift | | Enrichment | Resolve signer, hash, reputation, token, path, ancestry, and asset context | Network dependency, cache staleness, path races, inaccessible objects | | Correlation | Relate events across processes, threads, sessions, and hosts | Identifier reuse, incomplete windows, sampling, backend delay | | Decision | Score, alert, block, isolate, or collect evidence | Policy 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. 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. ### 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. 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 family | Primary source under test | Independent residual sources | Required outcome evidence | |---|---|---|---| | Process creation | Process Ex callback | Image map, ETW, handle lineage, service audit | Creation status, raw event, entity lineage, policy action | | Remote thread behavior | Thread callback or user hook | Object callbacks, memory telemetry, ETW-TI, target state | Source/target identity, rights, start address, thread result | | Image introduction | Image-load callback | Minifilter, code integrity, memory scan, ETW | Mapping state, signer/hash enrichment, execution outcome | | Registry persistence | Registry callback | Process lineage, service/task telemetry, later activation | Pre/post status, resolved key/value, initiating identity | | Sensitive-process access | Object callback | Audit events, memory reads, thread behavior, protection state | Original/reduced access, granted handle, follow-on operation | | File transformation | Minifilter | Process/thread, entropy/content analysis, volume telemetry | Pre/post result, bytes/path identity, rate and scope | | Script or managed code | ETW/AMSI path | Process, image, file, network, memory | Provider enablement, content availability, downstream behavior | | Network connection | WFP/ALE | Domain Name System (DNS), process lineage, proxy, Transport Layer Security (TLS), remote-side logs | Layer, direction, endpoints, app identity, action | | User-hook bypass | Instrumented export | Kernel callbacks, ETW, WFP, minifilter, memory | Hook integrity, actual call path, residual event graph | | Driver load or abuse | Code integrity and driver policy | Image callback, service registry, minifilter, kernel telemetry | Load 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. ```powershell title="Collect-EdrSurface.ps1" {4-6,8-9,11-13} $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. 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 - Microsoft, [PsSetCreateProcessNotifyRoutineEx](https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/nf-ntddk-pssetcreateprocessnotifyroutineex) and [PS_CREATE_NOTIFY_INFO](https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/ns-ntddk-_ps_create_notify_info). - Microsoft, [PsSetCreateThreadNotifyRoutine](https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/nf-ntddk-pssetcreatethreadnotifyroutine) and [PCREATE_THREAD_NOTIFY_ROUTINE](https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/nc-ntddk-pcreate_thread_notify_routine). - Microsoft, [PsSetLoadImageNotifyRoutine](https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/nf-ntddk-pssetloadimagenotifyroutine) and [PLOAD_IMAGE_NOTIFY_ROUTINE](https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/nc-ntddk-pload_image_notify_routine). - Microsoft, [Registering for Registry Notifications](https://learn.microsoft.com/en-us/windows-hardware/drivers/kernel/registering-for-notifications) and [REG_NOTIFY_CLASS](https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ne-wdm-_reg_notify_class). - Microsoft, [ObRegisterCallbacks](https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-obregistercallbacks), [OB_OPERATION_REGISTRATION](https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_ob_operation_registration), and [OB_PRE_CREATE_HANDLE_INFORMATION](https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_ob_pre_create_handle_information). - Microsoft, [Filter Manager Concepts](https://learn.microsoft.com/en-us/windows-hardware/drivers/ifs/filter-manager-concepts), [Minifilter Pre/Post Callback Ordering](https://learn.microsoft.com/en-us/windows-hardware/drivers/ifs/writing-preoperation-and-postoperation-callback-routines), and [Load Order Groups and Altitudes](https://learn.microsoft.com/en-us/windows-hardware/drivers/ifs/load-order-groups-and-altitudes-for-minifilter-drivers). - Microsoft, [About Event Tracing](https://learn.microsoft.com/en-us/windows/win32/etw/about-event-tracing). - Microsoft, [Antimalware Scan Interface](https://learn.microsoft.com/en-us/windows/win32/amsi/antimalware-scan-interface-portal). - Microsoft, [`logman query`](https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/logman-query). - Microsoft, [Windows Filtering Platform Architecture](https://learn.microsoft.com/en-us/windows-hardware/drivers/network/windows-filtering-platform-architecture-overview), [WFP Components and Base Filtering Engine](https://learn.microsoft.com/en-us/windows/win32/fwp/about-windows-filtering-platform), [Application Layer Enforcement](https://learn.microsoft.com/en-us/windows/win32/fwp/application-layer-enforcement--ale-), and [WFP Operation](https://learn.microsoft.com/en-us/windows/win32/fwp/basic-operation). - Microsoft, [`netsh wfp`](https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/netsh-wfp). - Microsoft, [Detours: Using Detours](https://github.com/microsoft/Detours/wiki/Using-Detours). - Microsoft, [Recommended Driver Block Rules](https://learn.microsoft.com/en-us/windows/security/application-security/application-control/app-control-for-business/design/microsoft-recommended-driver-block-rules). - Microsoft, [Memory Integrity, VBS, and HVCI](https://learn.microsoft.com/en-us/windows/security/hardware-security/enable-virtualization-based-protection-of-code-integrity?tabs=intune). - Yosifovich, Ionescu, Russinovich, and Solomon, [*Windows Internals*, Seventh Edition, Part 1](https://learn.microsoft.com/en-us/sysinternals/resources/windows-internals), Microsoft Press, 2017. - Russinovich, Solomon, and Ionescu, *Windows Internals*, Sixth Edition, Part 1, Microsoft Press, 2012. Consulted for historical architecture only. - Altered Security, *Evasion Lab Manual*, learning objectives 9–11, 2025. Offline course material consulted for lab-specific debugging methodology.