Go编程之TCP扫描

本文介绍了Go语言中的TCP扫描技术,讲解了如何使用go run, go build等命令,并探讨了如何进行端口扫描,包括基本的扫描方法、多端口扫描以及通过并发优化提高扫描效率。同时,提到了并发扫描时需要注意的问题,如限制协程数量以确保扫描结果的准确性。" 125483414,12123651,Vue面试常见问题解析,"['vue.js', '前端', 'javascript']
摘要由CSDN通过智能技术生成

常用的Go工具命令

go run

该命令将编译并执行main包

例如执行命令go run main.go 就会编译并执行main.go中的代码

go build

go run虽然编译并执行了代码但并未生成独立的二进制文件. 使用go build 命令将会编译(包括所有的包及其依赖项)并生成一个二进制文件

使用以下命令可以去除生成二进制文件中包含调试信息和字符表,以减小文件的大小

>go build -ldflags "-w -s"

在运行build命令时可以设置GOOS和GOARCH来实现交叉编译

>GOOS="linux" GOARCH="amd64" go build main.go

使用以上命令将生成64位ELF文件

go doc

使用该命令可以查询有关包,函数,方法或变量的文档

如下为查询函数fmt.Println()的详细信息

>go doc fmt.Println

package fmt // import "fmt"

func Println(a ...any) (n int, err error)
    Println formats using the default formats for its operands and writes to
    standard output. Spaces are always added between operands and a newline
    is appended. It returns the number of bytes written and any write error
    encountered.

go get

使用go get命令来下载所需要的第三方包,下载后实际的包将会存放在$GOPATH/src目录中

go fmt

go fmt命令可以自动格式化源码,它将会使用正确的换行符,缩进和大括号对齐来设置代码样式

TCP扫描

   判断端口是否开放方法如下首先客户端发送个syn数据包,该数据包表示通信开始,然后服务器以syn/ack进行响应提示客户端以ack作为结束,之 后就可以进行数据传输。如果端口关闭则服务器会以个rst数据包而不是syn/ack进 行响应。如果流量被防火墙过滤’则客户端通常不会从服务器收到任何响应.

以下是一个基本的端口扫描,仅扫描scanme.nmap.org的80端口

package main

import (
	"fmt"
	"net" //用于进行网络连接的包
)

func main() {
	_, err := net.Dial("tcp", "scanme.nmap.org:80")
//net.Dial()函数第一个参数是选择网络连接的类型,第二个参数是选择连接的主机它的格式是"host:port"
//net.Dial()函数的返回值是Conn和error,如果连接成功error的值为空
	if err == nil {//判断err的值是否为空;如果为空则打印连接成功
		fmt.Println("Connection successful")
	}
}

下面需要对上面的代码进行改进,实现对多个端口的扫描

package main

import (
	"fmt"
	"net"
)

func main() {
	for i := 1; i <= 1024; i++ {
        //使用i代表端口号;循环对1-1024的端口进行连接
		address := fmt.Sprintf("scanme.nmap.org:%d", i)
		conn, err := net.Dial("tcp", address)
		if err != nil {
			// 如果连接的端口返回错误信息,则直接继续扫描下一个端口
			continue
		}
		conn.Close() //关闭成功的连接
		fmt.Printf("%d open\n", i)//输出开放的端口号 
	}
}

上面的代码虽然实现了对多个端口的扫描,但速度太慢不能突出go的优势,需要实现多个协程共同执行以增加端口扫描的速度.代码如下:

package main

import (
	"fmt"
	"net"
	"sync" //导入用于同步的包
)

func main() {
	var wg sync.WaitGroup//定义结构体WaitGroup防止因为主goroutine结束而其他goroutine没有完成扫描就结束整个程序
	for i := 1; i <= 1024; i++ {
		wg.Add(1)//每开启一个协程,wg就加一
		go func(j int) {
			defer wg.Done()//每结束一个协程,wg就减一
			address := fmt.Sprintf("scanme.nmap.org:%d", j)
			conn, err := net.Dial("tcp", address)
			if err != nil {
				return
			}
			conn.Close()
			fmt.Printf("%d open\n", j)
		}(i)//i是匿名函数传入的参数;相当于将i赋值给j
	}
	wg.Wait()//只有当wg的值为0时意味着所有goroutine执行完毕,才能退出程序,否则进行等待
}

使用以上的代码虽然能够加快扫描的速度但是还是存在问题,当需要扫描65535个端口时则会开启65535个协程会造成扫描的结果不准确.所以需要对代码进行改进来限制协程的数量以保证结果的准确性,代码如下:

package main

import (
	"fmt"
	"net"
	"sort"
)

func worker(ports, results chan int) {
	for p := range ports {
		address := fmt.Sprintf("scanme.nmap.org:%d", p)
		conn, err := net.Dial("tcp", address)
		if err != nil {
			results <- 0 //如果端口连接失败,向result通道传入0
			continue
		}
		conn.Close()
		results <- p //端口连接成功,向result通道传入端口号
	}
}

func main() {
	ports := make(chan int, 100) //创建一个名为ports容量为100的通道
	results := make(chan int)
	var openports []int //创建一个切片用于存放开放的端口

	for i := 0; i < cap(ports); i++ {
		go worker(ports, results) //循环启动100个协程开始进行端口扫描;100个协程从ports中获取要扫描的端口号;将扫描的结果存入results通道内
	}

	go func() { //另启动一个协程,将要扫描的端口号传入ports中
		for i := 1; i <= 1024; i++ {
			ports <- i
		}
	}()

	for i := 0; i < 1024; i++ {
		port := <-results //循环获取result通道中的结果1024次;因为当result中没有数据时会发生阻塞,所以会保证接收到1024个结果,而不会出现主程序执行完毕,而其他协程没有执行完的现象,所以不需要WaitGroup结构体
		if port != 0 {
			openports = append(openports, port) //将开放的端口存放到openports切片中
		}
	}

	close(ports) //关闭通道
	close(results)
	sort.Ints(openports) //将开放的端口按顺序排序
	for _, port := range openports {
		fmt.Printf("%d open\n", port) //将开放的端口顺序打印输出
	}
}

注:该代码会将所有端口扫描完毕之后统一输出而不是扫描到结果后立即输出所以会让人感觉不如上面的代码运行快,当然,可以尝试调大worker的数量以增加扫描的速度,但worker数量太大可能会降低扫描的准确性.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
C语言是一种广泛应用于系统编程编程语言,具备强大的功能和灵活性。在C语言中,可以使用socket编程实现TCP端口扫描器来检测目标主机上的开放端口。 TCP端口扫描器是一种网络工具,用于探测目标主机上开放的TCP端口,这些开放的端口可以用来建立网络连接。TCP端口扫描器通过尝试建立TCP连接并检查连接的结果,从而确定哪些端口是开放的。 要实现TCP端口扫描器,需要借助C语言的socket库函数。首先,我们需要创建一个socket,调用socket()函数来创建一个套接字。然后,我们可以使用connect()函数来尝试连接目标主机的端口。如果连接成功,表示该端口是开放的,如果连接失败,则表示该端口是关闭或被防火墙限制。 以下是一个简单的C语言代码示例,演示了如何实现TCP端口扫描器: ```c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/socket.h> #include <arpa/inet.h> int main(int argc, char *argv[]) { if (argc != 3) { printf("Usage: %s <IP> <port>\n", argv[0]); return 1; } const char *ip = argv[1]; int port = atoi(argv[2]); struct sockaddr_in address; address.sin_family = AF_INET; address.sin_addr.s_addr = inet_addr(ip); address.sin_port = htons(port); int sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) { perror("socket creation failed"); return 1; } if (connect(sockfd, (struct sockaddr*)&address, sizeof(address)) == 0) { printf("Port %d is open\n", port); } else { printf("Port %d is closed\n", port); } close(sockfd); return 0; } ``` 以上代码中,首先检查命令行参数,确保传入了目标主机的IP地址和要扫描的端口号。然后,通过socket()函数创建一个套接字,并使用connect()函数尝试连接指定的IP地址和端口号。最后,根据连接的结果输出相应的信息,然后关闭套接字。 需要注意的是,该代码只能检测单个端口的状态。如果需要扫描多个端口,可以使用循环将上述代码包裹起来,并逐个尝试不同的端口号。 综上所述,以上是使用C语言以socket编程实现TCP端口扫描器的简单示例。通过这种方式,我们可以方便地检测目标主机上哪些端口是开放的,有助于网络安全评估和漏洞扫描等操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

m0_48984604

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值