代码之家  ›  专栏  ›  技术社区  ›  EmFl

使用go读取ETW提供程序

  •  1
  • EmFl  · 技术社区  · 6 年前

    我正在尝试从Advapi32访问EnumerateTraceGuids函数。dll在go中。 我还处于非常早期的阶段,仍在试图破译我必须做的事情。我有以下代码不断给我错误:87,意思是Error\u INVALID\u参数。

    我使用此文件作为起点,尽管它只是在写入而不是读取: 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[输入,输出]一个指针数组来跟踪GUID\u属性结构。该结构如下( 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()
    }
    

    在这一点上,我不知道我必须做什么。我尝试了很多初始化数组的方法,但都没有成功。 希望有人能给我指出正确的方向。 谢谢

    编辑:根据注释更改了代码,但仍然得到相同的错误。

    附:这是我第一次在stackoverflow上发帖,我已经被告知在发帖后不到12个小时我就懒惰了(耶!)所以我不确定我问得对不对。。。我对go不太熟悉,以前从未从go调用过windows DLL,因为我一直在点击ERROR\u INVALID\u参数,所以我想伸出手来尝试通过第一道墙,以便能够同时掌握一些概念。希望这有助于理解我的要求(即我是和平地来的)。

    1 回复  |  直到 6 年前
        1
  •  2
  •   halfer    6 年前

    好的,我有一点空闲时间,还可以访问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 ,则, 有两个微妙之处 枚举跟踪指南 工程:

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

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

    • 请注意,在发生的更改之间存在着固有的竞争 在系统中(由于各种原因添加或删除的记录道) 以及 枚举跟踪指南

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

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

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

        我们预计该函数将失败 ERROR\u MORE\u数据 并将它“知道”的跟踪GUID的实际数量放入变量中 我们为它提供了一个指向的指针。

      2. 我们分配了那么多 TRACE\u GUID\u属性 内存块 第一次调用时指示的函数。 为此,我们使用 new() 具有某种行为的内置函数 喜欢 malloc() 在标准C库中,它为 指定类型的值,并返回指向已分配 内存块。

      3. 我们创建一个指向这些分配内存块的指针数组 和电话 枚举跟踪指南 再一次

      4. 如果它成功了,我们可以处理它返回的可能性更小 元素,并重新切片。

      5. 如果失败 ERROR\u MORE\u数据 ,我们用 无论需要多少个元素(为其 TRACE\u GUID\u属性 块),然后再次尝试调用该函数。

    • “幻数”234是 ERROR\u MORE\u数据 价值

    很抱歉最初的混淆。