使用libpcap获取http报文
在上一篇博客中简单对libpcap库基本函数及基本工作流程做了些简单说明,
今天我们先了解一下pcap_loop()及pcap_dispatch()函数的功能及作用:
(1)pcap_loop()循环进行数据包的抓取:
函数原型如下:
1 typedef void (*pcap_handler)(u_char *user, const struct pcap_pkthdr *h, 2 const u_char *bytes); 3 4 int pcap_loop(pcap_t *p, int cnt, pcap_handler callback, u_char *user); 5 /*参数说明: 6 功能:循环捕获数据包,不会响应pcap_open_live()函数设置的超时时间 7 参数 pcap_t *p: p是嗅探器会话句柄 8 参数 cnt:cnt用于设置所捕获数据包的个数,负数的cnt表示pcap_loop永远循环抓包,直到出现错误。 9 参数callback:是个回调函数指针,它的原型如下: 10 typedef void (*pcap_handler)(u_char *user, const struct pcap_pkthdr *h, 11 const u_char *bytes); 12 参数 user:用来给回调函数传递参数的,在callback函数当中只有第一个user指针是可以留给用户使用的, 13 如果你想给callback传递自己参数,那就只能通过pcap_loop的最后一个参数user来实现了*/ 14 15 struct pcap_pkthdr { 16 struct timeval ts; /* time stamp */ 17 bpf_u_int32 caplen; /* length of portion present */ 18 bpf_u_int32 len; /* length this packet (off wire) */ 19 }; 20 //ts——时间戳 21 //caplen——真正实际捕获的包的长度 22 //len——这个包的长度 23 24 /*因为在某些情况下你不能保证捕获的包是完整的,例如一个包长1480,但是你捕获到1000的时候, 25 可能因为某些原因就中止捕获了,所以caplen是记录实际捕获的包长,也就是1000,而len就是1480。*/
(2)pcap_dispatch()这个函数和pcap_loop()非常类似,只是在超过to_ms毫秒后就会返回(to_ms是pcap_open_live()的第4个参数)
下面是函数原型:
1 int pcap_dispatch(pcap_t *p, int cnt, 2 pcap_handler callback, u_char *user);
说完两个函数的作用,下面我们开始自制我们自己的sniffer,改程序的功能是循环抓取以太网报文并获取其中的http报文,解析并显示其相应的url及长度
下面直接贴出代码:
1 #include <stdio.h> 2 #include <string.h> 3 #include <stdlib.h> 4 #include <stdint.h> 5 #include <pcap.h> 6 #include <time.h> 7 #include <netinet/in.h> 8 #include <arpa/inet.h> 9 #include <linux/if_ether.h> 10 #include <linux/ip.h> 11 #include <linux/tcp.h> 12 13 #define DEVICE "enp0s3" 14 #define URL_MAX_LEN 2048 15 #define MAX_HOST_LEN 1024 16 #define MAX_GET_LEN 2048 17 18 #define get_u_int8_t(X,O) (*(uint8_t *)(((uint8_t *)X) + O)) 19 #define get_u_int16_t(X,O) (*(uint16_t *)(((uint8_t *)X) + O)) 20 #define get_u_int32_t(X,O) (*(uint32_t *)(((uint8_t *)X) + O)) 21 #define get_u_int64_t(X,O) (*(uint64_t *)(((uint8_t *)X) + O)) 22 23 /*Display Ethernet Header*/ 24 void show_ethhdr(struct ethhdr *eth) 25 { 26 printf("----------------eth---------------------\n"); 27 printf("destination eth addr: %02x:%02x:%02x:%02x:%02x:%02x\n", 28 eth->h_dest[0], eth->h_dest[1], 29 eth->h_dest[2], eth->h_dest[3], 30 eth->h_dest[4], eth->h_dest[5]); 31 printf("source eth addr: %02x:%02x:%02x:%02x:%02x:%02x\n", 32 eth->h_source[0], eth->h_source[1], 33 eth->h_source[2], eth->h_source[3], 34 eth->h_source[4], eth->h_source[5]); 35 printf("protocol is: %04x\n", ntohs(eth->h_proto)); 36 } 37 38 /*Display IP Header*/ 39 void show_iphdr(struct iphdr *ip) 40 { 41 struct in_addr addr; 42 43 printf("----------------ip----------------------\n"); 44 printf("version: %d\n", ip->version); 45 printf("head len: %d\n", ip->ihl * 4); 46 printf("total len: %d\n", ntohs(ip->tot_len)); 47 printf("ttl: %d\n", ip->ttl); 48 printf("protocol: %d\n", ip->protocol); 49 printf("check: %x\n", ip->check); 50 addr.s_addr = ip->saddr; 51 printf("saddr: %s\n", inet_ntoa(addr)); 52 addr.s_addr = ip->daddr; 53 printf("daddr: %s\n", inet_ntoa(addr)); 54 } 55 56 /*Display TCP Header*/ 57 void show_tcphdr(struct tcphdr *tcp) 58 { 59 printf("----------------tcp---------------------\n"); 60 printf("tcp len: %d\n", sizeof(struct tcphdr)); 61 printf("tcp->doff: %d\n", tcp->doff * 4); 62 printf("source port: %d\n", ntohs(tcp->source)); 63 printf("dest port: %d\n", ntohs(tcp->dest)); 64 printf("sequence number: %d\n", ntohs(tcp->seq)); 65 printf("ack sequence: %d\n", ntohs(tcp->ack_seq)); 66 } 67 68 int parse_http_head(const u_char *payload, int payload_len, char *url) 69 { 70 int line_len, offset; 71 int ustrlen; 72 int hstrlen; //"host: " 73 int hostlen; 74 int getlen; 75 char host[MAX_HOST_LEN]; 76 char get[MAX_GET_LEN]; 77 int a, b; 78 79 /*filter get packet*/ 80 if(memcmp(payload, "GET ", 4)) { 81 return 0; 82 } 83 84 for(a = 0, b = 0; a < payload_len - 1; a++) { 85 if (get_u_int16_t(payload, a) == ntohs(0x0d0a)) { 86 line_len = (u_int16_t)(((unsigned long) &payload[a]) - ((unsigned long)&payload[b])); 87 88 if (line_len >= (9 + 4) 89 && memcmp(&payload[line_len - 9], " HTTP/1.", 8) == 0) { 90 memcpy(get, payload + 4, line_len - 13); //"GET HTTP/1.x" 13bit 91 getlen = line_len - 13; 92 } 93 /*get url host of pcaket*/ 94 if (line_len > 6 95 && memcmp(&payload[b], "Host:", 5) == 0) { 96 if(*(payload + b + 5) == ' ') { 97 hstrlen = b + 6; 98 } else { 99 hstrlen = b + 5; 100 } 101 hostlen = a - hstrlen; 102 memcpy(host, payload + hstrlen, (a - hstrlen)); 103 } 104 b = a + 2; 105 } 106 } 107 offset = 7; 108 memcpy(url, "http://", offset); 109 memcpy(url + offset, host, hostlen); 110 offset += hostlen; 111 memcpy(url + offset, get, getlen); 112 113 return strlen(url); 114 } 115 116 void packet_http_handle(const u_char *tcp_payload, int payload_len) 117 { 118 int url_len; 119 char url[URL_MAX_LEN]; 120 121 url_len = parse_http_head(tcp_payload, payload_len, url); 122 if (url_len <= 7) { 123 return; 124 } 125 printf("----------------HTTP---------------------\n"); 126 printf("url_len: %d\n", url_len); 127 printf("url: %s\n", url); 128 } 129 130 int prase_packet(const u_char *buf, int caplen) 131 { 132 uint16_t e_type; 133 uint32_t offset; 134 int payload_len; 135 const u_char *tcp_payload; 136 137 /* ether header */ 138 struct ethhdr *eth = NULL; 139 eth = (struct ethhdr *)buf; 140 e_type = ntohs(eth->h_proto); 141 offset = sizeof(struct ethhdr); 142 show_ethhdr(eth); 143 144 /*vlan 802.1q*/ 145 while(e_type == ETH_P_8021Q) { 146 e_type = (buf[offset+2] << 8) + buf[offset+3]; 147 offset += 4; 148 } 149 if (e_type != ETH_P_IP) { 150 return -1; 151 } 152 153 /* ip header */ 154 struct iphdr *ip = (struct iphdr *)(buf + offset); 155 e_type = ntohs(ip->protocol); 156 offset += sizeof(struct iphdr); 157 show_iphdr(ip); 158 159 if(ip->protocol != IPPROTO_TCP) { 160 return -1; 161 } 162 163 /*tcp header*/ 164 struct tcphdr *tcp = (struct tcphdr *)(buf + offset); 165 offset += (tcp->doff << 2); 166 payload_len = caplen - offset; 167 tcp_payload = (buf + offset); 168 show_tcphdr(tcp); 169 170 /*prase http header*/ 171 packet_http_handle(tcp_payload, payload_len); 172 173 return 0; 174 } 175 176 void get_packet(u_char *user, const struct pcap_pkthdr *pkthdr, const u_char *packet) 177 { 178 static int count = 0; 179 printf("\n----------------------------------------\n"); 180 printf("\t\tpacket %d\n", count); 181 printf("----------------------------------------\n"); 182 printf("Packet id: %d\n", count); 183 printf("Packet length: %d\n", pkthdr->len); 184 printf("Number of bytes: %d\n", pkthdr->caplen); 185 printf("Recieved time: %s\n", ctime((const time_t *)&pkthdr->ts.tv_sec)); 186 187 prase_packet(packet, pkthdr->len); 188 count++; 189 } 190 191 int main() 192 { 193 char errBuf[PCAP_ERRBUF_SIZE]; /*error Buff*/ 194 struct pcap_pkthdr packet; /*The header that pcap gives us*/ 195 pcap_t *dev; /*network interface*/ 196 bpf_u_int32 netp, maskp; 197 char *net, *mask; 198 struct in_addr addr; 199 int ret; 200 201 /*look up device network addr and mask*/ 202 if(pcap_lookupnet(DEVICE, &netp, &maskp, errBuf)) { 203 printf("get net failure\n"); 204 exit(1); 205 } 206 addr.s_addr = netp; 207 net = inet_ntoa(addr); 208 printf("network: %s\n", net); 209 210 addr.s_addr = maskp; 211 mask = inet_ntoa(addr); 212 printf("mask: %s\n", mask); 213 214 /*open network device for packet capture*/ 215 dev = pcap_open_live(DEVICE, 65536, 1, 0, errBuf); 216 if(NULL == dev) { 217 printf("open %s failure\n", DEVICE); 218 exit(1); 219 } 220 221 /*process packets from a live capture or savefile*/ 222 pcap_loop(dev, 0, get_packet, NULL); 223 224 /*close device*/ 225 pcap_close(dev); 226 227 return 0; 228 } 229
下面是运行结果:
1 ---------------------------------------- 2 packet 3667 3 ---------------------------------------- 4 Packet id: 3667 5 Packet length: 198 6 Number of bytes: 198 7 Recieved time: Mon Aug 15 04:07:20 2016 8 9 ----------------eth--------------------- 10 destination eth addr: 00:90:0b:12:58:2b 11 source eth addr: 08:00:27:25:e7:52 12 protocol is: 0800 13 ----------------ip---------------------- 14 version: 4 15 head len: 20 16 total len: 184 17 ttl: 64 18 protocol: 6 19 check: f793 20 saddr: 192.168.16.125 21 daddr: 119.84.70.22 22 ----------------tcp--------------------- 23 tcp len: 20 24 tcp->doff: 20 25 source port: 55420 26 dest port: 80 27 sequence number: 12053 28 ack sequence: 5286 29 ----------------HTTP--------------------- 30 url_len: 54 31 url: http://cc.stream.qqmusic.qq.com/C200003a0iyj2fOc6y.m4a