使用 go 读取 ETW 提供程序

Reading ETW providers using go

我正在尝试从 Advapi32.dll 中访问 EnumerateTraceGuids 函数。 我还处于非常早期的阶段,仍在尝试破译我必须做什么。我有以下代码不断给我错误:87,意思是 ERROR_INVALID_PARAMETER.

虽然它只是写入而不是读取,但我已将此文件用作起点: https://github.com/moby/moby/blob/master/daemon/logger/etwlogs/etwlogs_windows.go

我要调用的函数的官方文档在这里: https://msdn.microsoft.com/en-us/library/windows/desktop/aa363713(v=vs.85).aspx

它需要 GuidPropertiesArray [in, out] 指向 TRACE_GUID_PROPERTIES 结构的指针数组。这个结构如下(https://msdn.microsoft.com/en-us/library/windows/desktop/aa364143(v=vs.85).aspx)

typedef struct _TRACE_GUID_PROPERTIES {
  GUID    Guid;
  ULONG   GuidType;
  ULONG   LoggerId;
  ULONG   EnableLevel;
  ULONG   EnableFlags;
  BOOLEAN IsEnable;
} TRACE_GUID_PROPERTIES, *PTRACE_GUID_PROPERTIES;

我有以下代码来尝试执行此操作:

package main
import (
    "errors"
    "fmt"
    "syscall"
    "unsafe"
    "github.com/sirupsen/logrus"
    "golang.org/x/sys/windows"
)

const (
    win32CallSuccess = 0
    MaxProv = 50
    nbProviders = 50
)

var (
    modAdvapi32          = windows.NewLazySystemDLL("Advapi32.dll")
    procEnumerateTraceGuids = modAdvapi32.NewProc("EnumerateTraceGuids")
)

type ulong int32

type TRACE_GUID_PROPERTIES struct {
    Guid syscall.GUID
    GuidType ulong
    LoggerId ulong
    EnableLevel ulong
    EnableFlags ulong
    IsEnable bool
}

func callEnumerateTraceGuids() error {
    GuidPropertiesArray:= make([]TRACE_GUID_PROPERTIES, 1)
    ptr := &GuidPropertiesArray[0]
    ret, _, _ := procEnumerateTraceGuids.Call(uintptr(unsafe.Pointer(&ptr)), MaxProv, nbProviders)
    if ret != win32CallSuccess {
        errorMessage := fmt.Sprintf("Failed to register ETW provider. Error: %d", ret)
        logrus.Error(errorMessage)
        return errors.New(errorMessage)
    }
    return nil
}

func main() {
    callEnumerateTraceGuids()
}

此时我不确定我必须做什么。我尝试了很多初始化数组的变体,但都没有成功。 希望有人能指出我正确的方向。 谢谢!

编辑:根据评论更改了代码,但仍然出现相同的错误。

PS :这是我第一次在 Whosebug 上发帖,我已经被告知我在发布问题后不到 12 小时就懒惰了(耶!)所以我不确定我在问这对...我不太熟悉 go 并且从来没有从 go 调用过 windows DLL 并且因为我一直在打那个 ERROR_INVALID_PARAMETER 我想伸出手来尝试通过这第一堵墙能够同时掌握一些概念。希望这有助于理解我的请求(即,我平安而来)。

好的,我有一点空闲时间并且可以使用 Windows XP 盒子, 所以我决定重温我的 Windows 编程技能 并编写了一个可行的解决方案:

package main

import (
    "golang.org/x/sys/windows"

    "log"
    "syscall"
    "unsafe"
)

var (
    modAdvapi32             = windows.NewLazySystemDLL("advapi32")
    procEnumerateTraceGuids = modAdvapi32.NewProc("EnumerateTraceGuids")
)

type traceGuidProperties struct {
    guid        syscall.GUID
    guidType    uint32
    loggerId    uint32
    enableLevel uint32
    enableFlags uint32
    isEnable    uint32
}

func enumerateTraceGuids(ptr **traceGuidProperties, count uint32, out *uint32) error {
    rc, _, _ := procEnumerateTraceGuids.Call(uintptr(unsafe.Pointer(ptr)),
        uintptr(count), uintptr(unsafe.Pointer(out)))
    if rc != 0 {
        return syscall.Errno(rc)
    }
    return nil
}

func enumTraceGuids() ([]*traceGuidProperties, error) {
    var errMoreData = syscall.Errno(234)

    var (
        dummyProps traceGuidProperties
        dummyPtr   = &dummyProps

        count uint32
    )

    err := enumerateTraceGuids(&dummyPtr, 0, &count)
    if err != errMoreData {
        return nil, err
    }

    items := make([]*traceGuidProperties, count)
    for i := range items {
        items[i] = new(traceGuidProperties)
    }

    for {
        err = enumerateTraceGuids(&items[0], count, &count)
        if err == nil {
            break
        }
        if err != errMoreData {
            return nil, err
        }
        for i := 0; i < int(count)-len(items); i++ {
            items = append(items, new(traceGuidProperties))
        }
    }

    return items[:count], nil
}

func main() {
    log.SetFlags(0)

    data, err := enumTraceGuids()
    if err != nil {
        log.Fatal(err)
    }
    log.Printf("len(data)=%d\n", len(data))
    for i := range data {
        log.Println(*(data[i]))
    }
}

要点:

  • 我跟你说错了 «你……应该分配一个结构数组(而不是指针)»——事实上 EnumerateTraceGuids 确实需要一个指针数组。

  • 如提示here, EnumerateTraceGuids 的工作方式有两个微妙之处:

    • 与其文档所述相反, 它实际上支持用 PropertyArrayCount 调用 参数设置为 0,在这种情况下,它应该 return ERROR_MORE_DATA 同时将 GuidCount 设置为输入的元素数 (下一次)调用成功完成所需的数组。 IOW,这样我们就知道系统当前有多少跟踪 GUID "knows about".
    • 尽管如此,即使在这种情况下,该函数也会执行有效性检查 在输入数组上(见下文)。
  • 事实证明,该函数需要一个指向的指针数组 TRACE_GUID_PROPERTIES 由您分配。

    换句话说,如果它说你知道大约 10 个跟踪 GUID, 你必须分配 10 个 TRACE_GUID_PROPERTIES 类型的值, 然后创建一个包含 10 个指向这些值的指针的数组并传递一个指针 到该数组的第一个元素到函数。

  • 请注意,发生的更改之间存在固有的竞争 在系统中(由于各种原因添加或删除的痕迹) 以及对 EnumerateTraceGuids.

    的调用

    这意味着如果第一次调用此函数时告诉您 "knows" 大约 10 个跟踪 GUID,在下一次调用时可能会出现 已经有 20 个跟踪 GUID,或 5 个 GUID (或任何其他数量的 FWIW)。

    所以我们通过以下方式考虑这两种可能性:

    1. 首先我们调用一个指向单个(但有效)的指针 TRACE_GUID_PROPERTIES 值,静态分配 (因此函数 "sees" 看起来像单个元素的数组), 在告诉函数输入 "array" 有零个元素时。

      我们预计函数会失败 ERROR_MORE_DATA 并将它 "knows" 的跟踪 GUID 的实际数量放入变量中 我们已经为它提供了一个指针。

    2. 我们分配了那么多TRACE_GUID_PROPERTIES内存块 第一次调用时指示的功能。 为此,我们使用 new() 内置函数 就像标准 C 库中的 malloc()——它为 指定类型的值和 returns 指向分配的指针 内存块.

    3. 我们创建一个指向这些已分配内存块的指针数组 并再次调用 EnumerateTraceGuids

    4. 如果成功,我们会处理它 returned less 的可能性 比我们分配的元素,并重新切片我们的切片。

    5. 如果 ERROR_MORE_DATA 失败,我们扩展切片 无论需要多少元素(为它们分配内存 TRACE_GUID_PROPERTIES 首先阻塞),然后尝试再次调用该函数。

  • "magic number" 234 是 ERROR_MORE_DATA 值的实际代码。

对于最初的困惑,我们深表歉意。