libpcap抓取数据包

libpcap是数据包捕获函数库。该库提供的C函数接口可用于需要捕获经过网络接口数据包的系统开发上。libpcap提供的接口函数主要实现和封装了与数据包截获有关的过程。这个库为不同的平台提供了一致的编程接口,在安装了libpcap的平台上,以libpcap为接口写的程序,能够自由的跨平台使用。

linux下libpcap的安装:sudo apt-get install libpcap-dev

linux下gcc编译程序:gcc my_pcap.c -lpcap

执行程序的时候如果报错:no suitable device found,以管理员权限运行程序即可,sudo ./my_pcap

 

libpcap的抓包框架:

头文件: #include <pcap.h> 在/usr/local/include/pcap目录下

1.查找网络设备

char *pcap_lookupdev(char *errbuf) 

该函数用于返回可被pcap_open_live()或pcap_lookupnet()函数调用的网络设备名(一个字符串指针)。如果函数出错,则返回NULL,同时errbuf中存放相关的错误消息。

2.获得指定网络设备的网络号和掩码

int pcap_lookupnet(char *device, bpf_u_int32 *netp, bpf_u_int32 *maskp, char *errbuf) 

netp参数和maskp参数都是bpf_u_int32指针。如果函数出错,则返回-1,同时errbuf中存放相关的错误消息。

Bpf_u_int32:32位无符号数

Struct in_addr

{

   unsigned long s_addr;

}

inet_ntoa();以a.b.c.d的形式显示地址。

3.打开网络设备 

 pcap_t *pcap_open_live(char *device,  int snaplen,  int promisc, int to_ms, char *ebuf) 

获得用于捕获网络数据包的数据包捕获描述字。device参数为指定打开的网络设备名。snaplen参数定义捕获数据的最大字节数,65535是最大值。promisc指定 是否将网络接口置于混杂模式,设置为1表示混杂模式。to_ms参数指定超时时间(毫秒),设置为0表示超时时间无限大。ebuf参数则仅在pcap_open_live()函数出错返回NULL时用于传递 错误消息。

typedef struct pcap pcap_t;

pcap结构在libpcap源码的pcap-int.h定义,使用时一般都是使用其指针类型)。

4.打开已有的网络数据包 //如果是抓取数据包,这个过程不需要

pcap_t *pcap_open_offline(char *fname, char *errbuf)

fname参数指定打开的文件名。该文件中的数据格式与tcpdump兼容。errbuf参数则仅在pcap_open_offline()函数出错返回NULL时用于传递错误消息。

pcap_t *pcap_fopen_offline(FILE *fp, char *errbuf)打开文件指针。

5.编译和设置过滤条件

int pcap_compile(pcap_t *p, struct bpf_program *fp, char *str,  int optimize, bpf_u_int32 netmask) 

设置过滤条件,举一些例子:

  • src host 192.168.1.1:只接收源ip地址是192.168.1.1的数据包
  • dst port 80:只接收tcp、udp的目的端口是80的数据包
  • not tcp:只接收不使用tcp协议的数据包
  • tcp[13] == 0x02 and (dst port 22 or dst port 23) :只接收 SYN 标志位置位且目标端口是 22 或 23 的数据包( tcp 首部开始的第 13 个字节)
  • icmp[icmptype] == icmp-echoreply or icmp[icmptype] == icmp-echo:只接收 icmp 的 ping 请求和 ping 响应的数据包
  • ehter dst 00:e0:09:c1:0e:82:只接收以太网 mac 地址是 00:e0:09:c1:0e:82 的数据包
  • ip[8] == 5:只接收 ip 的 ttl=5 的数据包(ip首部开始的第8个字节)

str参数指定的字符串编译到过滤程序中。fp是一个bpf_program结构的指针,在pcap_compile()函数中被赋值。optimize参数控制结果代码的优化。netmask参数指定本地网络的网络掩码,当不知道的时候可以设为0。出错时返回-1.

int pcap_setfilter(pcap_t *p, struct bpf_program *fp) 

指定一个过滤程序。fp参数是bpf_program结构指针,通常取自pcap_compile()函数调用。出错时返回-1。

6.抓取和读取数据包

int pcap_dispatch(pcap_t *p, int cnt, pcap_handler callback,  u_char *user) 

捕获并处理数据包。cnt参数指定函数返回前所处理数据包的最大值。cnt=-1表示在一个缓冲区中处理所有的数据包。callback参数指定一个带有三个参数的回调函数,这三个参数为:一个从 pcap_dispatch()函数传递过来的u_char指针,一个pcap_pkthdr结构的指针,和指向caplen大小的数据包的u_char指针。

struct pcap_pkthdr { 

     struct tim ts;   // ts是一个结构struct timeval,它有两个部分,第一部分是1900开始以来的秒数,第二部分是当前秒之后的毫秒数

     bpf_u_int32 caplen;    //表示抓到的数据长度

     bpf_u_int32 len;    //表示数据包的实际长度

}; 

user参数是留给用户使用的,当callback被调用的时候这个值会传递给callback的第一个参数(也叫user)。

成功 则返回读到的数据包数。返回0没有抓到数据包。出错时则返回-1,此时可调用pcap_perror()或pcap_geterr()函数获取错误消息。返回-2表示调用了pcap_breakloop(). 

int pcap_loop(pcap_t *p, int cnt, pcap_handler callback, u_char *user)  

功能基本与pcap_dispatch()函数类似,只不过此函数在cnt个数据包被处理或出现错误时才返回,但读取超时不会返回。

u_char *pcap_next(pcap_t *p, struct pcap_pkthdr *h)  

读取下一个数据包,类似于pcap_dispatch()中cnt参数设为1,返回指向读到的数据包的指针,但是不返回这个包的pcap_pkthdr结构的参数。

7.关闭文件释放资源

void pcap_close(pcap_t *p)

关闭P指针。

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <pcap.h>
  4 
  5 #define PCAP_DATABUF_MAX 65535
  6 
  7 #define ETHERTYPE_IPV4 0x0800
  8 #define ETHERTYPE_IPV6 0x86DD
  9 
 10 typedef unsigned char   u_int8;
 11 typedef unsigned short  u_int16;
 12 typedef unsigned int    u_int32;
 13 typedef unsigned long   u_int64;
 14 
 15 /*MAC头,总长度14字节 */
 16 typedef struct _eth_hdr{
 17     u_int8 dst_mac[6];
 18     u_int8 src_mac[6];
 19     u_int16 eth_type;
 20 }eth_hdr;
 21 eth_hdr *ethernet;
 22 
 23 /*IP头*/
 24 typedef struct _ip_hdr{
 25     u_int8 ver_hl;    //版本和头长
 26     u_int8 serv_type; //服务类型
 27     u_int16 pkt_len;  //包总长
 28     u_int16 re_mark;  //重组标志
 29     u_int16 flag_seg; //标志位和段偏移量
 30     u_int8 surv_tm;    //生存时间
 31     u_int8 protocol;  //协议码(判断传输层是哪一个协议)
 32     u_int16 h_check;  //头检验和
 33     u_int32 src_ip;   //源ip
 34     u_int32 dst_ip;   //目的ip
 35     u_int32 option;   //可选选项
 36 }ip_hdr;
 37 ip_hdr *ip;
 38 
 39 /*TCP头,总长度20字节,不包括可选选项*/
 40 typedef struct _tcp_hdr{
 41     u_int16 sport;     //源端口
 42     u_int16 dport;     //目的端口
 43     u_int32 seq;       //序列号
 44     u_int32 ack;       //确认序号
 45     u_int8  head_len;  //头长度
 46     u_int8  flags;     //保留和标记位
 47     u_int16 wind_size; //窗口大小
 48     u_int16 check_sum; //校验和
 49     u_int16 urgent_p;  //紧急指针
 50 }tcp_hdr;
 51 tcp_hdr *tcp;
 52 
 53 /*UDP头,总长度8个字节*/
 54 typedef struct _udp_hdr{
 55     u_int16 sport;     //源端口
 56     u_int16 dport;     //目的端口
 57     u_int16 pktlen;    //UDP头和数据的总长度
 58     u_int16 check_sum; //校验和
 59 }udp_hdr;
 60 udp_hdr *udp;
 61 
 62 //ip整型转换点分十进制
 63 char *InttoIpv4str(u_int32 num){
 64     char* ipstr = (char*)calloc(128, sizeof(char*)); 
 65   
 66     if (ipstr)
 67         sprintf(ipstr, "%d.%d.%d.%d", num >> 24 & 255, num >> 16 & 255, num >> 8 & 255, num & 255);
 68     else
 69         printf("failed to Allocate memory...");
 70     
 71     return ipstr;
 72 }
 73 
 74 void pcap_callback(u_char *useless,const struct pcap_pkthdr *pkthdr, const u_char *packet)
 75 {
 76     printf("data len:%u\n", pkthdr->caplen); //抓到时的数据长度
 77     printf("packet size:%u\n", pkthdr->len); //数据包实际的长度
 78 
 79     /*解析数据链路层 以太网头*/
 80     ethernet = (struct _eth_hdr*)packet;
 81     u_int64 src_mac = ntohs( ethernet->src_mac );
 82     u_int64 dst_mac = ntohs( ethernet->dst_mac );
 83    
 84     printf("src_mac:%lu\n",src_mac);
 85     printf("dst_mac:%lu\n",dst_mac);
 86     printf("eth_type:%u\n",ethernet->eth_type);
 87     
 88     u_int32 eth_len = sizeof(struct _eth_hdr);  //以太网头的长度
 89     u_int32 ip_len; //ip头的长度
 90     u_int32 tcp_len = sizeof(struct _tcp_hdr);  //tcp头的长度
 91     u_int32 udp_len = sizeof(struct _udp_hdr);  //udp头的长度
 92     
 93     /*解析网络层  IP头*/
 94     if(ntohs(ethernet->eth_type) == ETHERTYPE_IPV4){  //IPV4
 95         printf("It's IPv4!\n");
 96         
 97         ip = (struct _ip_hdr*)(packet + eth_len);
 98         ip_len = (ip->ver_hl & 0x0f)*4;            //ip头的长度
 99         u_int32 saddr = (u_int32)ntohl(ip->src_ip); //网络字节序转换成主机字节序
100         u_int32 daddr = (u_int32)ntohl(ip->dst_ip);
101         
102         printf("eth_len:%u  ip_len:%u  tcp_len:%u  udp_len:%u\n", eth_len, ip_len, tcp_len, udp_len);
103         
104         printf("src_ip:%s\n", InttoIpv4str(saddr));  //源IP地址
105         printf("dst_ip:%s\n", InttoIpv4str(daddr));  //目的IP地址
106         
107         printf("ip->proto:%u\n", ip->protocol);      //传输层用的哪一个协议
108         
109         /*解析传输层  TCP、UDP、ICMP*/
110         if(ip->protocol == 6){         //TCP
111             tcp = (struct _tcp_hdr*)(packet + eth_len + ip_len);
112             printf("tcp_sport = %u\n", tcp->sport);
113             printf("tcp_dport = %u\n", tcp->dport);
114             
115 /**********(pcaket + eth_len + ip_len + tcp_len)就是TCP协议传输的正文数据了***********/
116 
117         }else if(ip->protocol == 17){  //UDP
118             udp = (struct _udp_hdr*)(packet + eth_len + ip_len);
119             printf("udp_sport = %u\n", udp->sport);
120             printf("udp_dport = %u\n", udp->dport);
121             
122 /**********(pcaket + eth_len + ip_len + udp_len)就是UDP协议传输的正文数据了***********/
123             
124         }else if(ip->protocol == 1){   //ICMP
125             
126         }
127         
128     }else if(ntohs(ethernet->eth_type) == ETHERTYPE_IPV6){ //IPV6
129         printf("It's IPv6!\n");
130     }
131     
132     printf("============================================\n");
133 }
134 
135 int main()
136 {
137     char *dev;  //设备名
138     char errbuf[PCAP_ERRBUF_SIZE] = {}; //PCAP_ERRBUF_SIZE在pcap.h中已经定义
139     bpf_u_int32 netp, maskp;  //网络号和掩码
140     pcap_t *handler;          //数据包捕获描述字
141     struct bpf_program *fp;   
142     char *filter_str = "port 9000";  //过滤条件
143     
144     /*Find network devices*/
145     if((dev = pcap_lookupdev(errbuf)) == NULL){
146         printf("lookupdev failed:%s\n", errbuf);
147         exit(1);
148     }else{
149         printf("Device:%s\n", dev);
150     }
151     
152     /*Get the network number and mask of the network device*/
153     if(pcap_lookupnet(dev, &netp, &maskp, errbuf) == -1){
154         printf("%s\n", errbuf);
155         exit(1);
156     }
157     
158     /*Open network device*/
159     if((handler = pcap_open_live(dev, PCAP_DATABUF_MAX, 1, 0, errbuf)) == NULL){
160         printf("%s\n", errbuf);
161         exit(1);
162     }
163     
164     /*Compiling and setting filtering conditions*/
165     if(pcap_compile(handler, fp, filter_str, 0, maskp) == -1){
166         printf("pcap_compile error...\n");
167         exit(1);
168     }
169     if(pcap_setfilter(handler, fp) == -1){
170         printf("pcap_setfilter error...\n");
171         exit(1);
172     }
173     
174     /*Capturing and processing data packets*/
175     if(pcap_loop(handler, -1, pcap_callback, NULL) == -1){
176         printf("pcap_loop error...\n");
177         pcap_close(handler);
178     }
179     
180     return 0;    
181 }   

 

posted on 2017-11-23 15:29  Itsad  阅读(4165)  评论(0编辑  收藏  举报

导航