zoukankan      html  css  js  c++  java
  • 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 }   
  • 相关阅读:
    在线教程
    《Linux命令行与shell脚本编程大全 第3版》Shell脚本编程基础---46
    《Linux命令行与shell脚本编程大全 第3版》Shell脚本编程基础---45
    《Linux命令行与shell脚本编程大全 第3版》Shell脚本编程基础---44
    《Linux命令行与shell脚本编程大全 第3版》Shell脚本编程基础---42
    《Linux命令行与shell脚本编程大全 第3版》Shell脚本编程基础---43
    《Linux命令行与shell脚本编程大全 第3版》Shell脚本编程基础---41
    《Linux命令行与shell脚本编程大全 第3版》Shell脚本编程基础---40
    《Linux命令行与shell脚本编程大全 第3版》Shell脚本编程基础---37
    《Linux命令行与shell脚本编程大全 第3版》Shell脚本编程基础---36
  • 原文地址:https://www.cnblogs.com/itsad/p/7885113.html
Copyright © 2011-2022 走看看