其他分享
首页 > 其他分享> > 使用 libpcap 在路由上捕捉 http 请求网址 自制 SWF

使用 libpcap 在路由上捕捉 http 请求网址 自制 SWF

作者:互联网

最近研究 kernel netfilter BPF 又想起来以前用 tcpdump 抓包的快乐时光。

就想着做一个,抓包软件名称想好了,就是大名鼎鼎的,哥WF 现在有正名了叫《数据安全网关》,所以取名为 SWF 即,简易版哥WF。

首先是基础知识,这些很有用,其实使用原始的 BPF 也能很方便的抓取数据包,但是语法怪怪的,使用 libpcap 能简化开发。

OSI参考模型
7层模型
应用层
表示层
会话层
传输层
网络层
数据链路层
物理层

5层模型
应用层 --》 对应7层前3层 ->> HTTP FTP
传输层 ->> TCP
网络层 ->> IP ICMP IGMP RIP
数据链路层 ->> ARP RARP IEEE802.3 PPP CSMA/CD
物理层 ->> FE 自协商 Manchester MLT-3 4A PAM5

使用 wireshark 抓包一个普通的 http 包

1,配置过波规则 tcp.dstport == 80

2,使用 curl http://www.baidu.com 发出请求

1 Frame 33: 141 bytes on wire (1128 bits), 141 bytes captured (1128 bits) on interface 0 -------------> 以太网 链路层 总长度
2 Ethernet II, Src: Vmware_b0:f1:9c (00:0c:29:b0:f1:9c), Dst: Vmware_fe:94:1d (00:50:56:fe:94:1d) ------> 发送接收 MAC 和 帧类型 长度 14
3 Internet Protocol Version 4, Src: 192.168.80.129, Dst: 35.232.111.17 ------> IP 长度 20
4 Transmission Control Protocol, Src Port: 39266, Dst Port: 80, Seq: 1, Ack: 1, Len: 87 ------> TCP
5 Hypertext Transfer Protocol ------> HTTP 真实数据

IP 中保存 发送长度 减去 TCP 头大小后,即真实数据

但 TCP 头部大小会变化,例:握手连接时

 1 Frame 7: 74 bytes on wire (592 bits), 74 bytes captured (592 bits) on interface 0
 2 Ethernet II, Src: Vmware_b0:f1:9c (00:0c:29:b0:f1:9c), Dst: Vmware_fe:94:1d (00:50:56:fe:94:1d)
 3 Internet Protocol Version 4, Src: 192.168.80.129, Dst: 182.61.200.7
 4 Transmission Control Protocol, Src Port: 45486, Dst Port: 80, Seq: 0, Len: 0
 5 Source Port: 45486
 6 Destination Port: 80
 7 [Stream index: 0]
 8 [TCP Segment Len: 0]
 9 Sequence number: 0 (relative sequence number)
10 [Next sequence number: 0 (relative sequence number)]
11 Acknowledgment number: 0
12 1010 .... = Header Length: 40 bytes (10) --------------> 40Byte
13 Flags: 0x002 (SYN)
14 Window size value: 64240
15 [Calculated window size: 64240]
16 Checksum: 0xadd3 [unverified]
17 [Checksum Status: Unverified]
18 Urgent pointer: 0
19 Options: (20 bytes), Maximum segment size, SACK permitted, Timestamps, No-Operation (NOP), Window scale
20 [Timestamps]
21 
22 Frame 9: 54 bytes on wire (432 bits), 54 bytes captured (432 bits) on interface 0
23 Ethernet II, Src: Vmware_b0:f1:9c (00:0c:29:b0:f1:9c), Dst: Vmware_fe:94:1d (00:50:56:fe:94:1d)
24 Internet Protocol Version 4, Src: 192.168.80.129, Dst: 182.61.200.7
25 Transmission Control Protocol, Src Port: 45486, Dst Port: 80, Seq: 1, Ack: 1, Len: 0
26 Source Port: 45486
27 Destination Port: 80
28 [Stream index: 0]
29 [TCP Segment Len: 0]
30 Sequence number: 1 (relative sequence number)
31 [Next sequence number: 1 (relative sequence number)]
32 Acknowledgment number: 1 (relative ack number)
33 0101 .... = Header Length: 20 bytes (5) ------------> 20Byte
34 Flags: 0x010 (ACK)
35 Window size value: 64240
36 [Calculated window size: 64240]
37 [Window size scaling factor: -2 (no window scaling used)]
38 Checksum: 0x1526 [unverified]
39 [Checksum Status: Unverified]
40 Urgent pointer: 0
41 [SEQ/ACK analysis]
42 [Timestamps]

先是 以太网帧,然后是 ip 帧,然后是 tcp 头,然后是数据。

下面是实现代码:

  1 /**
  2  * soft: smple WF
  3  * author: nejidev
  4  * date: 2021-12-11 21:19
  5  */
  6 #define _GNU_SOURCE         /* See feature_test_macros(7) */
  7 #include <stdio.h>
  8 #include <string.h>
  9 #include <time.h>
 10 
 11 #include <arpa/inet.h>
 12 #include <net/ethernet.h>
 13 #include <linux/ip.h>
 14 #include <linux/tcp.h>
 15 
 16 #include <sys/socket.h>
 17 #include <netinet/in.h>
 18 #include <arpa/inet.h>
 19 
 20 #include <pcap/pcap.h>
 21 
 22 //ubuntu 18.0.4 sudo apt-get install libpcap-dev
 23 //gcc pcap_test.c -lpcap -o pcap_test
 24 //sudo ./pcap_test
 25 
 26 #define HTTP_HOST_FILE "http_host.txt"
 27 
 28 #define LOG_D(fmt, ...) printf("[D: fun:%s line:%d] " fmt "\n", __func__, __LINE__, ##__VA_ARGS__);
 29 #define LOG_I(fmt, ...) printf("[I: fun:%s line:%d] " fmt "\n", __func__, __LINE__, ##__VA_ARGS__);
 30 
 31 static void write_date(FILE *file)
 32 {
 33     time_t now_time;
 34     struct tm *tm;
 35     char now_date[32] = {0};
 36 
 37     time (&now_time);
 38     tm = gmtime(&now_time);
 39 
 40     snprintf(now_date, sizeof(now_date), "\n%d-%d-%d %d:%d:%d ",
 41         1900 + tm->tm_year,
 42         1 + tm->tm_mon,
 43         tm->tm_mday,
 44         8 + tm->tm_hour,
 45         tm->tm_min,
 46         tm->tm_sec
 47     );
 48     fwrite(now_date, 1, strlen(now_date), file);
 49 }
 50 
 51 void capture_one_test()
 52 {
 53     char error_buf[PCAP_ERRBUF_SIZE] = {0};
 54     int  ret           = 0;
 55     char *net_dev      = NULL;
 56     FILE *file         = NULL;
 57     unsigned char *mac = NULL;
 58     //保存接收到的数据包
 59     const unsigned char *packet_content    = NULL; 
 60     struct pcap_pkthdr protocol_header     = {0};
 61     // 分析以太网中的 源mac、目的mac  
 62     struct ether_header *ethernet_protocol = NULL;
 63 
 64     net_dev = pcap_lookupdev(error_buf);
 65 
 66     LOG_D("default net_dev:%s", net_dev);
 67 
 68     /*
 69     device:网络接口的名字,为第一步获取的网络接口字符串(pcap_lookupdev() 的返回值 ),也可人为指定,如“eth0”。
 70     snaplen:捕获数据包的长度,长度不能大于 65535 个字节。
 71     promise:“1” 代表混杂模式,其它非混杂模式。什么为混杂模式,请看《原始套接字编程》。
 72     to_ms:指定需要等待的毫秒数,超过这个数值后,获取数据包的函数就会立即返回(这个函数不会阻塞,后面的抓包函数才会阻塞)。
 73            0 表示一直等待直到有数据包到来。
 74     ebuf:存储错误信息。
 75     */
 76     pcap_t *pcap_handle = pcap_open_live(net_dev, 65535, 1, 0, error_buf);
 77 
 78     if (! pcap_handle)
 79     {
 80         LOG_I("pcap_open_live failed");
 81         return ;
 82     }
 83 
 84     LOG_D("wait capture");
 85 
 86     packet_content = pcap_next(pcap_handle, &protocol_header);
 87 
 88     //数据包的实际长度
 89     LOG_D("protocol_header.len:%d", protocol_header.len);
 90 
 91     //以太网帧头部
 92     ethernet_protocol = (struct ether_header *)packet_content;
 93 
 94     //获取源mac
 95     mac = (unsigned char *)ethernet_protocol->ether_shost;  
 96     LOG_D("Mac Source Address is %02x:%02x:%02x:%02x:%02x:%02x",
 97         *(mac+0),*(mac+1),*(mac+2),*(mac+3),*(mac+4),*(mac+5));
 98 
 99     //获取目的mac
100     mac = (unsigned char *)ethernet_protocol->ether_dhost;  
101     LOG_D("Mac Destination Address is %02x:%02x:%02x:%02x:%02x:%02x",
102         *(mac+0),*(mac+1),*(mac+2),*(mac+3),*(mac+4),*(mac+5));
103 
104     //save to file
105     file = fopen("test_once.pcap", "w");
106     fwrite(packet_content, 1, protocol_header.len, file);
107     fclose(file);
108 
109     pcap_close(pcap_handle);
110 }
111 
112 void ethernet_protocol_callback(unsigned char *argument, const struct pcap_pkthdr *packet_heaher,
113                                 const unsigned char *packet_content) 
114 {
115     int offset = 0;
116     unsigned char *mac = NULL;
117     char *host         = NULL;
118     char *host_end     = NULL;
119     int   host_len     = 0;
120     FILE *cap_file     = NULL;
121     //以太网帧头部 分析以太网中的 源mac、目的mac  
122     struct ether_header *ethernet_protocol = NULL;
123     //以太网类型
124     unsigned short ethernet_type           = 0;
125     //IP
126     struct iphdr *ip_header                = NULL;
127     //socket ip
128     struct in_addr addr                    = {0};
129     //TCP
130     struct tcphdr *tcp_header              = NULL;
131 
132     LOG_I("new package len:%d", packet_heaher->len);
133 
134     LOG_D("pkthdr len:%ld", sizeof(struct pcap_pkthdr)); //24
135     LOG_D("ip len:%ld",     sizeof(struct iphdr)); //20
136     LOG_D("tcp len:%ld",    sizeof(struct tcphdr)); //20 ?
137 
138     ethernet_protocol = (struct ether_header *)packet_content;
139 
140     //获取源mac
141     mac = (unsigned char *)ethernet_protocol->ether_shost;
142     LOG_D("Mac Source Address is %02x:%02x:%02x:%02x:%02x:%02x\n",*(mac+0),*(mac+1),*(mac+2),
143                                         *(mac+3),*(mac+4),*(mac+5));
144 
145     //获取目的mac
146     mac = (unsigned char *)ethernet_protocol->ether_dhost; 
147     LOG_D("Mac Destination Address is %02x:%02x:%02x:%02x:%02x:%02x\n",*(mac+0),*(mac+1),*(mac+2),
148                                         *(mac+3),*(mac+4),*(mac+5));
149 
150     //获得以太网的类型 
151     ethernet_type = ntohs(ethernet_protocol->ether_type);
152 
153     LOG_D("Ethernet type is :%04x\n", ethernet_type);
154 
155     switch(ethernet_type)
156     {
157         case 0x0800: LOG_D("IP protocol");  break;
158         case 0x0806: LOG_D("ARP protocol"); break;
159         case 0x0835: LOG_D("RARP protocol"); break;
160         default: break;
161     }
162 
163     if (0x0800 != ethernet_type)
164     {
165         return;
166     }
167 
168     ip_header = (struct iphdr *)(packet_content + sizeof(struct ether_header));
169 
170     addr.s_addr  = ip_header->saddr;
171     LOG_D("ip saddr:%s", inet_ntoa(addr));
172 
173     addr.s_addr  = ip_header->daddr;
174     LOG_D("ip daddr:%s", inet_ntoa(addr));
175 
176     switch (ip_header->protocol)
177     {
178         case 1: LOG_D("ICMP protocol"); break;
179         case 2: LOG_D("ICMP protocol"); break;
180         case 6: LOG_D("TCP protocol");  break;
181         case 17: LOG_D("UDP protocol"); break;
182     }
183 
184     if (6 != ip_header->protocol)
185     {
186         return ;
187     }
188 
189     tcp_header = (struct tcphdr *)(packet_content + sizeof(struct ether_header) + sizeof(struct iphdr));
190 
191     LOG_D("tcp source port:%d", ntohs(tcp_header->source));
192     LOG_D("tcp dest port:%d", ntohs(tcp_header->dest));
193     LOG_D("tcp header size:%ld", (tcp_header->doff)*sizeof(int));
194 
195     offset += sizeof(struct pcap_pkthdr);
196     offset += sizeof(struct iphdr);
197     offset += (tcp_header->doff)*sizeof(int);
198 
199     LOG_D("package offset:%d", offset);
200     LOG_D("package data:%s", packet_content + offset);
201 
202     host = strcasestr((const char *)packet_content + offset, "host");
203 
204     if (host)
205     {
206         host += strlen("host:");
207 
208         LOG_I("capture host:%s", host);
209 
210         host_end = strstr(host, "\n");
211         //host_end--;
212         host_len = host_end - host;
213 
214         LOG_D("host_len:%d", host_len);
215 
216         //eat space
217         while (' ' == *host)
218         {
219             host++;
220             host_len--;
221         }
222 
223         //save to file
224         cap_file = fopen(HTTP_HOST_FILE, "a");
225         write_date(cap_file);
226         fwrite(host, 1, host_len, cap_file);
227         fclose(cap_file);
228     }
229 }
230 
231 void capture_all_test()
232 {
233     char error_buf[PCAP_ERRBUF_SIZE] = {0};
234     int  ret                         = 0;
235     char *net_dev                    = NULL;
236     pcap_t *pcap_handle              = NULL;
237     struct bpf_program filter        = {0};
238     FILE *cap_file                   = NULL;
239 
240     net_dev = pcap_lookupdev(error_buf);
241 
242     LOG_D("default net_dev:%s", net_dev);
243 
244     pcap_handle = pcap_open_live(net_dev, 65535, 1, 0, error_buf);
245 
246     if (! pcap_handle)
247     {
248         LOG_I("pcap_open_live failed");
249         return ;
250     }
251 
252     cap_file = fopen(HTTP_HOST_FILE, "w");
253     fclose(cap_file);
254 
255     //wireshark tcp.dstport == 80
256     pcap_compile(pcap_handle, &filter, "dst port 80", 1, 0);  
257     pcap_setfilter(pcap_handle, &filter);
258 
259     if (0 > pcap_loop(pcap_handle, -1, ethernet_protocol_callback, NULL))
260     {
261         LOG_I("pcap_loop failed");
262         return ;
263     }
264 
265     LOG_I("wait loop exit");
266 }
267 
268 int main(int argc, char **argv)
269 {
270     //capture_one_test();
271     capture_all_test();
272     return 0;
273 }

测试环境:ubuntu 18.0.4 sudo apt-get install libpcap-dev
编译:gcc pcap_test.c -lpcap -o pcap_test
运行:sudo ./pcap_test

使用 curl 或 firefox 随便上,http 的网站,然后就会被记录下来。

capture_one_test(); 这个能抓包原始数据,经过对比和 Wireshark 中一致。

 

标签:http,LOG,libpcap,SWF,header,mac,protocol,pcap,02x
来源: https://www.cnblogs.com/ningci/p/15677170.html