//go:build windows package client import ( "strings" ) func collectPersistenceAudit() PersistenceAuditReport { report := PersistenceAuditReport{Platform: "windows"} queries := []struct { kind, hive, sub string }{ {"registry_run", "HKCU", `Software\Microsoft\Windows\CurrentVersion\Run`}, {"registry_run", "HKCU", `Software\Microsoft\Windows\CurrentVersion\RunOnce`}, {"registry_run", "HKLM", `Software\Microsoft\Windows\CurrentVersion\Run`}, {"registry_run", "HKLM", `Software\Microsoft\Windows\CurrentVersion\RunOnce`}, } for _, q := range queries { out, err := silentCombinedOutput("reg", "query", q.hive+`\`+q.sub) if err != nil { continue } for _, line := range strings.Split(string(out), "\n") { line = strings.TrimSpace(line) if line == "" || strings.HasPrefix(line, "HKEY_") || strings.HasPrefix(line, q.sub) { continue } parts := strings.Fields(line) if len(parts) < 2 { continue } name := parts[0] val := strings.Join(parts[2:], " ") if strings.EqualFold(name, "REG_SZ") || strings.EqualFold(name, "REG_EXPAND_SZ") { continue } report.Entries = append(report.Entries, PersistenceAuditEntry{ Kind: q.kind, Name: name, Detail: q.hive + `\` + q.sub + ` → ` + val, }) } } taskOut, err := silentCombinedOutput("schtasks", "/Query", "/FO", "LIST", "/V") if err == nil { var curName, curRun string flush := func() { if curName != "" { report.Entries = append(report.Entries, PersistenceAuditEntry{ Kind: "scheduled_task", Name: curName, Detail: curRun, Enabled: true, }) } curName, curRun = "", "" } for _, line := range strings.Split(string(taskOut), "\n") { line = strings.TrimSpace(line) if strings.HasPrefix(line, "TaskName:") { flush() curName = strings.TrimSpace(strings.TrimPrefix(line, "TaskName:")) } else if strings.HasPrefix(line, "Task To Run:") { curRun = strings.TrimSpace(strings.TrimPrefix(line, "Task To Run:")) } } flush() } report.Count = len(report.Entries) return report }