原始套接字來兩種類型: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 );
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_DONTLINGER(也就是說將linger結構的l_onoff域設爲零,則 closesocket()調用當即返回。可是,若是可能,排隊的數據將在套接口關閉前發送。請注意,在這種狀況下WINDOWS套接口實現將在一段不確 定的時間內保留套接口以及其餘資源,這對於想用因此套接口的應用程序來講有必定影響。
IPPROTO_IP:
IP_HDRINCL 在數據包中包含IP首部 int
這個選項經常使用於黑客技術中,隱藏本身的IP地址
IP_OPTINOS IP首部選項 int
IP_TOS 服務類型
IP_TTL 生存時間 int
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 的大小
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; }