linux原始套接字(3)-構造IP_TCP發送與接收

来源:http://www.cnblogs.com/yuuyuu/archive/2016/01/29/5169931.html
-Advertisement-
Play Games

一.概述 tcp報文封裝在ip報文中,創建tcp的原始套接字如下: 1 sockfd = socket(PF_INET, SOCK_RAW, IPPROTO_TCP); 此時只能構造tcp報文,如果想進一步構造ip首部,那麼就要開啟sockfd的IP_HDRINCL選項: 1 int on = 1;


一.概述                                                   

tcp報文封裝在ip報文中,創建tcp的原始套接字如下:

1 sockfd = socket(PF_INET, SOCK_RAW, IPPROTO_TCP);

此時只能構造tcp報文,如果想進一步構造ip首部,那麼就要開啟sockfd的IP_HDRINCL選項:

1 int on = 1;
2 setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on));

ip報文格式:

ip首部結構定義在netinet/ip.h

 1 struct ip
 2 {
 3 #if __BYTE_ORDER == __LITTLE_ENDIAN
 4     unsigned int ip_hl:4;        /* header length */
 5     unsigned int ip_v:4;        /* version */
 6 #endif
 7 #if __BYTE_ORDER == __BIG_ENDIAN
 8     unsigned int ip_v:4;        /* version */
 9     unsigned int ip_hl:4;        /* header length */
10 #endif
11     u_int8_t ip_tos;            /* type of service */
12     u_short ip_len;            /* total length */
13     u_short ip_id;            /* identification */
14     u_short ip_off;            /* fragment offset field */
15 #define    IP_RF 0x8000            /* reserved fragment flag */
16 #define    IP_DF 0x4000            /* dont fragment flag */
17 #define    IP_MF 0x2000            /* more fragments flag */
18 #define    IP_OFFMASK 0x1fff        /* mask for fragmenting bits */
19     u_int8_t ip_ttl;            /* time to live */
20     u_int8_t ip_p;            /* protocol */
21     u_short ip_sum;            /* checksum */
22     struct in_addr ip_src, ip_dst;    /* source and dest address */
23 };

tcp報文格式:

tcp首部結構定義在netinet/tcp.h

 1 struct tcphdr
 2   {
 3     __extension__ union
 4     {
 5       struct
 6       {
 7     u_int16_t th_sport;        /* source port */
 8     u_int16_t th_dport;        /* destination port */
 9     tcp_seq th_seq;        /* sequence number */
10     tcp_seq th_ack;        /* acknowledgement number */
11 # if __BYTE_ORDER == __LITTLE_ENDIAN
12     u_int8_t th_x2:4;        /* (unused) */
13     u_int8_t th_off:4;        /* data offset */
14 # endif
15 # if __BYTE_ORDER == __BIG_ENDIAN
16     u_int8_t th_off:4;        /* data offset */
17     u_int8_t th_x2:4;        /* (unused) */
18 # endif
19     u_int8_t th_flags;
20 # define TH_FIN    0x01
21 # define TH_SYN    0x02
22 # define TH_RST    0x04
23 # define TH_PUSH    0x08
24 # define TH_ACK    0x10
25 # define TH_URG    0x20
26     u_int16_t th_win;        /* window */
27     u_int16_t th_sum;        /* checksum */
28     u_int16_t th_urp;        /* urgent pointer */
29       };
30       struct
31       {
32     u_int16_t source;
33     u_int16_t dest;
34     u_int32_t seq;
35     u_int32_t ack_seq;
36 # if __BYTE_ORDER == __LITTLE_ENDIAN
37     u_int16_t res1:4;
38     u_int16_t doff:4;
39     u_int16_t fin:1;
40     u_int16_t syn:1;
41     u_int16_t rst:1;
42     u_int16_t psh:1;
43     u_int16_t ack:1;
44     u_int16_t urg:1;
45     u_int16_t res2:2;
46 # elif __BYTE_ORDER == __BIG_ENDIAN
47     u_int16_t doff:4;
48     u_int16_t res1:4;
49     u_int16_t res2:2;
50     u_int16_t urg:1;
51     u_int16_t ack:1;
52     u_int16_t psh:1;
53     u_int16_t rst:1;
54     u_int16_t syn:1;
55     u_int16_t fin:1;
56 # else
57 #  error "Adjust your <bits/endian.h> defines"
58 # endif
59     u_int16_t window;
60     u_int16_t check;
61     u_int16_t urg_ptr;
62       };
63     };
64 };

對照結構的定義和上面結構圖很容易理解。註意:ip和tcp的四位首部長度都是指占多少個32bit。如果普通ip首部長度是20位元組,4位元組占32位,那麼這個值就是20/4=5。

二.構造IP_TCP發送                              

  1 /**
  2  * @file ip_tcp_send.c
  3  */
  4 
  5 #include <stdio.h>
  6 #include <stdlib.h>
  7 #include <string.h>
  8 #include <unistd.h>
  9 #include <sys/socket.h>
 10 #include <arpa/inet.h>
 11 #include <netinet/in.h>
 12 #include <netinet/ip.h>
 13 #include <netinet/tcp.h>
 14 
 15 /* ip首部長度 */
 16 #define IP_HEADER_LEN sizeof(struct ip)
 17 /* tcp首部長度 */
 18 #define TCP_HEADER_LEN sizeof(struct tcphdr)
 19 /* ip首部 + tcp首部長度 */
 20 #define IP_TCP_HEADER_LEN IP_HEADER_LEN + TCP_HEADER_LEN
 21 
 22 void err_exit(const char *err_msg)
 23 {
 24     perror(err_msg);
 25     exit(1);
 26 }
 27 
 28 /* 填充ip首部 */
 29 struct ip *fill_ip_header(const char *src_ip, const char *dst_ip, int ip_packet_len)
 30 {
 31     struct ip *ip_header;
 32 
 33     ip_header = (struct ip *)malloc(IP_HEADER_LEN);
 34     ip_header->ip_v = IPVERSION;
 35     ip_header->ip_hl = sizeof(struct ip) / 4;        /* 這裡註意,ip首部長度是指占多個32位的數量,4位元組=32位,所以除以4 */
 36     ip_header->ip_tos = 0;
 37     ip_header->ip_len = htons(ip_packet_len);        /* 整個IP數據報長度,包括普通數據 */
 38     ip_header->ip_id = 0;                            /* 讓內核自己填充標識位 */
 39     ip_header->ip_off = 0;
 40     ip_header->ip_ttl = MAXTTL;
 41     ip_header->ip_p = IPPROTO_TCP;                   /* ip包封裝的協議類型 */
 42     ip_header->ip_sum = 0;                           /* 讓內核自己計算校驗和 */
 43     ip_header->ip_src.s_addr = inet_addr(src_ip);    /* 源IP地址 */
 44     ip_header->ip_dst.s_addr = inet_addr(dst_ip);    /* 目標IP地址 */
 45 
 46     return ip_header;
 47 }
 48 
 49 /* 填充tcp首部 */
 50 struct tcphdr *fill_tcp_header(int src_port, int dst_port)
 51 {
 52     struct tcphdr *tcp_header;
 53 
 54     tcp_header = (struct tcphdr *)malloc(TCP_HEADER_LEN);
 55     tcp_header->source = htons(src_port); 
 56     tcp_header->dest = htons(dst_port);
 57     /* 同IP首部一樣,這裡是占32位的位元組多少個 */
 58     tcp_header->doff = sizeof(struct tcphdr) / 4;
 59     /* 發起連接 */
 60     tcp_header->syn = 1;
 61     tcp_header->window = 4096;
 62     tcp_header->check = 0;
 63 
 64     return tcp_header;
 65 }
 66 
 67 /* 發送ip_tcp報文 */
 68 void ip_tcp_send(const char *src_ip, int src_port, const char *dst_ip, int dst_port, const char *data)
 69 {
 70     struct ip *ip_header;
 71     struct tcphdr *tcp_header;
 72     struct sockaddr_in dst_addr;
 73     socklen_t sock_addrlen = sizeof(struct sockaddr_in);
 74 
 75     int data_len = strlen(data);
 76     int ip_packet_len = IP_TCP_HEADER_LEN + data_len;
 77     char buf[ip_packet_len];
 78     int sockfd, ret_len, on = 1;
 79 
 80     bzero(&dst_addr, sock_addrlen);
 81     dst_addr.sin_family = PF_INET;
 82     dst_addr.sin_addr.s_addr = inet_addr(dst_ip);
 83     dst_addr.sin_port = htons(dst_port);
 84 
 85     /* 創建tcp原始套接字 */
 86     if ((sockfd = socket(PF_INET, SOCK_RAW, IPPROTO_TCP)) == -1)
 87         err_exit("socket()");
 88 
 89     /* 開啟IP_HDRINCL,自定義IP首部 */
 90     if (setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)) == -1)
 91         err_exit("setsockopt()");
 92 
 93     /* ip首部 */
 94     ip_header = fill_ip_header(src_ip, dst_ip, ip_packet_len);
 95     /* tcp首部 */
 96     tcp_header = fill_tcp_header(src_port, dst_port);
 97 
 98     bzero(buf, ip_packet_len);
 99     memcpy(buf, ip_header, IP_HEADER_LEN);
100     memcpy(buf + IP_HEADER_LEN, tcp_header, TCP_HEADER_LEN);
101     memcpy(buf + IP_TCP_HEADER_LEN, data, data_len);
102 
103     /* 發送報文 */
104     ret_len = sendto(sockfd, buf, ip_packet_len, 0, (struct sockaddr *)&dst_addr, sock_addrlen);
105     if (ret_len > 0)
106         printf("sendto() ok!!!\n");
107     else printf("sendto() failed\n");
108 
109     close(sockfd);
110     free(ip_header);
111     free(tcp_header);
112 }
113 
114 int main(int argc, const char *argv[])
115 {
116     if (argc != 6)
117     {
118         printf("usage:%s src_ip src_port dst_ip dst_port data\n", argv[0]);
119         exit(1);
120     }
121 
122     /* 發送ip_tcp報文 */
123     ip_tcp_send(argv[1], atoi(argv[2]), argv[3], atoi(argv[4]), argv[5]);
124 
125     return 0;
126 }

流程:命令行接收的參數分別是:源ip地址源埠目標ip地址目標埠普通數據。然後通過源/目標ip構造ip首部,通過源/目標埠構造tcp首部。把目標ip/埠填充到sockaddr_in,最後把構造的ip首部,tcp首部,普通數據全部複製到緩衝區,一併發送到剛剛的sockaddr_in的地址!!!

三.接收IP_TCP                                      

 1 /**
 2  * @file ip_tcp_recv.c
 3  */
 4 
 5 #include <stdio.h>
 6 #include <stdlib.h>
 7 #include <string.h>
 8 #include <unistd.h>
 9 #include <sys/socket.h>
10 #include <arpa/inet.h>
11 #include <netinet/in.h>
12 #include <netinet/ip.h>
13 #include <netinet/tcp.h>
14 
15 /* ip首部長度 */
16 #define IP_HEADER_LEN sizeof(struct ip)
17 /* tcp首部長度 */
18 #define TCP_HEADER_LEN sizeof(struct tcphdr)
19 /* ip首部 + tcp首部長度 */
20 #define IP_TCP_HEADER_LEN IP_HEADER_LEN + TCP_HEADER_LEN
21 /* 接收數據緩衝大小 */
22 #define BUFFER_SIZE 1024
23 /* ip首部 + tcp首部 + 數據緩衝區大小 */
24 #define IP_TCP_BUFF_SIZE IP_TCP_HEADER_LEN + BUFFER_SIZE
25 
26 void err_exit(const char *err_msg)
27 {
28     perror(err_msg);
29     exit(1);
30 }
31 
32 /* 原始套接字接收 */
33 void raw_socket_recv()
34 {
35     struct ip *ip_header;
36     struct tcphdr *tcp_header;
37     int sock_raw_fd, ret_len;
38     char buf[IP_TCP_BUFF_SIZE];
39 
40     if ((sock_raw_fd = socket(PF_INET, SOCK_RAW, IPPROTO_TCP)) == -1)
41         err_exit("socket()");
42 
43     /* 接收數據 */
44     while (1)
45     {
46         bzero(buf, IP_TCP_BUFF_SIZE);
47         ret_len = recv(sock_raw_fd, buf, IP_TCP_BUFF_SIZE, 0);
48         if (ret_len > 0)
49         {
50             /* 取出ip首部 */
51             ip_header = (struct ip *)buf;
52             /* 取出tcp首部 */
53             tcp_header = (struct tcphdr *)(buf + IP_HEADER_LEN);
54             printf("=======================================\n");
55             printf("from ip:%s\n", inet_ntoa(ip_header->ip_src));
56             printf("from port:%d\n", ntohs(tcp_header->source));
57             /* 取出數據 */
58             printf("get data:%s\n", buf + IP_TCP_HEADER_LEN);
59         }
60     }
61     
62     close(sock_raw_fd);
63 }
64 
65 int main(void)
66 {
67     /* 原始套接字接收 */
68     raw_socket_recv();
69 
70     return 0;
71 }

流程:創建TCP類型的原始套接,原始套接字是點對點傳輸,不像TCP/UDP是端對端,故原始套接字不存在埠概念,可以直接接收。這裡接收的是整個IP報文,裡面包含了TCP報文。接收後依次取出ip首部,tcp首部,普通數據!!!

四.實驗                                                  

發送端和接收端都在本機,我們打開wireshark監聽lo介面。以root身份運行2個程式:

上面發送了2個tcp包,每次的源ip,源埠都是偽造的。DDOS程式就是這個原理。

wireshark結果


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1、假設備份文件xxxx.bak大小約300G,還原後所占用的空間為900G 2、磁碟空間只有1T,若將備份文件拷貝過來,空間剩餘700G,無法成功還原,因此通過遠程方式還原。 例子如下: SQLSERVER服務實例名稱: 192.168.12.163需要備份的資料庫名稱: a備份機器名稱(Clie
  • 配置文件內容:[root@yoon etc]# cat mongod.conf logpath=/export/log/mongodb.loglogappend=truefork = truedbpath=/export/data/dbpidfilepath = /export/mongodb/et
  • ===================================================================================================================#!/bin/sh. /etc/profile DATE=`date
  • Output子句日常灰常有用,而且用的地方也挺多,但是確好多時候被我們忽視,今天我就也簡單掃盲一下這個語句的用法。 Output子句 返回受 INSERT、UPDATE、DELETE 或 MERGE 語句影響的各行中的信息,或返回基於受這些語句影響的各行的表達式。 這些結果可以返回到處理應用程式,以
  • 一直沒有在意過資料庫處理樹形數據的重要性,直到有一天朋友問起我關於樹形數據查詢的問題時才發現根本不會,正好這個時候也要用到遞歸進行樹形數據的查詢於是在網上查了一圈,語法總結如下 參考文獻:https://msdn.microsoft.com/query/dev10.query?appId=Dev10
  • declare @temp_temp uniqueidentifier--臨時變數 DECLARE aaa CURSOR for select Id from A -------------------打開游標 open aaa --先查詢一次再迴圈,防止有多個游標時@@FETCH_STATUS=-
  • 目錄 1 建立資料庫檔案 2 在sqlite3提示列下操作 3 SQL的指令格式 4 建立資料表 5 建立索引 6 加入一筆資料 7 查詢資料 8 如何更改或刪除資料 9 其他sqlite的特別用法 10 小結 建立資料庫檔案 用sqlite3建立資料庫的方法很簡單,只要在shell下鍵入(以下$符
  • 一.概述 同上一篇tcp一樣,udp也是封裝在ip報文裡面。創建UDP的原始套接字如下: 1 (sockfd = socket(PF_INET, SOCK_RAW, IPPROTO_UDP); 同樣,如果要構造udp的ip首部,要開啟IP_HDRINCL選項! udp首部格式: udp的不可靠性,比
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...