Created
April 5, 2020 10:32
-
-
Save ls0f/941912ca0cf6e756eeb4524e497a7095 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"github.com/google/gopacket" | |
"github.com/google/gopacket/layers" | |
"log" | |
"net" | |
"os" | |
"strconv" | |
"time" | |
) | |
// get the local ip and port based on our destination ip | |
func localIPPort(dstip net.IP) (net.IP, int) { | |
serverAddr, err := net.ResolveUDPAddr("udp", dstip.String()+":12345") | |
if err != nil { | |
log.Fatal(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 { | |
if udpaddr, ok := con.LocalAddr().(*net.UDPAddr); ok { | |
return udpaddr.IP, udpaddr.Port | |
} | |
} | |
log.Fatal("could not get local ip: " + err.Error()) | |
return nil, -1 | |
} | |
func main() { | |
if len(os.Args) != 3 { | |
log.Printf("Usage: %s <host/ip> <port>\n", os.Args[0]) | |
os.Exit(-1) | |
} | |
log.Println("starting") | |
dstaddrs, err := net.LookupIP(os.Args[1]) | |
if err != nil { | |
log.Fatal(err) | |
} | |
// parse the destination host and port from the command line os.Args | |
dstip := dstaddrs[0].To4() | |
var dstport layers.TCPPort | |
if d, err := strconv.ParseUint(os.Args[2], 10, 16); err != nil { | |
log.Fatal(err) | |
} else { | |
dstport = layers.TCPPort(d) | |
} | |
srcip, sport := localIPPort(dstip) | |
srcport := layers.TCPPort(sport) | |
log.Printf("using srcip: %v", srcip.String()) | |
// 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, | |
Seq: 1105024978, | |
SYN: true, | |
Window: 14600, | |
} | |
tcp.SetNetworkLayerForChecksum(ip) | |
// Serialize. Note: we only serialize the TCP layer, because the | |
// socket we get with net.ListenPacket wraps our data in IPv4 packets | |
// already. We do still need the IP layer to compute checksums | |
// correctly, though. | |
buf := gopacket.NewSerializeBuffer() | |
opts := gopacket.SerializeOptions{ | |
ComputeChecksums: true, | |
FixLengths: true, | |
} | |
if err := gopacket.SerializeLayers(buf, opts, tcp); err != nil { | |
log.Fatal(err) | |
} | |
conn, err := net.ListenPacket("ip4:tcp", "0.0.0.0") | |
if err != nil { | |
log.Fatal(err) | |
} | |
defer conn.Close() | |
log.Println("writing request") | |
sendMsg(conn, dstip, opts, tcp) | |
if err := conn.SetDeadline(time.Now().Add(10 * time.Second)); err != nil { | |
log.Fatal(err) | |
} | |
payload1 := []byte("GET /404 HTTP/1.0\r\nHost: baidu.com") | |
payload2 := []byte("\r\n\r\n") | |
// payload := []byte(string(payload1) + string(payload2)) | |
seq := tcp.Seq + 1 | |
for { | |
b := make([]byte, 4096) | |
log.Println("reading from conn") | |
n, addr, err := conn.ReadFrom(b) | |
if err != nil { | |
log.Println("error reading packet: ", err) | |
return | |
} 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 { | |
rtcp, _ := tcpLayer.(*layers.TCP) | |
if rtcp.DstPort == srcport { | |
if rtcp.SYN && rtcp.ACK { | |
log.Printf("Port %d is OPEN\n", dstport) | |
// 数据包设置ACK位 | |
tcp.ACK = true | |
tcp.Ack = rtcp.Seq + 1 | |
tcp.PSH = true | |
tcp.SYN = false | |
// 模拟第一个数据包丢失,先发最后四个字节,这个包发出去也会握手成功 | |
// 先发payload2 | |
tcp.Seq = seq + uint32(len(payload1)) | |
sendMsg(conn, dstip, opts, tcp, gopacket.Payload(payload2)) | |
} else if rtcp.ACK && rtcp.Ack == seq { | |
// 客户端重发相同的握手ack | |
// 发送payload1 | |
tcp.Seq = rtcp.Ack | |
sendMsg(conn, dstip, opts, tcp, gopacket.Payload(payload1)) | |
} else if rtcp.ACK && rtcp.Ack == seq + uint32(len(payload1)) { | |
// payload1 ack了 | |
// 再发payload2 | |
tcp.Seq = rtcp.Ack | |
sendMsg(conn, dstip, opts, tcp, gopacket.Payload(payload2)) | |
return | |
} | |
} | |
} | |
} else { | |
log.Printf("Got packet not matching addr") | |
} | |
} | |
} | |
func sendMsg(conn net.PacketConn, dstip net.IP, opts gopacket.SerializeOptions, layer ...gopacket.SerializableLayer) { | |
buf := gopacket.NewSerializeBuffer() | |
if err := gopacket.SerializeLayers(buf, opts, layer...); err != nil { | |
log.Fatal(err) | |
} | |
if _, err := conn.WriteTo(buf.Bytes(), &net.IPAddr{IP: dstip}); err != nil { | |
log.Fatal(err) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment