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