Go语言学习#18-实现一个端口扫描器


=Start=

缘由:

用看代码、改代码的方式学习Go 语言。

正文:

参考解答:

放一个网上找到的,既容易理解,效果也不错的代码:

package main

import (
    "context"
    "fmt"
    "net"
    "os/exec"
    "strconv"
    "strings"
    "sync"
    "time"

    "golang.org/x/sync/semaphore"
)

/*
    https://medium.com/@KentGruber/building-a-high-performance-port-scanner-with-golang-9976181ec39d
*/

// 将对IP的端口扫描抽象成 PortScanner 结构体
type PortScanner struct {
    ip   string
    lock *semaphore.Weighted // 加权信号量
}

// 通过 os/exec 模块调用命令 `ulimit -n` 并将它输出作为返回
func Ulimit() int64 {
    out, err := exec.Command("ulimit", "-n").Output()
    if err != nil {
        panic(err)
    }

    s := strings.TrimSpace(string(out))

    i, err := strconv.ParseInt(s, 10, 64)
    if err != nil {
        panic(err)
    }

    return i
}

// 扫描原理的核心逻辑——使用 net.DialTimeout 进行连接尝试
func ScanPort(ip string, port int, timeout time.Duration) {
    target := fmt.Sprintf("%s:%d", ip, port)
    conn, err := net.DialTimeout("tcp", target, timeout)

    if err != nil {
        if strings.Contains(err.Error(), "too many open files") {
            time.Sleep(timeout)
            ScanPort(ip, port, timeout)
        } else {
            fmt.Println(port, "closed")
        }
        return
    }

    conn.Close()
    fmt.Println(port, "open")
}

func (ps *PortScanner) Start(f, l int, timeout time.Duration) {
    wg := sync.WaitGroup{}
    defer wg.Wait()

    for port := f; port <= l; port++ {
        ps.lock.Acquire(context.TODO(), 1) // 对每一个 ScanPort 调用都加锁处理
        wg.Add(1)
        go func(port int) {
            defer ps.lock.Release(1)
            defer wg.Done()
            ScanPort(ps.ip, port, timeout)
        }(port)
    }
}

func main() {
    start_time := time.Now()

    ps := &PortScanner{
        ip:   "127.0.0.1",
        lock: semaphore.NewWeighted(Ulimit()),
    }
    ps.Start(1, 65535, 500*time.Millisecond)

    fmt.Printf("Spend %v seconds.\n", time.Since(start_time).Seconds())
}

 

参考链接:

=END=


《“Go语言学习#18-实现一个端口扫描器”》 有 5 条评论

  1. context包定义Context上下文类型,它跨API边界和进程之间传递截止日期、取消信号和其他请求作用域值。
    即使函数允许,也不要传递nil上下文。如果还不确定要使用哪个上下文,那就先使用context.TODO。
    https://golang.org/pkg/context/#TODO

    semaphore包提供了一个加权信号量的实现
    https://www.godoc.org/golang.org/x/sync/semaphore
    https://github.com/golang/sync/blob/master/semaphore/semaphore.go

    深入理解 sync.RWMutex:解决读者-写者问题
    https://studygolang.com/articles/14760?fr=sidebar

  2. Golang安全资源合集
    https://github.com/re4lity/Hacking-With-Golang
    `
    # 扫描工具
    blacksheepwall – Go语言编写的域名信息搜集工具
    amass – Go语言编写的子域名收集工具
    vuls – Go语言编写的Linux/FreeBSD漏洞扫描器
    gryffin – 大规模Web安全扫描平台
    Gobuster – Kai下敏感目录扫描工具
    OnionScan – Go语言编写的暗网扫描仪
    x-crack – Go语言编写的弱口令扫描器
    kraken – Go语言编写的YARA跨平台扫描器

    # 网络工具
    GoReplay – Go语言编写HTTP流量记录重放工具
    NATBypass – LCX/Htran在Golang下的实现
    ngrok – 反向代理/内网穿透工具
    brook – Go语言编写的一款跨平台代理应用
    Hyperfox – HTTP/HTTPS流量监控工具
    gost – Go语言编写多功能网络代理转发工具
    gomitmproxy – Go语言实现的Mitmproxy
    `

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注