From 31e17c333e498f39c000d2cf220d9bb31a266742 Mon Sep 17 00:00:00 2001 From: Christian Uhsat Date: Sun, 9 Jun 2024 01:03:41 +0200 Subject: [PATCH] version v0.26.0 --- cmd/flog.evtx/main.go | 74 ----------------------- cmd/flog/main.go | 30 ++++------ internal/flog/evtx.go | 61 +++++++++++++++++++ pkg/flog/evtx/evtx.go | 118 ------------------------------------- pkg/flog/evtx/evtx_test.go | 63 -------------------- pkg/flog/flog.go | 60 ++++++++++++++++--- pkg/flog/flog_test.go | 56 ++++++++++++++++++ 7 files changed, 182 insertions(+), 280 deletions(-) delete mode 100644 cmd/flog.evtx/main.go create mode 100644 internal/flog/evtx.go delete mode 100644 pkg/flog/evtx/evtx.go delete mode 100644 pkg/flog/evtx/evtx_test.go diff --git a/cmd/flog.evtx/main.go b/cmd/flog.evtx/main.go deleted file mode 100644 index f793c3c..0000000 --- a/cmd/flog.evtx/main.go +++ /dev/null @@ -1,74 +0,0 @@ -// Log Windows event logs as JSON in ECS. -// -// Usage: -// -// flog.evtx [-pqhv] [-D DIRECTORY] [FILE ...] -// -// The flags are: -// -// -D directory -// The log directory. -// -p -// Pretty JSON. -// -q -// Quiet mode. -// -h -// Show usage. -// -v -// Show version. -// -// The arguments are: -// -// file -// The event log file(s) to process. -// Defaults to STDIN if not given. -package main - -import ( - "flag" - "io" - - "github.com/cuhsat/fact/internal/fact" - "github.com/cuhsat/fact/internal/sys" - "github.com/cuhsat/fact/pkg/flog" - "github.com/cuhsat/fact/pkg/flog/evtx" - "golang.org/x/sync/errgroup" -) - -func main() { - D := flag.String("D", "", "Log directory") - p := flag.Bool("p", false, "Pretty JSON") - q := flag.Bool("q", false, "Quiet mode") - h := flag.Bool("h", false, "Show usage") - v := flag.Bool("v", false, "Show version") - - flag.CommandLine.SetOutput(io.Discard) - flag.Parse() - - files := flog.StripHash(sys.Args()) - - if *v { - sys.Final("flog.evtx", fact.Version) - } - - if *h || len(files) == 0 { - sys.Usage("flog.evtx [-pqhv] [-D DIRECTORY] [FILE ...]") - } - - if *q { - sys.Progress = nil - } - - g := new(errgroup.Group) - - for _, f := range files { - g.Go(func() (err error) { - _, err = evtx.Log(f, *D, *p) - return - }) - } - - if err := g.Wait(); err != nil { - sys.Fatal(err) - } -} diff --git a/cmd/flog/main.go b/cmd/flog/main.go index 2a027be..151671c 100644 --- a/cmd/flog/main.go +++ b/cmd/flog/main.go @@ -2,7 +2,7 @@ // // Usage: // -// flog [-pqhv] [-D DIRECTORY] [FILE ...] +// flog [-pqhv] [-D DIR] [FILE ...] // // The flags are: // @@ -20,13 +20,14 @@ // The arguments are: // // file -// The artifact file(s) to process. +// The event log file(s) to process. // Defaults to STDIN if not given. package main import ( "flag" "io" + "path/filepath" "github.com/cuhsat/fact/internal/fact" "github.com/cuhsat/fact/internal/sys" @@ -51,28 +52,23 @@ func main() { } if *h || len(files) == 0 { - sys.Usage("flog [-pqhv] [-D DIRECTORY] [FILE ...]") - } - - args := make([]string, 0) - - if len(*D) > 0 { - args = append(args, "-D", *D) - } - - if *p { - args = append(args, "-p") + sys.Usage("flog [-pqhv] [-D DIR] [FILE ...]") } if *q { - args = append(args, "-q") + sys.Progress = nil } g := new(errgroup.Group) - g.Go(func() error { - return flog.Evtx(files, args) - }) + for _, f := range files { + if filepath.Ext(f) == flog.Evtx { + g.Go(func() (err error) { + _, err = flog.LogEvent(f, *D, *p) + return + }) + } + } if err := g.Wait(); err != nil { sys.Fatal(err) diff --git a/internal/flog/evtx.go b/internal/flog/evtx.go new file mode 100644 index 0000000..9f17d9f --- /dev/null +++ b/internal/flog/evtx.go @@ -0,0 +1,61 @@ +// Evtx functions. +package flog + +import ( + "os" + "path/filepath" + + "github.com/cuhsat/fact/internal/fact/ez" + "github.com/cuhsat/fact/internal/sys" +) + +func ImportEvent(src, dir string) (lines []string, err error) { + log, err := evtxecmd(src, src+".json", dir) + + if err != nil { + return + } + + lines, err = ReadLines(log) + + if err != nil { + return + } + + err = os.Remove(log) + + return +} + +func ExportEvent(b []byte, dst string) (err error) { + f, err := os.Create(dst) + + if err != nil { + return + } + + _, err = f.Write(b) + + f.Close() + + return +} + +func evtxecmd(src, dst, dir string) (log string, err error) { + asm, err := ez.Path("EvtxECmd.dll") + + if err != nil { + return + } + + if len(dir) == 0 { + dir = filepath.Dir(dst) + } + + dst = filepath.Base(dst) + log = filepath.Join(dir, dst) + + _, err = sys.StdCall("dotnet", asm, "-f", src, "--fj", "--json", dir, "--jsonf", dst) + + return +} diff --git a/pkg/flog/evtx/evtx.go b/pkg/flog/evtx/evtx.go deleted file mode 100644 index c691958..0000000 --- a/pkg/flog/evtx/evtx.go +++ /dev/null @@ -1,118 +0,0 @@ -// Evtx implementation details. -package evtx - -import ( - "fmt" - "os" - "path/filepath" - "strings" - - "github.com/cuhsat/fact/internal/fact/ez" - "github.com/cuhsat/fact/internal/flog" - "github.com/cuhsat/fact/internal/sys" - "github.com/cuhsat/fact/pkg/ecs" -) - -const ( - Evtx = "evtx" -) - -func Log(src, dir string, pty bool) (logs []string, err error) { - lines, err := _import(src, dir) - - if err != nil { - return - } - - b := filepath.Base(src) - - f := strings.TrimSuffix(b, filepath.Ext(b)) - - for i, line := range lines { - dst := filepath.Join(dir, fmt.Sprintf("%s_%08d.json", f, i)) - - if err = _export(src, dst, line, pty); err != nil { - sys.Error(err) - continue - } - - l, err := filepath.Abs(dst) - - if err != nil { - sys.Error(err) - continue - } - - if sys.Progress != nil { - sys.Progress(l) - } - - logs = append(logs, l) - } - - return logs, nil -} - -func _import(src, dir string) (lines []string, err error) { - log, err := evtxecmd(src, src+".json", dir) - - if err != nil { - return - } - - lines, err = flog.ReadLines(log) - - if err != nil { - return - } - - err = os.Remove(log) - - return -} - -func _export(src, dst, line string, pty bool) (err error) { - e, err := ecs.MapEvent(line, src) - - if err != nil { - return - } - - b, err := e.Bytes(pty) - - if err != nil { - return - } - - f, err := os.Create(dst) - - if err != nil { - return - } - - _, err = f.Write(b) - - f.Close() - - return -} - -func evtxecmd(src, dst, dir string) (log string, err error) { - asm, err := ez.Path("EvtxECmd.dll") - - if err != nil { - return - } - - if len(dir) == 0 { - dir = filepath.Dir(dst) - } - - dst = filepath.Base(dst) - - _, err = sys.StdCall("dotnet", asm, "-f", src, "--fj", "--json", dir, "--jsonf", dst) - - log = filepath.Join(dir, dst) - - return -} diff --git a/pkg/flog/evtx/evtx_test.go b/pkg/flog/evtx/evtx_test.go deleted file mode 100644 index 5083278..0000000 --- a/pkg/flog/evtx/evtx_test.go +++ /dev/null @@ -1,63 +0,0 @@ -// Evtx implementation tests. -package evtx - -import ( - "encoding/json" - "os" - "path/filepath" - "testing" - - "github.com/cuhsat/fact/internal/fact/zip" - "github.com/cuhsat/fact/internal/test" -) - -func TestMain(m *testing.M) { - os.Setenv("EZTOOLS", "../../../bin") - os.Exit(m.Run()) -} - -func TestLog(t *testing.T) { - cases := []struct { - name, file, evtx string - }{ - { - name: "Test log for Windows", - file: test.Testdata("windows", "evtx.zip"), - evtx: "System.evtx", - }, - } - - for _, tt := range cases { - tmp, _ := os.MkdirTemp(os.TempDir(), "log") - - err := zip.Unzip(tt.file, tmp) - - if err != nil { - t.Fatal(err) - } - - t.Run(tt.name, func(t *testing.T) { - evt := filepath.Join(tmp, tt.evtx) - - l, err := Log(evt, tmp, true) - - if err != nil { - t.Fatal(err) - } - - if len(l) != 1 { - t.Fatal("file count differs") - } - - b, err := os.ReadFile(l[0]) - - if err != nil { - t.Fatal(err) - } - - if !json.Valid(b) { - t.Fatal("invalid json") - } - }) - } -} diff --git a/pkg/flog/flog.go b/pkg/flog/flog.go index 148c3e8..b04531b 100644 --- a/pkg/flog/flog.go +++ b/pkg/flog/flog.go @@ -2,24 +2,68 @@ package flog import ( + "fmt" "path/filepath" "strings" "github.com/cuhsat/fact/internal/fact" + "github.com/cuhsat/fact/internal/flog" "github.com/cuhsat/fact/internal/sys" - "github.com/cuhsat/fact/pkg/flog/evtx" + "github.com/cuhsat/fact/pkg/ecs" ) -func Evtx(files, args []string) (err error) { - for _, f := range files { - if filepath.Ext(f) == evtx.Evtx { - args = append(args, f) - } +const ( + Evtx = "evtx" +) + +func LogEvent(src, dir string, pty bool) (logs []string, err error) { + lines, err := flog.ImportEvent(src, dir) + + if err != nil { + return } - _, err = sys.StdCall("flog.evtx", args...) + b := filepath.Base(src) - return + f := strings.TrimSuffix(b, filepath.Ext(b)) + + for i, line := range lines { + dst := filepath.Join(dir, fmt.Sprintf("%s_%08d.json", f, i)) + + e, err := ecs.MapEvent(line, src) + + if err != nil { + sys.Error(err) + continue + } + + b, err := e.Bytes(pty) + + if err != nil { + sys.Error(err) + continue + } + + if err = flog.ExportEvent(b, dst); err != nil { + sys.Error(err) + continue + } + + l, err := filepath.Abs(dst) + + if err != nil { + sys.Error(err) + continue + } + + if sys.Progress != nil { + sys.Progress(l) + } + + logs = append(logs, l) + } + + return logs, nil } func StripHash(in []string) (out []string) { diff --git a/pkg/flog/flog_test.go b/pkg/flog/flog_test.go index 345d87b..8976120 100644 --- a/pkg/flog/flog_test.go +++ b/pkg/flog/flog_test.go @@ -2,11 +2,67 @@ package flog import ( + "encoding/json" + "os" + "path/filepath" "testing" "github.com/cuhsat/fact/internal/fact" + "github.com/cuhsat/fact/internal/fact/zip" + "github.com/cuhsat/fact/internal/test" ) +func TestMain(m *testing.M) { + os.Setenv("EZTOOLS", "../../../bin") + os.Exit(m.Run()) +} + +func TestLog(t *testing.T) { + cases := []struct { + name, file, evtx string + }{ + { + name: "Test log for Windows", + file: test.Testdata("windows", "evtx.zip"), + evtx: "System.evtx", + }, + } + + for _, tt := range cases { + tmp, _ := os.MkdirTemp(os.TempDir(), "log") + + err := zip.Unzip(tt.file, tmp) + + if err != nil { + t.Fatal(err) + } + + t.Run(tt.name, func(t *testing.T) { + evt := filepath.Join(tmp, tt.evtx) + + l, err := Evtx(evt, tmp, true) + + if err != nil { + t.Fatal(err) + } + + if len(l) != 1 { + t.Fatal("file count differs") + } + + b, err := os.ReadFile(l[0]) + + if err != nil { + t.Fatal(err) + } + + if !json.Valid(b) { + t.Fatal("invalid json") + } + }) + } +} + func TestStripHash(t *testing.T) { cases := []struct { name, file, hash string