Geoff Chappell, Software Analyst
SKETCH OF HOW RESEARCH MIGHT CONTINUE AND RESULTS BE PRESENTED
This function sends a data block through the kernel to user-mode registrations of an event provider, and optionally receives replies.
ULONG EtwSendNotification ( ETW_NOTIFICATION_HEADER *DataBlock, ULONG ReceiveDataBlockSize, PVOID ReceiveDataBlock, ULONG *ReplyReceived, ULONG *ReplySizeNeeded);
The required DataBlock argument is the address of the data block to send. Its size in bytes is in the header on input.
The optional ReceiveDataBlock and ReceiveDataBlockSize are respectively the address and size of an ouput buffer that is to receive the replies. Whether any reply is sought must be specified in the header on input. If no reply is sought then these arguments and the next two are ignored. But if a reply is sought, then these arguments and the next two are required.
The optional ReplyReceived argument is the address of a variable that the function may set on output. On success, it tell how many replies were received. On failure, it may tell how many replies were available.
The optional ReplySizeNeeded argument is the address of a variable that the function may set on output. On success, it tells how many bytes of the output buffer have been used for replies. On failure, it may tell how many bytes were needed.
The function returns zero for success, else a Win32 error code.
The error code ERROR_INSUFFICIENT_BUFFER is the function’s indication that it failed only because the output buffer was too small for the replies. The fuction sets the variables at ReplyReceived and ReplySizeNeeded but the contents of the output buffer are undefined. For other error codes, the variables at ReplyReceived and ReplySizeNeeded may be set but not meaningfully.
The EtwSendNotification function is exported by name from NTDLL.DLL in version 6.0 and higher.
Microsoft does not document EtwSendNotification.
A C-language declaration of EtwSendNotification is published by Microsoft in a file named NTETW.H in the Enterprise edition of the Windows Driver Kit (WDK) for Windows 10 Version 1511.
The function’s communication with the kernel is through NtTraceControl with 0x11 as the FunctionCode to send and 0x13 to receive. The first is called just once to send the input. The second may be called multiple times to build successive replies in the output buffer. Note that the kernel may edit the input, notably to tell how many replies to receive.
The function’s input is a fixed-size header followed by arbitrary other data. The function’s only interpretation of the header on input is that:
The kernel, of course, interprets more of this header and of the data block beyond. Especially notable on the kernel side are:
Kernel-level detail is in preparation for publication elsewhere.
If the input is sent successfully, its header will have been reused as the kernel’s output. If the function is to receive replies, then
Note that although the header on input is formally an ETW_NOTIFICATION_HEADER, the function interprets the header on output (from the kernel) as an ETWP_NOTIFICATION_HEADER. (or knows to interpret the former’s Reserved2 as the latter’s ReplyHandle). If the function is given distinct input and output buffers, the kernel’s changes to the input persist and the curious caller can learn what handle was used, though it will by then be stale: the kernel creates the object and the function closes the handle.
Given that the input is sent successfully and ReplyRequested was non-zero on input, the function aims to call the kernel ReplyCount times to collect that many replies. Each reply is a fixed-size ETW_NOTIFICATION_HEADER followed by arbitrary other data. Successive replies are received successively further into the buffer, each with 8-byte alignment. The function trusts that each reply has its size as NotificationSize. The function itself records as Offset the distance in bytes from the start of one reply to the start of the next, with zero signifying the last reply.
Once the total time taken for replies exceeds the timeout, the function returns ERROR_TIMEOUT without setting the variables at ReplyReceived or ReplySizeNeeded. Perhaps from a coding oversight, it does this even if the reply that times out is the last that’s sought and has succeeded.
Special handling applies when the NotificationType is EtwNotificationTypeEnable. The function reads replies but to a buffer on the stack. The replies are discarded and the ReceiveDataBlock and ReceiveDataBlockSize arguments are ignored, even though the successful function does set the variables at ReplyReceived and ReplySizeNeeded as if the output buffer has meaningful content.