diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 2e2016c..93438e1 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -6,8 +6,10 @@ on: pull_request: branches: [ "main" ] -jobs: +env: + EZTOOLS: ./bin +jobs: build: runs-on: ubuntu-latest steps: @@ -18,6 +20,9 @@ jobs: with: go-version: "1.22" + - name: Tools + run: make tools + - name: Build run: go build -v -race ./... diff --git a/.gitignore b/.gitignore index 8f9bd47..3ed0353 100644 --- a/.gitignore +++ b/.gitignore @@ -22,7 +22,11 @@ go.work # Fact files bin/ + *.dd *.evtx *.zip -!internal/testdata/windows*.zip + +!cmd/* +!pkg/* +!internal/testdata/windows*.zip \ No newline at end of file diff --git a/README.md b/README.md index 0de2d25..c1c67b2 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Forensic Artifacts Collecting Toolkit. - [fmount.dd](docs/fmount.dd.md) - [ffind](docs/ffind.md) - [flog](docs/flog.md) -- [flog.evt](docs/flog.evt.md) +- [flog.evtx](docs/flog.evtx.md) ## License Released under the [MIT License](LICENSE). \ No newline at end of file diff --git a/cmd/flog.evt/main.go b/cmd/flog.evtx/main.go similarity index 81% rename from cmd/flog.evt/main.go rename to cmd/flog.evtx/main.go index 2e61be6..ad98162 100644 --- a/cmd/flog.evt/main.go +++ b/cmd/flog.evtx/main.go @@ -2,7 +2,7 @@ // // Usage: // -// flog [-hv] [-D DIRECTORY] [FILE ...] +// flog.evtx [-hv] [-D DIRECTORY] [FILE ...] // // The flags are: // @@ -27,7 +27,7 @@ import ( "github.com/cuhsat/fact/internal/fact" "github.com/cuhsat/fact/internal/sys" "github.com/cuhsat/fact/pkg/flog" - "github.com/cuhsat/fact/pkg/flog/evt" + "github.com/cuhsat/fact/pkg/flog/evtx" "golang.org/x/sync/errgroup" ) @@ -42,18 +42,18 @@ func main() { files := flog.StripHash(sys.Args()) if *v { - sys.Print("flog", fact.Version) + sys.Print("flog.evtx", fact.Version) } if *h || len(files) == 0 { - sys.Usage("flog [-hv] [-D DIRECTORY] [FILE ...]") + sys.Usage("flog.evtx [-hv] [-D DIRECTORY] [FILE ...]") } g := new(errgroup.Group) for _, f := range files { g.Go(func() error { - return evt.Log(f, *D) + return evtx.Log(f, *D) }) } diff --git a/cmd/flog/main.go b/cmd/flog/main.go index 0c60717..b2b3f77 100644 --- a/cmd/flog/main.go +++ b/cmd/flog/main.go @@ -57,7 +57,7 @@ func main() { g := new(errgroup.Group) g.Go(func() error { - return flog.Evt(files, args) + return flog.Evtx(files, args) }) if err := g.Wait(); err != nil { diff --git a/cmd/fmount.dd/main.go b/cmd/fmount.dd/main.go new file mode 100644 index 0000000..0913736 --- /dev/null +++ b/cmd/fmount.dd/main.go @@ -0,0 +1,119 @@ +// Mount forensic raw or dd disk images for read-only processing. +// +// Usage: +// +// fmount.dd [-fsuzhv] [-H CRC32|MD5|SHA1|SHA256] [-V SUM] [-D DIRECTORY] IMAGE +// +// The flags are: +// +// -D directory +// The mount point directory. +// -H algorithm +// The hash algorithm to use. +// -V sum +// The hash sum to verify. +// -f +// Force type (bypass check). +// -s +// System partition only. +// -u +// Unmount image. +// -z +// Unzip image. +// -h +// Show usage. +// -v +// Show version. +// +// The arguments are: +// +// image +// The disk images filename. +package main + +import ( + "flag" + "io" + "strings" + + "github.com/cuhsat/fact/internal/fact" + "github.com/cuhsat/fact/internal/sys" + "github.com/cuhsat/fact/pkg/fmount" + "github.com/cuhsat/fact/pkg/fmount/dd" +) + +func main() { + D := flag.String("D", "", "Mount point") + H := flag.String("H", "", "Hash algorithm") + V := flag.String("V", "", "Hash sum") + f := flag.Bool("f", false, "Force mounting") + s := flag.Bool("s", false, "System partition only") + u := flag.Bool("u", false, "Unmount image") + z := flag.Bool("z", false, "Unzip image") + h := flag.Bool("h", false, "Show usage") + v := flag.Bool("v", false, "Show version") + + flag.CommandLine.SetOutput(io.Discard) + flag.Parse() + + img := sys.Arg() + + if *v { + sys.Print("fmount.dd", fact.Version) + } + + if *h || len(img) == 0 { + sys.Usage("fmount.dd [-fsuzhv] [-H CRC32|MD5|SHA1|SHA256] [-V SUM] [-D DIRECTORY] IMAGE") + } + + if *z { + ex, err := fmount.Extract(img) + + if err != nil { + sys.Fatal(err) + } else { + img = ex + } + } + + if (len(*H) == 0) != (len(*V) == 0) { + sys.Fatal("hash algorithm and sum are required") + } + + if len(*H) > 0 && len(*V) > 0 { + ok, err := fmount.Verify(img, *H, *V) + + if err != nil { + sys.Fatal(err) + } + + if !ok { + sys.Fatal("hash sum does not match") + } + } + + if !*f { + is, err := dd.Is(img) + + if err != nil { + sys.Fatal(err) + } + + if !is { + sys.Fatal("image type not supported") + } + } + + if *u { + dd.Unmount(img) + return + } + + p, err := dd.Mount(img, *D, *s) + + if err != nil { + sys.Fatal(err) + } + + sys.Print(strings.Join(p, "\n")) +} diff --git a/docs/flog.evt.md b/docs/flog.evtx.md similarity index 69% rename from docs/flog.evt.md rename to docs/flog.evtx.md index f6a66c1..70db2e3 100644 --- a/docs/flog.evt.md +++ b/docs/flog.evtx.md @@ -1,8 +1,8 @@ -# flog.evt +# flog.evtx Log Windows event log artifacts in [ECS](https://www.elastic.co/guide/en/ecs/current/index.html) schema. ```sh -$ flog.evt [-hv] [-D DIRECTORY] [FILE ...] +$ flog.evtx [-hv] [-D DIRECTORY] [FILE ...] ``` Available options: @@ -15,7 +15,7 @@ Required system commands: - [dotnet](https://dotnet.microsoft.com/en-us/download/dotnet/6.0) -> Use `scripts/eztools.sh` to install [Eric Zimmerman's Tools](https://ericzimmerman.github.io/#!index.md). +> Use `make tools` to install [Eric Zimmerman's Tools](https://ericzimmerman.github.io/#!index.md). --- Part of the [Forensic Artifacts Collecting Toolkit](../README.md). \ No newline at end of file diff --git a/docs/flog.md b/docs/flog.md index 9334dc3..1158bae 100644 --- a/docs/flog.md +++ b/docs/flog.md @@ -13,7 +13,7 @@ Available options: Supported artifacts for Windows 7+ systems: -- [System Event Logs](flog.evt.md) +- [System Event Logs](flog.evtx.md) --- Part of the [Forensic Artifacts Collecting Toolkit](../README.md). \ No newline at end of file diff --git a/internal/fact/fact.go b/internal/fact/fact.go index 3ec6396..c843064 100644 --- a/internal/fact/fact.go +++ b/internal/fact/fact.go @@ -1,4 +1,4 @@ -// FACT definitions. +// Fact definitions. package fact // Set at compile time diff --git a/internal/fact/3rd.go b/internal/fact/tools.go similarity index 88% rename from internal/fact/3rd.go rename to internal/fact/tools.go index 1a66a9d..5831cd6 100644 --- a/internal/fact/3rd.go +++ b/internal/fact/tools.go @@ -1,4 +1,4 @@ -// FACT 3rd party functions. +// Fact implementation details. package fact import ( diff --git a/internal/fact/tools_test.go b/internal/fact/tools_test.go new file mode 100644 index 0000000..5115329 --- /dev/null +++ b/internal/fact/tools_test.go @@ -0,0 +1,36 @@ +// Fact implementation tests. +package fact + +import ( + "os" + "testing" +) + +func TestTools(t *testing.T) { + cases := []struct { + name, tool string + }{ + { + name: "Test for EvtxECmd", + tool: "EvtxECmd.dll", + }, + } + + for _, tt := range cases { + t.Run(tt.name, func(t *testing.T) { + p, err := EzTools(tt.tool) + + if err != nil { + t.Fatal(err) + } + + if len(p) == 0 { + t.Fatal(tt.tool + " not found") + } + + if _, err := os.Stat(p); os.IsNotExist(err) { + t.Fatal(tt.tool + " not found") + } + }) + } +} diff --git a/internal/fact/zip/zip_test.go b/internal/fact/zip/zip_test.go index 281effd..90552b2 100644 --- a/internal/fact/zip/zip_test.go +++ b/internal/fact/zip/zip_test.go @@ -12,7 +12,7 @@ import ( func TestIndex(t *testing.T) { t.Run("Test index", func(t *testing.T) { - idx, err := Index(test.Testdata("windows.zip")) + idx, err := Index(test.Testdata("windows", "image.zip")) if err != nil { t.Fatal(err) @@ -28,7 +28,7 @@ func TestUnzip(t *testing.T) { t.Run("Test unzip", func(t *testing.T) { tmp, _ := os.MkdirTemp(os.TempDir(), "zip") - err := Unzip(test.Testdata("windows.zip"), tmp) + err := Unzip(test.Testdata("windows", "image.zip"), tmp) if err != nil { t.Fatal(err) diff --git a/internal/test/test.go b/internal/test/test.go index b98969b..782652c 100644 --- a/internal/test/test.go +++ b/internal/test/test.go @@ -6,12 +6,14 @@ import ( "runtime" ) -func Testdata(name string) string { +func Testdata(args ...string) string { _, c, _, ok := runtime.Caller(0) if !ok { return "error" } - return filepath.Join(filepath.Dir(c), "..", "testdata", name) + p := []string{filepath.Dir(c), "..", "testdata"} + + return filepath.Join(append(p, args...)...) } diff --git a/internal/testdata/windows.dd.zip b/internal/testdata/windows.dd.zip deleted file mode 100644 index c0a1e42..0000000 Binary files a/internal/testdata/windows.dd.zip and /dev/null differ diff --git a/internal/testdata/windows.evtx.zip b/internal/testdata/windows.evtx.zip deleted file mode 100644 index 7f1fa3f..0000000 Binary files a/internal/testdata/windows.evtx.zip and /dev/null differ diff --git a/internal/testdata/windows.zip b/internal/testdata/windows.zip deleted file mode 100644 index 2123e68..0000000 Binary files a/internal/testdata/windows.zip and /dev/null differ diff --git a/pkg/ffind/ffind_test.go b/pkg/ffind/ffind_test.go index 9f6f170..0d66b7a 100644 --- a/pkg/ffind/ffind_test.go +++ b/pkg/ffind/ffind_test.go @@ -14,7 +14,7 @@ import ( var ( tmp, _ = os.MkdirTemp(os.TempDir(), "ffind") - archive = filepath.Join(tmp, "archive.zip") + archive = filepath.Join(tmp, "artifacts.zip") sysroot = filepath.Join(tmp, "sysroot") ) @@ -24,7 +24,7 @@ func TestFind(t *testing.T) { }{ { name: "Test find for Windows", - file: test.Testdata("windows.zip"), + file: test.Testdata("windows", "image.zip"), }, } @@ -62,7 +62,7 @@ func TestFind(t *testing.T) { func BenchmarkFind(b *testing.B) { b.Run("Benchmark find", func(b *testing.B) { - file := test.Testdata("windows.zip") + file := test.Testdata("windows", "image.zip") if err := zip.Unzip(file, sysroot); err != nil { b.Fatal(err) diff --git a/pkg/flog/evt/evt_test.go b/pkg/flog/evt/evt_test.go deleted file mode 100644 index 4fc5a88..0000000 --- a/pkg/flog/evt/evt_test.go +++ /dev/null @@ -1,22 +0,0 @@ -// Evt implementation tests. -package evt - -import ( - "testing" - - "github.com/cuhsat/fact/internal/fact" -) - -func TestTool(t *testing.T) { - t.Run("", func(t *testing.T) { - asm, err := fact.EzTools("EvtxECmd.dll") - - if err != nil { - t.Fatal(err) - } - - if len(asm) == 0 { - t.Fatal("tool not found") - } - }) -} diff --git a/pkg/flog/evt/evt.go b/pkg/flog/evtx/evtx.go similarity index 96% rename from pkg/flog/evt/evt.go rename to pkg/flog/evtx/evtx.go index c55a94b..f0e85b5 100644 --- a/pkg/flog/evt/evt.go +++ b/pkg/flog/evtx/evtx.go @@ -1,5 +1,5 @@ -// Evt implementation details. -package evt +// Evtx implementation details. +package evtx import ( "fmt" @@ -14,7 +14,7 @@ import ( ) const ( - Evt = "evtx" + Evtx = "evtx" ) func Log(src string, dir string) (err error) { diff --git a/pkg/flog/evtx/evtx_test.go b/pkg/flog/evtx/evtx_test.go new file mode 100644 index 0000000..69a8862 --- /dev/null +++ b/pkg/flog/evtx/evtx_test.go @@ -0,0 +1,2 @@ +// Evtx implementation tests. +package evtx diff --git a/pkg/flog/flog.go b/pkg/flog/flog.go index c850f40..148c3e8 100644 --- a/pkg/flog/flog.go +++ b/pkg/flog/flog.go @@ -7,17 +7,17 @@ import ( "github.com/cuhsat/fact/internal/fact" "github.com/cuhsat/fact/internal/sys" - "github.com/cuhsat/fact/pkg/flog/evt" + "github.com/cuhsat/fact/pkg/flog/evtx" ) -func Evt(files, args []string) (err error) { +func Evtx(files, args []string) (err error) { for _, f := range files { - if filepath.Ext(f) == evt.Evt { + if filepath.Ext(f) == evtx.Evtx { args = append(args, f) } } - _, err = sys.StdCall("flog.evt", args...) + _, err = sys.StdCall("flog.evtx", args...) return } diff --git a/pkg/fmount/dd/dd_test.go b/pkg/fmount/dd/dd_test.go index b4c6ce7..35b1331 100644 --- a/pkg/fmount/dd/dd_test.go +++ b/pkg/fmount/dd/dd_test.go @@ -16,7 +16,7 @@ func TestDD(t *testing.T) { }{ { name: "Test mount for Windows", - file: test.Testdata("windows.dd.zip"), + file: test.Testdata("windows", "image.dd.zip"), }, } diff --git a/scripts/eztools.sh b/scripts/eztools.sh index a59d139..2df3282 100644 --- a/scripts/eztools.sh +++ b/scripts/eztools.sh @@ -8,15 +8,15 @@ echo "Download tools" if [ ! -f ${GOBIN}/EvtxECmd.dll ] ; then echo " EvtxECmd" - wget -q "https://f001.backblazeb2.com/file/EricZimmermanTools/net6/EvtxECmd.zip" -O ${GOBIN}/evt.zip - unzip -q ${GOBIN}/evt.zip -d ${GOBIN} + wget -q "https://f001.backblazeb2.com/file/EricZimmermanTools/net6/EvtxECmd.zip" -O ${GOBIN}/evtx.zip + unzip -q ${GOBIN}/evtx.zip -d ${GOBIN} cp ${GOBIN}/EvtxeCmd/EvtxECmd.dll ${GOBIN} cp ${GOBIN}/EvtxeCmd/EvtxECmd.runtimeconfig.json ${GOBIN} cp -r ${GOBIN}/EvtxeCmd/Maps ${GOBIN} rm -rf ${GOBIN}/EvtxeCmd* - rm -f ${GOBIN}/evt.zip + rm -f ${GOBIN}/evtx.zip fi export EZTOOLS=$(realpath ${GOBIN}) diff --git a/scripts/gobuild.sh b/scripts/gobuild.sh index 4b7c988..71e2ed0 100644 --- a/scripts/gobuild.sh +++ b/scripts/gobuild.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash GO="go" -GOFLAGS="build -race" +GOFLAGS="build -v -race" GOBIN="bin" VERSION=$(git describe --tags --abbrev=0) LDFLAGS="-X 'github.com/cuhsat/fact/internal/fact.Version=$VERSION'"