分析某款go端口扫描器之三

这篇具有很好参考价值的文章主要介绍了分析某款go端口扫描器之三。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、概述

前两篇主要分析些工具集,已经针对web服务的指纹和端口指纹信息进行识别,并没有真正开始扫描。本篇主要分析如何进行IP存活探测以及tcp扫描实现。

项目来源:https://github.com/XinRoom/go-portScan/blob/main/util/file.go

二、/core/host/ 目录(进行主机的ping探测存活)

/core/host/ping.go

此代码主要是用来进行主机存活的。

首先设置判断变量和常见端口列表

var CanIcmp bool // 用于标识是否支持发送 ICMP 包。
var TcpPingPorts = []uint16{80, 22, 445, 23, 443, 81, 161, 3389, 8080, 8081}//是用于 TCP Ping 的默认常见端口列表。

函数列表分析:

  • func IcmpOK(host string) bool 

函数尝试直接发送 ICMP 包来检查主机是否存活。

// IcmpOK 直接发ICMP包
func IcmpOK(host string) bool {
	pinger, err := ping.NewPinger(host)
	if err != nil {
		return false
	}
	pinger.SetPrivileged(true)
	pinger.Count = 1
	pinger.Timeout = 800 * time.Millisecond
	if pinger.Run() != nil { // Blocks until finished. return err
		return false
	}
	if stats := pinger.Statistics(); stats.PacketsRecv > 0 {
		return true
	}
	return false
}
  • func PingOk(host string) bool

函数尝试通过执行不同操作系统的 Ping 命令来检查主机是否存活。

// PingOk Ping命令模式
func PingOk(host string) bool {
	switch runtime.GOOS {
	case "linux":
		cmd := exec.Command("ping", "-c", "1", "-W", "1", host)
		var out bytes.Buffer
		cmd.Stdout = &out
		cmd.Run()
		if strings.Contains(out.String(), "ttl=") {
			return true
		}
	case "windows":
		cmd := exec.Command("ping", "-n", "1", "-w", "500", host)
		var out bytes.Buffer
		cmd.Stdout = &out
		cmd.Run()
		if strings.Contains(out.String(), "TTL=") {
			return true
		}
	case "darwin":
		cmd := exec.Command("ping", "-c", "1", "-t", "1", host)
		var out bytes.Buffer
		cmd.Stdout = &out
		cmd.Run()
		if strings.Contains(out.String(), "ttl=") {
			return true
		}
	}
	return false
}
  • func TcpPing(host string, ports []uint16, timeout time.Duration) (ok bool)

函数使用 TCP 连接在指定端口上对主机进行存活探测,在连接成功或者连接被拒绝时,都会判断主机为存活状态。

func TcpPing(host string, ports []uint16, timeout time.Duration) (ok bool) {
	var wg sync.WaitGroup              // 创建一个 WaitGroup 用于等待所有端口的探测完成
	ctx, cancel := context.WithCancel(context.Background()) // 创建一个上下文和取消函数
	d := net.Dialer{                   // 创建一个 Dialer,用于建立 TCP 连接
		Timeout:   timeout + time.Second, // 设置连接超时时间
		KeepAlive: 0,                    // 禁用 KeepAlive
	}
	for _, port := range ports {      // 遍历端口列表
		time.Sleep(10 * time.Millisecond) // 间隔一段时间再探测下一个端口,避免过于频繁的连接
		wg.Add(1)                     // 每个端口探测前增加 WaitGroup 计数
		go func(_port uint16) {       // 并发进行端口探测
			conn, err := d.DialContext(ctx, "tcp", fmt.Sprintf("%s:%d", host, _port)) // 尝试建立 TCP 连接
			if conn != nil {          // 如果成功建立连接
				conn.Close()          // 关闭连接
				ok = true             // 设置存活状态为 true
			} else if err != nil && strings.Contains(err.Error(), "refused it") { // 如果连接被拒绝
				ok = true             // 设置存活状态为 true
			}
			if ok {                   // 如果已经确认存活
				cancel()              // 取消其他端口的探测
			}
			wg.Done()                 // 完成当前端口的探测
		}(_port)                      // 传入端口号进行端口探测
	}
	wg.Wait()                        // 等待所有端口的探测完成
	return                            // 返回存活状态
}
  • func init()

函数在包加载时运行,它尝试在本地(127.0.0.1)发送 ICMP 包来检查系统是否支持 ICMP。

func init() {
	if IcmpOK("127.0.0.1") {
		CanIcmp = true
	}
}
  • func IsLive(ip string, tcpPing bool, tcpTimeout time.Duration) (ok bool)

IsLive 函数是用来检查主机是否存活的主要函数。如果支持 ICMP,则使用 IcmpOK 函数;否则,使用 PingOk 函数。如果前面的检查未能确定主机存活且 tcpPing 参数为真,则尝试使用 TcpPing 函数进行 TCP 存活探测。

func IsLive(ip string, tcpPing bool, tcpTimeout time.Duration) (ok bool) {
	if CanIcmp {
		ok = IcmpOK(ip)
	} else {
		ok = PingOk(ip)
	}
	if !ok && tcpPing {
		ok = TcpPing(ip, TcpPingPorts, tcpTimeout)
	}
	return
}

ps:总体而言,这些函数提供了多种方法来检查主机是否存活,包括使用 ICMP 包、执行系统命令(如 Ping 命令)以及 TCP 连接到常见端口。这样做可以在不同系统和网络环境下更全面地检查主机的存活状态

三、port端口解析,以及tcp扫描

1、/core/port/port.go

这个文件代码主要包含了端口扫描相关的逻辑和数据结构。

  • var TopTcpPorts = []uint16{} 常见端口列表

分析某款go端口扫描器之三,golang,网络,服务器,golang,开发语言,后端

  • 各结构体列表
type Scanner interface {//这是一个接口,定义了扫描器的操作,包括关闭、等待、扫描指定 IP 和端口,以及等待速率限制器。
	Close()
	Wait()
	Scan(ip net.IP, dst uint16) error
	WaitLimiter() error
}

// OpenIpPort retChan,这个结构体表示一个开放的 IP 和端口,包含 IP 地址、端口号、服务名和可能的 HTTP 信息。
type OpenIpPort struct {
	Ip       net.IP
	Port     uint16
	Service  string
	HttpInfo *HttpInfo
}


// Option ...这个结构体包含了扫描的参数配置,如速率限制、超时时间、网卡信息和是否进行服务探测等。
type Option struct {
	Rate        int    // 每秒速度限制, 单位: s, 会在1s内平均发送, 相当于每个包之间的延迟
	Timeout     int    // TCP连接响应延迟, 单位: ms
	NextHop     string // pcap dev name
	FingerPrint bool   // 服务探测
	Httpx       bool   // HttpInfo 探测
}

// HttpInfo Http服务基础信息,这个结构体存储了 HTTP 服务的基础信息,包括状态码、响应包大小、URL、重定向路径、标题、服务名、TLS 信息和 Web 指纹
type HttpInfo struct {
	StatusCode int      // 状态码
	ContentLen int      // 相应包大小
	Url        string   // Url
	Location   string   // 302、301重定向路径
	Title      string   // 标题
	Server     string   // 服务名
	TlsCN      string   // tls使用者名称
	TlsDNS     []string // tlsDNS列表
	Fingers    []string // 识别到的web指纹
}
  • func (op OpenIpPort) String() string

这个函数是为了实现 Stringer 接口,这个接口包含一个 String() 方法,用于定义该类型的字符串表示形式。对于 OpenIpPort 类型的实例,这个函数会返回其 IP 地址、端口号以及可能的服务信息的字符串表示形式

func (op OpenIpPort) String() string {
	buf := strings.Builder{}
	buf.WriteString(op.Ip.String())
	buf.WriteString(":")
	buf.WriteString(strconv.Itoa(int(op.Port)))
	if op.Service != "" {
		buf.WriteString(" ")
		buf.WriteString(op.Service)
	}
	if op.HttpInfo != nil {
		buf.WriteString("\n")
		buf.WriteString(op.HttpInfo.String())
	}
	return buf.String()
}
  • func (hi *HttpInfo) String() string

这个函数也是实现 Stringer 接口的方法。对于 HttpInfo 类型的实例,这个函数返回其包含的 HTTP 信息的字符串表示形式,包括状态码、响应包大小、URL、重定向路径、标题、服务名和 Web 指纹等。

func (hi *HttpInfo) String() string {
	if hi == nil {
		return ""
	}
	var buf strings.Builder
	buf.WriteString(fmt.Sprintf("[HttpInfo]%s StatusCode:%d ContentLen:%d Title:%s ", hi.Url, hi.StatusCode, hi.ContentLen, hi.Title))
	if hi.Location != "" {
		buf.WriteString("Location:" + hi.Location + " ")
	}
	if hi.TlsCN != "" {
		buf.WriteString("TlsCN:" + hi.TlsCN + " ")
	}
	if len(hi.TlsDNS) > 0 {
		buf.WriteString("TlsDNS:" + strings.Join(hi.TlsDNS, ",") + " ")
	}
	if hi.Server != "" {
		buf.WriteString("Server:" + hi.Server + " ")
	}
	if len(hi.Fingers) != 0 {
		buf.WriteString(fmt.Sprintf("Fingers:%s ", hi.Fingers))
	}
	return buf.String()
}
  • func ParsePortRangeStr(portStr string) (out [][]uint16, err error)

这个函数用于解析端口字符串,将其转换为端口范围的列表。

// ParsePortRangeStr 解析端口字符串
func ParsePortRangeStr(portStr string) (out [][]uint16, err error) {
	portsStrGroup := strings.Split(portStr, ",")//逗号分隔的端口
	var portsStrGroup3 []string
	var portStart, portEnd uint64
	for _, portsStrGroup2 := range portsStrGroup {
		if portsStrGroup2 == "top1000" {
			continue
		}
		portsStrGroup3 = strings.Split(portsStrGroup2, "-")//-作为端口范围的
		portStart, err = strconv.ParseUint(portsStrGroup3[0], 10, 16)//返回端口的整数值,10进制,类型为int16
		if err != nil {
			return
		}
		portEnd = portStart
		if len(portsStrGroup3) == 2 {
			portEnd, err = strconv.ParseUint(portsStrGroup3[1], 10, 16)
		}
		if err != nil {
			return
		}
		out = append(out, []uint16{uint16(portStart), uint16(portEnd)})
	}
	return
}
  • func IsInPortRange(port uint16, portRanges [][]uint16) bool

这个函数用于检查指定端口是否在端口范围内

// IsInPortRange 判断port是否在端口范围里
func IsInPortRange(port uint16, portRanges [][]uint16) bool {
	for _, portRange := range portRanges {
		if port >= portRange[0] && port <= portRange[1] {
			return true
		}
	}
	return false
}
  • func ShuffleParseAndMergeTopPorts(portStr string) (ports []uint16, err error)

这个函数主要实现了对端口的解析、合并和随机化处理。它会解析传入的端口字符串,根据配置信息选取一些端口,优先使用常见的 TCP 端口,然后从用户指定的端口范围中选择未被选取的端口,并最终随机排序这些端口

// ShuffleParseAndMergeTopPorts shuffle parse portStr and merge TopTcpPorts
func ShuffleParseAndMergeTopPorts(portStr string) (ports []uint16, err error) {
	if portStr == "" {
		ports = TopTcpPorts //未指定则用默认top端口
		return
	}
	var portRanges [][]uint16
	portRanges, err = ParsePortRangeStr(portStr)
	if err != nil {
		return
	}
	// 优先发送top端口
	selectTopPort := make(map[uint16]struct{}) // TopPort
	hasTopStr := strings.Contains(portStr, "top1000")
	for _, _port := range TopTcpPorts {
		if hasTopStr || IsInPortRange(_port, portRanges) { //检测端口是否在范围内
			selectTopPort[_port] = struct{}{}
			ports = append(ports, _port)
		}
	}
	selectPort := make(map[uint16]struct{}) // OtherPort
	for _, portRange := range portRanges {
		var ok bool
		for _port := portRange[0]; _port <= portRange[1]; _port++ {
			if _port == 0 {
				continue
			}
			if _, ok = selectTopPort[_port]; ok {
				continue
			} else if _, ok = selectPort[_port]; ok {
				continue
			}
			selectPort[_port] = struct{}{}
			ports = append(ports, _port) //得到所有端口,并将top端口排在前面
			if _port == 65535 {
				break
			}
		}
	}
	if len(ports) == 0 {
		err = errors.New("ports len is 0")
		return
	}
	// 端口随机化
	skip := uint64(len(selectTopPort)) // 跳过Top
	_ports := make([]uint16, len(ports))
	copy(_ports, ports)
	sf := util.NewShuffle(uint64(len(ports)) - skip)
	if sf != nil {
		for i := skip; i < uint64(len(_ports)); i++ {
			ports[i] = _ports[skip+sf.Get(i-skip)]
		}
	}
	return
}

ps:这些函数主要提供了对端口进行解析、筛选和随机化的功能,以便用于端口扫描和服务探测。它将常见的 TCP 端口列表与用户输入的端口范围合并,随机化排序以减少扫描的可预测性。

2、/core/port/tcp/tcp.go

整体来说,这个代码文件定义了一个 TCP 端口扫描器,可以根据指定的 IP 地址和端口号对目标进行扫描,并可选地进行服务探测和 HTTP 信息探测。同时,它实现了速率限制以及 goroutine 的管理,确保扫描操作的安全性和效率。

var DefaultTcpOption = port.Option{//这里定义了默认的 TCP 扫描选项,包括扫描速率和超时时间等。
	Rate:    1000,
	Timeout: 800,
}

type TcpScanner struct {//管理 TCP 端口扫描器的状态和操作。结构体中包含了需要的字段和方法。
	ports   []uint16             // 指定端口
	retChan chan port.OpenIpPort // 返回值队列
	limiter *limiter.Limiter
	ctx     context.Context
	timeout time.Duration
	isDone  bool
	option  port.Option
	wg      sync.WaitGroup
}
  • func NewTcpScanner(retChan chan port.OpenIpPort, option port.Option) (ts *TcpScanner, err error)

这个函数是一个构造器,用于创建一个新的 TCP 扫描器实例。它接收一个返回值通道 retChan 和扫描选项 option,并返回一个 TcpScanner 的指针。函数首先对传入的选项进行验证,确保速率大于等于 10,超时时间大于 0。然后,它初始化了一个 TcpScanner 结构体实例,设置了返回通道、速率限制器、上下文、超时时间和其他选项,并将该实例赋值给 ts,最后返回该实例和可能的错误。

// NewTcpScanner Tcp扫描器
func NewTcpScanner(retChan chan port.OpenIpPort, option port.Option) (ts *TcpScanner, err error) {
	// option verify
	if option.Rate < 10 {
		err = errors.New("rate can not be set less than 10") // 如果速率小于 10,则返回错误
		return
	}
	if option.Timeout <= 0 {
		err = errors.New("timeout can not be set to 0") // 如果超时时间小于等于 0,则返回错误
		return
	}

	// 初始化 TcpScanner 结构体
	ts = &TcpScanner{
		retChan: retChan, // 设置返回通道
		limiter: limiter.NewLimiter(limiter.Every(time.Second/time.Duration(option.Rate)), option.Rate/10), // 设置速率限制器
		ctx:     context.Background(), // 初始化上下文
		timeout: time.Duration(option.Timeout) * time.Millisecond, // 设置超时时间
		option:  option, // 设置选项
	}

	return // 返回 TcpScanner 实例和可能的错误
}
  • func (ts *TcpScanner) Scan(ip net.IP, dst uint16) error

这个函数用于执行对指定 IP 和目标端口进行扫描的操作。它会启动一个 goroutine,在其中进行端口扫描并将结果发送到 retChan 通道中。函数首先检查扫描器是否已关闭,然后将一个任务添加到等待组 wg 中。接着,它初始化了一个 port.OpenIpPort 结构体实例,表示正在扫描的 IP 和端口。接下来的部分涉及服务指纹识别和 HTTP 信息探测的逻辑。如果设置了相应的选项,它将调用相关的函数进行识别并填充 openIpPort 结构体中的信息。最后,如果没有进行服务指纹识别或者 HTTP 信息探测,它将尝试通过 net.DialTimeout 进行连接,判断端口是否开放,并将结果发送到通道中。

// Scan 对指定IP和dis port进行扫描
func (ts *TcpScanner) Scan(ip net.IP, dst uint16) error {
	if ts.isDone {
		return errors.New("scanner is closed") // 如果扫描器已关闭,则返回错误
	}
	ts.wg.Add(1) // 增加等待组中的任务数
	go func() {
		defer ts.wg.Done() // 标记任务结束
		//fmt.Println(1)
		openIpPort := port.OpenIpPort{
			Ip:   ip,
			Port: dst,
		}
		var isDailErr bool
		if ts.option.FingerPrint {
			openIpPort.Service, isDailErr = fingerprint.PortIdentify("tcp", ip, dst, 2*time.Second) // 进行服务指纹识别
			if isDailErr {
				return // 如果识别过程出错,直接返回
			}
		}
		if ts.option.Httpx && (openIpPort.Service == "" || openIpPort.Service == "http" || openIpPort.Service == "https") {
			openIpPort.HttpInfo, isDailErr = fingerprint.ProbeHttpInfo(ip, dst, 2*time.Second) // 进行 HTTP 信息探测
			if isDailErr {
				return // 如果探测过程出错,直接返回
			}
			if openIpPort.HttpInfo != nil {
				if strings.HasPrefix(openIpPort.HttpInfo.Url, "https") {
					openIpPort.Service = "https" // 如果是 HTTPS,则标记为 HTTPS 服务
				} else {
					openIpPort.Service = "http" // 如果是 HTTP,则标记为 HTTP 服务
				}
			}
		}
		if !ts.option.FingerPrint && !ts.option.Httpx {
			conn, _ := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", ip, dst), ts.timeout) // 尝试连接端口
			if conn != nil {
				conn.Close() // 如果连接成功,则关闭连接
			} else {
				return // 如果连接失败,直接返回
			}
		}
		ts.retChan <- openIpPort // 将扫描结果发送到通道
	}()
	return nil
}
  • 辅助函数

func (ts *TcpScanner) Wait()

func (ts *TcpScanner) Close()

func (ts *TcpScanner) WaitLimiter() error文章来源地址https://www.toymoban.com/news/detail-758028.html

//这个方法用于等待所有启动的 goroutine 完成扫描操作。它会等待 wg 等待组中的所有 goroutine 完成。
func (ts *TcpScanner) Wait() {
	ts.wg.Wait()
}

// Close chan这个方法用于关闭 retChan 通道,表示扫描已经完成。它还会设置 isDone 标志,表示扫描器已关闭
func (ts *TcpScanner) Close() {
	ts.isDone = true
	close(ts.retChan)
}

// WaitLimiter Waiting for the speed limit这个方法用于等待速率限制器。它会通过 limiter 控制扫描的速率,以确保按照设定的速率发送扫描请求
func (ts *TcpScanner) WaitLimiter() error {
	return ts.limiter.Wait(ts.ctx)
}

到了这里,关于分析某款go端口扫描器之三的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • python-nmap库使用教程(Nmap网络扫描器的Python接口)(功能:主机发现、端口扫描、操作系统识别等)

    Python-nmap是一个Python库,用于帮助进行网络扫描。它是Nmap网络扫描器的Python接口,允许开发者从Python脚本中直接调用Nmap的功能。这篇文章将详细介绍如何使用python-nmap库来执行各种网络扫描任务。 在开始之前,需要确保安装了Nmap和python-nmap库。可以通过以下命令安装Nmap: 对

    2024年02月21日
    浏览(47)
  • 了解Portscan扫描器

    Portscan端口扫描器程序可以帮助用户寻找到当前网络上正在运行的所有设备。Portscan端口扫描器其拥有200多个线程,是企业用户扫描ip地址的首选扫描程序。 一、使用Portscan扫描器扫描网站 1.1.双击“PortScan.exe”图标。如图1所示 图1 1.2.Portscan扫描器界面。如图2所示 图2 1.3.选择

    2024年02月12日
    浏览(56)
  • 漏洞扫描器-AWVS

    漏洞扫描是指基于漏洞数据库,通过扫描等手段对指定的远程或者本地计算机系统的安全脆弱性进行检测,发现可利用漏洞的一种安全检测(渗透攻击)行为。 ✓ 针对某类漏洞的:sql注入(sqlmap)、weblogic(weblogicscan) ✓ 针对某类CMS的:wordpress(wpscan)、dedecms(dedecmsscan)

    2024年02月13日
    浏览(66)
  • Nmap网络扫描器实验

    网络入侵的一般流程是确定目标、信息收集、漏洞挖掘、实施攻击、留下后门、清除日志。在信息收集环节又可分为网络踩点、网络扫描和网络查点。在网络扫描部分,按照攻击者的入侵流程又可分为主机扫描、端口扫描、系统类型探测和漏洞扫描。 网络扫描是针对整个目标

    2024年02月03日
    浏览(57)
  • 开源 LLM 安全扫描器

    Vigil 是一款开源安全扫描程序,可检测即时注入、越狱以及对大型语言模型(LLM) 的其他潜在威胁。 当攻击者使用专门设计的输入成功影响 LLM 时,就会出现即时注入。这导致 LLM 无意中实现了攻击者设定的目标。 ​ 我对 LLM 的可能性感到非常兴奋,但也注意到围绕它们构建的

    2024年01月18日
    浏览(65)
  • wordpres漏洞扫描器——wpscan

    使用PHP语言开发的博客平台 WordPress是使用PHP语言开发的博客平台,用户可以在支持PHP和MySQL数据库的服务器上架设属于自己的网站。也可以把 WordPress当作一个内容管理系统(CMS)来使用。 WordPress是一款个人博客系统,并逐步演化成一款内容管理系统软件,它是使用PHP语言和

    2023年04月13日
    浏览(49)
  • Nessus: 漏洞扫描器-网络取证工具

    要理解网络漏洞攻击,应该理解攻击者不是单独攻击,而是组合攻击。因此,本文介绍了关于Nessus历史的研究,它是什么以及它如何与插件一起工作。研究了Nessus的特点,使其成为网络取证中非常推荐的网络漏洞扫描工具。本文还介绍了如何下载Nessus以及所涉及的步骤。使用

    2023年04月16日
    浏览(52)
  • 关于漏洞扫描器AWVS的安装和破解

    下载AVWS的压缩包 解压压缩包,解压后会有下面三个文件!acunetix_14.5.211115146_x64.sh license_info.json wa_data.dat 使用下面命令进行安装  按回车同意隐私条款,到末尾输入yes,然后输入用户名和密码以及邮箱。 接下来浏览器输入https://IP:3443/ 用刚才设置的账号和密码登录 接下来开始

    2024年02月12日
    浏览(46)
  • 轻量级网络IP扫描器WatchYourLAN

    什么是 WatchYourLAN ? WatchYourLAN 是一款带有 Web GUI 的轻量级网络 IP 扫描器。支持使用不同的主题和色彩模式进行个性化设置。 扫描网络,首先要找到对应的网络接口,一般常见的包括 eth0 、 lo 、 docker0 等,可以在 SSH 客户端命令行,执行下面的命令 但是目前 WatchYourLAN 还不支

    2024年02月06日
    浏览(71)
  • 青云扫描器(QingScan):智能安全检测的新星

    项目地址:https://gitcode.com/78778443/QingScan 青云扫描器(QingScan)是一个开源的安全扫描工具,旨在帮助开发者和企业发现并修复应用程序中的安全漏洞。该项目由78778443开发并维护,采用先进的自动化技术,可以对Web应用、移动应用以及服务器进行全方位的安全检查。 QingScan的核

    2024年04月12日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包