

手把手教你用Go语言打造一款简易TCP端口扫描器
source link: http://developer.51cto.com/art/202101/639962.htm
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

前言
Hey,大家好呀,我是码农,星期八。
这次呢, 咱们来实现一个简单的TCP端口扫描器!
也来体验一下黑客的风采!
TCP扫描本质
我们在使用TCP进行连接时,需要知道对方机器的ip:port
正常握手
连接成功的话,流程如下。
连接失败
有正常,就有失败,如果被连接方关闭的话,流程如下。
如果有防火墙
还有一种可能是,端口开放,但是防火墙拦截,流程如下。
代码
本质理解之后,就可以开始撸代码了。
在Go中,我们通常使用net.Dial进行TCP连接。
它就两种情况
成功:返回conn。
失败:err != nil。
普通版
相对来说,刚开始时,我们可能都不是太胆大,都是先写原型,也不考虑性能。
代码
package mainimport ( "fmt" "net")func main() { var ip = "192.168.43.34" for i := 21; i <= 120; i++ { var address = fmt.Sprintf("%s:%d", ip, i) conn, err := net.Dial("tcp", address) if err != nil { fmt.Println(address, "是关闭的") continue } conn.Close() fmt.Println(address, "打开") }}
执行结果
但是这个过程是非常缓慢的。
因为net.Dial如果连接的是未开放的端口,一个端口可能就是20s+,所以,我们为什么学习多线程懂了把!!!
多线程版
上述是通过循环去一个个连接ip:port的,那我们就知道了,在一个个连接的位置,让多个人去干就好了。
所以,多线程如下。
代码
package mainimport ( "fmt" "net" "sync" "time")func main() { var begin =time.Now() //wg var wg sync.WaitGroup //ip var ip = "192.168.99.112" //var ip = "192.168.43.34" //循环 for j := 21; j <= 65535; j++ { //添加wg wg.Add(1) go func(i int) { //释放wg defer wg.Done() var address = fmt.Sprintf("%s:%d", ip, i) //conn, err := net.DialTimeout("tcp", address, time.Second*10) conn, err := net.Dial("tcp", address) if err != nil { //fmt.Println(address, "是关闭的", err) return } conn.Close() fmt.Println(address, "打开") }(j)} //等待wg wg.Wait() var elapseTime = time.Now().Sub(begin) fmt.Println("耗时:", elapseTime)}
执行结果
其实是同时开启了6W多个线程,去扫描每个ip:port。
所以耗时最长的线程结束的时间,就是程序结束的时间。
感觉还行,20s+扫描完6w多个端口!!!
线程池版
上面我们简单粗暴的方式为每个ip:port都创建了一个协程。
虽然在Go中,理论上协程开个几十万个都没问题,但是还是有一些压力的。
所以我们应该采用一种相对节约的方式进行精简代码,一般采用线程池方式。
本次使用的线程池包:gohive
地址:https://github.com/loveleshsharma/gohive
简单介绍
代码
package main//线程池方式import ( "fmt" "github.com/loveleshsharma/gohive" "net" "sync" "time")//wgvar wg sync.WaitGroup//地址管道,100容量var addressChan = make(chan string, 100)//工人func worker() { //函数结束释放连接 defer wg.Done() for { address, ok := <-addressChan if !ok { break } //fmt.Println("address:", address) conn, err := net.Dial("tcp", address) //conn, err := net.DialTimeout("tcp", address, 10) if err != nil { //fmt.Println("close:", address, err) continue } conn.Close() fmt.Println("open:", address)}}func main() { var begin = time.Now() //ip var ip = "192.168.99.112" //线程池大小 var pool_size = 70000 var pool = gohive.NewFixedSizePool(pool_size) //拼接ip:端口 //启动一个线程,用于生成ip:port,并且存放到地址管道种 go func() { for port := 1; port <= 65535; port++ { var address = fmt.Sprintf("%s:%d", ip, port) //将address添加到地址管道 //fmt.Println("<-:",address) addressChan <- address } //发送完关闭 addressChan 管道 close(addressChan)}() //启动pool_size工人,处理addressChan种的每个地址 for work := 0; work < pool_size; work++ { wg.Add(1) pool.Submit(worker)} //等待结束 wg.Wait() //计算时间 var elapseTime = time.Now().Sub(begin) fmt.Println("耗时:", elapseTime)}
执行结果
我设置的线程池大小是7w个,所以也是一下子开启6w多个协程的,但是我们已经可以进行线程大小约束了。
假设现在有这样的去求,有100个ip,需要扫描每个ip开放的端口,如果采用简单粗暴开线程的方式.
那就是100+65535=6552300,600多w个线程,还是比较消耗内存的,可能系统就会崩溃,如果采用线程池方式。
将线程池控制在50w个,或许情况就会好很多。
但是有一点的是,在Go中,线程池通常需要配合chan使用,可能需要不错的基础。
总结
本篇更偏向于乐趣篇,了解一下好玩的玩意。
其实还可以通过net.DialTimeout连接ip:port,这个可以设置超时时间,比如超时5s就判定端口未开放。
此处就不做举例了。
咱们主要使用三种方式来实现功能。
正常版,没有并发,速度很慢。
多协程版,并发,性能很高,但是协程太多可能会崩溃。
协程池版,并发,性能高,协程数量可控。
通常情况下,如果基础可以,更推荐使用协程池方式。
如果在操作过程中有任何问题,记得下面留言,我们看到会第一时间解决问题。
用微笑告诉别人,今天的我比昨天强,今后也一样。
Recommend
-
54
手把手教你用R语言分析歌词(附代码)
-
61
Loki:一款简单强大的IOC和事件响应扫描器
-
70
Sitadel实际上是WAScan的升级版,不过是Python版本(>= 3.4)的,这样有助于研究人员根据自己的需要去进行自定义开发,并引入新的功能模块。...
-
48
很多安全研究人员都会面临这样一个问题,那就是如何在不安装代理或软件包的情况下,检查100多个不同的Linux系统是否匹配某些入侵威胁指标IoC。...
-
20
BlueScan BlueScan是一款功能强大的蓝牙扫描器,广大研究人员可以使用BlueScan来扫描BR/LE设备、LMP、SDP、GATT以及相关的蓝...
-
9
Jack Jiang 我的最新工程MobileIMSDK:http://git.oschina.net/jackjiang/MobileIMSDK posts - 225, comments - 13, trackbacks - 0, articles - 0...
-
7
本文作者芋艿,原题“使用 Netty 实现 IM 聊天贼简单”,本底价有修订和改动。一、本文引言上篇《跟着源码学IM(七):手把手教你用WebSocket打造Web...
-
10
为被动扫描器量身打造一款爬虫-LSpider ...
-
21
发表日期:2018-11-12 仅需10步,手把手教你用树莓派打造 Python 开发服务器 —— 基本上一台普通的 Linux 电脑能做的事情,树莓派也能完成。今天这篇教程玩的可能会比较“高级”一丁点,我们将手把手带你用树莓派打造一个你自己...
-
4
摘要:本文由葡萄城技术团队于博客园原创并首发。转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具、解决方案和服务,赋能开发者。 迷你图(Mini Chart)最早起源于流程图和组织...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK