Go语言实现IP段端口扫描工具教程

白榆
白榆
发布时间:2026-10-14 | 5人浏览过
原创

本教程分享一个基于Go语言开发的简易端口扫描器,旨在演示如何利用Golang的高并发特性快速检测指定IP段的开放端口。文章涵盖核心算法解析、多线程并发控制及数据持久化输出方法。

工具概述

该程序是一个命令行工具,主要功能包括生成连续的IP地址序列、对每个IP的目标端口建立TCP连接(即端口扫描),并根据扫描结果将开放的IP列表写入以当前日期命名的文本文件中。由于使用了Go语言的协程(goroutine)机制,它能够显著提升大规模IP段的扫描速度。

核心功能与逻辑

1. IP地址数值化处理

为了高效生成连续的IP段,程序首先需要将点分十进制的IP地址转换为整数,并反之亦然。这通常通过位运算完成:

  • ip2num:将IP字符串按“.”分割为四个整数,并通过左移和按位或操作合并为一个32位整数。
  • num2ip:将整数通过右移和按位与操作还原为四个八位字节,再拼接回标准的IP格式。

2. 并发扫描模型

程序不采用单线程轮询,而是启用了多个并发工作协程。扫描逻辑分为两步:

  1. 入队:根据起始和结束IP生成所有目标IP,压入全局队列中。
  2. 处理:启动多个协程,从队列中取出IP并与目标端口进行TCP握手。如果握手成功,则认为端口开放。

3. 结果存储

扫描成功后,程序会将通过的IP地址追加写入到本地文件。文件名遵循 日期_端口_ok.txt 的格式,例如 2023-10-27_80_ok.txt。

完整代码解析

以下是该工具的完整源代码。请注意,原代码中存在部分变量命名不规范和并发竞争条件(Race Condition)的隐患,此处保持原汁原味展示其逻辑结构,建议在实际生产环境中优化锁机制。

package main

import (
	"container/list"
	"fmt"
	"net"
	"os"
	"strconv"
	"strings"
	"time"
)

// ip2num: 将IP地址转换为整型值
func ip2num(ip string) int {
	canSplit := func(c rune) bool { return c == '.' }
	lisit := strings.FieldsFunc(ip, canSplit)
	ip1_str_int, _ := strconv.Atoi(lisit[0])
	ip2_str_int, _ := strconv.Atoi(lisit[1])
	ip3_str_int, _ := strconv.Atoi(lisit[2])
	ip4_str_int, _ := strconv.Atoi(lisit[3])
	return ip1_str_int<<24 | ip2_str_int<<16 | ip3_str_int<<8 | ip4_str_int
}

// num2ip: 将整型值转换为IP地址
func num2ip(num int) string {
	ip1_int := (num & 0xff000000) >> 24
	ip2_int := (num & 0x00ff0000) >> 16
	ip3_int := (num & 0x0000ff00) >> 8
	ip4_int := num & 0x000000ff
	data := fmt.Sprintf("%d.%d.%d.%d", ip1_int, ip2_int, ip3_int, ip4_int)
	return data
}

var l = list.New()

// gen_ip: 生成IP段并入队
func gen_ip(Aip1 int, Aip2 int) {
	index := Aip1
	for index < Aip2 {
		ip_data := num2ip(index)
		l.PushBack(ip_data)
		index++
	}
}

// socket_ip: 尝试TCP连接判断端口状态
func socket_ip(host string, port string) bool {
	remote := host + ":" + port
	tcpAddr, _ := net.ResolveTCPAddr("tcp4", remote)
	conn, err := net.DialTCP("tcp", nil, tcpAddr)
	if err != nil {
		fmt.Printf("no==%s:%s\r\n", host, port)
		return false
	}
	defer conn.Close()
	fmt.Printf("ok==%s:%s\r\n", host, port)
	return true
}

// text_add: 向文件中追加数据
func text_add(name string, data string) {
	f, err := os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0x644)
	if err != nil {
		panic(err)
	}
	defer f.Close()
	_, err = f.WriteString(data)
	_, err = f.WriteString("\r\n")
	if err != nil {
		panic(err)
	}
}

// for_ip: 协程入口,持续出队扫描
func for_ip(port string) {
	now := time.Now()
	year, mon, day := now.UTC().Date()
	file_name := fmt.Sprintf("%d-%d-%d_%s", year, mon, day, port)
	
	for {
		if l.Len() <= 0 {
			break
		}
		i1 := l.Front()
		l.Remove(i1)
		IP, _ := i1.Value.(string)
		
		if socket_ip(IP, port) {
			text_add(file_name+"_ok.txt", IP)
		}
		time.Sleep(time.Millisecond * 500)
	}
}

func main() {
	argsLen := len(os.Args)
	if argsLen != 4 {
		fmt.Println("用法: main.go 起始IP 结束IP 端口号")
		fmt.Println("示例: main.go 58.215.20.30 58.215.201.30 80")
	} else {
		// 生成IP队列
		gen_ip(ip2num(os.Args[1]), ip2num(os.Args[2]))
		
		// 启动200个并发协程
		for index := 0; index < 200; index++ {
			go for_ip(os.Args[3])
		}
		
		// 主进程休眠防止退出
		for {
			time.Sleep(1 * time.Second)
		}
	}
}

使用方法

编译并运行该程序时,需要通过命令行传递三个参数:起始IP、结束IP 和 扫描端口。程序将自动计算中间的IP数量并按顺序发起连接请求。

  1. 确保系统已安装 Go 环境。
  2. 保存上述代码至 main.go。
  3. 执行编译命令:go build main.go
  4. 运行工具:./main 192.168.1.1 192.168.1.10 22

完成后,在当前目录下查找类似 202X-X-X_22_ok.txt 的文件,即可看到扫描成功的IP列表。

以上就是Go语言实现IP段端口扫描工具的详细内容,更多关于Golang网络编程与安全审计的资料请关注本站其它相关文章!

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表