Bugfix - First Working
All checks were successful
build-binaries / build (.exe, amd64, windows) (push) Has been skipped
build-binaries / release (push) Has been skipped
build-binaries / publish-agent (push) Has been skipped

This commit is contained in:
2026-04-24 18:06:49 +02:00
parent 0059cb1978
commit 7515bda711
2 changed files with 101 additions and 51 deletions

152
main.go
View File

@@ -41,6 +41,10 @@ const (
ServiceLogInfo = 1
)
const (
ERROR_EVT_INVALID_OPERATION syscall.Errno = 15010
)
const AgentConfigPath = `C:\ProgramData\WinEventForwarder\agent.json`
type ChannelConfig struct {
@@ -181,6 +185,7 @@ func (m *myservice) Execute(args []string, r <-chan svc.ChangeRequest, status ch
log.Printf("[%s] Starte Watcher-Chunk %d/%d mit %d IDs",
cfg.Name, chunkNo+1, len(chunks), len(cfg.IDs))
runChannelWatcher(ctx, hostname, cfg, out)
log.Println("Prozess fertig oder abgebrochen", cfg)
}(i, workerCfg)
}
}
@@ -325,16 +330,16 @@ func runDebug(cfg *AgentConfig, state *AgentState) {
}
func runChannelWatcher(ctx context.Context, hostname string, cfg ChannelConfig, out chan<- LogPayload) {
query := buildXPathQuery(cfg.IDs)
query := buildXPathQuery2(cfg.IDs)
signal, err := windows.CreateEvent(nil, 1, 0, nil)
signalEvent, err := windows.CreateEvent(nil, 1, 0, nil)
if err != nil {
log.Printf("[%s] CreateEvent-Fehler: %v", cfg.Name, err)
return
}
defer windows.CloseHandle(signal)
defer windows.CloseHandle(signalEvent)
sub, err := evtSubscribe(cfg.Name, query, signal, evtSubscribeToFutureEvents)
sub, err := evtSubscribe(cfg.Name, query, signalEvent, evtSubscribeToFutureEvents)
if err != nil {
log.Printf("[%s] Subscribe-Fehler: %v | Query=%s", cfg.Name, err, query)
return
@@ -343,35 +348,28 @@ func runChannelWatcher(ctx context.Context, hostname string, cfg ChannelConfig,
log.Printf("[%s] Überwachung gestartet mit Filter %s", cfg.Name, query)
for {
select {
case <-ctx.Done():
return
default:
}
waitStatus, err := windows.WaitForSingleObject(signal, PollWaitMS)
if err != nil {
log.Printf("[%s] WaitForSingleObject-Fehler: %v", cfg.Name, err)
time.Sleep(2 * time.Second)
continue
}
switch waitStatus {
case uint32(windows.WAIT_OBJECT_0):
drain := func() {
for {
events, err := evtNext(sub, BatchSize, 0)
if err != nil {
if isIgnorableEvtNextError(err) {
_ = windows.ResetEvent(signal)
continue
code := winErrCode(err)
// Diese Fehler sind bei deinem Polling nicht fatal.
if code == windows.ERROR_TIMEOUT ||
code == windows.ERROR_NO_MORE_ITEMS ||
strings.Contains(strings.ToLower(err.Error()), "operation identifier is not valid") {
return
}
log.Printf("[%s] EvtNext-Fehler: %v", cfg.Name, err)
_ = windows.ResetEvent(signal)
time.Sleep(2 * time.Second)
continue
log.Printf("[%s] EvtNext-Fehler: %v | Code=%d", cfg.Name, err, uint32(code))
return
}
log.Printf("[%s] EvtNext lieferte %d Events", cfg.Name, len(events))
if len(events) == 0 {
return
}
log.Printf("[%s] %d neue Events gefunden", cfg.Name, len(events))
for _, h := range events {
payload, err := buildPayloadFromEventHandle(hostname, cfg.Name, h)
@@ -382,37 +380,64 @@ func runChannelWatcher(ctx context.Context, hostname string, cfg ChannelConfig,
continue
}
log.Printf("[%s] Event empfangen: EventID=%d Source=%s Time=%s",
if !cfg.IDs[payload.EventID] {
log.Printf("[%s] EventID %d ignoriert, nicht in Filter", cfg.Name, payload.EventID)
continue
}
log.Printf("[%s] Event erkannt: ID=%d Source=%s Time=%s",
cfg.Name,
payload.EventID,
payload.Source,
payload.Time.Format(time.RFC3339),
)
if !cfg.IDs[payload.EventID] {
log.Printf("[%s] Event weggefiltert: EventID=%d", cfg.Name, payload.EventID)
continue
}
log.Printf("[%s] Event wird gesendet: EventID=%d", cfg.Name, payload.EventID)
select {
case out <- payload:
case <-ctx.Done():
return
}
}
_ = windows.ResetEvent(signal)
case uint32(windows.WAIT_TIMEOUT):
continue
default:
log.Printf("[%s] Unerwarteter Wait-Status: %d", cfg.Name, waitStatus)
time.Sleep(2 * time.Second)
}
}
for {
select {
case <-ctx.Done():
return
default:
}
waitStatus, err := windows.WaitForSingleObject(signalEvent, PollWaitMS)
if err != nil {
log.Printf("[%s] WaitForSingleObject-Fehler: %v", cfg.Name, err)
time.Sleep(2 * time.Second)
continue
}
if waitStatus == uint32(windows.WAIT_OBJECT_0) {
_ = windows.ResetEvent(signalEvent)
drain()
continue
}
if waitStatus == uint32(windows.WAIT_TIMEOUT) {
// Wichtig: bei dir nötig, weil das Signal offenbar nicht zuverlässig kommt.
drain()
continue
}
_ = windows.ResetEvent(signalEvent)
log.Printf("[%s] Unerwarteter Wait-Status: %d", cfg.Name, waitStatus)
time.Sleep(2 * time.Second)
}
}
func winErrCode(err error) syscall.Errno {
var errno syscall.Errno
if errors.As(err, &errno) {
return errno
}
return 0
}
func runSender(
@@ -557,6 +582,28 @@ func buildXPathQuery(ids map[uint32]bool) string {
return fmt.Sprintf("*[System[(%s)]]", strings.Join(parts, " or "))
}
func buildXPathQuery2(ids map[uint32]bool) string {
if len(ids) == 0 {
return "*"
}
list := make([]int, 0, len(ids))
for id := range ids {
list = append(list, int(id))
}
sort.Ints(list)
parts := make([]string, 0, len(list))
for _, id := range list {
// Manche Windows-Versionen bevorzugen EventID ohne System/ davor
// innerhalb des System-Knotens.
parts = append(parts, fmt.Sprintf("EventID=%d", id))
}
// WICHTIG: Keine unnötigen Leerzeichen innerhalb der XPath-Klammern
return fmt.Sprintf("*[System[(%s)]]", strings.Join(parts, " or "))
}
func sendBatch(client *http.Client, backendURL string, state *AgentState, enrollmentKey string, batch []LogPayload) (bool, error) {
data, err := json.Marshal(batch)
if err != nil {
@@ -660,11 +707,14 @@ func evtNext(resultSet windows.Handle, maxHandles uint32, timeout uint32) ([]win
0,
uintptr(unsafe.Pointer(&returned)),
)
// r1 == 0 bedeutet, die Funktion war nicht erfolgreich (False)
if r1 == 0 {
if e1 != syscall.Errno(0) {
return nil, e1
// Wir prüfen, welcher Fehlercode vorliegt
if e1 == windows.ERROR_NO_MORE_ITEMS || e1 == windows.ERROR_TIMEOUT {
return nil, nil // Das ist kein Fehler, nur das Ende der Schlange
}
return nil, errors.New("EvtNext fehlgeschlagen")
return nil, e1 // Ein echter Windows-Fehler
}
if returned == 0 {
@@ -737,9 +787,9 @@ func evtClose(h windows.Handle) error {
func isIgnorableEvtNextError(err error) bool {
var errno syscall.Errno
if errors.As(err, &errno) {
if errno == windows.ERROR_TIMEOUT || errno == windows.ERROR_NO_MORE_ITEMS {
return true
}
return errno == windows.ERROR_TIMEOUT ||
errno == windows.ERROR_NO_MORE_ITEMS ||
errno == ERROR_EVT_INVALID_OPERATION
}
return false
}

BIN
siem-agent.exe Normal file

Binary file not shown.