├── README.md ├── cmd └── akawaka │ └── akawaka.go ├── go.mod ├── go.sum └── pkg ├── config └── options.go ├── runner ├── banner.go └── runner.go └── utils ├── base.go ├── color.go └── file.go /README.md: -------------------------------------------------------------------------------- 1 | # akawaka 2 | 一个简单的文件内容搜索工具 3 | 4 | 支持指定后缀名搜索文件内容 5 | 6 | 可用于应急响应、内网终端信息收集、代码审计等 7 | 8 | ``` 9 | __ __ 10 | /\ \ /\ \ 11 | __ \ \ \/'\ __ __ __ __ __ \ \ \/'\ __ 12 | /'__`\ \ \ , < /'__`\ /\ \/\ \/\ \ /'__`\ \ \ , < /'__`\ 13 | /\ \L\.\_\ \ \\`\ /\ \L\.\_\ \ \_/ \_/ \/\ \L\.\_\ \ \\`\ /\ \L\.\_ 14 | \ \__/.\_\\ \_\ \_\ \__/.\_\\ \___x___/'\ \__/.\_\\ \_\ \_\ \__/.\_\ 15 | \/__/\/_/ \/_/\/_/\/__/\/_/ \/__//__/ \/__/\/_/ \/_/\/_/\/__/\/_/ 16 | 17 | Author: dean 18 | Version: 0.0.5 19 | 20 | NAME: 21 | Akawaka - 腚的文件内容搜索小工具 22 | 23 | USAGE: 24 | Akawaka [global options] command [command options] [arguments...] 25 | 26 | VERSION: 27 | 0.0.5 28 | 29 | COMMANDS: 30 | help, h Shows a list of commands or help for one command 31 | 32 | GLOBAL OPTIONS: 33 | --extension value, -e value 文件扩展名 eg: -e txt,jsp,asp 34 | --extension-file value, --ef value 文件扩展名文本 eg: -ef extens.txt 35 | --directory value, -d value 搜索目录 eg: -d D:\web (default: "D:\\Tools\\myTools\\Go\\akawaka\\cmd\\akawaka\\akawaka_releases_v0.0.5") 36 | --keyword value, -k value 搜索关键词 eg: -k keyword1,keyword2 37 | --keyword-file value, --kf value 搜索关键词文本 eg: -kf keywords.txt 38 | --is-filename, --if 搜索文件名 eg: -if true (default: false) 39 | --verbose, -m 详细输出 eg: -v true (default: false) 40 | --Thread value, -t value 线程 eg: -t 10 (default: 10) 41 | --help, -h show help (default: false) 42 | --version, -v print the version (default: false) 43 | ``` 44 | 45 | ![image](https://user-images.githubusercontent.com/67625626/206980323-1d851182-8571-4418-8cad-3d7355036acf.png) 46 | 47 | -------------------------------------------------------------------------------- /cmd/akawaka/akawaka.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "akawaka/pkg/config" 5 | "akawaka/pkg/runner" 6 | "akawaka/pkg/utils" 7 | "fmt" 8 | "github.com/urfave/cli/v2" 9 | "os" 10 | ) 11 | 12 | var options = &config.Options{} 13 | 14 | func main() { 15 | runner.ShowBanner() 16 | 17 | app := cli.NewApp() 18 | app.Name = "Akawaka" 19 | app.Usage = "腚的文件内容搜索小工具" 20 | app.Version = runner.ShowVersion() 21 | app.Flags = []cli.Flag{ 22 | &cli.StringFlag{Name: "extension", Aliases: []string{"e"}, Value: "", Destination: &options.Extension, Usage: "文件扩展名 eg: -e txt,jsp,asp"}, 23 | &cli.StringFlag{Name: "extension-file", Aliases: []string{"ef"}, Value: "", Destination: &options.Extensions_File, Usage: "文件扩展名文本 eg: -ef extens.txt"}, 24 | &cli.StringFlag{Name: "directory", Aliases: []string{"d"}, Value: utils.GetWd(), Destination: &options.DirPath, Usage: "搜索目录 eg: -d D:\\web"}, 25 | &cli.StringFlag{Name: "keyword", Aliases: []string{"k"}, Value: "", Destination: &options.Keyword, Usage: "搜索关键词 eg: -k keyword1,keyword2"}, 26 | &cli.StringFlag{Name: "keyword-file", Aliases: []string{"kf"}, Value: "", Destination: &options.Keywords_File, Usage: "搜索关键词文本 eg: -kf keywords.txt"}, 27 | &cli.BoolFlag{Name: "is-filename", Aliases: []string{"if"}, Value: false, Destination: &options.Is_Filename, Usage: "搜索文件名 eg: -if true"}, 28 | &cli.BoolFlag{Name: "more", Aliases: []string{"m"}, Value: false, Destination: &options.Verbose, Usage: "详细输出 eg: -m true"}, 29 | &cli.IntFlag{Name: "Thread", Aliases: []string{"t"}, Value: 10, Destination: &options.Thread, Usage: "线程 eg: -t 10"}, 30 | } 31 | 32 | app.Action = func(c *cli.Context) error { 33 | 34 | // 转换参数 35 | err := utils.Transform(options) 36 | if err != nil { 37 | return err 38 | } 39 | 40 | // 运行搜索逻辑 41 | err = runner.New(options) 42 | return err 43 | } 44 | 45 | err := app.Run(os.Args) 46 | if err != nil { 47 | fmt.Println(utils.LogColor.GetColor("Red", "[!] start akawaka failed,"+err.Error())) 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module akawaka 2 | 3 | go 1.19 4 | 5 | require ( 6 | github.com/gookit/color v1.5.2 7 | github.com/panjf2000/ants/v2 v2.7.0 8 | github.com/urfave/cli/v2 v2.20.2 9 | ) 10 | 11 | require ( 12 | github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect 13 | github.com/russross/blackfriday/v2 v2.1.0 // indirect 14 | github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect 15 | github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect 16 | golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5 // indirect 17 | ) 18 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= 2 | github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 3 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 5 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/gookit/color v1.5.2 h1:uLnfXcaFjlrDnQDT+NCBcfhrXqYTx/rcCa6xn01Y8yI= 7 | github.com/gookit/color v1.5.2/go.mod h1:w8h4bGiHeeBpvQVePTutdbERIUf3oJE5lZ8HM0UgXyg= 8 | github.com/panjf2000/ants/v2 v2.7.0 h1:Y3Bgpfo9HDkBoHNVFbMfY5mAvi5TAA17y3HbzQ74p5Y= 9 | github.com/panjf2000/ants/v2 v2.7.0/go.mod h1:KIBmYG9QQX5U2qzFP/yQJaq/nSb6rahS9iEHkrCMgM8= 10 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 11 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 12 | github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= 13 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 14 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 15 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 16 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 17 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 18 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 19 | github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= 20 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 21 | github.com/urfave/cli/v2 v2.20.2 h1:dKA0LUjznZpwmmbrc0pOgcLTEilnHeM8Av9Yng77gHM= 22 | github.com/urfave/cli/v2 v2.20.2/go.mod h1:1CNUng3PtjQMtRzJO4FMXBQvkGtuYRxxiR9xMa7jMwI= 23 | github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8= 24 | github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= 25 | github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= 26 | github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= 27 | golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= 28 | golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 29 | golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 30 | golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5 h1:y/woIyUBFbpQGKS0u1aHF/40WUDnek3fPOyD08H5Vng= 31 | golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 32 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 33 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 34 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 35 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 36 | -------------------------------------------------------------------------------- /pkg/config/options.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type Options struct { 4 | DirPath string 5 | Keywords_File string 6 | Keyword string 7 | Keywords []string 8 | Extension string 9 | Extensions []string 10 | Extensions_File string 11 | Is_Filename bool 12 | Count int 13 | CurrentCount int 14 | Thread int 15 | Verbose bool 16 | } 17 | -------------------------------------------------------------------------------- /pkg/runner/banner.go: -------------------------------------------------------------------------------- 1 | package runner 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func ShowBanner() { 8 | banner := " __ __\n" + 9 | " /\\ \\ /\\ \\\n" + 10 | " __ \\ \\ \\/'\\ __ __ __ __ __ \\ \\ \\/'\\ __\n" + 11 | " /'__`\\ \\ \\ , < /'__`\\ /\\ \\/\\ \\/\\ \\ /'__`\\ \\ \\ , < /'__`\\\n" + 12 | "/\\ \\L\\.\\_\\ \\ \\\\`\\ /\\ \\L\\.\\_\\ \\ \\_/ \\_/ \\/\\ \\L\\.\\_\\ \\ \\\\`\\ /\\ \\L\\.\\_\n" + 13 | "\\ \\__/.\\_\\\\ \\_\\ \\_\\ \\__/.\\_\\\\ \\___x___/'\\ \\__/.\\_\\\\ \\_\\ \\_\\ \\__/.\\_\\\n" + 14 | " \\/__/\\/_/ \\/_/\\/_/\\/__/\\/_/ \\/__//__/ \\/__/\\/_/ \\/_/\\/_/\\/__/\\/_/\n" + 15 | "\n" + 16 | "Author: dean\n" + 17 | "Version: " + ShowVersion() + "\n\n" 18 | 19 | //test := " __ __\n /\\ \\ /\\ \\\n __ \\ \\ \\/'\\ __ __ __ __ __ \\ \\ \\/'\\ __\n /'__`\\ \\ \\ , < /'__`\\ /\\ \\/\\ \\/\\ \\ /'__`\\ \\ \\ , < /'__`\\\n/\\ \\L\\.\\_\\ \\ \\\\`\\ /\\ \\L\\.\\_\\ \\ \\_/ \\_/ \\/\\ \\L\\.\\_\\ \\ \\\\`\\ /\\ \\L\\.\\_\n\\ \\__/.\\_\\\\ \\_\\ \\_\\ \\__/.\\_\\\\ \\___x___/'\\ \\__/.\\_\\\\ \\_\\ \\_\\ \\__/.\\_\\\n \\/__/\\/_/ \\/_/\\/_/\\/__/\\/_/ \\/__//__/ \\/__/\\/_/ \\/_/\\/_/\\/__/\\/_/\n\n\n" 20 | fmt.Printf(banner) 21 | 22 | } 23 | 24 | func ShowVersion() string { 25 | return "0.0.5" 26 | } 27 | -------------------------------------------------------------------------------- /pkg/runner/runner.go: -------------------------------------------------------------------------------- 1 | package runner 2 | 3 | import ( 4 | "akawaka/pkg/config" 5 | "akawaka/pkg/utils" 6 | "fmt" 7 | "github.com/panjf2000/ants/v2" 8 | "math/rand" 9 | "sync" 10 | "time" 11 | ) 12 | 13 | type WaitGroupTask struct { 14 | Key int 15 | Value any 16 | } 17 | 18 | type Runner struct { 19 | options *config.Options 20 | } 21 | 22 | var lock sync.Mutex 23 | 24 | func New(options *config.Options) error { 25 | // 验证文件目录路径是否正确 26 | err := utils.IsValid(options.DirPath) 27 | if err != nil { 28 | return err 29 | } 30 | 31 | // 初始化参数 32 | utils.Init(options) 33 | 34 | files, _ := utils.GetFileList(options.DirPath) 35 | options.Count = len(files) 36 | options.CurrentCount = 0 37 | fmt.Printf("\n[!] starting search...\n") 38 | defer fmt.Printf("\r\n[!] search finished...\r\n") 39 | 40 | err = excute(options, files) 41 | return nil 42 | } 43 | 44 | func excute(options *config.Options, files []string) error { 45 | 46 | var wg sync.WaitGroup 47 | p, _ := ants.NewPoolWithFunc(options.Thread, func(wgTask any) { 48 | defer wg.Done() 49 | filePath := wgTask.(WaitGroupTask).Value.(string) 50 | //add: check target alive 51 | if !utils.IsDir(filePath) { 52 | if options.Is_Filename { 53 | utils.SearchFilename(filePath) 54 | } else { 55 | utils.Search(filePath, options.Verbose) 56 | } 57 | } 58 | outputProcess(options) 59 | }) 60 | 61 | defer p.Release() 62 | for k, target := range files { 63 | wg.Add(1) 64 | _ = p.Invoke(WaitGroupTask{Value: target, Key: k}) 65 | } 66 | wg.Wait() 67 | /* 68 | for _, filePath := range files { 69 | if utils.IsDir(filePath) { 70 | continue 71 | } else { 72 | if options.Is_Filename { 73 | utils.SearchFilename(filePath) 74 | } else { 75 | utils.Search(filePath) 76 | } 77 | } 78 | options.CurrentCount++ 79 | fmt.Printf("\r%d/%d | %d%% ", options.CurrentCount, options.Count, options.CurrentCount*100/options.Count) 80 | } 81 | */ 82 | 83 | return nil 84 | } 85 | 86 | func outputProcess(options *config.Options) { 87 | lock.Lock() 88 | options.CurrentCount++ 89 | fmt.Printf("\r%d/%d | %d%% ", options.CurrentCount, options.Count, options.CurrentCount*100/options.Count) 90 | lock.Unlock() 91 | RandSleep(1) 92 | } 93 | 94 | func RandSleep(millisencond int) { 95 | ms := millisencond + rand.Intn(millisencond) 96 | time.Sleep(time.Duration(ms) * time.Millisecond) 97 | } 98 | -------------------------------------------------------------------------------- /pkg/utils/base.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "akawaka/pkg/config" 5 | "errors" 6 | "strings" 7 | ) 8 | 9 | func SetArray(str string, strArray *[]string) bool { 10 | if len(str) > 0 { 11 | arr := strings.Split(str, ",") 12 | if len(arr) > 0 { 13 | for _, v := range arr { 14 | *strArray = append(*strArray, strings.TrimSpace(v)) 15 | } 16 | println(*strArray) 17 | str = "a" 18 | return true 19 | } 20 | } 21 | return false 22 | } 23 | 24 | /* 25 | 将keyword转换为keywords数组 26 | */ 27 | func KeywordTransform(options *config.Options) error { 28 | // 判断关键词输入方式是文本还是多个字符串 29 | if len(options.Keyword) != 0 { 30 | if strings.Contains(options.Keyword, ",") { //输入形式为:-k "a,b,c" 31 | SetArray(options.Keyword, &options.Keywords) 32 | return nil 33 | } else { // 输入形式为 -k "a" 34 | options.Keywords = append(options.Keywords, options.Keyword) 35 | return nil 36 | } 37 | } else if len(options.Keywords_File) != 0 { 38 | var err error 39 | options.Keywords, err = ReadArrFromTxt(options.Keywords_File, "Keyword") 40 | if err != nil { 41 | return err 42 | } 43 | return nil 44 | } else { 45 | return errors.New("keyword is required...") 46 | } 47 | } 48 | 49 | /* 50 | 将extens转换成extensions数组 51 | */ 52 | func ExtensionTransform(options *config.Options) error { 53 | if len(options.Extension) != 0 { 54 | if strings.Contains(options.Extension, ",") { 55 | SetArray(options.Extension, &options.Extensions) 56 | return nil 57 | } else { 58 | options.Extensions = append(options.Extensions, options.Extension) 59 | return nil 60 | } 61 | } else if len(options.Extensions_File) != 0 { 62 | var err error 63 | 64 | options.Extensions, err = ReadArrFromTxt(options.Extensions_File, "Extension") 65 | if err != nil { 66 | return err 67 | } 68 | return nil 69 | } else { 70 | return errors.New("extension is required...") 71 | } 72 | } 73 | 74 | func Transform(options *config.Options) error { 75 | 76 | /* 77 | 优先判断用户受否有输入keyword,然后再判断extension后缀名 78 | */ 79 | // 转换关键字属性 80 | err1 := KeywordTransform(options) 81 | if err1 != nil { 82 | return err1 83 | } 84 | 85 | // 转换后缀名属性 86 | err2 := ExtensionTransform(options) 87 | if err2 != nil { 88 | return err2 89 | } 90 | return nil 91 | } 92 | -------------------------------------------------------------------------------- /pkg/utils/color.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "github.com/gookit/color" 5 | "strings" 6 | ) 7 | 8 | type Color struct { 9 | Green func(a ...any) string 10 | White func(a ...any) string 11 | Red func(a ...any) string 12 | } 13 | 14 | var LogColor *Color 15 | 16 | func init() { 17 | if LogColor == nil { 18 | LogColor = NewColor() 19 | } 20 | } 21 | 22 | func NewColor() *Color { 23 | return &Color{ 24 | Green: color.FgLightGreen.Render, 25 | White: color.FgLightWhite.Render, 26 | Red: color.FgLightRed.Render, 27 | } 28 | } 29 | 30 | func (c *Color) GetColor(color string, msg string) string { 31 | color = strings.ToLower(color) 32 | switch color { 33 | case "green": 34 | return c.Green(msg) 35 | case "white": 36 | return c.White(msg) 37 | case "red": 38 | return c.Red(msg) 39 | default: 40 | return c.White(msg) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /pkg/utils/file.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "akawaka/pkg/config" 5 | "bufio" 6 | "errors" 7 | "fmt" 8 | "io" 9 | "os" 10 | "path" 11 | "path/filepath" 12 | "runtime" 13 | "strings" 14 | "sync" 15 | ) 16 | 17 | var extens = []string{} 18 | var keywords = []string{} 19 | 20 | /* 21 | 遍历目录文件名字,返回字符串数组 22 | */ 23 | func GetFileList(rootPath string) ([]string, error) { 24 | var lock sync.Mutex 25 | var count int = 0 26 | var files []string 27 | var inExtens_files = []string{} 28 | err := filepath.Walk(rootPath, func(path string, info os.FileInfo, err error) error { 29 | files = append(files, path) 30 | lock.Lock() 31 | count++ 32 | fmt.Printf("\r遍历文件:%v", count) 33 | lock.Unlock() 34 | return nil 35 | }) 36 | if err != nil { 37 | return []string{}, err 38 | } 39 | 40 | for _, v := range files { 41 | if InExtens(v) { 42 | inExtens_files = append(inExtens_files, v) 43 | } 44 | } 45 | fmt.Printf("\n共找到指定扩展名文件数:%v\n", len(inExtens_files)) 46 | return inExtens_files, err 47 | } 48 | 49 | /* 50 | 获取当前目录 51 | */ 52 | func GetWd() string { 53 | dir, _ := os.Getwd() 54 | return dir 55 | } 56 | 57 | /* 58 | 从文本中读出keywords并转换成字符串数组,每行一个元素 59 | */ 60 | func ReadArrFromTxt(fileName string, msg string) ([]string, error) { 61 | var err error 62 | var arr = []string{} 63 | fileSuffix := path.Ext(fileName) 64 | fmt.Printf("%s file: %s\n", msg, fileName) 65 | if fileSuffix != ".txt" { 66 | return arr, errors.New("file only supports txt") 67 | } 68 | 69 | f, err := os.Open(fileName) 70 | if err != nil { 71 | return arr, err 72 | } 73 | defer f.Close() 74 | 75 | r := bufio.NewReader(f) 76 | for { 77 | line, err := r.ReadString('\n') 78 | if len(line) != 0 { 79 | arr = append(arr, strings.TrimSpace(line)) 80 | } 81 | if err == io.EOF { 82 | break 83 | } else if err != nil { 84 | fmt.Printf("error reading file %s", err) 85 | break 86 | } 87 | } 88 | return arr, nil 89 | } 90 | 91 | /* 92 | 判断是否为目录 93 | */ 94 | func IsDir(filepath string) bool { 95 | s, err := os.Stat(filepath) 96 | if err != nil { 97 | return false 98 | } 99 | return s.IsDir() 100 | } 101 | 102 | /* 103 | 判断后缀名是否在指定范围 104 | */ 105 | func InExtens(fileName string) bool { 106 | fileSuffix := path.Ext(fileName) 107 | for _, e := range extens { 108 | if fileSuffix == "."+e { 109 | return true 110 | } 111 | } 112 | return false 113 | } 114 | 115 | /* 116 | 读取文件内容 117 | */ 118 | func ReadFile(fileName string) (string, error) { 119 | b, err := os.ReadFile(fileName) 120 | if err != nil { 121 | fmt.Printf("err: %v\n", err) 122 | return "", err 123 | } else { 124 | content := string(b[:]) 125 | return content, nil 126 | } 127 | } 128 | 129 | /* 130 | 匹配内容关键字 131 | */ 132 | func Search(filePath string, verbose bool) { 133 | content, err := ReadFile(filePath) 134 | if err != nil { 135 | fmt.Printf("err: %v\n", err) 136 | return 137 | } 138 | var msg string 139 | for _, keyword := range keywords { 140 | if strings.Contains(strings.ToLower(content), strings.ToLower(keyword)) { 141 | msg = "\r[+] " + filePath + " find keyword: \"" + keyword + "\":\r\n" 142 | fmt.Printf(msg) 143 | if verbose { 144 | OutputContext(strings.ToLower(content), strings.ToLower(keyword)) 145 | } 146 | } 147 | } 148 | 149 | } 150 | 151 | /* 152 | 搜索文件名关键字 153 | */ 154 | func SearchFilename(filePath string) { 155 | var msg string 156 | var filename string = "" 157 | sysType := runtime.GOOS 158 | if sysType == "linux" { 159 | temp := strings.Split(filePath, "/") 160 | filename = temp[len(temp)-1] 161 | } else if sysType == "windows" { 162 | if strings.Contains(filePath, "\\\\") { 163 | temp := strings.Split(filePath, "\\\\") 164 | filename = temp[len(temp)-1] 165 | } else { 166 | temp := strings.Split(filePath, "\\") 167 | filename = temp[len(temp)-1] 168 | } 169 | } 170 | 171 | for _, keyword := range keywords { 172 | if strings.Contains(strings.ToLower(filename), strings.ToLower(keyword)) { 173 | lightName := OutputFilename(strings.ToLower(filename), strings.ToLower(keyword)) 174 | msg = "\r[+] " + strings.Trim(filePath, filename) + lightName + "\r\n" 175 | fmt.Printf(msg) 176 | OutputFilename(strings.ToLower(filename), strings.ToLower(keyword)) 177 | } 178 | } 179 | } 180 | 181 | /* 182 | 输出上下文, 并去除前后的换行符回车符 183 | */ 184 | func OutputContext(content string, keyword string) { 185 | index := strings.Index(content, keyword) 186 | const ( 187 | intel int = 10 188 | blank = "\r\n" 189 | ) 190 | 191 | content_len := len(content) 192 | keyword_len := len(keyword) 193 | 194 | if index != -1 { 195 | s := 0 196 | d := 0 197 | if index >= intel { 198 | s = index - intel 199 | } else { 200 | s = 0 201 | } 202 | if (content_len - index - keyword_len) >= intel { 203 | d = index + keyword_len + intel 204 | } else { 205 | d = content_len 206 | } 207 | 208 | if strings.Contains(content[s:index], blank) { 209 | s = s + strings.LastIndex(content[s:index], blank) + len(blank) 210 | } 211 | 212 | if strings.Contains(content[index+keyword_len:d], blank) { 213 | d = index + keyword_len + strings.Index(content[index+keyword_len:d], blank) 214 | } 215 | 216 | msg := content[s:index] + LogColor.GetColor("Green", keyword) + content[index+keyword_len:d] 217 | fmt.Printf("\r%s\r\n", msg) 218 | //(content[s:index] + LogColor.GetColor("Green", keyword) + content[index+keyword_len:d]) 219 | next_content := content[index+keyword_len:] 220 | OutputContext(next_content, keyword) 221 | } 222 | } 223 | 224 | /* 225 | 文件名匹配高亮输出 226 | */ 227 | func OutputFilename(filename string, keyword string) string { 228 | index := strings.Index(filename, keyword) 229 | msg := filename[:index] + LogColor.GetColor("Green", keyword) + filename[index+len(keyword):] 230 | return msg 231 | } 232 | 233 | /* 234 | 目录是否有效 235 | */ 236 | func IsValid(dirPath string) error { 237 | _, err := os.Open(dirPath) 238 | return err 239 | } 240 | 241 | /* 242 | 初始化参数 243 | */ 244 | func Init(options *config.Options) { 245 | keywords = options.Keywords 246 | extens = options.Extensions 247 | } 248 | --------------------------------------------------------------------------------