Repository: canc3s/cDomain Branch: main Commit: 33b146dddddc Files: 14 Total size: 23.0 KB Directory structure: gitextract_3rdbp39r/ ├── README.md ├── cmd/ │ └── cDomain/ │ ├── README.md │ └── cDomain.go ├── go.mod ├── go.sum └── internal/ ├── fileutil/ │ ├── doc.go │ └── fileutil.go ├── filters/ │ └── filters.go ├── gologger/ │ ├── doc.go │ └── gologger.go ├── requests/ │ └── options.go └── runner/ ├── options.go ├── request.go └── runner.go ================================================ FILE CONTENTS ================================================ ================================================ FILE: README.md ================================================ # cDomain 利用天眼查查询企业备案 [下载地址](https://github.com/canc3s/cDomain/releases) ## 介绍 可以通过两种方式查询自己想要的企业子公司 1. `-n` 参数:利用(http://beian.tianyancha.com)接口给出的关键字先进行查询。(方便,但会搜索结果受关键字影响,假如会出现一些公司名类同的公司的备案) ![image-20210301143501471](https://cdn.jsdelivr.net/gh/canc3s/picBed/img/2021/ee1775ac6ca1ba9bd4d126596b2e4707083.png) 2. `-i` 参数:利用给出的公司id对该公司进行查询。(准确,结果唯一,但需要自己先去查找一级公司,该接口目前没有限制不需要 `cookie`) 3. `-f` 参数:对文件里的所有关键字和id进行查询。因为我比较推荐用id查询,而且为了方便多次递归查询,读文件时会先去该行尝试匹配是否存在公司id,假如不存在就把该行作为关键字进行查询。因此查询可以直接把`cSubsidiary`的结果文件当作`cDomain`的输入文件。 4. 因为天眼查风控比较严格,所以使用时会出现几种情况。一、因为某个ip一段时间内查询次数过多,所以查询时会自动跳到登陆界面,这种情况需要使用一个手机号进行登陆,然后增加cookie去继续查询。二、海外ip或者云服务器ip访问天眼查会显示海外用户,所以最好使用正常的出口ip进行查询。三、假如短时间呢使用很多很多次查询的话,天眼查会有人机判断的验证码,需要手动打开天眼查网站进行一下人机验证。(情况较少) ## 用法 ``` admin@admin cSubsidiary % go run cDomain.go -h ██████╗██████╗ ██████╗ ███╗ ███╗ █████╗ ██╗███╗ ██╗ ██╔════╝██╔══██╗██╔═══██╗████╗ ████║██╔══██╗██║████╗ ██║ ██║ ██║ ██║██║ ██║██╔████╔██║███████║██║██╔██╗ ██║ ██║ ██║ ██║██║ ██║██║╚██╔╝██║██╔══██║██║██║╚██╗██║ ╚██████╗██████╔╝╚██████╔╝██║ ╚═╝ ██║██║ ██║██║██║ ╚████║ ╚═════╝╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝╚═╝ ╚═══╝ v0.0.4 https://github.com/canc3s/cDomain Usage of cDomain: -c string 天眼查的Cookie -delay int 请求之间的延迟时间(秒) -f string 包含公司ID号码的文件 -i string 公司ID号码 -n string 公司名称 -no-color No Color -o string 结果输出的文件(可选) -silent Silent mode -timeout int 连接超时时间(秒) (default 15) -verbose 详细模式 -version 显示软件版本号 ``` 查询子公司 ``` admin@admin cSubsidiary % go run cSubsidiary.go -n 字节跳动 ██████╗██████╗ ██████╗ ███╗ ███╗ █████╗ ██╗███╗ ██╗ ██╔════╝██╔══██╗██╔═══██╗████╗ ████║██╔══██╗██║████╗ ██║ ██║ ██║ ██║██║ ██║██╔████╔██║███████║██║██╔██╗ ██║ ██║ ██║ ██║██║ ██║██║╚██╔╝██║██╔══██║██║██║╚██╗██║ ╚██████╗██████╔╝╚██████╔╝██║ ╚═╝ ██║██║ ██║██║██║ ╚████║ ╚═════╝╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝╚═╝ ╚═══╝ v0.0.4 https://github.com/canc3s/cDomain [INFO] 正在查询关键字 字节跳动 [Warning] find IP : 114.246.10.65 [Warning] find IP : 119.167.189.11 dgo.ink myzijie.com toutiaocloud.net toutiaocloud.com toutiaocloud.cn bytedance.cn bytedance.org bytedance.com bytedance.net zjtdchina.cn bytecdn.cn bytedns.net bytedance.tj.cn shuiwu360.com byteimg.com bytefcdn.com video518.com jinritoutiao.js.cn eat9.cn cdndns1.com cdndns2.com bytedns2.com bytedns1.com syzjtd.com mykailu.com hzzqw.cn yxlgzs.cn nmklw.com shangtout.com ``` ## 其他 软件难免有一些问题,假如大家发现,欢迎大家提意见或者建议。 还有一个工具 `cSubsidiary` 我一般两个一起使用,参考[文章](https://canc3s.github.io/2021/03/01/cSubsidiary和cDomain使用指南/) ## Changelog * 增加请求延迟功能,防止触发反爬虫(-delay 默认为0,不开启) ================================================ FILE: cmd/cDomain/README.md ================================================ # cDomain 利用天眼查查询企业备案 [下载地址](https://github.com/canc3s/cDomain/releases) ## 介绍 可以通过两种方式查询自己想要的企业子公司 1. `-n` 参数:利用(http://beian.tianyancha.com)接口给出的关键字先进行查询。(方便,但会搜索结果受关键字影响,假如会出现一些公司名类同的公司的备案) ![image-20210301143501471](https://cdn.jsdelivr.net/gh/canc3s/picBed/img/2021/ee1775ac6ca1ba9bd4d126596b2e4707083.png) 2. `-i` 参数:利用给出的公司id对该公司进行查询。(准确,结果唯一,但需要自己先去查找一级公司,该接口目前没有限制不需要 `cookie`) 3. `-f` 参数:对文件里的所有关键字和id进行查询。因为我比较推荐用id查询,而且为了方便多次递归查询,读文件时会先去该行尝试匹配是否存在公司id,假如不存在就把该行作为关键字进行查询。因此查询可以直接把`cSubsidiary`的结果文件当作`cDomain`的输入文件。 4. 因为天眼查风控比较严格,所以使用时会出现几种情况。一、因为某个ip一段时间内查询次数过多,所以查询时会自动跳到登陆界面,这种情况需要使用一个手机号进行登陆,然后增加cookie去继续查询。二、海外ip或者云服务器ip访问天眼查会显示海外用户,所以最好使用正常的出口ip进行查询。三、假如短时间呢使用很多很多次查询的话,天眼查会有人机判断的验证码,需要手动打开天眼查网站进行一下人机验证。(情况较少) ## 用法 ``` admin@admin cSubsidiary % go run cDomain.go -h ██████╗██████╗ ██████╗ ███╗ ███╗ █████╗ ██╗███╗ ██╗ ██╔════╝██╔══██╗██╔═══██╗████╗ ████║██╔══██╗██║████╗ ██║ ██║ ██║ ██║██║ ██║██╔████╔██║███████║██║██╔██╗ ██║ ██║ ██║ ██║██║ ██║██║╚██╔╝██║██╔══██║██║██║╚██╗██║ ╚██████╗██████╔╝╚██████╔╝██║ ╚═╝ ██║██║ ██║██║██║ ╚████║ ╚═════╝╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝╚═╝ ╚═══╝ v0.0.4 https://github.com/canc3s/cDomain Usage of cDomain: -c string 天眼查的Cookie -f string 包含公司ID号码的文件 -i string 公司ID号码 -n string 公司名称 -no-color No Color -o string 结果输出的文件(可选) -silent Silent mode -timeout int 连接超时时间 (default 15) -verbose 详细模式 -version 显示软件版本号 ``` 查询子公司 ``` admin@admin cSubsidiary % go run cSubsidiary.go -n 字节跳动 ██████╗██████╗ ██████╗ ███╗ ███╗ █████╗ ██╗███╗ ██╗ ██╔════╝██╔══██╗██╔═══██╗████╗ ████║██╔══██╗██║████╗ ██║ ██║ ██║ ██║██║ ██║██╔████╔██║███████║██║██╔██╗ ██║ ██║ ██║ ██║██║ ██║██║╚██╔╝██║██╔══██║██║██║╚██╗██║ ╚██████╗██████╔╝╚██████╔╝██║ ╚═╝ ██║██║ ██║██║██║ ╚████║ ╚═════╝╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝╚═╝ ╚═══╝ v0.0.4 https://github.com/canc3s/cDomain [INFO] 正在查询关键字 字节跳动 [Warning] find IP : 114.246.10.65 [Warning] find IP : 119.167.189.11 dgo.ink myzijie.com toutiaocloud.net toutiaocloud.com toutiaocloud.cn bytedance.cn bytedance.org bytedance.com bytedance.net zjtdchina.cn bytecdn.cn bytedns.net bytedance.tj.cn shuiwu360.com byteimg.com bytefcdn.com video518.com jinritoutiao.js.cn eat9.cn cdndns1.com cdndns2.com bytedns2.com bytedns1.com syzjtd.com mykailu.com hzzqw.cn yxlgzs.cn nmklw.com shangtout.com ``` ## 其他 软件难免有一些问题,假如大家发现,欢迎大家提意见或者建议。 还有一个工具 `cSubsidiary` 我一般两个一起使用,我后面写个文章,详细写一下 ================================================ FILE: cmd/cDomain/cDomain.go ================================================ package main import ( "github.com/canc3s/cDomain/internal/runner" ) func main() { options := runner.ParseOptions() runner.RunEnumeration(options) } ================================================ FILE: go.mod ================================================ module github.com/canc3s/cDomain go 1.15 require ( github.com/antchfx/htmlquery v1.2.3 github.com/logrusorgru/aurora v2.0.3+incompatible github.com/mattn/go-colorable v0.1.8 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 ) ================================================ FILE: go.sum ================================================ github.com/antchfx/htmlquery v1.2.3 h1:sP3NFDneHx2stfNXCKbhHFo8XgNjCACnU/4AO5gWz6M= github.com/antchfx/htmlquery v1.2.3/go.mod h1:B0ABL+F5irhhMWg54ymEZinzMSi0Kt3I2if0BLYa3V0= github.com/antchfx/xpath v1.1.6 h1:6sVh6hB5T6phw1pFpHRQ+C4bd8sNI+O58flqtg7h0R0= github.com/antchfx/xpath v1.1.6/go.mod h1:Yee4kTMuNiPYJ7nSNorELQMr1J33uOpXDMByNYhvtNk= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8= github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 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-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= ================================================ FILE: internal/fileutil/doc.go ================================================ // Package fileutil contains all the funcionality related to deal with files package fileutil ================================================ FILE: internal/fileutil/fileutil.go ================================================ package fileutil import ( "bufio" "io" "os" ) // FileExists checks if a file exists and is not a directory func FileExists(filename string) bool { info, err := os.Stat(filename) if os.IsNotExist(err) { return false } return !info.IsDir() } // FolderExists checks if a folder exists func FolderExists(folderpath string) bool { _, err := os.Stat(folderpath) return !os.IsNotExist(err) } // HasStdin determines if the user has piped input func HasStdin() bool { stat, err := os.Stdin.Stat() if err != nil { return false } mode := stat.Mode() isPipedFromChrDev := (mode & os.ModeCharDevice) == 0 isPipedFromFIFO := (mode & os.ModeNamedPipe) != 0 return isPipedFromChrDev || isPipedFromFIFO } func ReadImf(input io.Reader) []string { var imf []string scanner := bufio.NewScanner(input) for scanner.Scan() { imf = append(imf,scanner.Text()) } return imf } ================================================ FILE: internal/filters/filters.go ================================================ package filters import "regexp" type Result struct { Domains []string Ips []string } func FilterIP(domains []string) Result { var results Result for _,domain := range domains { re := regexp.MustCompile(`((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)`) if re.MatchString(domain) { results.Ips = append(results.Ips , domain) } else { results.Domains = append(results.Domains , domain) } } return results } ================================================ FILE: internal/gologger/doc.go ================================================ // Package gologger contains all the funcionality package gologger ================================================ FILE: internal/gologger/gologger.go ================================================ package gologger import ( "fmt" "github.com/logrusorgru/aurora" "github.com/mattn/go-colorable" "os" "strings" "sync" ) // Level defines all the available levels we can log at type Level int // Available logging levels const ( Null Level = iota Fatal Silent Label Misc Error Warning Info Debug Verbose ) var ( // UseColors can be used to control coloring of the output UseColors = true // MaxLevel is the maximum level to log at. By default, logging // is done at Info level. Using verbose will display all the errors too, // Using silent will display only the most relevant information. MaxLevel = Info labels = map[Level]string{ Warning: "Warning", Error: "Error", Label: "WRN", Fatal: "Fatal", Debug: "DEBUG", Info: "INFO", } mutex = &sync.Mutex{} output = colorable.NewColorableStdout() ) var stringBuilderPool = &sync.Pool{New: func() interface{} { return new(strings.Builder) }} // wrap wraps a given label for a message to a logg-able representation. // It checks if colors are specified and what level we are logging at. func wrap(label string, level Level) string { // Check if we are not using colors, if not, return if !UseColors { return label } switch level { case Silent: return label case Info, Verbose: return aurora.Blue(label).String() case Fatal: return aurora.Bold(aurora.Red(label)).String() case Error: return aurora.Red(label).String() case Debug: return aurora.Magenta(label).String() case Warning, Label: return aurora.Yellow(label).String() default: return label } } // getLabel generates a label for a given message, depending on the level // and the label passed. func getLabel(level Level, label string, sb *strings.Builder) { switch level { case Silent, Misc: return case Error, Fatal, Info, Warning, Debug, Label: sb.WriteString("[") sb.WriteString(wrap(labels[level], level)) sb.WriteString("]") sb.WriteString(" ") return case Verbose: sb.WriteString("[") sb.WriteString(wrap(label, level)) sb.WriteString("]") sb.WriteString(" ") return default: return } } // log logs the actual message to the screen func log(level Level, label string, format string, args ...interface{}) { // Don't log if the level is null if level == Null { return } if level <= MaxLevel { // Build the log message using the string builder pool sb := stringBuilderPool.Get().(*strings.Builder) // Get the label and append it to string builder getLabel(level, label, sb) message := fmt.Sprintf(format, args...) sb.WriteString(message) //if strings.HasSuffix(message, "\n") == false { // sb.WriteString("\n") //} mutex.Lock() switch level { case Silent: fmt.Fprint(os.Stdout, sb.String()) default: fmt.Fprint(output, sb.String()) } mutex.Unlock() sb.Reset() stringBuilderPool.Put(sb) } } // Infof writes a info message on the screen with the default label func Infof(format string, args ...interface{}) { log(Info, "", format, args...) } // Warningf writes a warning message on the screen with the default label func Warningf(format string, args ...interface{}) { log(Warning, "", format, args...) } // Errorf writes an error message on the screen with the default label func Errorf(format string, args ...interface{}) { log(Error, "", format, args...) } // Debugf writes an error message on the screen with the default label func Debugf(format string, args ...interface{}) { log(Debug, "", format, args...) } // Verbosef writes a verbose message on the screen with a tabel func Verbosef(format string, label string, args ...interface{}) { log(Verbose, label, format, args...) } // Silentf writes a message on the stdout with no label func Silentf(format string, args ...interface{}) { log(Silent, "", format, args...) } // Fatalf exits the program if we encounter a fatal error func Fatalf(format string, args ...interface{}) { log(Fatal, "", format, args...) os.Exit(1) } // Printf prints a string on screen without any extra stuff func Printf(format string, args ...interface{}) { log(Misc, "", format, args...) } // Labelf prints a string on screen with a label interface func Labelf(format string, args ...interface{}) { log(Label, "", format, args...) } ================================================ FILE: internal/requests/options.go ================================================ package requests import ( "crypto/tls" "golang.org/x/net/html" "net" "net/http" "time" ) type Request struct { Url string Cookie string } type Response struct { Body []byte Page *html.Node } func DefaultTransport() *http.Transport { transport := &http.Transport{ Proxy: http.ProxyFromEnvironment, DialContext: (&net.Dialer{ Timeout: 30 * time.Second, KeepAlive: 30 * time.Second, }).DialContext, MaxIdleConnsPerHost: -1, TLSClientConfig: &tls.Config{ InsecureSkipVerify: true, }, DisableKeepAlives: true, } return transport } ================================================ FILE: internal/runner/options.go ================================================ package runner import ( "flag" "github.com/canc3s/cDomain/internal/fileutil" "github.com/canc3s/cDomain/internal/gologger" "os" ) const banner = ` ██████╗██████╗ ██████╗ ███╗ ███╗ █████╗ ██╗███╗ ██╗ ██╔════╝██╔══██╗██╔═══██╗████╗ ████║██╔══██╗██║████╗ ██║ ██║ ██║ ██║██║ ██║██╔████╔██║███████║██║██╔██╗ ██║ ██║ ██║ ██║██║ ██║██║╚██╔╝██║██╔══██║██║██║╚██╗██║ ╚██████╗██████╔╝╚██████╔╝██║ ╚═╝ ██║██║ ██║██║██║ ╚████║ ╚═════╝╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝╚═╝ ╚═══╝ v` // Version is the current version of C const Version = `0.0.5` type Options struct { KeyWord string CompanyID string // Target is a single URL/Domain to scan usng a template InputFile string // Targets specifies the targets to scan using templates. Cookie string Delay int Timeout int Output string // Output is the file to write found subdomains to. Silent bool NoColor bool Verbose bool Version bool // Version specifies if we should just show version and exit } func ParseOptions() *Options { options := &Options{} flag.StringVar(&options.KeyWord, "n", "", "公司名称") flag.StringVar(&options.CompanyID, "i", "", "公司ID号码") flag.StringVar(&options.InputFile, "f", "", "包含公司ID号码的文件") flag.StringVar(&options.Cookie, "c", "", "天眼查的Cookie") flag.IntVar(&options.Timeout, "timeout", 15, "连接超时时间(秒)") flag.IntVar(&options.Delay, "delay", 0, "请求之间的延迟时间(秒)") flag.StringVar(&options.Output, "o", "", "结果输出的文件(可选)") flag.BoolVar(&options.Silent, "silent", false, "Silent mode") flag.BoolVar(&options.NoColor, "no-color", false, "No Color") flag.BoolVar(&options.Verbose, "verbose", false, "详细模式") flag.BoolVar(&options.Version, "version", false, "显示软件版本号") flag.Parse() options.configureOutput() showBanner() if options.Version { gologger.Infof("Current Version: %s\n", Version) os.Exit(0) } options.validateOptions() return options } func (options *Options) validateOptions() { if options.CompanyID != "" && len(options.CompanyID) < 5 { gologger.Fatalf("公司ID %s 不正确!\n", options.CompanyID) } if options.InputFile != "" && !fileutil.FileExists(options.InputFile) { gologger.Fatalf("文件 %s 不存在!\n", options.InputFile) } if options.KeyWord == "" && options.CompanyID == "" && options.InputFile == "" { flag.PrintDefaults() os.Exit(0) } } // showBanner is used to show the banner to the user func showBanner() { gologger.Printf("%s%s\n", banner,Version) gologger.Printf("\t\thttps://github.com/canc3s/cDomain\n\n") //gologger.Labelf("请谨慎使用,您应对自己的行为负责\n") //gologger.Labelf("开发人员不承担任何责任,也不对任何滥用或损坏负责.\n") } func (options *Options) configureOutput() { // If the user desires verbose output, show verbose output if options.Verbose { gologger.MaxLevel = gologger.Verbose } if options.NoColor { gologger.UseColors = false } if options.Silent { gologger.MaxLevel = gologger.Silent } } ================================================ FILE: internal/runner/request.go ================================================ package runner import ( "github.com/antchfx/htmlquery" "github.com/canc3s/cDomain/internal/gologger" "github.com/canc3s/cDomain/internal/requests" "golang.org/x/net/html" "io/ioutil" "net/http" "strconv" "strings" "time" ) func GetPage(url string, options *Options) requests.Response { time.Sleep(time.Duration(options.Delay) * time.Second) var transport = requests.DefaultTransport() var client = &http.Client{ Transport: transport, //Timeout: time.Duration(options.Timeout), CheckRedirect: func(req *http.Request, via []*http.Request) error { return http.ErrUseLastResponse /* 不进入重定向 */ }, } req, _ := http.NewRequest("GET", url, nil) req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5026.0 Safari/537.36 Edg/103.0.1254.0") if options.Cookie != "" { req.Header.Set("Cookie", options.Cookie) } resp, err := client.Do(req) if err != nil { gologger.Fatalf("请求发生错误,请检查网络连接\n%s\n", err) } if resp.StatusCode == 403 { gologger.Fatalf("海外用户或者云服务器ip被禁止访问网站,请更换ip\n") } else if resp.StatusCode == 401 { gologger.Fatalf("天眼查Cookie有问题或过期,请重新获取\n") } else if resp.StatusCode == 302 { gologger.Fatalf("天眼查免费查询次数已用光,需要加Cookie\n") } body, _ := ioutil.ReadAll(resp.Body) page,_ := htmlquery.Parse(strings.NewReader(string(body))) return requests.Response{ Body: body, Page:page, } } func JudgePagesK(page *html.Node) int { list := htmlquery.Find(page, "/html/body/div[2]/div/div[2]/div[1]/div[2]/div[3]/ul/li/a") num := 1 if len(list) > 2 { var err error pages := htmlquery.InnerText(list[len(list)-2]) num,err = strconv.Atoi(strings.Trim(pages, ".")) if err != nil { num = 1 } } return num } func JudgePagesI(page *html.Node) int { list := htmlquery.Find(page, "/html/body/div/ul/li/a") return len(list) - 1 } func EnuDomainByKey(page *html.Node, domains *[]string) { list := htmlquery.Find(page, "/html/body/div[2]/div/div[2]/div[1]/div[2]/div[2]/table/tbody/tr/td[5]/span") for _,node := range list { domain := htmlquery.InnerText(node) *domains = append(*domains, domain) } } func GetInformation(page *html.Node) []string { list := htmlquery.Find(page, "/html/body/table/tbody/tr/td[5]") var domains []string for _,node := range list { domain := htmlquery.InnerText(node) domains = append(domains, domain) } return domains } func GetDomain(options *Options) []string { resp := GetPage("https://www.tianyancha.com/pagination/icp.xhtml?ps=30&isAjaxLoad=true&pn=1&id="+options.CompanyID, options) page := JudgePagesI(resp.Page) domains := GetInformation(resp.Page) for i := 2; i <= page; i++ { resp := GetPage("https://www.tianyancha.com/pagination/icp.xhtml?ps=30&isAjaxLoad=true&pn="+strconv.Itoa(i)+"&id="+options.CompanyID, options) domains = append(domains,GetInformation(resp.Page)...) } return domains } ================================================ FILE: internal/runner/runner.go ================================================ package runner import ( "fmt" "github.com/canc3s/cDomain/internal/fileutil" "github.com/canc3s/cDomain/internal/filters" "github.com/canc3s/cDomain/internal/gologger" "net/url" "os" "regexp" "strconv" ) type Targets struct { ID []string Name []string } func GetDomainByKey(options *Options) []string { var domains []string gologger.Infof("正在查询关键字 %s\n",options.KeyWord) url := "https://beian.tianyancha.com/search/"+ url.QueryEscape(options.KeyWord) resp := GetPage(url, options) EnuDomainByKey(resp.Page, &domains) num := JudgePagesK(resp.Page) if num > 5 && options.Cookie == "" { gologger.Errorf("域名过多,需要cookie才能完整查询\n") num = 5 } if num > 1 { for i := 2; i <= num; i++ { resp := GetPage(url+"/p"+strconv.Itoa(num), options) EnuDomainByKey(resp.Page, &domains) } } return domains } func GetDomainByID(options *Options) []string { domains := GetDomain(options) return domains } func RunEnumeration(options *Options) { var domains []string if options.InputFile != "" { fin, error := os.OpenFile(options.InputFile, os.O_RDONLY, 0) if error != nil { gologger.Fatalf("文件读取失败:%s",error) } defer fin.Close() imf := fileutil.ReadImf(fin) targets := TransImf(imf) for _,id := range targets.ID { options.CompanyID = id domains = append(domains,GetDomainByID(options)...) } for _,name := range targets.Name { options.KeyWord = name domains = append(domains,GetDomainByKey(options)...) } } else { if options.KeyWord != "" && options.CompanyID == "" { domains = GetDomainByKey(options) } else if options.CompanyID != "" { domains = GetDomainByID(options) } } results := filters.FilterIP(domains) for _,ip := range results.Ips { gologger.Warningf("find IP : %s\n",ip) } for _,domain := range results.Domains { fmt.Println(domain) } if options.Output != "" { file, err := os.OpenFile(options.Output, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if err != nil { gologger.Fatalf("结果无法写入文件:%s", err) } defer file.Close() for _,domain := range results.Domains { file.WriteString(domain+"\n") } } } func TransImf(imf []string) Targets { var targets Targets for _,i := range imf { re := regexp.MustCompile(`(\d{5,11})`) buf := re.FindStringSubmatch(i) if buf == nil { targets.Name = append(targets.Name, i) }else{ targets.ID = append(targets.ID, buf[0]) } } return targets }