Geoff Chappell, Software Analyst
DRAFT: Take more than your usual care.
Right from its introduction for Windows 2000, before it even had its current name, Event Tracing for Windows (ETW) has parallel implementations in kernel and user modes. The practical difference is not just with where the code is but with where the trace buffers are managed. One aim of ETW is that events are written quickly into trace buffers which are then serviced independently from a logger thread, e.g., to flush full buffers to an Event Trace Log (ETL) file. In an important special case for programs that trace their own diagnostics, an event provider that executes as a user-mode process may be enabled into a tracing session that executes in the same process and chooses the user-mode ETW implementation. For such a tracing session, the trace buffers are managed in user mode and writing an event (to a trace buffer) avoids the delay of going to and from kernel mode. This of course requires a substantial user-mode implementation, while also retaining access to the kernel-mode implementation in case the same provider is also enabled into a tracing session in a different process. In this more general case for which writing an event to a trace buffer means sending it to the kernel, some substance is desirable in the user-mode implementation so that the expense of sending an event to the kernel is avoided if it can be known that the event isn’t wanted by any tracing session that has kernel-mode tracing buffers.
Thus does it happen that NTDLL keeps no small amount of its own state about each user-mode registration of an event provider in addition to the ETW_REG_ENTRY that the kernel keeps. The ETW_REGISTRATION_ENTRY structure is this user-mode state.
The ETW_REGISTRATION_ENTRY structure is not documented. Neither is it known even from public symbol files. The only public disclosure that is yet known of this structure’s name by Microsoft is that the WMITRACE.DLL debugger extension knows to look for it to support its obsolete !regtable command. The suggestion is strong that the type information that WMITRACE needs for this is available only in private symbol files and even then only for Windows 7.
The ETW_REGISTRATION_ENTRY has been relatively stable. Windows 7 saw an insertion, which lengthened the structure. The only significant change and reordering came with Windows 8 and shows in the relevant API functions as a reinterpretation of the opaque REGHANDLE.
Versions | Size (x86) | Size (x64) |
---|---|---|
6.0 | 0xB8 | 0xC8 |
6.1 | 0xD0 | 0xF0 |
6.2 and higher | 0xC8 | 0x0100 |
These sizes, and the offsets, types and names in the detailed layout below, come from inspection of the binaries for NTDLL. The user-mode registration entry has some points in common with the two kernel-mode structures ETW_REG_ENTRY and ETW_GUID_ENTRY which are known from public symbol files for the kernel, but the correspondence is not close enough even for suppositions of Microsoft’s names and types. A smattering of such names and types are inferred with reasonable certainty by matching known use with the names that WMITRACE seeks from private symbol files. Where the use continues to later versions, the corresponding names and types are assumed to too.
Offset (x86) | Offset (x64) | Definition | Versions |
---|---|---|---|
0x00 | 0x00 | an RTL_BALANCED_NODE | 6.2 and higher |
0x18 | unaccounted eight bytes | 6.2 and higher | |
0x00 (6.0 to 6.1); 0x0C |
0x00 (6.0 to 6.1); 0x20 |
GUID ProviderGuid; |
6.0 and higher |
0x10 (6.0 to 6.1) | 0x10 (6.0 to 6.1) | unknown HANDLE to the kernel-mode registration | 6.0 to 6.1 |
0x14 (6.0 to 6.1) | 0x18 (6.0 to 6.1) |
<unknown-type> RegistrationHandle; |
6.0 to 6.1 |
0x1C (6.1) | 0x20 (6.1) | unknown CRITICAL_SECTION | 6.1 only |
0x1C (6.0); 0x34 (6.1); 0x1C |
0x20 (6.0); 0x48 (6.1); 0x30 |
PVOID Callback; |
6.0 and higher |
0x20 (6.0); 0x38 (6.1); 0x20 |
0x28 (6.0); 0x50 (6.1); 0x38 |
PVOID Context; |
6.0 and higher |
0x24 | 0x40 | unknown SRWLOCK | 6.2 and higher |
0x28 | 0x48 | unknown SRWLOCK | 6.2 and higher |
0x2C | 0x50 | unknown 32-bit thread ID | 6.2 and higher |
0x30 | 0x58 | unknown HANDLE to the kernel-mode registration | 6.2 and higher |
0x34 | 0x60 | unknown 16-bit sequence number | 6.2 and higher |
0x24 (6.0); 0x3C (6.1); 0x36 |
0x30 (6.0); 0x58 (6.1); 0x62 |
ULONG Type; |
6.0 to 6.1 |
struct { /* USHORT bit fields, see below */ }; |
6.2 and higher | ||
0x28 (6.0); 0x40 (6.1); 0x38 |
0x38 (6.0); 0x60 (6.1); 0x68 |
unknown 0x18-byte structure for kernel-mode registration | 6.0 and higher |
0x40 (6.0); 0x58 (6.1); 0x50 |
0x50 (6.0); 0x78 (6.1); 0x80 |
unknown array of four 0x18-byte structures for private registrations | 6.0 and higher |
0xA0 (6.0); 0xB8 (6.1); 0xB0 |
0xB0 (6.0); 0xD8 (6.1); 0xE0 |
unknown 0x18-byte structure as aggregate of private registrations | 6.0 and higher |
0xF8 | unaccounted eight bytes | 6.2 and higher |
The Type takes its values from the ETW_NOTIFICATION_TYPE enumeration. Windows 8 narrows it to 16 bits and squeezes in a one-bit flag. Windows 10 slips in another.
Mask | Description | Versions |
---|---|---|
0x7FFF (6.2 to 6.3); 0x3FFF |
USHORT Type : 15; |
6.2 to 6.3 |
USHORT Type : 14; |
10.0 and higher | |
0x4000 | use descriptor type | 10.0 and higher |
0x8000 | track provider binary | 6.2 and higher |