TCP半连接端口扫描器
0x00 半连接
TCP半连接端口扫描器可以复用前面开发好的TCP全连接端口扫描 器的代码,只需要将执行全连接扫描的Connect(ip string, port int)函数修改为半连接扫描的函数即可,代码片断如下所示
func SynScan(dstIp string, dstPort int) (string, int, error) {
srcIp, srcPort, err := localIPPort(net.ParseIP(dstIp)) //ParseIP将s解析为IP地址,并返回该地址
dstAddrs, err := net.LookupIP(dstIp) //LookupIP函数查询主机的ipv4和ipv6地址序列。
if err != nil {
return dstIp, 0, err
}
dstip := dstAddrs[0].To4() //这不是传入的参数,这个ip小写了 To4将一个IPv4地址转换为4字节表示
var dstport layers.TCPPort
dstport = layers.TCPPort(dstPort)
srcport := layers.TCPPort(srcPort)
// Our IP header... not used, but necessary for TCP checksumming.
ip := &layers.IPv4{
SrcIP: srcIp,
DstIP: dstip,
Protocol: layers.IPProtocolTCP,
}
// Our TCP header
tcp := &layers.TCP{
SrcPort: srcport,
DstPort: dstport,
SYN: true,
}
err = tcp.SetNetworkLayerForChecksum(ip)
buf := gopacket.NewSerializeBuffer()
opts := gopacket.SerializeOptions{
ComputeChecksums: true,
FixLengths: true,
}
if err := gopacket.SerializeLayers(buf, opts, tcp); err != nil {
return dstIp, 0, err
}
conn, err := net.ListenPacket("ip4:tcp", "0.0.0.0")
if err != nil {
return dstIp, 0, err
}
defer conn.Close()
if _, err := conn.WriteTo(buf.Bytes(), &net.IPAddr{IP: dstip}); err != nil {
return dstIp, 0, err
}
// Set deadline so we don't wait forever.
if err := conn.SetDeadline(time.Now().Add(3 * time.Second)); err != nil {
return dstIp, 0, err
}
for {
b := make([]byte, 4096)
n, addr, err := conn.ReadFrom(b)
if err != nil {
return dstIp, 0, err
} else if addr.String() == dstip.String() {
// Decode a packet
packet := gopacket.NewPacket(b[:n], layers.LayerTypeTCP, gopacket.Default)
// Get the TCP layer from this packet
if tcpLayer := packet.Layer(layers.LayerTypeTCP); tcpLayer != nil {
tcp, _ := tcpLayer.(*layers.TCP)
if tcp.DstPort == srcport {
if tcp.SYN && tcp.ACK {
// log.Printf("%v:%d is OPEN\n", dstIp, dstport)
return dstIp, dstPort, err
} else {
return dstIp, 0, err
}
}
}
}
}
}
0x01 获取本地ip地址和端口
//获取本地ip和端口
func localIPPort(dstip net.IP) (net.IP, int, error) {
//暂定为:通过测试连接得知本地ip和端口
serverAddr, err := net.ResolveUDPAddr("udp", dstip.String()+":54321")
//参数addr格式为"host:port"或"[ipv6-host%zone]:port",解析得到网络名和端口名
if err != nil {
return nil, 0, err
}
// We don't actually connect to anything, but we can determine
// based on our destination ip what source ip we should use.
if con, err := net.DialUDP("udp", nil, serverAddr); err == nil {
//如果laddr不是nil,将使用它作为本地地址,否则自动选择一个本地地址。
if udpaddr, ok := con.LocalAddr().(*net.UDPAddr); ok {
//LocalAddr返回本地网络地址
return udpaddr.IP, udpaddr.Port, nil
}
}
return nil, -1, err
}
ResolveUDPAddr
func ResolveUDPAddr(network, address string) (*UDPAddr, error)
解析UDP添加返回 UDP 端点的地址。
网络必须是 UDP 网络名称。
如果地址参数中的主机不是文字 IP 地址,或者端口不是文字端口号,则解析UDPAddr 会将该地址解析为 UDP 端点的地址。否则,它会将地址解析为一对文字 IP 地址和端口号。地址参数可以使用主机名,但不建议这样做,因为它最多将返回主机名的 IP 地址之一。
DialUDP
func DialUDP(network string, laddr, raddr *UDPAddr) (*UDPConn, error)
拨号UDP的作用类似于UDP网络的拨号。
网络必须是 UDP 网络名称;
如果 laddr 为零,则会自动选择本地地址。如果 raddr 的 IP 字段为零或未指定的 IP 地址,则假定为本地系统。