使用go写一个高性能端口扫描器,支持IP范围,端口号范围

功能:可以快速扫描指定端口范围,ip地址范围。将扫描结果保存到本地!先来体验一下运行后的效果:

image.png
image.png

帮助信息

scanPort -h 
Options:
  -h    帮助信息
  -ip string
        ip地址 例如:-ip 192.168.0.1-255 或直接输入域名 xs25.cn (default "127.0.0.1")
  -n int
        进程数 例如:-n 10 (default 100)
  -p string
        端口号范围 例如:-p 80,81,88-1000 (default "80")
  -path string
        日志地址 例如:-path log (default "log")
  -t int
        超时时长(毫秒) 例如:-t 200 (default 200)

例1:指定端口号扫描,如我们要扫描xs25.cn这台服务的开放端口,使用1000个协程进行

scanport -p 80,81,88-3306 -ip xs25.cn -n 1000

例2:指定IP范围扫描,如我们扫描 192.168.0.1-255 网段的端口 80-10000

scanPort -ip 192.168.0.1-255 -p 80-10000

注:程序扫描完后开放端口放在log目录中,如想更改目录名请加 -path 参数来指定

具体代码如下

详细代码见:https://github.com/xs25cn/scanPort
main.go 文件:

package main
import (
    "flag"
    "fmt"
    "os"
    "github.com/xs25cn/scanPort/lib"
    "github.com/xs25cn/scanPort/scan"
    "time"
)

var (
    startTime = time.Now()
    ip        = flag.String("ip", "127.0.0.1", "ip地址 例如:-ip=192.168.0.1-255 或直接输入域名 xs25.cn")
    port      = flag.String("p", "80-1000", "端口号范围 例如:-p=80,81,88-1000")
    path      = flag.String("path", "log", "日志地址 例如:-path=log")
    timeout   = flag.Int("t", 200, "超时时长(毫秒) 例如:-t=200")
    process   = flag.Int("n", 100, "进程数 例如:-n=10")
    h         = flag.Bool("h", false, "帮助信息")
)
//go run main.go -h
func main() {
    flag.Parse()
    //帮助信息
    if *h == true {
        lib.Usage("scanPort version: scanPort/1.10.0\n Usage: scanPort [-h] [-ip ip地址] [-n 进程数] [-p 端口号范围] [-t 超时时长] [-path 日志保存路径]\n\nOptions:\n")
        return
    }

    fmt.Printf("========== Start %v ip:%v,port:%v ==================== \n", time.Now().Format("2006-01-02 15:04:05"), *ip, *port)

    //创建目录
    lib.Mkdir(*path)

    //初始化
    scanIP := scan.ScanIp{
        Debug:   true,
        Timeout: *timeout,
        Process: *process,
    }
    ips, err := scanIP.GetAllIp(*ip)
    if err != nil {
        fmt.Println(err.Error())
        return
    }
    //扫所有的ip
    fileName := *path + "/" + *ip + "_port.txt"
    for i := 0; i < len(ips); i++ {
        ports := scanIP.GetIpOpenPort(ips[i], *port)
        if len(ports) > 0 {
            f, err := os.OpenFile(fileName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
            if err != nil {
                if err := f.Close(); err != nil {
                    fmt.Println(err)
                }
                continue
            }
            var str = fmt.Sprintf("%v ip:%v,开放端口:%v \n", time.Now().Format("2006-01-02 15:04:05"), ips[i], ports)
            if _, err := f.WriteString(str); err != nil {
                if err := f.Close(); err != nil {
                    fmt.Println(err)
                }
                continue
            }
        }
    }
    fmt.Printf("========== End %v 总执行时长:%.2fs ================ \n", time.Now().Format("2006-01-02 15:04:05"), time.Since(startTime).Seconds())

}

scan.go 文件

package scan

import (
    "errors"
    "fmt"
    "math"
    "net"
    "os"
    "strconv"
    "strings"
    "sync"
    "time"
)

//ip 扫描
type ScanIp struct {
    Debug   bool
    Timeout int
    Process int
}

//获取开放端口号
func (s *ScanIp) GetIpOpenPort(ip string, port string) []int {
    var (
        total     int
        pageCount int
        num       int
        openPorts []int
        mutex     sync.Mutex
    )
    ports, _ := s.getAllPort(port)
    total = len(ports)
    if total < s.Process {
        pageCount = total
    } else {
        pageCount = s.Process
    }
    num = int(math.Ceil(float64(total) / float64(pageCount)))

    s.sendLog(fmt.Sprintf("%v 【%v】需要扫描端口总数:%v 个,总协程:%v 个,每个协程处理:%v 个,超时时间:%v毫秒", time.Now().Format("2006-01-02 15:04:05"), ip, total, pageCount, num, s.Timeout))
    start := time.Now()
    all := map[int][]int{}
    for i := 1; i <= pageCount; i++ {
        for j := 0; j < num; j++ {
            tmp := (i-1)*num + j
            if tmp < total {
                all[i] = append(all[i], ports[tmp])
            }
        }
    }

    wg := sync.WaitGroup{}
    for k, v := range all {
        wg.Add(1)
        go func(value []int, key int) {
            defer wg.Done()
            var tmpPorts []int
            for i := 0; i < len(value); i++ {
                opened := s.isOpen(ip, value[i])
                if opened {
                    tmpPorts = append(tmpPorts, value[i])
                }
            }
            mutex.Lock()
            openPorts = append(openPorts, tmpPorts...)
            mutex.Unlock()
            if len(tmpPorts) > 0 {
                s.sendLog(fmt.Sprintf("%v 【%v】协程%v 执行完成,时长: %.3fs,开放端口: %v", time.Now().Format("2006-01-02 15:04:05"), ip, key, time.Since(start).Seconds(), tmpPorts))
            }
        }(v, k)
    }
    wg.Wait()

    s.sendLog(fmt.Sprintf("%v 【%v】扫描结束,执行时长%.3fs , 所有开放的端口:%v", time.Now().Format("2006-01-02 15:04:05"), ip, time.Since(start).Seconds(), openPorts))
    time.Sleep(time.Second * 1)
    return openPorts
}

//获取所有ip
func (s *ScanIp) GetAllIp(ip string) ([]string, error) {
    var (
        ips []string
    )

    ipTmp := strings.Split(ip, "-")
    firstIp, err := net.ResolveIPAddr("ip", ipTmp[0])
    if err != nil {
        return ips, errors.New(ipTmp[0] + "域名解析失败" + err.Error())
    }
    if net.ParseIP(firstIp.String()) == nil {
        return ips, errors.New(ipTmp[0] + " ip地址有误~")
    }
    //域名转化成ip再塞回去
    ipTmp[0] = firstIp.String()
    ips = append(ips, ipTmp[0]) //最少有一个ip地址

    if len(ipTmp) == 2 {
        //以切割第一段ip取到最后一位
        ipTmp2 := strings.Split(ipTmp[0], ".")
        startIp, _ := strconv.Atoi(ipTmp2[3])
        endIp, err := strconv.Atoi(ipTmp[1])
        if err != nil || endIp < startIp {
            endIp = startIp
        }
        if endIp > 255 {
            endIp = 255
        }
        totalIp := endIp - startIp + 1
        for i := 1; i < totalIp; i++ {
            ips = append(ips, fmt.Sprintf("%s.%s.%s.%d", ipTmp2[0], ipTmp2[1], ipTmp2[2], startIp+i))
        }
    }
    return ips, nil
}



//记录日志
func (s *ScanIp) sendLog(str string) {
    if s.Debug == true {
        fmt.Println(str)
    }
}


//获取所有端口
func (s *ScanIp) getAllPort(port string) ([]int, error) {
    var ports []int
    //处理 ","号 如 80,81,88 或 80,88-100
    portArr := strings.Split(strings.Trim(port, ","), ",")
    for _, v := range portArr {
        portArr2 := strings.Split(strings.Trim(v, "-"), "-")
        startPort, err := s.filterPort(portArr2[0])
        if err != nil {
            continue
        }
        //第一个端口先添加
        ports = append(ports, startPort)
        if len(portArr2) > 1 {
            //添加第一个后面的所有端口
            endPort, _ := s.filterPort(portArr2[1])
            if endPort > startPort {
                for i := 1; i <= endPort-startPort; i++ {
                    ports = append(ports, startPort+i)
                }
            }
        }
    }
    //去重复
    ports = s.arrayUnique(ports)

    return ports, nil
}

//端口合法性过滤
func (s *ScanIp) filterPort(str string) (int, error) {
    port, err := strconv.Atoi(str)
    if err != nil {
        return 0, err
    }
    if port < 1 || port > 65535 {
        return 0, errors.New("端口号范围超出")
    }
    return port, nil
}



//查看端口号是否打开
func (s *ScanIp) isOpen(ip string, port int) bool {
    conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", ip, port), time.Millisecond*time.Duration(s.Timeout))
    if err != nil {
        if strings.Contains(err.Error(),"too many open files"){
            fmt.Println("连接数超出系统限制!"+err.Error())
            os.Exit(1)
        }
        return false
    }
    _ = conn.Close()
    return true
}

//数组去重
func (s *ScanIp) arrayUnique(arr []int) []int {
    var newArr []int
    for i := 0; i < len(arr); i++ {
        repeat := false
        for j := i + 1; j < len(arr); j++ {
            if arr[i] == arr[j] {
                repeat = true
                break
            }
        }
        if !repeat {
            newArr = append(newArr, arr[i])
        }
    }
    return newArr
}

详细代码见:https://github.com/xs25cn/scanPort

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,444评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,421评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,363评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,460评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,502评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,511评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,280评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,736评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,014评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,190评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,848评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,531评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,159评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,411评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,067评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,078评论 2 352

推荐阅读更多精彩内容