diff --git a/.gitignore b/.gitignore index 139ae32..52afcbc 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,5 @@ # Dependency directories (remove the comment below to include it) # vendor/ +.idea/ bin/ \ No newline at end of file diff --git a/cmd/spray.go b/cmd/spray.go index 4c362d7..6fbd653 100644 --- a/cmd/spray.go +++ b/cmd/spray.go @@ -1,21 +1,24 @@ package main import ( - "flag" + "fmt" "github.com/chainreactors/logs" "github.com/chainreactors/spray/internal" + "github.com/jessevdk/go-flags" ) func main() { var runner internal.Runner - flag.StringVar(&runner.URL, "u", "", "url") - flag.StringVar(&runner.URLFile, "U", "", "url filename") - flag.StringVar(&runner.WordFile, "w", "", "wordlist filename") - flag.StringVar(&runner.OutputFile, "f", "", "output filename") - flag.BoolVar(&runner.Debug, "debug", false, "print debug info") - flag.Parse() + parser := flags.NewParser(&runner, flags.Default) + _, err := parser.Parse() + if err != nil { + if err.(*flags.Error).Type != flags.ErrHelp { + fmt.Println(err.Error()) + } + return + } - err := runner.Prepare() + err = runner.Prepare() if err != nil { logs.Log.Errorf(err.Error()) return diff --git a/go.mod b/go.mod index ddde37c..4e15729 100644 --- a/go.mod +++ b/go.mod @@ -3,16 +3,27 @@ module github.com/chainreactors/spray go 1.17 require ( - github.com/chainreactors/gogo/v2 v2.8.4 - github.com/chainreactors/logs v0.4.2 + github.com/chainreactors/gogo/v2 v2.8.5 + github.com/chainreactors/logs v0.5.0 github.com/go-dedup/simhash v0.0.0-20170904020510-9ecaca7b509c github.com/panjf2000/ants/v2 v2.5.0 ) +require github.com/projectdiscovery/goflags v0.0.9 + require ( github.com/chainreactors/files v0.2.0 // indirect github.com/chainreactors/ipcs v0.0.9 // indirect + github.com/chainreactors/words v0.0.1 // indirect + github.com/cnf/structhash v0.0.0-20201127153200-e1b16c1ebc08 // indirect github.com/go-dedup/megophone v0.0.0-20170830025436-f01be21026f5 // indirect github.com/go-dedup/text v0.0.0-20170907015346-8bb1b95e3cb7 // indirect + github.com/jessevdk/go-flags v1.5.0 // indirect + github.com/karrick/godirwalk v1.16.1 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/projectdiscovery/fileutil v0.0.0-20210928100737-cab279c5d4b5 // indirect + github.com/projectdiscovery/stringsutil v0.0.0-20210804142656-fd3c28dbaafe // indirect github.com/twmb/murmur3 v1.1.6 // indirect + golang.org/x/sys v0.0.0-20220209214540-3681064d5158 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index a78f1d9..83b9f49 100644 --- a/go.sum +++ b/go.sum @@ -5,10 +5,17 @@ github.com/chainreactors/files v0.2.0 h1:LeN97o4VxIvK9ZACjXfdRTR+N7puXuWyQO5GarC github.com/chainreactors/files v0.2.0/go.mod h1:/Xa9YXhjBlaC33JTD6ZTJFig6pcplak2IDcovf42/6A= github.com/chainreactors/gogo/v2 v2.8.4 h1:jwV6tawOcUn/FM91WxxOp7ZQN/Msm0losAx5230QgL4= github.com/chainreactors/gogo/v2 v2.8.4/go.mod h1:hoNawrwZtkIciwApI6kzu88LLOGOJEqtegcHBuiPyPw= +github.com/chainreactors/gogo/v2 v2.8.5 h1:7DfAQdyupToU4f+OYG+Hz4+0phyz3UGRrsAyTmX9Fm0= +github.com/chainreactors/gogo/v2 v2.8.5/go.mod h1:SqlLDXXN/WgjVHpRIjB8OytRmHnj8l4Ic+h7pe/4wsY= github.com/chainreactors/ipcs v0.0.9 h1:4Onroq7gXLG5SLCCgNDx3JmtLxB4XgepGdHCtLp1Ows= github.com/chainreactors/ipcs v0.0.9/go.mod h1:E9M3Ohyq0TYQLlV4i2dbM9ThBZB1Nnd7Oexoie2xLII= -github.com/chainreactors/logs v0.4.2 h1:MYRmkvwTBkxWi+QKq9sMqMC0UgtUCHPEicnf4GGN7wg= github.com/chainreactors/logs v0.4.2/go.mod h1:Y0EtAnoF0kiASIJUnXN0pcOt420iRpHOAnOhEphzRHA= +github.com/chainreactors/logs v0.5.0 h1:6CwTR1KaqZS0quIQRU3Ceq/gBoV0+seUF8gUJTrZvew= +github.com/chainreactors/logs v0.5.0/go.mod h1:Y0EtAnoF0kiASIJUnXN0pcOt420iRpHOAnOhEphzRHA= +github.com/chainreactors/words v0.0.1 h1:d0rLwuKVzwLWD1CtYeTK7u2E5tv8dmebpq6EhUtNhhg= +github.com/chainreactors/words v0.0.1/go.mod h1:QIWX1vMT5j/Mp9zx3/wgZh3FqskhjCbo/3Ffy/Hxj9w= +github.com/cnf/structhash v0.0.0-20201127153200-e1b16c1ebc08 h1:ox2F0PSMlrAAiAdknSRMDrAr8mfxPCfSZolH+/qQnyQ= +github.com/cnf/structhash v0.0.0-20201127153200-e1b16c1ebc08/go.mod h1:pCxVEbcm3AMg7ejXyorUXi6HQCzOIBf7zEDVPtw0/U4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -20,18 +27,33 @@ github.com/go-dedup/text v0.0.0-20170907015346-8bb1b95e3cb7 h1:11wFcswN+37U+Byjx github.com/go-dedup/text v0.0.0-20170907015346-8bb1b95e3cb7/go.mod h1:wSsK4VOECOSfSYTzkBFw+iGY7wj59e7X96ABtNj9aCQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc= +github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= +github.com/karrick/godirwalk v1.16.1 h1:DynhcF+bztK8gooS0+NDJFrdNZjJ3gzVzC545UNA9iw= +github.com/karrick/godirwalk v1.16.1/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= github.com/mdlayher/arp v0.0.0-20220512170110-6706a2966875/go.mod h1:kfOoFJuHWp76v1RgZCb9/gVUc7XdY877S2uVYbNliGc= github.com/mdlayher/ethernet v0.0.0-20220221185849-529eae5b6118/go.mod h1:ZFUnHIVchZ9lJoWoEGUg8Q3M4U8aNNWA3CVSUTkW4og= github.com/mdlayher/packet v1.0.0/go.mod h1:eE7/ctqDhoiRhQ44ko5JZU2zxB88g+JH/6jmnjzPjOU= github.com/mdlayher/socket v0.2.1/go.mod h1:QLlNPkFR88mRUNQIzRBMfXxwKal8H7u1h3bL1CV+f0E= github.com/panjf2000/ants/v2 v2.5.0 h1:1rWGWSnxCsQBga+nQbA4/iY6VMeNoOIAM0ZWh9u3q2Q= github.com/panjf2000/ants/v2 v2.5.0/go.mod h1:cU93usDlihJZ5CfRGNDYsiBYvoilLvBF5Qp/BT2GNRE= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/projectdiscovery/fileutil v0.0.0-20210928100737-cab279c5d4b5 h1:2dbm7UhrAKnccZttr78CAmG768sSCd+MBn4ayLVDeqA= +github.com/projectdiscovery/fileutil v0.0.0-20210928100737-cab279c5d4b5/go.mod h1:U+QCpQnX8o2N2w0VUGyAzjM3yBAe4BKedVElxiImsx0= +github.com/projectdiscovery/goflags v0.0.9 h1:bPsYIPE1LvdgYaM3XNX0YmS68e6huv22W22rKh5IscI= +github.com/projectdiscovery/goflags v0.0.9/go.mod h1:t/dEhv2VDOzayugXZCkbkX8n+pPeVmRD+WgQRSgReeI= +github.com/projectdiscovery/stringsutil v0.0.0-20210804142656-fd3c28dbaafe h1:tQTgf5XLBgZbkJDPtnV3SfdP9tzz5ZWeDBwv8WhnH9Q= +github.com/projectdiscovery/stringsutil v0.0.0-20210804142656-fd3c28dbaafe/go.mod h1:oTRc18WBv9t6BpaN9XBY+QmG28PUpsyDzRht56Qf49I= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg= github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -42,13 +64,17 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220209214540-3681064d5158 h1:rm+CHSpPEEW2IsXUib1ThaHIjuBVZjxNgSKmBLFfD4c= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/internal/baseline.go b/internal/baseline.go index 4e250ce..9cf40db 100644 --- a/internal/baseline.go +++ b/internal/baseline.go @@ -3,7 +3,6 @@ package internal import ( "encoding/json" "fmt" - gogoutil "github.com/chainreactors/gogo/v2/pkg" "github.com/chainreactors/gogo/v2/pkg/dsl" "github.com/chainreactors/logs" "github.com/chainreactors/spray/pkg" @@ -63,6 +62,7 @@ func NewBaseline(u *url.URL, resp *http.Response) *baseline { // todo extract // todo 指纹识别 + bl.Frameworks = pkg.FingerDetect(bl.Body) return bl } @@ -84,20 +84,20 @@ func NewInvalidBaseline(u *url.URL, resp *http.Response) *baseline { } type baseline struct { - Url *url.URL `json:"-"` - UrlString string `json:"url_string"` - Body []byte `json:"-"` - BodyLength int64 `json:"body_length"` - Header string `json:"-"` - HeaderLength int `json:"header_length"` - RedirectURL string `json:"redirect_url"` - Status int `json:"status"` - Md5 string `json:"md5"` - Mmh3 string `json:"mmh3"` - Simhash string `json:"simhash"` - IsDynamicUrl bool `json:"is_dynamic_url"` // 判断是否存在动态的url - Spended int `json:"spended"` // 耗时, 毫秒 - Frameworks gogoutil.Frameworks `json:"frameworks"` + Url *url.URL `json:"-"` + UrlString string `json:"url_string"` + Body []byte `json:"-"` + BodyLength int64 `json:"body_length"` + Header string `json:"-"` + HeaderLength int `json:"header_length"` + RedirectURL string `json:"redirect_url"` + Status int `json:"status"` + Md5 string `json:"md5"` + Mmh3 string `json:"mmh3"` + Simhash string `json:"simhash"` + IsDynamicUrl bool `json:"is_dynamic_url"` // 判断是否存在动态的url + Spended int `json:"spended"` // 耗时, 毫秒 + Frameworks pkg.Frameworks `json:"frameworks"` Err error `json:"-"` IsValid bool `json:"-"` diff --git a/internal/pool.go b/internal/pool.go index 48e34c3..1089219 100644 --- a/internal/pool.go +++ b/internal/pool.go @@ -4,9 +4,11 @@ import ( "context" "fmt" "github.com/chainreactors/spray/pkg" + "github.com/chainreactors/words" "github.com/panjf2000/ants/v2" "net/http" "sync" + "time" ) var ( @@ -15,18 +17,19 @@ var ( CheckWaf func(*http.Response) bool ) -func NewPool(config *pkg.Config, outputCh chan *baseline) (*Pool, error) { - var ctx context.Context +func NewPool(ctx context.Context, config *pkg.Config, outputCh chan *baseline) (*Pool, error) { err := config.Init() if err != nil { return nil, fmt.Errorf("pool init failed, %w", err) } - //ctx, cancel := context.WithCancel(nil) + poolctx, cancel := context.WithCancel(ctx) + pool := &Pool{ Config: config, //ctx: ctx, client: pkg.NewClient(config.Thread, 2), + worder: words.NewWorder(config.Wordlist), //baseReq: req, outputCh: outputCh, wg: &sync.WaitGroup{}, @@ -47,15 +50,17 @@ func NewPool(config *pkg.Config, outputCh chan *baseline) (*Pool, error) { var bl *baseline unit := i.(*Unit) req := pool.genReq(unit.path) - resp, err := pool.client.Do(ctx, req) + resp, err := pool.client.Do(poolctx, req) if err != nil { //logs.Log.Debugf("%s request error, %s", strurl, err.Error()) pool.errorCount++ bl = &baseline{Err: err} } else { - if pool.PreCompare(resp) { + if err = pool.PreCompare(resp); err == nil { // 通过预对比跳过一些无用数据, 减少性能消耗 bl = NewBaseline(req.URL, resp) + } else if err == ErrWaf { + cancel() } else { bl = NewInvalidBaseline(req.URL, resp) } @@ -91,7 +96,8 @@ type Pool struct { errorCount int genReq func(string) *http.Request //wordlist []string - wg *sync.WaitGroup + worder *words.Worder + wg *sync.WaitGroup } func (p *Pool) Add(u *Unit) error { @@ -136,28 +142,41 @@ func (p *Pool) Init() error { return nil } -func (p *Pool) Run() { - for _, u := range p.Wordlist { - p.totalCount++ - _ = p.Add(newUnit(u, WordSource)) +func (p *Pool) Run(ctx context.Context) { + +Loop: + for { + select { + case u, ok := <-p.worder.C: + if !ok { + break Loop + } + p.totalCount++ + _ = p.Add(newUnit(u, WordSource)) + case <-time.NewTimer(time.Duration(p.DeadlineTime)).C: + break Loop + case <-ctx.Done(): + break Loop + } } + p.wg.Wait() } -func (p *Pool) PreCompare(resp *http.Response) bool { +func (p *Pool) PreCompare(resp *http.Response) error { if !CheckStatusCode(resp.StatusCode) { - return false + return ErrBadStatus } if CheckRedirect != nil && !CheckRedirect(resp) { - return false + return ErrRedirect } if CheckWaf != nil && !CheckWaf(resp) { - return false + return ErrWaf } - return true + return nil } func (p *Pool) RunWithWord(words []string) { diff --git a/internal/runner.go b/internal/runner.go index f227977..946ebf0 100644 --- a/internal/runner.go +++ b/internal/runner.go @@ -1,6 +1,7 @@ package internal import ( + "context" "fmt" "github.com/chainreactors/logs" "github.com/chainreactors/spray/pkg" @@ -15,21 +16,21 @@ var BlackStatus = []int{404, 410} var FuzzyStatus = []int{403, 500, 501, 503} type Runner struct { - URL string - URLFile string + URL string `short:"u" long:"url"` + URLFile string `short:"l" long:"list"` URLList []string - WordFile string + WordFile string `short:"w" long:"work"` Wordlist []string - Headers http.Header - OutputFile string - Offset int - Limit int - Threads int - PoolSize int + Headers http.Header `long:"header"` + OutputFile string `short:"f"` + Offset int `long:"offset"` + Limit int `long:"limit"` + Threads int `short:"t" long:"thread" default:"20"` + PoolSize int `short:"p" long:"pool"` Pools map[string]*Pool - Deadline int // todo 总的超时时间,适配云函数的deadline - Debug bool - Mod string + Deadline int `long:"deadline"` // todo 总的超时时间,适配云函数的deadline + Debug bool `long:"debug"` + Mod string `short:"m" long:"mod" default:"path"` OutputCh chan *baseline } @@ -104,6 +105,7 @@ func (r *Runner) Prepare() error { func (r *Runner) Run() { // todo pool 结束与并发控制 + ctx := context.Background() var wg sync.WaitGroup for _, u := range r.URLList { wg.Add(1) @@ -115,8 +117,9 @@ func (r *Runner) Run() { Thread: r.Threads, Timeout: 2, Headers: r.Headers, + Mod: pkg.ModMap[r.Mod], } - pool, err := NewPool(config, r.OutputCh) + pool, err := NewPool(ctx, config, r.OutputCh) if err != nil { logs.Log.Error(err.Error()) return @@ -129,7 +132,7 @@ func (r *Runner) Run() { } r.Pools[u] = pool // todo pool 总超时时间 - pool.Run() + pool.Run(ctx) wg.Done() }() } diff --git a/internal/types.go b/internal/types.go new file mode 100644 index 0000000..d0f5c60 --- /dev/null +++ b/internal/types.go @@ -0,0 +1,22 @@ +package internal + +type ErrorType uint + +const ( + ErrBadStatus ErrorType = iota + ErrWaf + ErrRedirect +) + +func (e ErrorType) Error() string { + switch e { + case ErrBadStatus: + return "bad status" + case ErrWaf: + return "maybe ban of waf" + case ErrRedirect: + return "duplicate redirect url" + default: + return "unknown error" + } +} diff --git a/pkg/config.go b/pkg/config.go index fccadcc..04537b3 100644 --- a/pkg/config.go +++ b/pkg/config.go @@ -13,15 +13,21 @@ const ( CustomSpray ) +var ModMap = map[string]SprayMod{ + "path": PathSpray, + "host": HostSpray, +} + type Config struct { - BaseURL string - Wordlist []string - Thread int - Timeout int - BaseReq *http.Request - Method string - Mod SprayMod - Headers http.Header + BaseURL string + Wordlist []string + Thread int + Timeout int + BaseReq *http.Request + Method string + Mod SprayMod + Headers http.Header + DeadlineTime int } func (c *Config) Init() (err error) { diff --git a/pkg/utils.go b/pkg/utils.go index 2879c1b..85d5033 100644 --- a/pkg/utils.go +++ b/pkg/utils.go @@ -2,9 +2,11 @@ package pkg import ( "fmt" + "github.com/chainreactors/gogo/v2/pkg/fingers" "github.com/go-dedup/simhash" "math/rand" "os" + "strings" "time" "unsafe" ) @@ -57,3 +59,27 @@ func RandPath() string { } return *(*string)(unsafe.Pointer(&b)) } + +var Fingers fingers.Fingers + +func FingerDetect(body []byte) Frameworks { + var frames Frameworks + content := string(body) + for _, finger := range Fingers { + frame, _, ok := fingers.FingerMatcher(finger, content, 0, nil) + if ok { + frames = append(frames, frame) + } + } + return frames +} + +type Frameworks []*fingers.Framework + +func (fs Frameworks) ToString() string { + frameworkStrs := make([]string, len(fs)) + for i, f := range fs { + frameworkStrs[i] = "[" + f.ToString() + "]" + } + return strings.Join(frameworkStrs, " ") +}