Чтение провайдеров ETW с помощью go - PullRequest
0 голосов
/ 02 мая 2018

Я пытаюсь получить доступ к функции EnumerateTraceGuids из Advapi32.dll на ходу. Я на самой ранней стадии и все еще пытаюсь понять, что я должен делать. У меня есть следующий код, который продолжает выдавать мне сообщение об ошибке: 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 голосов
/ 02 мая 2018

ОК, у меня было немного свободного времени и доступ к коробке с 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 действительно ожидает массив указателей.

  • Как намекнул здесь , Есть две тонкости с тем, как EnumerateTraceGuids работает:

    • Вопреки тому, что говорится в его документации, он на самом деле поддерживает вызов с помощью PropertyArrayCount параметр установлен в 0, и в этом случае ожидается возврат ERROR_MORE_DATA при установке GuidCount на количество элементов ввода массив, необходимый для успешного завершения (следующего) вызова. Таким образом, мы знаем, сколько трассировочных GUID в настоящее время находится в системе. "знает о".
    • Тем не менее, даже в этом случае функция выполняет проверку достоверности во входном массиве (см. ниже).
  • Как оказалось, функция ожидает массив указателей на TRACE_GUID_PROPERTIES выделенных вами блоков .

    Другими словами, если он говорит, что знает о 10 GUID трассировки, Вы должны выделить 10 значений типа TRACE_GUID_PROPERTIES, затем создайте массив из 10 указателей на эти значения и передайте указатель к 1-му элементу этого массива функции.

  • Обратите внимание, что между происходящими изменениями существует внутренняя гонка в системе (эти следы добавлены или удалены по ряду причин) и звонки на EnumerateTraceGuids.

    Это означает, что если первый вызов этой функции сказал вам, что он "знает" около 10 трассировочных GUID, при следующем звонке может получиться уже есть 20 идентификаторов GUID или 5 идентификаторов GUID (или любое другое число из них FWIW).

    Таким образом, мы учитываем обе эти возможности следующим образом:

    1. Сначала мы делаем вызов с указателем на один (но действительный) TRACE_GUID_PROPERTIES значение, назначаемое статически (следовательно, функция «видит» то, что выглядит как массив из одного элемента), сообщая функции, что входной массив содержит ноль элементов.

      Мы ожидаем, что функция завершится ошибкой с ERROR_MORE_DATA и поместите фактическое число идентификаторов GUID, о которых он «знает», в переменную мы указали указатель на.

    2. Мы выделяем столько TRACE_GUID_PROPERTIES блоков памяти функция указана при первом вызове. Для этого мы используем встроенную функцию new(), которая ведет себя несколько как malloc() в стандартной библиотеке C - она ​​выделяет память для значение указанного типа и возвращает указатель на выделенный блок памяти.

    3. Мы создаем массив указателей на эти выделенные блоки памяти и снова позвоните EnumerateTraceGuids.

    4. Если это удастся, мы обрабатываем возможность, которую он вернул меньше элементы, которые мы выделили, и измените наш срез.

    5. Если произойдет сбой с ERROR_MORE_DATA, мы расширим наш фрагмент с помощью сколько угодно элементов (выделение памяти для их TRACE_GUID_PROPERTIES блокирует сначала), и попробуйте вызвать функцию снова.

  • «Магическое число» 234 является действительным кодом для значения ERROR_MORE_DATA.

Извините за первоначальное замешательство.

...