Register User-Mode GUID

When given 0x0F as its FunctionCode argument, the NtTraceControl function tells the kernel about a user-mode registration of an event provider. Microsoft’s name for this function code is not known. This note deals only with the function’s behaviour that is specific to this function code. The function’s general behaviour is here taken as assumed knowledge.

A provider is represented by a GUID and can have any number of registrations. For a user-mode registration, the effect really is like opening a provider in that it produces an Object Manager handle to the underlying registration object. As well as opening this handle for the user-mode caller, the function also produces information. In all versions, this includes to describe a tracing session, if any, that has started in anticipation of the provider’s registration. Starting with version 6.1, the output can continue with a description of a schematized event filter that is already known for the GUID.

The function returns STATUS_INVALID_PARAMETER if any of the following are true:

Microsoft’s name for the 0xA0 bytes of fixed-size input and output is not known. Indeed, it is not known that Microsoft defines them as one structure. They certainly are interpreted in parts, not that Microsoft’s names are known for these either. A 0x28-byte header describes the event provider, both to tell the kernel about the provider and to return the handle that the kernel creates for the registration.

Offset Definition Versions Remarks
0x00 the provider’s GUID 6.0 and higher input
0x10 an ETW_NOTIFICATION_TYPE 6.0 and higher input
0x14 the process’s 16-bit index for this registration 6.0 and higher input
0x18 a 64-bit allowance for a HANDLE to the provider’s ETW_REG_ENTRY 6.0 and higher output
0x20 apparently reserved for future definition 6.0 to 6.2  
a 64-bit allowance for the address of the provider’s callback function 6.3 and higher input

Incidentally, that the kernel is nowadays told of the provider’s user-mode callback function is not because the kernel has any role in the calling back. It is instead so that the kernel can identify the calling module. This matters in version 6.3 and higher because tracing sessions can get system-defined events written to them to record which providers were enabled for the session.

Though the remaining 0x78 bytes are expected on input, they are used only for output. Moreover, though the header looks to be particular to this function code, the remaining 0x78 bytes (and any variable-size data that follows) are shared with function code 0x11 to describe how a provider is enabled for a tracing session. In this case, the tracing session that’s described as output for function code 0x0F is the logger for which the provider was most recently enabled. Microsoft’s name for this structure is not known. Relevant routines refer to it as an enable notification: the name ETW_ENABLE_NOTIFICATION_PACKET would follow a pattern that might be induced from the known name for another structure that begins with an ETW_NOTIFICATION_HEADER, but is too much of a guess to commit to here.

Offset Definition Versions Remarks
0x00 an ETW_NOTIFICATION_HEADER 6.0 and higher NotificationSize for output
(but see note after table)
0x48 a TRACE_ENABLE_INFO 6.0 and higher output
0x68 a TRACE_ENABLE_CONTEXT 6.0 and higher output
0x70 0 to disable events;
1 to enable events
6.0 and higher output
0x74 apparently reserved for future definition 6.0 only  
indicator of what data follows 6.1 and higher output

The ETW_NOTIFICATION_HEADER appears to be just a formality—literally from sharing the form of data that does matter for function code 0x11. For function code 0x0F, the version 6.0 kernel ignores this header completely and in no version does the kernel set anything in this header other than the NotificationSize. Even then, versions 6.1 and 6.2 set it only if following with variable-size data. It’s set always on success in versions 6.3 and higher, but to the size of the whole output when surely what would be expected is the size just from the notification header onwards. (NTDLL, however, expects nothing.)

What can follow this fixed-size data is a description of the schematized event filters that yet apply to the provider. The format for this description is version-dependent. In versions 6.1 and 6.2, it is a sequence of EVENT_FILTER_HEADER structures, each introducing variable-size data. The header’s Size member is the total size, in bytes, of the header and data. Its NextOffset member is the possibly larger number of bytes from the header to the next header, or is zero in the last header. In version 6.3 and higher, there is first an EVENT_FILTER_DESCRIPTOR. Its Ptr member is the number of bytes from the notification header to the EVENT_FILTER_HEADER sequence. Its Size is that of the sequence. This change of format also affects the indicator at offset 0x74. In versions 6.1 and 6.2, this is 0x80000000, which may be intended as the filter type, specifically EVENT_FILTER_TYPE_SCHEMATIZED. In later versions, the indicator is boolean and the filter type is explicitly in the Type member of the EVENT_FILTER_DESCRIPTOR.

This function code is expressly not for registering the security provider. If the provider’s GUID is {54849625-5478-4994-A5BA-3E3B0328C30D}, which Microsoft represents symbolically as SecurityProviderGuid, the function fails, returning STATUS_ACCESS_DENIED.