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 地址,则假定为本地系统。

0x02 运行结果

image.png

posted @ 2023-04-13 11:17  MeetA  阅读(163)  评论(0)    收藏  举报