go-Portscan 源码学习
项目介绍
项目地址:https://github.com/XinRoom/go-portScan
一款端口扫描方面很全面的工具:
- 主机验活
- 端口扫描
- 端口指纹识别
- WEB指纹识别
项目结构
1 |
|
主机验活
core/host/ping.go
这里验活用的是 3 种方式:
1 |
|
- 使用
github.com/go-ping/ping
包发送 ICMP 进行主机验活
1 |
|
- 执行系统
ping
命令
1 |
|
- 对常见的端口进行 TCP 连接
1 |
|
这里即使用了 ping 包又使用了 ping 命令可能是 ping 包有一定的使用限制:
Linux 要启用 sudo sysctl -w net.ipv4.ping_group_range="0 2147483647"
的,不是都直接可以通用了的,所以又添加了一个执行 ping 命令的函数。
然后还有一个 TCP 连接常见端口的:
1 |
|
这里是因为防火墙可以设置为禁 ping 默认的,所以这里就使用 TCP 连接下常见端口进行验活。
不过这种方式也是不能够完全 OK 的,所以感觉可以简化一下,或者直接对我们获取的 IP 进行全端口扫描就行了。
端口扫描
基础知识
SYN
端口扫描这里是有 2 种方式一种 SYN 还有就是 TCP ,TCP 就是使用 net.DialTimeout
去连接,这里就不细看了,主要是 SYN 。
TCP SYN 端口扫描就是所谓的半连接,TCP 连接需要进行 3 次握手,四次挥手。

这里的 SYN 扫描就是客户端发送一个 SYN ,然后只要服务端响应了 SYN+ACK 就证明其端口开放,只后就响应一个 RST 表示关闭连接。也就是这样:
使用 tcp 连接去判断端口开放就需要等待,而 syn 扫描的宗旨就是不去等待,也就是所谓了”无状态”,发送 syn 包的线程就一直发送,另起一个线程去监听响应。以此来达到快速端口扫描的目的,而到底多块其实就取决于 syn 发包的速度了,这里简单记录下 masscan 为什么能号称 “6分钟扫描全网” ,使用 syn 只是一点,更重要的其实就是 PF_RING 驱动了,它发包速率快( 1000万/秒 ),自然扫的快了。而 pcap 最高才 150万/秒。
因为 SYN 半连接扫描的特性,端口扫描的速度也就只是受发包速率的影响了,可以看下 gomasscan 的介绍,了解下发包速率的差距。
协议介绍
1 |
|
再看一下具体的 SYN 包:
1 |
|
响应的 SYN + ACK:
1 |
|
可以看到,如果需要去构造一个 SYN 请求需要这些字段:
- 源 MAC、目标 MAC
- 源 IP、目标 IP
- 源端口、目标端口
- SEQ
其他字段都可以去固定,或者生成,只有这些是需要我们指定的。
目标 MAC 的获取是需要 ARP ,简单介绍一下,这个协议是用来寻找 IP 地址对应的 MAC 地址的,注意是内网 IP 对应的 MAC ~
对应公网 IP 我们的目的 MAC 就是我们网卡的 MAC 地址 ~
1 |
|
接下来就看 go-Portscan 是怎样实现这些的吧。
SYN 扫描
1 |
|
先整体过一遍代码,再去按照流程进行分析。
device.go
就是去获取网卡信息,源 IP、MAC、网关 MAC、网卡名称
也对应了上面说的 SYN 请求包需要的一些信息
1 |
|
然后就是 syn.go
这是 SYN 扫描的主逻辑:
NewSynScanner
这里就是先获取下设备的基础信息:
1 |
|
然后把这些信息传入 SynScanner,因为这些基本每个 SYN 包都要使用,所以先获取了。
然后就是设置过滤器,启动监听协程了:
1 |
|
监听这里:
- ARP 响应:获取其 MAC 地址,存入 MAC 表中
- ACK 响应:先看 IP 表,是否有,是否监听超时了,如果没有就记录下来,再响应其一个 RST 报文
1 |
|
然后看看发包:
1 |
|
它这里的发包使用了一个 buf 池,这个比较好,复用
1 |
|
在看一下它的 ARP 获取 MAC 的:
1 |
|
整体的 SYN 端口扫描看完了,其主要流程:
- 获取设备基础信息 => 源 MAC、IP ….
- 启动监听协程
- ARP => 添加到 ARP 表中
- ACK => 先判断端口范围,再判断是否在 IP 监视表中是否超时等待,再去获取开放端口,然后就是响应 RST 报文
- 发包协程
- 通过 ARP 获取目标 MAC 地址
- 构造 SYN 进行发包
指纹识别
指纹识别在 fingerprint 包,先看下端口的指纹识别:
1 |
|
先看下规则的样子,端口的指纹识别和 WEB 的其实差不多,都是通过判断响应中是否有匹配的字段。
这里是端口默认对应的服务,在端口识别的时候优先判断对应的服务会快很多,而不需要全部规则都判断。
1 |
|
端口的规则:
1 |
|
这里可以看出端口指纹的规则和 WEB 还是有不同之处的,有些协议需要特定的请求才行。
这些规则来自于 nmap :raw.githubusercontent.com/nmap/nmap/master/nmap-service-probes
在看看具体是怎么识别的:
1 |
|
具体流程:
- 先获取该端口的默认服务,然后使用 matchRule 进行服务判断,记录下现在匹配的服务
- 匹配一些不需要发送特定请求的服务,使用 net.DialTimeout 建立连接,匹配哪些服务的规则 记录下现在匹配的服务
- 然后去判断 TOP 服务,先看下这个服务之前是否匹配过了,没匹配过的再使用 matchRule 进行服务判断,记录下现在匹配的服务
- 到最后就是遍历所有的规则,匹配之前没匹配过的服务了
它进行服务匹配使用的是 matchRule 具体来看一看,它主要做到就是建立 TCP 连接 然后遍历规则进行判断,需要特定请求的就发送特定的请求,之后使用 matchRuleWhithBuf 函数进行规则匹配。
1 |
|
matchRuleWhithBuf 这里对规则替换,对数据转 utf-8 码后进行正则匹配 还有包含
1 |
|
要转 UTF-8 再匹配是因为这个:Golang 的字符编码与 regexp (seebug.org)
之后是一个 WEB 指纹识别了;
指纹是这样的:
1 |
|
通过正则或者关键字包含来判断:
1 |
|
其他的和之前读过的 WEB 指纹识别差不多,就不再细写了。
学习总结
go-Portscan 在端口扫描方面做到很全,读一遍对端口扫描即端口指纹识别都有一定的了解,收获挺大。