From 174e62882509b94989cd81e5c82ce2a1b2b0c990 Mon Sep 17 00:00:00 2001 From: Noteworthy Date: Tue, 10 Jan 2023 14:18:30 +1100 Subject: [PATCH] Chore: Remove cobra dependency from cmd/pedumper (#56) * remove cobra dependency from cmd/pedumper * drop golang 1.15 from CI test --- .github/workflows/ci.yaml | 10 +-- cmd/{pedumper.go => dump.go} | 149 ++++++++--------------------------- cmd/main.go | 113 ++++++++++++++++++++++++++ 3 files changed, 150 insertions(+), 122 deletions(-) rename cmd/{pedumper.go => dump.go} (58%) create mode 100644 cmd/main.go diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 5de8ede..845b9be 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -8,7 +8,7 @@ jobs: strategy: fail-fast: false matrix: - go-version: [1.15.x, 1.16.x, 1.17.x, 1.18.x, 1.19.x] + go-version: [1.16.x, 1.17.x, 1.18.x, 1.19.x] os: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.os }} steps: @@ -37,17 +37,17 @@ jobs: uses: codecov/codecov-action@v2 with: files: ./coverage - if: matrix.os == 'windows-latest' && matrix.go-version == '1.17.x' + if: matrix.os == 'windows-latest' && matrix.go-version == '1.19.x' - name: Go vet run: | go vet . - if: matrix.os == 'windows-latest' && matrix.go-version == '1.17.x' + if: matrix.os == 'windows-latest' && matrix.go-version == '1.19.x' - name: Staticcheck - uses: dominikh/staticcheck-action@v1.2.0 + uses: dominikh/staticcheck-action@v1.3.0 with: version: "2022.1" install-go: false cache-key: ${{ matrix.go }} - if: matrix.os == 'windows-latest' && matrix.go-version == '1.17.x' + if: matrix.os == 'windows-latest' && matrix.go-version == '1.19.x' diff --git a/cmd/pedumper.go b/cmd/dump.go similarity index 58% rename from cmd/pedumper.go rename to cmd/dump.go index 56a387f..58a0e51 100644 --- a/cmd/pedumper.go +++ b/cmd/dump.go @@ -1,4 +1,4 @@ -// Copyright 2022 Saferwall. All rights reserved. +// Copyright 2018 Saferwall. All rights reserved. // Use of this source code is governed by Apache v2 license // license that can be found in the LICENSE file. @@ -8,26 +8,12 @@ import ( "bytes" "encoding/json" "fmt" - "io/ioutil" "os" "path/filepath" "text/tabwriter" peparser "github.com/saferwall/pe" "github.com/saferwall/pe/log" - "github.com/spf13/cobra" -) - -var ( - all bool - verbose bool - dosHeader bool - richHeader bool - ntHeader bool - directories bool - sections bool - resources bool - clr bool ) func prettyPrint(buff []byte) string { @@ -77,15 +63,38 @@ func isDirectory(path string) bool { return fileInfo.IsDir() } -func parsePE(filename string, cmd *cobra.Command) { +func parse(filePath string, cfg config) { + + // filePath points to a file. + if !isDirectory(filePath) { + parsePE(filePath, cfg) + + } else { + // filePath points to a directory, + // walk recursively through all files. + fileList := []string{} + filepath.Walk(filePath, func(path string, f os.FileInfo, err error) error { + if !isDirectory(path) { + fileList = append(fileList, path) + } + return nil + }) + + for _, file := range fileList { + parsePE(file, cfg) + } + } +} + +func parsePE(filename string, cfg config) { logger := log.NewStdLogger(os.Stdout) - logger = log.NewFilter(logger, log.FilterLevel(log.LevelError)) + logger = log.NewFilter(logger, log.FilterLevel(log.LevelInfo)) log := log.NewHelper(logger) log.Infof("parsing filename %s", filename) - data, _ := ioutil.ReadFile(filename) + data, _ := os.ReadFile(filename) pe, err := peparser.NewBytes(data, &peparser.Options{ Logger: logger, }) @@ -130,14 +139,12 @@ func parsePE(filename string, cmd *cobra.Command) { log.Debug("File is Driver") } - wantDosHeader, _ := cmd.Flags().GetBool("dosheader") - if wantDosHeader { + if cfg.wantDOSHeader { dosHeader, _ := json.Marshal(pe.DOSHeader) fmt.Print(prettyPrint(dosHeader)) } - wantRichHeader, _ := cmd.Flags().GetBool("rich") - if wantRichHeader { + if cfg.wantRichHeader { richheader := pe.RichHeader fmt.Printf("RICH HEADER\n\n") w := tabwriter.NewWriter(os.Stdout, 1, 1, 3, ' ', tabwriter.AlignRight) @@ -155,14 +162,12 @@ func parsePE(filename string, cmd *cobra.Command) { hexDump(richheader.Raw) } - wantNtHeader, _ := cmd.Flags().GetBool("ntheader") - if wantNtHeader { + if cfg.wantNTHeader { ntHeader, _ := json.Marshal(pe.NtHeader) log.Info(prettyPrint(ntHeader)) } - wantSections, _ := cmd.Flags().GetBool("sections") - if wantSections { + if cfg.wantSections { for _, sec := range pe.Sections { log.Infof("Section Name : %s\n", sec.NameString()) log.Infof("Section VirtualSize : %x\n", sec.Header.VirtualSize) @@ -173,14 +178,7 @@ func parsePE(filename string, cmd *cobra.Command) { log.Info(prettyPrint(sectionsHeaders)) } - wantResources, _ := cmd.Flags().GetBool("resources") - if wantResources { - rsrc, _ := json.Marshal(pe.Resources) - log.Info(prettyPrint(rsrc)) - } - - wantCLR, _ := cmd.Flags().GetBool("clr") - if wantCLR { + if cfg.wantCLR { dotnetMetadata, _ := json.Marshal(pe.CLR) log.Info(prettyPrint(dotnetMetadata)) if modTable, ok := pe.CLR.MetadataTables[peparser.Module]; ok { @@ -193,88 +191,5 @@ func parsePE(filename string, cmd *cobra.Command) { } } - wantAll, _ := cmd.Flags().GetBool("all") - if wantAll { - dosHeader, _ := json.Marshal(pe.DOSHeader) - ntHeader, _ := json.Marshal(pe.NtHeader) - sectionsHeaders, _ := json.Marshal(pe.Sections) - log.Info(prettyPrint(dosHeader)) - log.Info(prettyPrint(ntHeader)) - log.Info(prettyPrint(sectionsHeaders)) - return - } - fmt.Println() } - -func parse(cmd *cobra.Command, args []string) { - filePath := args[0] - - // filePath points to a file. - if !isDirectory(filePath) { - parsePE(filePath, cmd) - - } else { - // filePath points to a directory, - // walk recursively through all files. - fileList := []string{} - filepath.Walk(filePath, func(path string, f os.FileInfo, err error) error { - if !isDirectory(path) { - fileList = append(fileList, path) - } - return nil - }) - - for _, file := range fileList { - parsePE(file, cmd) - } - } -} - -func main() { - - var rootCmd = &cobra.Command{ - Use: "pedumper", - Short: "A Portable Executable file parser", - Long: "A PE-Parser built for speed and malware-analysis in mind by Saferwall", - Run: func(cmd *cobra.Command, args []string) { - }, - } - - var versionCmd = &cobra.Command{ - Use: "version", - Short: "Print version number", - Long: "Print version number", - Run: func(cmd *cobra.Command, args []string) { - fmt.Print("You are using version 1.1.7") - }, - } - - var dumpCmd = &cobra.Command{ - Use: "dump", - Short: "Dumps the file", - Long: "Dumps interesting structure of the Portable Executable file", - Args: cobra.MinimumNArgs(1), - Run: parse, - } - - // Init root command. - rootCmd.AddCommand(versionCmd) - rootCmd.AddCommand(dumpCmd) - - // Init flags - rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "verbose output") - dumpCmd.Flags().BoolVarP(&dosHeader, "dosheader", "", false, "Dump DOS header") - dumpCmd.Flags().BoolVarP(&richHeader, "rich", "", false, "Dump Rich header") - dumpCmd.Flags().BoolVarP(&ntHeader, "ntheader", "", false, "Dump NT header") - dumpCmd.Flags().BoolVarP(&directories, "directories", "", false, "Dump data directories") - dumpCmd.Flags().BoolVarP(§ions, "sections", "", false, "Dump section headers") - dumpCmd.Flags().BoolVarP(&resources, "resources", "", false, "Dump resources") - dumpCmd.Flags().BoolVarP(&clr, "clr", "", false, "Dump .NET metadata") - dumpCmd.Flags().BoolVarP(&all, "all", "", false, "Dump everything") - - if err := rootCmd.Execute(); err != nil { - fmt.Print(err) - os.Exit(1) - } -} diff --git a/cmd/main.go b/cmd/main.go new file mode 100644 index 0000000..81512e3 --- /dev/null +++ b/cmd/main.go @@ -0,0 +1,113 @@ +// Copyright 2018 Saferwall. All rights reserved. +// Use of this source code is governed by Apache v2 license +// license that can be found in the LICENSE file. + +package main + +import ( + "flag" + "fmt" + "os" +) + +type config struct { + wantDOSHeader bool + wantRichHeader bool + wantNTHeader bool + wantCOFF bool + wantDataDirs bool + wantSections bool + wantExport bool + wantImport bool + wantRsrc bool + wantExceptions bool + wantCertificates bool + wantReloc bool + wantDebug bool + wantTLS bool + wantLoadCfg bool + wantBoundImp bool + wantIAT bool + wantDelayImp bool + wantCLR bool +} + +func main() { + + dumpCmd := flag.NewFlagSet("dump", flag.ExitOnError) + dumpDOSHdr := dumpCmd.Bool("dosheader", false, "Dump DOS header") + dumpRichHdr := dumpCmd.Bool("richheader", false, "Dump Rich header") + dumpNTHdr := dumpCmd.Bool("ntheader", false, "Dump NT header") + dumpCOFF := dumpCmd.Bool("coff", false, "Dump COFF symbols") + dumpDirs := dumpCmd.Bool("directories", false, "Dump data directories") + dumpSections := dumpCmd.Bool("sections", false, "Dump sections") + dumpExport := dumpCmd.Bool("export", false, "Dump export table") + dumpImport := dumpCmd.Bool("import", false, "Dump import table") + dumpRsrc := dumpCmd.Bool("resources", false, "Dump resource table") + dumpExceptions := dumpCmd.Bool("exceptions", false, "Dump exception table") + dumpCertificate := dumpCmd.Bool("cert", false, "Dump certificate directory") + dumpReloc := dumpCmd.Bool("reloc", false, "Dump relocation table") + dumpDebug := dumpCmd.Bool("debug", false, "Dump debug infos") + dumpTLS := dumpCmd.Bool("tls", false, "Dump TLS") + dumpLoadCfg := dumpCmd.Bool("loadconfig", false, "Dump load configuration table") + dumpBoundImport := dumpCmd.Bool("bound", false, "Dump bound import table") + dumpIAT := dumpCmd.Bool("iat", false, "Dump IAT") + dumpDelayedImport := dumpCmd.Bool("delay", false, "Dump delay import descriptor") + dumpCLR := dumpCmd.Bool("clr", false, "Dump CLR") + + verCmd := flag.NewFlagSet("version", flag.ExitOnError) + + if len(os.Args) < 2 { + showHelp() + } + + switch os.Args[1] { + + case "dump": + dumpCmd.Parse(os.Args[3:]) + + cfg := config{ + wantDOSHeader: *dumpDOSHdr, + wantRichHeader: *dumpRichHdr, + wantNTHeader: *dumpNTHdr, + wantCOFF: *dumpCOFF, + wantDataDirs: *dumpDirs, + wantSections: *dumpSections, + wantExport: *dumpExport, + wantImport: *dumpImport, + wantRsrc: *dumpRsrc, + wantExceptions: *dumpExceptions, + wantCertificates: *dumpCertificate, + wantReloc: *dumpReloc, + wantDebug: *dumpDebug, + wantTLS: *dumpTLS, + wantLoadCfg: *dumpLoadCfg, + wantBoundImp: *dumpBoundImport, + wantIAT: *dumpIAT, + wantDelayImp: *dumpDelayedImport, + wantCLR: *dumpCLR, + } + + parse(os.Args[2], cfg) + + case "version": + verCmd.Parse(os.Args[2:]) + fmt.Println("You are using version 1.3.0") + default: + showHelp() + } +} + +func showHelp() { + fmt.Print( + ` +╔═╗╔═╗ ┌─┐┌─┐┬─┐┌─┐┌─┐┬─┐ +╠═╝║╣ ├─┘├─┤├┬┘└─┐├┤ ├┬┘ +╩ ╚═╝ ┴ ┴ ┴┴└─└─┘└─┘┴└─ + + A PE-Parser built for speed and malware-analysis in mind. + Brought to you by Saferwall (c) 2018 MIT +`) + fmt.Println("\nAvailable sub-commands 'dump' or 'version' subcommands") + os.Exit(1) +}