etw

package module
v0.0.0-...-7945f3e Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 30, 2021 License: MIT Imports: 9 Imported by: 0

README

etw

GoDev Go Report Card

etw is a Go-package that allows you to receive Event Tracing for Windows (ETW) events in go code.

etw allows you to process events from new TraceLogging providers as well as from classic (aka EventLog) providers, so you could actually listen to anything you can see in Event Viewer window.

ETW API expects you to pass stdcall callback to process events, so etw requires CGO to be used. To use etw you need to have mingw-w64 installed and pass some environment to the Go compiler (take a look at build/vars.sh and examples/tracer/Makefile).

Docs

Package reference is available at https://pkg.go.dev/github.com/bi-zone/etw

Examples are located in examples folder.

Usage

package main

import (
	"log"
	"os"
	"os/signal"
	"sync"

	"github.com/bi-zone/etw"
	"golang.org/x/sys/windows"
)

func main() {
	// Subscribe to Microsoft-Windows-DNS-Client
	guid, _ := windows.GUIDFromString("{1C95126E-7EEA-49A9-A3FE-A378B03DDB4D}")
	session, err := etw.NewSession(guid)
	if err != nil {
		log.Fatalf("Failed to create etw session: %s", err)
	}

	// Wait for "DNS query request" events to log outgoing DNS requests.
	cb := func(e *etw.Event) {
		if e.Header.ID != 3006 {
			return
		}
		if data, err := e.EventProperties(); err == nil && data["QueryType"] == "1" {
			log.Printf("PID %d just queried DNS for domain %v", e.Header.ProcessID, data["QueryName"])
		}
	}

	// `session.Process` blocks until `session.Close()`, so start it in routine.
	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		if err := session.Process(cb); err != nil {
			log.Printf("[ERR] Got error processing events: %s", err)
		}
		wg.Done()
	}()

	// Trap cancellation.
	sigCh := make(chan os.Signal, 1)
	signal.Notify(sigCh, os.Interrupt)
	<-sigCh

	if err := session.Close(); err != nil {
		log.Printf("[ERR] Got error closing the session: %s", err)
	}
	wg.Wait()
}

Note: to run the example you may need to pass CGO-specific variables to Go compiler, the easiest way to do it is:

bash -c 'source ./build/vars.sh && go run main.go'

More sophisticated examples can be found in examples folder.

Contributing

Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.

Please make sure to update tests as appropriate.

Documentation

Rendered for windows/amd64

Overview

Package etw allows you to receive Event Tracing for Windows (ETW) events.

etw allows you to process events from new TraceLogging providers as well as from classic (aka EventLog) providers, so you could actually listen to anything you can see in Event Viewer window.

For possible usage examples take a look at https://github.com/bi-zone/etw/tree/master/examples

Index

Constants

View Source
const (
	TRACE_LEVEL_CRITICAL    = TraceLevel(1)
	TRACE_LEVEL_ERROR       = TraceLevel(2)
	TRACE_LEVEL_WARNING     = TraceLevel(3)
	TRACE_LEVEL_INFORMATION = TraceLevel(4)
	TRACE_LEVEL_VERBOSE     = TraceLevel(5)
)
View Source
const (
	// Include in the ExtendedEventInfo the security identifier (SID) of the user.
	EVENT_ENABLE_PROPERTY_SID = EnableProperty(0x001)

	// Include in the ExtendedEventInfo the terminal session identifier.
	EVENT_ENABLE_PROPERTY_TS_ID = EnableProperty(0x002)

	// Include in the ExtendedEventInfo a call stack trace for events written
	// using EventWrite.
	EVENT_ENABLE_PROPERTY_STACK_TRACE = EnableProperty(0x004)

	// Filters out all events that do not have a non-zero keyword specified.
	// By default events with 0 keywords are accepted.
	EVENT_ENABLE_PROPERTY_IGNORE_KEYWORD_0 = EnableProperty(0x010)

	// Filters out all events that are either marked as an InPrivate event or
	// come from a process that is marked as InPrivate. InPrivate implies that
	// the event or process contains some data that would be considered private
	// or personal. It is up to the process or event to designate itself as
	// InPrivate for this to work.
	EVENT_ENABLE_PROPERTY_EXCLUDE_INPRIVATE = EnableProperty(0x200)
)
View Source
const KernelLoggerName = "NT Kernel Logger"
View Source
const KernelProviderProcess uint64 = C.EVENT_TRACE_FLAG_PROCESS
View Source
const KernelProviderTCPIP uint64 = C.EVENT_TRACE_FLAG_NETWORK_TCPIP

Variables

View Source
var KernelLoggerGUID windows.GUID

Functions

func KillSession

func KillSession(name string) error

KillSession forces the session with a given @name to stop. Don't having a session handle we can't shutdown it gracefully unsubscribing from all the providers first, so we just stop the session itself.

Use KillSession only to destroy session you've lost control over. If you have a session handle always prefer `.Close`.

Types

type EnableProperty

type EnableProperty C.ULONG

EnableProperty enables a property of a provider session is subscribing for.

For more info about available properties check original API reference: https://docs.microsoft.com/en-us/windows/win32/api/evntrace/ns-evntrace-enable_trace_parameters

type Event

type Event struct {
	Header EventHeader
	// contains filtered or unexported fields
}

Event is a single event record received from ETW provider. The only thing that is parsed implicitly is an EventHeader (which just translated from C structures mostly 1:1), all other data are parsed on-demand.

Events will be passed to the user EventCallback. It's invalid to use Event methods outside of an EventCallback.

func (*Event) EventProperties

func (e *Event) EventProperties() (map[string]interface{}, error)

EventProperties returns a map that represents events-specific data provided by event producer. Returned data depends on the provider, event type and even provider and event versions.

The simplest (and the recommended) way to parse event data is to use TDH family of functions that render event data to the strings exactly as you can see it in the Event Viewer.

EventProperties returns a map that could be interpreted as "structure that fit inside a map". Map keys is a event data field names, map values is field values rendered to strings. So map values could be one of the following:

  • `[]string` for arrays of any types;
  • `map[string]interface{}` for fields that are structures;
  • `string` for any other values.

Take a look at `TestParsing` for possible EventProperties values.

func (*Event) ExtendedInfo

func (e *Event) ExtendedInfo() ExtendedEventInfo

ExtendedInfo extracts ExtendedEventInfo structure from native buffers of received event record.

If no ExtendedEventInfo is available inside an event record function returns the structure with all fields set to nil.

type EventCallback

type EventCallback func(e *Event)

EventCallback is any function that could handle an ETW event. EventCallback is called synchronously and sequentially on every event received by Session one by one.

If EventCallback can't handle all ETW events produced, OS will handle a tricky file-based cache for you, however, it's recommended not to perform long-running tasks inside a callback.

N.B. Event pointer @e is valid ONLY inside a callback. You CAN'T copy a whole event, only EventHeader, EventProperties and ExtendedEventInfo separately.

type EventDescriptor

type EventDescriptor struct {
	ID      uint16
	Version uint8
	Channel uint8
	Level   uint8
	OpCode  uint8
	Task    uint16
	Keyword uint64
}

EventDescriptor contains low-level metadata that defines received event. Most of fields could be used to refine events filtration.

For detailed information about fields values refer to EVENT_DESCRIPTOR docs: https://docs.microsoft.com/ru-ru/windows/win32/api/evntprov/ns-evntprov-event_descriptor

type EventHeader

type EventHeader struct {
	EventDescriptor

	ThreadID  uint32
	ProcessID uint32
	TimeStamp time.Time

	ProviderID windows.GUID
	ActivityID windows.GUID

	Flags         uint16
	KernelTime    uint32
	UserTime      uint32
	ProcessorTime uint64
}

EventHeader contains an information that is common for every ETW event record.

EventHeader fields is self-descriptive. If you need more info refer to the original struct docs: https://docs.microsoft.com/en-us/windows/win32/api/evntcons/ns-evntcons-event_header

func (EventHeader) HasCPUTime

func (h EventHeader) HasCPUTime() bool

HasCPUTime returns true if the event has separate UserTime and KernelTime measurements. Otherwise the value of UserTime and KernelTime is meaningless and you should use ProcessorTime instead.

type EventInstanceInfo

type EventInstanceInfo struct {
	InstanceID       uint32
	ParentInstanceID uint32
	ParentGUID       windows.GUID
}

EventInstanceInfo defines the relationship between events if its provided.

type EventStackTrace

type EventStackTrace struct {
	MatchedID uint64
	Addresses []uint64
}

EventStackTrace describes a call trace of the event occurred.

type ExistsError

type ExistsError struct{ SessionName string }

ExistsError is returned by NewSession if the session name is already taken.

Having ExistsError you have an option to force kill the session:

var exists etw.ExistsError
s, err = etw.NewSession(s.guid, etw.WithName(sessionName))
if errors.As(err, &exists) {
	err = etw.KillSession(exists.SessionName)
}

func (ExistsError) Error

func (e ExistsError) Error() string

type ExtendedEventInfo

type ExtendedEventInfo struct {
	SessionID    *uint32
	ActivityID   *windows.GUID
	UserSID      *windows.SID
	InstanceInfo *EventInstanceInfo
	StackTrace   *EventStackTrace
}

ExtendedEventInfo contains additional information about received event. All ExtendedEventInfo fields are optional and are nils being not set by provider.

Presence of concrete fields is controlled by WithProperty option and an ability of event provider to set the required fields.

More info about fields is available at EVENT_HEADER_EXTENDED_DATA_ITEM.ExtType documentation: https://docs.microsoft.com/en-us/windows/win32/api/evntcons/ns-evntcons-event_header_extended_data_item

type Option

type Option func(cfg *SessionOptions)

Option is any function that modifies SessionOptions. Options will be called on default config in NewSession. Subsequent options that modifies same fields will override each other.

func WithKernelEnableFlags

func WithKernelEnableFlags(flags uint64) Option

func WithLevel

func WithLevel(lvl TraceLevel) Option

WithLevel specifies a maximum level consumer is interested in. Higher levels imply that you get lower levels as well. For example, with TRACE_LEVEL_ERROR you'll get all events except ones with level critical.

func WithMatchKeywords

func WithMatchKeywords(anyKeyword, allKeyword uint64) Option

WithMatchKeywords allows to specify keywords of receiving events. Each event has a set of keywords associated with it. That keywords are encoded as bit masks and matched with provided @anyKeyword and @allKeyword values.

A session will receive only those events whose keywords masks has ANY of @anyKeyword and ALL of @allKeyword bits sets.

For more info take a look a SessionOptions docs. To query keywords defined by specific provider identified by <GUID> try:

logman query providers <GUID>

func WithName

func WithName(name string) Option

WithName specifies a provided @name for the creating session. Further that session could be controlled from other processed by it's name, so it should be unique.

func WithProperty

func WithProperty(p EnableProperty) Option

WithProperty enables additional provider feature toggled by @p. Subsequent WithProperty options will enable all provided options.

For more info about available properties check EnableProperty doc and original API reference: https://docs.microsoft.com/en-us/windows/win32/api/evntrace/ns-evntrace-enable_trace_parameters

type Session

type Session struct {
	// contains filtered or unexported fields
}

Session represents a Windows event tracing session that is ready to start events processing. Session subscribes to the given ETW provider only on `.Process` call, so having a Session without `.Process` called should not affect OS performance.

Session should be closed via `.Close` call to free obtained OS resources even if `.Process` has never been called.

func NewKernelSession

func NewKernelSession(options ...Option) (*Session, error)

func NewSession

func NewSession(providerGUID windows.GUID, options ...Option) (*Session, error)

NewSession creates a Windows event tracing session instance. Session with no options provided is a usable session, but it could be a bit noisy. It's recommended to refine the session with level and match keywords options to get rid of unnecessary events.

You MUST call `.Close` on session after use to clear associated resources, otherwise it will leak in OS internals until system reboot.

func (*Session) Close

func (s *Session) Close() error

Close stops trace session and frees associated resources.

func (*Session) Process

func (s *Session) Process(cb EventCallback) error

Process starts processing of ETW events. Events will be passed to @cb synchronously and sequentially. Take a look to EventCallback documentation for more info about events processing.

N.B. Process blocks until `.Close` being called!

func (*Session) UpdateOptions

func (s *Session) UpdateOptions(options ...Option) error

UpdateOptions changes subscription parameters in runtime. The only option that can't be updated is session name. To change session name -- stop and recreate a session with new desired name.

type SessionOptions

type SessionOptions struct {
	// Name specifies a name of ETW session being created. Further a session
	// could be controlled from other processed by it's name, so it should be
	// unique.
	Name string

	// Level represents provider-defined value that specifies the level of
	// detail included in the event. Higher levels imply that you get lower
	// levels as well. For example, with TRACE_LEVEL_ERROR you'll get all
	// events except ones with level critical. Check `EventDescriptor.Level`
	// values for current event verbosity level.
	Level TraceLevel

	// MatchAnyKeyword is a bitmask of keywords that determine the category of
	// events that you want the provider to write. The provider writes the
	// event if any of the event's keyword bits match any of the bits set in
	// this mask.
	//
	// If MatchAnyKeyword is not set the session will receive ALL possible
	// events (which is equivalent setting all 64 bits to 1).
	//
	// Passed as is to EnableTraceEx2. Refer to its remarks for more info:
	// https://docs.microsoft.com/en-us/windows/win32/api/evntrace/nf-evntrace-enabletraceex2#remarks
	MatchAnyKeyword uint64

	// MatchAllKeyword is an optional bitmask that further restricts the
	// category of events that you want the provider to write. If the event's
	// keyword meets the MatchAnyKeyword condition, the provider will write the
	// event only if all of the bits in this mask exist in the event's keyword.
	//
	// This mask is not used if MatchAnyKeyword is zero.
	//
	// Passed as is to EnableTraceEx2. Refer to its remarks for more info:
	// https://docs.microsoft.com/en-us/windows/win32/api/evntrace/nf-evntrace-enabletraceex2#remarks
	MatchAllKeyword uint64

	// EnableProperties defines a set of provider properties consumer wants to
	// enable. Properties adds fields to ExtendedEventInfo or asks provider to
	// sent more events.
	//
	// For more info about available properties check EnableProperty doc and
	// original API reference:
	// https://docs.microsoft.com/en-us/windows/win32/api/evntrace/ns-evntrace-enable_trace_parameters
	EnableProperties []EnableProperty

	KernelEnableFlags uint64
	// contains filtered or unexported fields
}

SessionOptions describes Session subscription options.

Most of options will be passed to EnableTraceEx2 and could be refined in its docs: https://docs.microsoft.com/en-us/windows/win32/api/evntrace/nf-evntrace-enabletraceex2

type TraceLevel

type TraceLevel C.UCHAR

TraceLevel represents provider-defined value that specifies the level of detail included in the event. Higher levels imply that you get lower levels as well.

Directories

Path Synopsis
examples

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL