使用go读取ETW提供者

时间:2018-05-01 22:03:37

标签: winapi go etw

我正在尝试从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:这是我第一次发布到stackoverflow,我已经被告知我在发布问题后不到12小时就懒了(耶!)所以不确定我问的是这个......我我不太熟悉go并且之前从未调用过windows DLL,因为我一直打到那个ERROR_INVALID_PARAMETER我想伸出手来尝试通过这个第一面墙,以便能够同时掌握一些概念。希望这有助于理解我的要求(即我和平相处)。

1 个答案:

答案 0 :(得分:1)

好的,我有一点空闲时间和访问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确实需要一组指针。

  • 正如所暗示hereEnumerateTraceGuids如何运作有两个细微之处:

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

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

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

    这意味着如果第一次调用此函数告诉你它“知道” 大约10个跟踪GUID,在下次调用时可能会结果 已经有20个跟踪GUID或5个GUID (或任何其他数量的FWIW)。

    因此,我们通过以下方式解释了这两种可能性:

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

      我们希望该功能失败,ERROR_MORE_DATA 并将它“知道”的实际跟踪GUID数放入变量中 我们已经提供了指向。

    2. 的指针
    3. 我们分配了很多TRACE_GUID_PROPERTIES内存块 第一次通话时显示的功能。 为此,我们使用new()内置函数,它在某种程度上表现出来 与标准C库中的malloc()类似,它为内存分配 指定类型的值,并返回指向已分配的指针 记忆块。

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

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

    6. 如果它与ERROR_MORE_DATA失败,我们会延长切片 无论需要多少元素(为他们分配内存) 先TRACE_GUID_PROPERTIES阻止,然后再次尝试调用该函数。

  • “幻数”234是ERROR_MORE_DATA值的实际代码。

抱歉最初的混乱。