原始 socket 編程

原始套接字簡介

普通 socket 的權力和原始 socket 權力對比。

1.原始 socket 能夠和內核同樣直接對全部層進行操做(除了物理層)。能夠更改 mac 更改 ip 更改端口。so dos 攻擊就能夠經過原始 socket 編程來僞造 ip 進行。
2.也能夠訪問通過網卡的全部數據.普通的 socket 只能訪問發送給本身端口的數據。
這裏寫圖片描述
linux

api 介紹

int socket(int protofamily, int type, int protocol);//返回sockfd算法

protofamily:

這個參數指定一個協議簇,也每每被稱爲協議域。系統存在許多能夠的協議簇,常見有AF_INET──指定爲IPv4協議,AF_INET6──指定爲IPv6,AF_LOCAL──指定爲UNIX 協議域等等。它值都是系統預先定義的宏,系統支持哪些協議咱們纔可使用,不然會調用失敗。協議簇是網絡層的協議。一種是處理IP層即其上的數據,經過指定socket第一個參數爲AF_INET來建立這種套接字。有兩種原始套接字。另外一種是處理數據鏈路層即其上的數據,經過指定socket第一個參數爲AF_PACKET來建立這種套接字。 PF_PACKET支持SOCK_DGRAM和SOCK_RAW兩種socket類型。編程

type:

這個參數指定一個套接口的類型,套接口可能的類型有:SOCK_STREAM、SOCK_DGRAM、SOCK_SEQPACKET、SOCK_RAW等等,它們分別代表字節流、數據報、有序分組、原始套接口。這其實是指定內核爲咱們提供的服務抽象,好比咱們要一個字節流。須要注意的,並非每一種協議簇都支持這裏的全部的類型,因此類型與協議簇要匹配。api

protocol:

指定相應的傳輸協議,也就是諸如TCP或UDP協議等等,系統針對每個協議簇與類型提供了一個默認的協議,咱們經過把protocol設置爲0來使用這個默認的值。注意這裏的協議與上面的協議簇是兩個不一樣的概念,前者是指網絡層的協議,因爲它對於到傳輸層會出現許多協議,好比IPv4能夠用來實現TCP或UDP等等傳輸層協議,因此稱爲協議簇。相應的傳輸層的協議就簡單地稱爲協議。常見的協議有TCP、UDP、SCTP,要指定它們分別使用宏 IPPROTO_TCP、IPPROTO_UPD、IPPROTO_SCTP來指定。 到linux/in.h看可使用哪些傳輸層的協議安全

例子:

socket(AF_INET, SOCK_RAW, IPPROTO_TCP|IPPROTO_UDP|IPPROTO_ICMP)發送接收ip數據包
能:該套接字能夠接收協議類型爲(tcp udp icmp等)發往本機的ip數據包
不能:收到非發往本地ip的數據包(ip軟過濾會丟棄這些不是發往本機ip的數據包)
不能:收到從本機發送出去的數據包發送的話須要本身組織tcp udp icmp等頭部.能夠setsockopt來本身包裝ip頭部這種套接字用來寫個ping程序比較適合網絡

socket(PF_PACKET, SOCK_RAW|SOCK_DGRAM, htons(ETH_P_IP|ETH_P_ARP|ETH_P_ALL))
發送接收以太網數據幀這種套接字比較強大,能夠監聽網卡上的全部數據幀
能: 接收發往本地mac的數據幀
能: 接收從本機發送出去的數據幀(第3個參數須要設置爲ETH_P_ALL)
能: 接收非發往本地mac的數據幀(網卡須要設置爲promisc混雜模式)
協議類型一共有四個
ETH_P_IP 0x800 只接收發往本機mac的ip類型的數據幀
ETH_P_ARP 0x806 只接受發往本機mac的arp類型的數據幀
ETH_P_RARP 0x8035 只接受發往本機mac的rarp類型的數據幀
ETH_P_ALL 0x3 接收發往本機mac的全部類型ip arp rarp的數據幀, 接收從本機發出的全部類型的數據幀.(混雜模式打開的狀況下,會接收到非發往本地mac的數據幀)併發

每層經常使用協議

鏈路層

報文格式

鏈路層有不少種協議,這個只是拿出一個典型的報文格式。socket

這裏寫圖片描述tcp

數據鏈路層是OSI參考模型中的第二層,介乎於物理層和網絡層之間。數據鏈路層在物理層提供的服務的基礎上向網絡層提供服務,其最基本的服務是將源自網絡層來的數據可靠地傳輸到相鄰節點的目標機網絡層。ide

數據鏈路層的主要協議有:

1.Point-to-Point Protocal; PPP點到點
2.Ethernet; 以太網
3.High-Level Data Link Control Protocal HDLC高級鏈路控制協議
4.Frame Relay; 幀中繼
5.Asynchronous Transfer Mode;ATM

網絡層

報文格式:

這裏寫圖片描述

1.Version - 4位字段,指出當前使用的 IP 版本。

2.IP Header Length (IHL) ─ 指數據報協議頭長度,表示協議頭具備32位字長的數量。指向數據起點。正確協議頭最小值爲5。(實際長度爲 5*4。對於 ip->ihl = sizeof( struct iphdr ) >> 2 是由於這裏是以4字節爲單位的。右移2位至關於除4)

3.Type-of-Service ─ 指出上層協議對處理當前數據報所指望的服務質量,並對數據報按照重要性級別進行分配。這些8位字段用於分配優先級、延遲、吞吐量以及可靠性。(即TOS)

4.Total Length ─ 指定整個 IP 數據包的字節長度,包括數據和協議頭(傳輸層和ip層數據總和)。其最大值爲65,535字節。典型的主機能夠接收576字節的數據報。

5.Identification ─ 每個IP封包都有一個16位的惟一識別碼。當程序產生的數據要經過網絡傳送時都會被拆散成封包形式發送,當封包要進行重組的時候這個ID就是依據了。佔16位。標識字段惟一地標識主機發送的每一份數據報。一般每發送一份消息它的值就會加1。RFC791認爲標識字段應該由讓IP發送數據報的上層來選擇。假
設有兩個連續的IP數據報,其中一個是由TCP生成的,而另外一個是由UDP生成的,那麼它們可能具備相同的標識字段。儘管這也能夠照常工做(由重組算法來
處理),可是在大多數從伯克利派生出來的系統中,每發送一個IP數據報,IP層都要把一個內核變量的值加1,無論交給IP的數據來自哪一層。內核變量的初
始值根據系統引導時的時間來設置。

6.Flags ─ 由3位字段構成,其中最低位(MF)控制分片,存在下一個分片置爲1,不然置0表明結束分片。中間位(DF)指出數據包是否可進行分片。第三位即最高位保留不使用,可是必須爲0。

7.Fragment Offset IP協議頭格式規定當封包被分段以後,因爲網路狀況或其它因素影響其抵達順序不會和當初切割順序一至,因此當封包進行分段的時候會爲各片斷作好定位記錄,以便在重組的時候就可以對號入座。值爲多少個字節,若是封包並無被分段,則FO值爲「0」。 佔13位。

8.Time-to-Live生存時間字段設置了數據報能夠通過的最多路由器數,表示數據包在網絡上生存多久。TTL的初始值由源主機設置(一般爲32或64),一旦通過一個處理它的
路由器,它的值就減去1。當該字段的值爲0時,數據報就被丟棄,併發送ICMP消息通知源主機。這樣當封包在傳遞過程當中由於某些緣由而未能抵達目的地的時
候就能夠避免其一直充斥在網路上面。佔8位。

9.Protocol ─ 指出在 IP 處理過程完成以後,有哪一種上層協議接收導入數據包。
這裏寫圖片描述

10.Header Checksum ─ 幫助確保 IP 協議頭的完整性。因爲某些協議頭字段的改變,如生存期(Time to Live),這就須要對每一個點從新計算和檢驗。Internet 協議頭須要進行處理。
11.Source Address ─ 源主機IP地址。
12.Destination Address ─ 目標主機IP地址。
13.Options ─ 容許 IP 支持各類選項,如安全性。這是一個可變長的字段。由於IP包頭長度(Header Length)部分的單位爲32bit,因此IP包頭的長度必須爲32bit的整數倍。所以,在可選項後面,IP協議會填充若干個0,以達到32bit的整數倍

傳輸層

傳輸層咱們以 udp 爲例子。tcp 還要維持連接,太煩了。dos 攻擊可不須要握手創建整個連接的。

UDP 簡介

UDP 報文UDP協議在IP協議上增長了複用、分用和差錯檢測功能。UDP的特色:
1.是無鏈接的,不須要連接和釋放連接
2.是面向報文的,也就是說UDP協議將應用層傳輸下來的數據封裝在一個UDP包中,不進行拆分或合併。
3.沒有重傳機制,是盡最大努力交付的。也就是說UDP協議沒法保證數據可以準確的交付到目的主機。也不須要對接收到的UDP報文進行確認。
4.沒有擁塞控制。所以UDP協議的發送速率不送網絡的擁塞度影響。
5.UDP支持一對1、一對多、多對一和多對多的交互通訊。

UDP報文格式

這裏寫圖片描述

UDP協議分爲首部字段和數據字段,其中首部字段只佔用8個字節,分別是個佔用兩個字節的源端口、目的端口、長度和檢驗和。
1.源端口:2字節 = 16bit =0 ~ 65535
2.目的端口:2字節
3.長度:2字節 用戶數據包的長度+ 8 字節固定 udp 報頭。
4.檢驗和:2字節在進行檢驗和計算時,會添加一個僞首部一塊兒進行運算。


僞首部(佔用12個字節)爲 4個字節的源IP地址、4個字節的目的IP地址、1個字節的0、一個字節的數字17(協議類型 udp就是數字 17)、以及佔用2個字節UDP長度。這個僞首部不是報文的真正首部,只是引入爲了計算校驗和。相對於IP協議的只計算首部,UDP檢驗和會把首部和數據一塊兒進行校驗。接收端進行的校驗和與UDP報文中的校驗和相與,若是無差錯應該全爲1。若是有誤,則將報文丟棄或者發給應用層、並附上差錯警告。

校驗算法:
1.把校驗和字段置爲0
2.對IP頭部中的每16bit進行二進制求和
3.若是和的高16bit不爲0,則將和的高16bit和低16bit反覆相加,直到和的高16bit爲0,從而得到一個16bit的值
4.將該16bit的值取反,存入校驗和字段。
s當接收IP包時,須要對報頭進行確認,檢查IP頭是否有誤,算法同上二、3步,而後判斷取反的結果是否爲0,是則正確,不然有錯。

TCP 簡介

1.是有鏈接的,面向流的。
2.是面向流的。TCP把數據流分區成適當長度的報文段(一般受該計算機鏈接的網絡的數據鏈路層的最大傳輸單元([1] MTU)的限制)
3.有重傳,去重機制。TCP協議中採用自適應的超時及重傳策略。TCP的接收端必須丟棄重複的數據。
4.TCP還能提供流量控制。

TCP 報文格式

這裏寫圖片描述

1.源端口號(16位),標識主機上發起傳送的應用程序

2.目的端口(16位)標識主機上傳送要到達的應用程序

3.源端和目的端的端口號,用於尋找發端和收 端應用進程。這兩個值加上IP首部中的源端IP地址和目的端IP地址惟一肯定一個TCP鏈接。

4.順序號字段:佔32比特。用來標識從TCP源端向TCP目標端發送的數據字節流,它表示在這個報文段中的第一個數據字節。

5.確認號字段:佔32比特。只有ACK標誌爲1時,確認號字段纔有效。它包含目標端所指望收到源端的下一個數據字節。

6.頭部長度字段:佔4比特。給出頭部佔32比特的數目。沒有任何選項字段的TCP頭部長度爲20字節;最多能夠有60字節的TCP頭部

7.預留:由跟在數據偏移字段後的6位構成,預留位一般爲0.

8.標誌位字段(U、A、P、R、S、F):佔6比特。各比特的含義以下:  
  ◆URG:緊急指針(urgent pointer)有效。  
  ◆ACK:確認序號有效。  
  ◆PSH:接收方應該儘快將這個報文段交給應用層。  
  ◆RST:重建鏈接。  
  ◆SYN:發起一個鏈接。  
  ◆FIN:釋放一個鏈接。
  
9.窗口大小字段:佔16比特。此字段用來進行流量控制。單位爲字節數,這個值是本機指望一次接收的字節數。

10.TCP校驗和字段:佔16比特。對整個TCP報文段,即TCP頭部和TCP數據進行校驗和計算,並由目標端進行驗證

11.緊急指針字段:佔16比特。它是一個偏移量,和序號字段中的值相加表示緊急數據最後一個字節的序號

12.選項字段:佔32比特。可能包括」窗口擴大因子」、」時間戳」等選項。

應用例子:抓取全部IP包

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; 

typedef struct _udphdr //定義UDP首部
{
    unsigned short uh_sport;    //16位源端口
    unsigned short uh_dport;    //16位目的端口
    unsigned int uh_len;//16位UDP包長度
    unsigned int uh_sum;//16位校驗和
}UDP_HEADER;

typedef struct _tcphdr //定義TCP首部 
{ 
    unsigned short th_sport; //16位源端口 
    unsigned short th_dport; //16位目的端口 
    unsigned int th_seq; //32位序列號 
    unsigned int th_ack; //32位確認號 
    unsigned char th_lenres;//4位首部長度/6位保留字 
    unsigned char th_flag; //6位標誌位
    unsigned short th_win; //16位窗口大小
    unsigned short th_sum; //16位校驗和
    unsigned short th_urp; //16位緊急數據偏移量
}TCP_HEADER; 

typedef struct _icmphdr {  
    unsigned char  icmp_type;  
    unsigned char icmp_code; /* type sub code */  
    unsigned short icmp_cksum;  
    unsigned short icmp_id;  
    unsigned short icmp_seq;  
    /* This is not the std header, but we reserve space for time */  
    unsigned short icmp_timestamp;  
}ICMP_HEADER;

void analyseIP(IP_HEADER *ip);
void analyseTCP(TCP_HEADER *tcp);
void analyseUDP(UDP_HEADER *udp);
void analyseICMP(ICMP_HEADER *icmp);


int main(void)
{
    int sockfd;
     IP_HEADER *ip;
    char buf[10240];
    ssize_t n;
    /* capture ip datagram without ethernet header */
    if ((sockfd = socket(PF_PACKET,  SOCK_DGRAM, htons(ETH_P_IP)))== -1)
    {    
        printf("socket error!\n");
        return 1;
    }
    while (1)
    {
        n = recv(sockfd, buf, sizeof(buf), 0);
        if (n == -1)
        {
            printf("recv error!\n");
            break;
        }
        else if (n==0)
            continue;
        //接收數據不包括數據鏈路幀頭
        ip = ( IP_HEADER *)(buf);
        analyseIP(ip);
        size_t iplen =  (ip->h_verlen&0x0f)*4;
        TCP_HEADER *tcp = (TCP_HEADER *)(buf +iplen);
        if (ip->proto == IPPROTO_TCP)
        {
            TCP_HEADER *tcp = (TCP_HEADER *)(buf +iplen);
            analyseTCP(tcp);
        }
        else if (ip->proto == IPPROTO_UDP)
        {
            UDP_HEADER *udp = (UDP_HEADER *)(buf + iplen);
            analyseUDP(udp);
        }
        else if (ip->proto == IPPROTO_ICMP)
        {
            ICMP_HEADER *icmp = (ICMP_HEADER *)(buf + iplen);
            analyseICMP(icmp);
        }
        else if (ip->proto == IPPROTO_IGMP)
        {
            printf("IGMP----\n");
        }
        else
        {
            printf("other protocol!\n");
        }        
        printf("\n\n");
    }
    close(sockfd);
    return 0;
}

void analyseIP(IP_HEADER *ip)
{
    unsigned char* p = (unsigned char*)&ip->sourceIP;
    printf("Source IP   : %u.%u.%u.%u\n",p[0],p[1],p[2],p[3]);
    p = (unsigned char*)&ip->destIP;
    printf("Destination IP  : %u.%u.%u.%u\n",p[0],p[1],p[2],p[3]);

}

void analyseTCP(TCP_HEADER *tcp)
{
    printf("TCP -----\n");
    printf("Source port: %u\n", ntohs(tcp->th_sport));
    printf("Dest port: %u\n", ntohs(tcp->th_dport));
}

void analyseUDP(UDP_HEADER *udp)
{
    printf("UDP -----\n");
    printf("Source port: %u\n", ntohs(udp->uh_sport));
    printf("Dest port: %u\n", ntohs(udp->uh_dport));
}

void analyseICMP(ICMP_HEADER *icmp)
{
    printf("ICMP -----\n");
    printf("type: %u\n", icmp->icmp_type);
    printf("sub code: %u\n", icmp->icmp_code);
}

--------------------- 本文來自 helloworldyu 的CSDN 博客 ,全文地址請點擊:https://blog.csdn.net/yuhaiyang457288/article/details/78090673?utm_source=copy

相關文章
相關標籤/搜索