原始套接字學習

原始套接字來兩種類型:1)在IP頭中使用預約義的協議,ICMPios

                                             2)  在IP頭中使用自定義的協議算法


建立能夠是 socket / WSASocket  只不過要將套接字設置爲 SOCK_RAW服務器

注意 WIDNOWS XP SP2 已經再也不支持原始TCP數據的發送了網絡


下面的內容MSDB沒有顯示徹底 ,小菜在各類網絡搜索獲得:併發

int setsockopt(
  __in  SOCKET s,   //<span style="color: rgb(51, 51, 51); font-family: Arial; font-size: 14.44444465637207px; line-height: 26px;">s(套接字): 指向一個打開的套接口描述字</span>
  __in  int level,
  __in  int optname,
  __in  const char *optval,
  __in  int optlen
);

level:(級別) 指定選項代碼的類型。 4種類型:
SOL_SOCKET: 基本套接口

IPPROTO_IP: IPv4套接口app

IPPROTO_IPV6: IPv6套接口socket

IPPROTO_TCP: TCP套接口
tcp

optname(選項名):選項名稱  每種level 對應幾種選項名ide

SOL_SOCKET:
函數

SO_BROADCAST 容許發送廣播數據 int
適用於 UDP socket。其意義是容許 UDP socket 「廣播」(broadcast)訊息到網路上。

SO_DEBUG 容許調試 int

SO_DONTROUTE 不查找路由 int

SO_ERROR 得到套接字錯誤 int

SO_KEEPALIVE 保持鏈接 int

檢測對方主機是否崩潰,避免(服務器)永遠阻塞於TCP鏈接的輸入。 設置該選項後,若是2小時內在此套接口的任一方向都沒有數據交換,TCP就自動給對方 發一個保持存活探測分節(keepalive probe)。這是一個對方必須響應的TCP分節.它會致使如下三種狀況: 對方接收一切正常:以指望的 ACK響應。2小時後,TCP將發出另外一個探測分節。 對方已崩潰且已從新啓動:以RST響應。套接口的待處理錯誤被置爲ECONNRESET,套接 口自己則被關閉。 對方無任何響應:源自berkeley的TCP發送另外8個探測分節,相隔75秒一個,試圖獲得 一個響應。在發出第一個探測分節11分鐘15秒後若仍無響應就放棄。套接口的待處理錯 誤被置爲ETIMEOUT,套接口自己則被關閉。如ICMP錯誤是「host unreachable (主機不 可達)」,說明對方主機並無崩潰,可是不可達,這種狀況下待處理錯誤被置爲 EHOSTUNREACH。
SO_DONTLINGER 若爲真,則SO_LINGER選項被禁止。
SO_LINGER 延遲關閉鏈接 struct linger
上面這兩個選項影響close行爲
選項 間隔 關閉方式 等待關閉與否
SO_DONTLINGER 不關心 優雅 否
SO_LINGER 零 強制 否
SO_LINGER 非零 優雅 是
若 設置了SO_LINGER(亦即linger結構中的l_onoff域設爲非零,參見2.4,4.1.7和4.1.21各節),並設置了零超時間隔,則 closesocket()不被阻塞當即執行,不管是否有排隊數據未發送或未被確認。這種關閉方式稱爲「強制」或「失效」關閉,由於套接口的虛電路當即被 復位,且丟失了未發送的數據。在遠端的recv()調用將以WSAECONNRESET出錯。
若設置了SO_LINGER並肯定了非零的超時間 隔,則closesocket()調用阻塞進程,直到所剩數據發送完畢或超時。這種關閉稱爲「優雅的」關閉。請注意若是套接口置爲非阻塞且 SO_LINGER設爲非零超時,則closesocket()調用將以WSAEWOULDBLOCK錯誤返回。

若在一個流類套接口上設置了 SO_DONTLINGER(也就是說將linger結構的l_onoff域設爲零,則 closesocket()調用當即返回。可是,若是可能,排隊的數據將在套接口關閉前發送。請注意,在這種狀況下WINDOWS套接口實現將在一段不確 定的時間內保留套接口以及其餘資源,這對於想用因此套接口的應用程序來講有必定影響。


IPPROTO_IP:

IP_HDRINCL 在數據包中包含IP首部 int
這個選項經常使用於黑客技術中,隱藏本身的IP地址

IP_OPTINOS IP首部選項 int
IP_TOS 服務類型
IP_TTL 生存時間 int


IPPROTO_TCP:

TCP_NODELAY

TCP_CORK

CP_NODELAY 和 TCP_CORK,
這 兩個選項都對網絡鏈接的行爲具備重要的做用。許多UNIX系統都實現了TCP_NODELAY選項,可是,TCP_CORK則是Linux系統所獨有的而 且相對較新;它首先在內核版本2.4上得以實現。此外,其餘UNIX系統版本也有功能相似的選項,值得注意的是,在某種由BSD派生的系統上的 TCP_NOPUSH選項其實就是TCP_CORK的一部分具體實現。
TCP_NODELAY和TCP_CORK基本上控制了包的 「Nagle化」,Nagle化在這裏的含義是採用Nagle算法把較小的包組裝爲更大的幀。John Nagle是Nagle算法的發明人,後者就是用他的名字來命名的,他在1984年首次用這種方法來嘗試解決福特汽車公司的網絡擁塞問題(欲瞭解詳情請參 看IETF RFC 896)。他解決的問題就是所謂的silly window syndrome ,中文稱「愚蠢窗口症候羣」,具體含義是,由於廣泛終端應用程序每產生一次擊鍵操做就會發送一個包,而典型狀況下一個包會擁有一個字節的數據載荷以及40 個字節長的包頭,因而產生4000%的過載,很輕易地就能令網絡發生擁塞,。 Nagle化後來成了一種標準而且當即在因特網上得以實現。它如今已經成爲缺省配置了,但在咱們看來,有些場合下把這一選項關掉也是合乎須要的。
現 在讓咱們假設某個應用程序發出了一個請求,但願發送小塊數據。咱們能夠選擇當即發送數據或者等待產生更多的數據而後再一次發送兩種策略。若是咱們立刻發送 數據,那麼交互性的以及客戶/服務器型的應用程序將極大地受益。例如,當咱們正在發送一個較短的請求而且等候較大的響應時,相關過載與傳輸的數據總量相比 就會比較低,並且,若是請求當即發出那麼響應時間也會快一些。以上操做能夠經過設置套接字的TCP_NODELAY選項來完成,這樣就禁用了Nagle算 法。
另一種狀況則須要咱們等到數據量達到最大時才經過網絡一次發送所有數據,這種數據傳輸方式有益於大量數據的通訊性能,典型的應用就是文件服 務器。應用 Nagle算法在這種狀況下就會產生問題。可是,若是你正在發送大量數據,你能夠設置TCP_CORK選項禁用Nagle化,其方式正好同 TCP_NODELAY相反(TCP_CORK 和 TCP_NODELAY 是互相排斥的)。下面就讓咱們仔細分析下其工做原理。
假設應用程序 使用sendfile()函數來轉移大量數據。應用協議一般要求發送某些信息來預先解釋數據,這些信息其實就是報頭內容。典型狀況下報頭很小,並且套接字 上設置了TCP_NODELAY。有報頭的包將被當即傳輸,在某些狀況下(取決於內部的包計數器),由於這個包成功地被對方收到後須要請求對方確認。這 樣,大量數據的傳輸就會被推遲並且產生了沒必要要的網絡流量交換。
可是,若是咱們在套接字上設置了TCP_CORK(能夠比喻爲在管道上插入 「塞子」)選項,具備報頭的包就會填補大量的數據,全部的數據都根據大小自動地經過包傳輸出去。當數據傳輸完成時,最好取消TCP_CORK 選項設置給鏈接「拔去塞子」以便任一部分的幀都能發送出去。這同「塞住」網絡鏈接同等重要。
總而言之,若是你確定能一塊兒發送多個數據集合(例如HTTP響應的頭和正文),那麼咱們建議你設置TCP_CORK選項,這樣在這些數據之間不存在延遲。能極大地有益於WWW、FTP以及文件服務器的性能,同時也簡化了你的工做。示例代碼以下:

intfd, on = 1;
…
/* 此處是建立套接字等操做,出於篇幅的考慮省略*/
…
setsockopt (fd, SOL_TCP, TCP_CORK, &on, sizeof (on)); /* cork */
write (fd, …);
fprintf (fd, …);
sendfile (fd, …);
write (fd, …);
sendfile (fd, …);
…
on = 0;
setsockopt (fd, SOL_TCP, TCP_CORK, &on, sizeof (on)); /* 拔去塞子 */


optval(選項值):是一個指向變量的指針 類型:整形,套接口結構, 其餘結構類型:linger{}, timeval{ }
optlen(選項長度) :optval 的大小


int WSAIoctl(
  __in   SOCKET s,
  __in   DWORD dwIoControlCode,
  __in   LPVOID lpvInBuffer,
  __in   DWORD cbInBuffer,
  __out  LPVOID lpvOutBuffer,
  __in   DWORD cbOutBuffer,
  __out  LPDWORD lpcbBytesReturned,
  __in   LPWSAOVERLAPPED lpOverlapped,
  __in   LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
)

dwIoControlCode:
SIO_RCVALL Enables a socket to receive all IPv4 or IPv6 packets passing throuigh a network interface

下面學習 IP TCP 結構:

版本號(Version):長度4比特。標識目前採用的IP協議的版本號。通常的值爲0100(IPv4),IPv6的值(0110)。

IP包頭長度(Header Length):長度4比特。這個字段的做用是爲了描述IP包頭的長度,由於在IP包頭中有變長的可選部分。該部分佔4個bit位,單位爲32bit(4個字節),即本區域值= IP頭部長度(單位爲bit)/(8*4),所以,一個IP包頭的長度最長爲「1111」,即15*4=60個字節。IP包頭最小長度爲20字節。對於標準ipv4報頭,這個字段的值確定是20/4=5(10進制)=0101(2進制)。

服務類型(Type of Service):長度8比特。這個子段能夠拆分紅兩個部分:Precedence和TOS。TOS目前不太使用。而Precedence則用於QOS("Quality of Service",中文名爲"服務質量")應用。(TOS字段的詳細描述RFC 1340 1349)。

IP包總長(Total Length):長度16比特。以字節爲單位計算的IP包的長度 (包括頭部和數據),因此IP包最大長度65535字節。

標識符(Identifier):長度16比特。該字段和Flag和Fragment Offest字段聯合使用,對大的上層數據包進行分段(fragment)操做。

標記(Flag):長度3比特。該字段第一位不使用。第二位是DF位,DF位設爲1時代表路由器不能對該上層數據包分段。若是一個上層數據包沒法在不分段的狀況下進行轉發,則路由器會丟棄該上層數據包並返回一個錯誤信息。第三位是MF位,當路由器對一個上層數據包分段,則路由器會在最後一個分段的IP包的包頭中將MF位設爲0,其他IP包的包頭中將MF設爲1。

Bit 0: reserved, must be zero
Bit 1: (DF) 0 = May Fragment, 1 = Don't Fragment.
Bit 2: (MF) 0 = Last Fragment, 1 = More Fragments.
 
分段序號(Fragment Offset):長度13比特。該字段對包含分段的上層數據包的IP包賦予序號。因爲IP包在網絡上傳送的時候不必定能按順序到達,這個字段保證了目標路由器在接受到IP包以後可以還原分段的上層數據包。到某個包含分段的上層數據包的IP包在傳送是丟失,則整個一系列包含分段的上層數據包的IP包都會被要求重傳。

生存時間(Time to Live):長度8比特。當IP包進行傳送時,先會對該字段賦予某個特定的值。當IP包通過每個沿途的路由器的時候,每一個沿途的路由器會將IP包的TTL值減小1。若是TTL減小爲0,則該IP包會被丟棄。這個字段能夠防止因爲故障而致使IP包在網絡中不停被轉發。

協議(Protocol):長度8比特。標識了上層所使用的協議。

頭部校驗(Header Checksum):長度16比特,因爲IP包頭是變長的,因此提供一個頭部校驗來保證IP包頭中信息的正確性。

源IP地址(Source IP Addresses):長度16比特。標識了這個IP包的源IP地址。

目的IP地址(Destination IP Address):長度16比特。標識了這個IP包的目的IP地址。

可選項(Options):這是一個可變長的字段,長度爲0或32bit的整倍數,最大320bit,若是不足則填充到滿。該字段由起源設備根據須要改寫。可選項目包含如下內容:

    鬆散源路由(Loose source routing):給出一連串路由器接口的IP地址。IP包必須沿着這些IP地址傳送,可是容許在相繼的兩個IP地址之間跳過多個路由器。

    嚴格源路由(Strict source routing):給出一連串路由器接口的IP地址。IP包必須沿着這些IP地址傳送,若是下一跳不在IP地址表中則表示發生錯誤。

    路由記錄(Record route):當IP包離開每一個路由器的時候記錄路由器的出站接口的IP地址。

    時間戳(Timestamps):當IP包離開每一個路由器的時候記錄時間。

填充(Padding):由於IP包頭長度(Header Length)部分的單位爲32bit,因此IP包頭的長度必須爲32bit的整數倍。所以,在可選項後面,IP協議會填充若干個0,以達到32bit的整數倍。


源端口號(Source Port):16位,標識主機上發起傳送的應用程序;

目的端口(Destonation Port):16位,標識主機上傳送要到達的應用程序。

源端,目的端的端口號,用於尋找發端和收端應用進程。這兩個值加上IP首部中的源端IP地址和目的端IP地址惟一肯定一個TCP鏈接。一個IP地址和一個端口號有時也稱爲一個插口(Socket),插口對(Socket Pair)(包含客戶IP地址、客戶端口號、服務器 IP地址和服務器端口號的四元組)可惟一肯定互聯網絡中每一個TCP鏈接的雙方。IP+TCP端口惟一肯定一個TCP鏈接。

TCP協議經過使用"端口"來標識源端和目標端的應用進程。端口號可使用0到65535之間的任何數字。在收到服務請求時,操做系統動態地爲客戶端的應用程序分配端口號。在服務器端,每種服務在"衆所周知的端口"(Well-Know Port)爲用戶提供服務。

序列碼(Sequence Number):32位,當SYN出現,序列碼其實是初始序列碼(ISN),而第一個數據字節是ISN+1。用來標識從TCP源端向TCP目標端發送的數據字節流,它表示在這個報文段中的第一個數據字節。

確認碼(Acknowledgment Numbwe):32位,若是設置了ACK控制位,這個值表示一個準備接收的包的序列碼,只有ACK標誌爲1時,確認號字段纔有效。它包含目標端所指望收到源端的下一個數據字節。

數據偏移量(Data Offset):4位,也就是頭部長度,指出TCP負載(數據)的開始位置。以4字節爲單位,如"0101"表示20字節位置的數據爲負載開始,也就是頭部長度爲20字節。

保留(Reserved):6位,這些位必須是0;


控制標記(Contral Flag):6位,  2表示第一次握手   18表示  第二次握手

    URG(Urgent data):緊急指針(urgent pointer)有效。若是URG爲1,表示這是一個攜有緊急資料的封包。

    ACK(Acknowledgment field significant):確認序號有效。若是ACK爲1,表示此封包屬於一個要回應的封包。通常都會爲1。

    PSH(Push function):接收方應該儘快將這個報文段交給應用層。若是PSH爲1,此封包所攜帶的數據會直接上傳給上層應用程序而無需通過TCP處理。

    RST(Reset):重建鏈接。若是RST爲1,要求重傳。表示要求從新設定封包再從新傳遞。

    SYN(Synchronize sequence number):發起一個鏈接。若是SYN爲1,表示要求雙方進行同步溝通。

    FIN(Finish-No more data for sender):釋放一個鏈接。若是FIN爲1,表示傳送結束,然後雙方發出結束迴應進而正式終止一個TCP傳送過程。

窗口(Window):16位,接收窗口大小。此字段用來進行流量控制,單位爲字節數,這個值是本機指望一次接收的字節數。這裏通常稱爲「滑動視窗(Sliding Window)」。

正如您剛纔看到的TCP封包會經過SQN和ACK序號來確保傳送的正確性,但若是每個封包都要等上一個封包的迴應才被髮送出去的話實在是太慢和難以接受的。這樣咱們能夠利用Sliding Window在傳送兩端劃分出一個範圍,規定出能夠一次性發送的最大封包數目。 

當TCP傳送創建起來之後兩端都會將window的設定值還原到初始值比方說每次傳送3個封包。而後發送端就一次發送三個封包出去,而後視窗則會往後移動三個封包填補發送出去之封包的空缺。若是接收端夠順利也能一次處理接收下來的三個封包的話,就會告訴發送端的window值爲3,但若是接收端太忙或是其它因素影響暫時只能處理兩個封包,那麼在視窗裏面就剩下一個封包,而後就會告訴發送端window值爲2。這個時候發送端就只送出兩個封包而視窗就會往後移動兩個封包填補發送出去的空缺。

校驗位(Checksum):16位,對整個TCP報文段,即TCP頭部和TCP數據進行校驗和計算,並由目標端進行驗證。

當資料要傳送出去的時候發送端會計算好封包資料大小然後得出這個檢驗值封包一塊兒發送當接收端收到封包之後會再對資料大小進行計算看看是否和檢驗值一致若是結果不相稱則被視爲殘缺封包會要求對方重發該個封包。

緊急指針(Urgent Pointer):16位,它是一個偏移量。指向後面是優先數據的字節,緊急指針指出在本報文段中的緊急數據的最後一個字節的序號,和序號字段中的值相加表示緊急數據最後一個字節的序號。

還記得剛纔講到Control Flag的時候咱們提到一個URG的標記嗎若是URG被設定爲1的時候,這裏就會指示出緊急資料所在位置。不過這種情形很是少見,例如:當資料流量超出頻寬的時候系統要求網路主機暫緩發送資料全部主機收到這樣的信息都須要優先處理。

選項(Options):長度不定;但長度必須以字節記;選項的具體內容咱們結合具體命令來看。

這個選項也比較少用。當那些須要使用同步動做的程式如Telnet要處理好終端的交互模式就會使用到option來指定資料封包的大小由於telnet使用的資料封包都不多但又須要即時迴應。

填充(Padding):不定長,填充的內容必須爲0,它是爲了保證包頭的結合和數據的開始處偏移量可以被32整除。


        SYN(synchronous)是TCP/IP創建鏈接時使用的握手信號。在客戶機和服務器之間創建正常的TCP網絡鏈接時,客戶機首先發出一個SYN消息,服務器使用SYN+ACK應答表示接收到了這個消息,最後客戶機再以ACK消息響應。這樣在客戶機和服務器之間才能創建起可靠的TCP鏈接,數據才能夠在客戶機和服務器之間傳遞。


        SYN攻擊屬於DDoS攻擊的一種,它利用TCP協議缺陷,經過發送大量的半鏈接請求,耗費CPU內存資源。SYN攻擊除了能影響主機外,還能夠危害路由器防火牆等網絡系統,事實上SYN攻擊並無論目標是什麼系統,只要這些系統打開TCP服務就能夠實施。服務器接收到鏈接請求(syn= j),將此信息加入未鏈接隊列,併發送請求包給客戶(syn=k,ack=j+1),此時進入SYN_RECV狀態。當服務器未收到客戶端的確認包時,重發請求包,一直到超時,纔將此條目從未鏈接隊列刪除。配合IP欺騙,SYN攻擊能達到很好的效果,一般,客戶端在短期內僞造大量不存在的IP地址,向服務器不斷地發送syn包,服務器回覆確認包,並等待客戶的確認,因爲源地址是不存在的,服務器須要不斷的重發直至超時,這些僞造的SYN包將長時間佔用未鏈接隊列,正常的SYN請求被丟棄,目標系統運行緩慢,嚴重者引發網絡堵塞甚至系統癱瘓。


檢測SYN攻擊:

當你在服務器上看到大量的半鏈接狀態時,特別是源IP地址是隨機的,基本上能夠判定這是一次SYN攻擊。咱們使用系統自帶的netstat 工具來檢測SYN攻擊:

# netstat -n -p TCP

 -n            以數字形式顯示地址和端口號。
 -p proto      顯示 proto 指定的協議的鏈接;proto 能夠是下列任
               何一個: TCP、UDP、TCPv6 或 UDPv6。若是與 -s 選
               項一塊兒用來顯示每一個協議的統計,proto 能夠是下列任
               何一個: IP、IPv六、ICMP、ICMPv六、TCP、TCPv六、UDP

  TCP    192.168.0.101:27309    192.168.0.101:168      SYN_SENT
  TCP    192.168.0.101:27310    192.168.0.101:169      SYN_SENT
  TCP    192.168.0.101:27311    192.168.0.101:170      SYN_SENT
  TCP    192.168.0.101:27312    192.168.0.101:171      SYN_SENT
  TCP    192.168.0.101:27313    192.168.0.101:172      SYN_SENT
  TCP    192.168.0.101:27314    192.168.0.101:173      SYN_SENT
  TCP    192.168.0.101:27315    192.168.0.101:174      SYN_SENT
  TCP    192.168.0.101:27316    192.168.0.101:175      SYN_SENT
  TCP    192.168.0.101:27317    192.168.0.101:176      SYN_SENT
  TCP    192.168.0.101:27318    192.168.0.101:177      SYN_SENT
  TCP    192.168.0.101:27319    192.168.0.101:178      SYN_SENT
  TCP    192.168.0.101:27320    192.168.0.101:179      SYN_SENT
  TCP    192.168.0.101:27321    192.168.0.101:180      SYN_SENT
  TCP    192.168.0.101:27322    192.168.0.101:181      SYN_SENT
  TCP    192.168.0.101:27323    192.168.0.101:182      SYN_SENT
  TCP    192.168.0.101:27324    192.168.0.101:183      SYN_SENT
  TCP    192.168.0.101:27325    192.168.0.101:184      SYN_SENT
  TCP    192.168.0.101:27326    192.168.0.101:185      SYN_SENT

在WINDOWS系統中是SYN_RECEIVED狀態,源IP地址都是隨機的,代表這是一種帶有IP欺騙的SYN攻擊。

netstat -n -p TCP | find "192.168.0.101"   查看所有連接

防範技術:


關於SYN攻擊防範技術,人們研究得比較早。概括起來,主要有兩大類,一類是經過防火牆、路由器等過濾網關防禦,另外一類是經過加固TCP/IP協議棧防範
豐富帶寬資源


不難看出syn攻擊消耗帶寬資源因此要想防護synflood一個豐富的帶寬資源是必要的,一般的流量攻擊,攻擊者也是利用肉雞的帶寬資源來達到攻擊堵死網絡的,因此這個是一個前提
防火牆


利用防火牆來進行防禦攻擊是目前最有效的方法


SYN代碼:

#include "stdafx.h"
#include <string.h>
#include <iostream>

#include <winsock2.h>
#pragma comment(lib,"WS2_32")

#include <WS2TCPIP.h>
#include <time.h>

// New WSAIoctl Options
#define SIO_RCVALL            _WSAIOW(IOC_VENDOR,1)
#define SIO_RCVALL_MCAST      _WSAIOW(IOC_VENDOR,2)
#define SIO_RCVALL_IGMPMCAST  _WSAIOW(IOC_VENDOR,3)
#define SIO_KEEPALIVE_VALS    _WSAIOW(IOC_VENDOR,4)
#define SIO_ABSORB_RTRALERT   _WSAIOW(IOC_VENDOR,5)
#define SIO_UCAST_IF          _WSAIOW(IOC_VENDOR,6)
#define SIO_LIMIT_BROADCASTS  _WSAIOW(IOC_VENDOR,7)
#define SIO_INDEX_BIND        _WSAIOW(IOC_VENDOR,8)
#define SIO_INDEX_MCASTIF     _WSAIOW(IOC_VENDOR,9)
#define SIO_INDEX_ADD_MCAST   _WSAIOW(IOC_VENDOR,10)
#define SIO_INDEX_DEL_MCAST   _WSAIOW(IOC_VENDOR,11) 


typedef struct _iphdr 
{
	unsigned char h_lenver; //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;

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; 

struct //定義TCP僞首部
{
	unsigned long saddr; //源地址
	unsigned long daddr; //目的地址
	char mbz;
	char ptcl; //協議類型
	unsigned short tcpl; //TCP長度
}psd_header;

SOCKET sockRaw = INVALID_SOCKET,sockListen = INVALID_SOCKET;
int BeginPort,EndPort;
char *HOST;
int iErrorCode;
struct sockaddr_in dest;
BOOL StopScan = FALSE;

#define SEQ 0x28376839

void CheckSockError(int ierror,char *pErrorMsg)
{
	if (ierror == SOCKET_ERROR)
	{
		printf("%s ErrorCode:%d\n",pErrorMsg,ierror);
		closesocket(sockRaw);
		ExitProcess(-1);
	}
}


void meesage()
{
	printf("\t-------syn by panda--------------------------\n");
	printf("\t-------syn_test [ip] [port-port]-------------\n");
	printf("\t-------example: syn_test 127.0.0.1 1-1000----\n");
}
BOOL DecodeIPHeader(char *RecvBuf)
{
	IP_HEADER *iphdr = (IP_HEADER*)RecvBuf;
	unsigned short iphdrlen = sizeof(unsigned long) * (iphdr->h_lenver&0xf);
	TCP_HEADER* tcphdr = (TCP_HEADER*)(RecvBuf + iphdrlen);
	
	if (iphdr->sourceIP != dest.sin_addr.s_addr)
	{
		return FALSE;
	}
	if (ntohl(tcphdr->th_ack) != (SEQ+1) && ntohl(tcphdr->th_ack) != SEQ)
	{
		return FALSE;
	}
	if (tcphdr->th_flag == 18)
	{
		printf("\t%d open \n",ntohs(tcphdr->th_sport));
		return true;
	}
	return FALSE;
}


DWORD WINAPI RecvThread(LPVOID para)//接受數據線程
{
	//監聽本機套接字
	sockListen = socket(AF_INET,SOCK_RAW,IPPROTO_IP);
	CheckSockError(sockListen,"RecvThread` socket");
	
	BOOL bOpt =true;
	iErrorCode = setsockopt(sockRaw,IPPROTO_IP,IP_HDRINCL,(char*)&bOpt,sizeof(bOpt));
	CheckSockError(iErrorCode,"RecvThread setsockopt");
	
	//得到本地IP
	char LocalName[256];
	gethostname(LocalName,sizeof(LocalName));
	struct hostent * my_hostent = gethostbyname(LocalName);
	
	SOCKADDR_IN sa;
	memcpy(&sa.sin_addr.S_un.S_addr,my_hostent->h_addr_list[0],my_hostent->h_length);
	sa.sin_family = AF_INET;
	sa.sin_port = htons(8000);
	
	iErrorCode = bind(sockListen,(sockaddr*)&sa,sizeof(sa));
	CheckSockError(iErrorCode,"bind");

	//設置SOCK_RAW爲SIO_RCVALL,以便接收全部的IP包
	DWORD dwBufferInLen = 1;
	DWORD dwBufferLen[10];
	DWORD dwBytesReturned = 0;
	iErrorCode = WSAIoctl(sockListen,SIO_RCVALL,\
		&dwBufferInLen,sizeof(dwBufferInLen),\
		&dwBufferLen,sizeof(dwBufferLen),&dwBytesReturned,NULL,NULL);
	CheckSockError(iErrorCode,"RecvThread WSAIoctl");
	
	char RecvBuf[65535]={0};
	memset(RecvBuf,0,sizeof(RecvBuf));
	
	while (1)//循環監聽  本地 數據包
	{
		iErrorCode = recv(sockListen,RecvBuf,sizeof(RecvBuf),0);
		//CheckSockError(iErrorCode,"RecvThread  recv");
		DecodeIPHeader(RecvBuf);
		if (StopScan == TRUE)
		{
			closesocket(sockListen);
			return 0;
		}
	}
	return 0;
}



USHORT CalcCheckSum(USHORT *buffer,int size)
{
	unsigned long cksum = 0;
	while (size > 1)
	{
		cksum += *buffer++;
		size -= sizeof(USHORT);
	}
	if (size)
	{
		cksum += *(USHORT*)buffer;
	}
	cksum = (cksum >> 16) + (cksum &0xffff);
	cksum += (cksum >>16);
	return (USHORT)(~cksum);
}
int play=0;
void progressbar(void)
{
	// 進度條 
	char *plays[12]= 
	{ 
		" | ", 
			" / ", 
			" - ", 
			" \\ ", 
			" | ", 
			" / ", 
			" - ", 
			" \\ ", 
			" | ", 
			" / ", 
			" - ", 
			" \\ ", 
	}; 
	printf(" =%s=\r", plays[play]);
	play = (play==11)?0:play+1;
	Sleep(2); 
}
int main(int argc, char* argv[])
{
	char *p;
	if (argc != 3)
	{
		meesage();
		return 0;
	}
	p = argv[2];
	if (strstr(argv[2],"-"))
	{
		BeginPort = atoi(argv[2]);
		while (*p)
		{
			if (*(p++) == '-')
			{
				break;
			}
		}
		EndPort = atoi(p);
		if (BeginPort <1 || BeginPort>65535 ||EndPort<1|| EndPort >65535|| EndPort<EndPort)
		{
			meesage();
			return 0;
		}
	}
	HOST = argv[1];
	meesage();

	WSADATA wsadata;
	iErrorCode = WSAStartup(MAKEWORD(2,2),&wsadata);
	CheckSockError(iErrorCode, "WsaStartup()");

	//////////////////////////////////////////////////////////////////////////////
	sockRaw = socket(AF_INET,SOCK_RAW,IPPROTO_IP);
	CheckSockError(sockRaw, "socket()");
	//設置IP頭操做選項 是發送TCP報文的套接字
	BOOL bOpt = true;
	setsockopt(sockRaw,IPPROTO_IP,IP_HDRINCL,(char*)&bOpt,sizeof(bOpt));
	CheckSockError(sockRaw,"setsockopt()");

	//得到目標主機IP ,經過發送主機  第一次握手包
	memset(&dest,0,sizeof(dest));
	dest.sin_family = AF_INET;
	dest.sin_port = ntohs(BeginPort);

	struct hostent *my_hostent;
	if ((dest.sin_addr.s_addr = inet_addr(HOST)) == INADDR_NONE)
	{
		if ((my_hostent = gethostbyname(HOST)) == NULL)
		{
			memcpy(&(dest.sin_addr),my_hostent->h_addr_list[0],my_hostent->h_length);
			dest.sin_family = my_hostent->h_addrtype;
			printf("dest.sin_addr = %s",inet_ntoa(dest.sin_addr));
		}
		else
		{
			CheckSockError(SOCKET_ERROR,"gethostbyname");
		}
	}
	//////////////////////////////////////////////////////////////////////////////
	sockListen = socket(AF_INET , SOCK_RAW , IPPROTO_IP);
	CheckSockError(sockListen, "socket");
	
	//得到本地IP
	SOCKADDR_IN sa;
	unsigned char LocalName[256];
	struct hostent *hp;
	
	iErrorCode = gethostname((char*)LocalName, sizeof(LocalName)-1);
	CheckSockError(iErrorCode, "gethostname()");
	if((hp = gethostbyname((char*)LocalName)) == NULL)
	{
		CheckSockError(SOCKET_ERROR, "gethostbyname()");
	}
	memcpy(&sa.sin_addr.S_un.S_addr, hp->h_addr_list[0],hp->h_length);
	sa.sin_family = AF_INET;
	sa.sin_port = htons(8000);
	iErrorCode = bind(sockListen, (PSOCKADDR)&sa, sizeof(sa));
	CheckSockError(iErrorCode, "bind");

	//開啓本地監聽 第二次握手包 線程
	HANDLE Thread = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)RecvThread,0,0,0);
	Sleep(1000);
    //////////////////////////////////////////////////////////////////////////////
	//發送第一次握手包
	
	IP_HEADER ip_header;
	TCP_HEADER tcp_header;
	//填充IP首部 一個IP包頭的長度最長爲「1111」,即15*4=60個字節。IP包頭最小長度爲20字節。對於標準ipv4報頭,這個字段的值確定是20/4=5(10進制)=0101(2進制)。
	ip_header.h_lenver = (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=sa.sin_addr.s_addr;  //32位源IP地址
	ip_header.destIP=dest.sin_addr.s_addr;  //32位目的IP地址
	
	//填充TCP首部
	tcp_header.th_sport = htons(8000);//源端口號
	tcp_header.th_lenres = (sizeof(TCP_HEADER)/4<<4 | 0);//TCP長度和保留位
	tcp_header.th_win=htons(16384); 

	//填充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));

	
	printf("\n");
	printf("Scaning %s port : %d-%d\n",HOST,BeginPort,EndPort);
	clock_t start,end;//程序運行的起始和結束時間

	  start=clock();//開始計時
	//開始發包~~~~
	  char SendBuf[128] = {0};
	for (;BeginPort < EndPort;BeginPort++)
	{
		// 進度條 
		progressbar();
		
		tcp_header.th_dport = htons(BeginPort); //目的端口號
		tcp_header.th_ack=0;                  //ACK序列號置爲0
		tcp_header.th_flag=2;                 //SYN 標誌
		tcp_header.th_seq=htonl(SEQ);         //SYN序列號
		tcp_header.th_urp=0;                  //偏移
		tcp_header.th_sum=0;                  //校驗和
		
		
		
		//計算TCP校驗和 即TCP頭部和TCP數據進行校驗和計算,並由目標端進行驗證。
		memcpy(SendBuf,&psd_header,sizeof(psd_header));
		memcpy(SendBuf+sizeof(psd_header), &tcp_header,sizeof(tcp_header));
		tcp_header.th_sum = CalcCheckSum((USHORT *)SendBuf,sizeof(psd_header)+sizeof(tcp_header));
		
		//計算IP校驗和 IP包頭是變長的,因此提供一個頭部校驗來保證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);
		ip_header.checksum = CalcCheckSum((USHORT *)SendBuf,sizeof(ip_header)+sizeof(tcp_header));

		//填充發送緩衝區
		memcpy(SendBuf,&ip_header,sizeof(ip_header));

		//發送TCP報文
		iErrorCode=sendto(sockRaw,SendBuf,sizeof(ip_header)+sizeof(tcp_header),0,\
			(struct sockaddr*) &dest,
			sizeof(dest));
		CheckSockError(iErrorCode, "sendto()");
	}
	//結束髮包~~~~
	end=clock();//計時結束
	StopScan = TRUE;
	printf("Closeing Scan.....\n");
	WaitForSingleObject(Thread,5000);
	CloseHandle(Thread);

	printf("Cost time: %f Sec",(float)(end-start) / CLOCKS_PER_SEC/*1000*/);
	
	if (sockRaw != INVALID_SOCKET)
	{
		closesocket(sockRaw);
	}
	if (sockListen!= INVALID_SOCKET)
	{
		closesocket(sockListen);
	}
	WSACleanup();
	return 0;
}
相關文章
相關標籤/搜索