diff --git a/pkg/etw/provider.go b/pkg/etw/provider.go index 28db0d5..2369601 100644 --- a/pkg/etw/provider.go +++ b/pkg/etw/provider.go @@ -94,26 +94,27 @@ func providerCallbackAdapter(sourceID *guid.GUID, state uintptr, level uintptr, // uses the same algorithm as used by .NET's EventSource class, which is based // on RFC 4122. More information on the algorithm can be found here: // https://blogs.msdn.microsoft.com/dcook/2015/09/08/etw-provider-names-and-guids/ -// The algorithm is roughly: -// Hash = Sha1(namespace + arg.ToUpper().ToUtf16be()) -// Guid = Hash[0..15], with Hash[7] tweaked according to RFC 4122 +// +// The algorithm is roughly the RFC 4122 algorithm for a V5 UUID, but differs in +// the following ways: +// - The input name is first upper-cased, UTF16-encoded, and converted to +// big-endian. +// - No variant is set on the result UUID. +// - The result UUID is treated as being in little-endian format, rather than +// big-endian. func providerIDFromName(name string) guid.GUID { buffer := sha1.New() - - namespace := []byte{0x48, 0x2C, 0x2D, 0xB2, 0xC3, 0x90, 0x47, 0xC8, 0x87, 0xF8, 0x1A, 0x15, 0xBF, 0xC1, 0x30, 0xFB} - buffer.Write(namespace) - + namespace := guid.GUID{0x482C2DB2, 0xC390, 0x47C8, [8]byte{0x87, 0xF8, 0x1A, 0x15, 0xBF, 0xC1, 0x30, 0xFB}} + namespaceBytes := namespace.ToArray() + buffer.Write(namespaceBytes[:]) binary.Write(buffer, binary.BigEndian, utf16.Encode([]rune(strings.ToUpper(name)))) sum := buffer.Sum(nil) sum[7] = (sum[7] & 0xf) | 0x50 - return guid.GUID{ - Data1: binary.LittleEndian.Uint32(sum[0:4]), - Data2: binary.LittleEndian.Uint16(sum[4:6]), - Data3: binary.LittleEndian.Uint16(sum[6:8]), - Data4: [8]byte{sum[8], sum[9], sum[10], sum[11], sum[12], sum[13], sum[14], sum[15]}, - } + a := [16]byte{} + copy(a[:], sum) + return guid.FromWindowsArray(a) } // NewProvider creates and registers a new ETW provider. The provider ID is diff --git a/pkg/etw/provider_test.go b/pkg/etw/provider_test.go new file mode 100644 index 0000000..69d9910 --- /dev/null +++ b/pkg/etw/provider_test.go @@ -0,0 +1,34 @@ +package etw + +import ( + "testing" + + "github.com/Microsoft/go-winio/pkg/guid" +) + +func mustGUIDFromString(t *testing.T, s string) guid.GUID { + g, err := guid.FromString(s) + if err != nil { + t.Fatal(err) + } + return g +} + +func Test_ProviderIDFromName(t *testing.T) { + type testCase struct { + name string + g guid.GUID + } + testCases := []testCase{ + {"wincni", mustGUIDFromString(t, "c822b598-f4cc-5a72-7933-ce2a816d033f")}, + {"Moby", mustGUIDFromString(t, "6996f090-c5de-5082-a81e-5841acc3a635")}, + {"ContainerD", mustGUIDFromString(t, "2acb92c0-eb9b-571a-69cf-8f3410f383ad")}, + {"Microsoft.Virtualization.RunHCS", mustGUIDFromString(t, "0B52781F-B24D-5685-DDF6-69830ED40EC3")}, + } + for _, tc := range testCases { + g := providerIDFromName(tc.name) + if g != tc.g { + t.Fatalf("Incorrect provider GUID.\nExpected: %s\nActual: %s", tc.g, g) + } + } +}