• 转载自:http://blog.csdn.net/tennysonsky/article/details/44811899
  • 概述
    • libpcap是一个网络数据包捕获函数库,tcpdump就是以libpcap为基础的。
    • 主要作用:
      • 捕获各种数据包,例如:网络流量统计
      • 过滤网络数据包,例如:过滤掉本地上的一些数据,类似防火墙
      • 分析网络数据包,例如:分析网络协议,数据的采集
      • 存储网络数据包,例如:保存捕获的数据以为将来进行分析
  • libpcap的抓包框架
    • pcap_lookupdev():函数用来查找网络设备,返回可被pcap_open_live()函数调用的网络设备名指针。
    • pcap_lookupnet():函数获得指定网络设备的网络号和掩码。
    • pcap_open_live():函数用于打开设备,并且返回用于捕获网络数据包的数据包捕获描述字。对于此网络设备的操作都要基于此网络设备描述字。
    • pcap_compile():函数用于将用户制定的过滤策略编译到过滤程序中
    • pcap_setfilter():函数用于设置过滤器
    • pcap_loop():与pcap_next()和pcap_next_ex()两个函数一样用来捕获数据包
    • pcap_close():函数用于关闭网络设备,释放资源
  • 利用libpcap函数库开发应用程序的步骤:

    • 打开网络设备
    • 设置过滤规则
    • 捕获数据
    • 关闭网络设备
  • 详细步骤:

    • 首先要使用libpcap,需要包含pcap.h头文件。
    • 获取网络设备接口:
      • char *pcap_lookupdev(char * errbuf);
      • 功能:自动获取可用的网络设备名指针
      • 参数:errbuf,存放出错信息字符串,有宏定义缓冲区大小,PCAP_ERRBUF_SIZE
      • 返回值:成功返回设备名指针(第一个合适的网络接口的字符串指针),失败则返回NULL,同时,errbuf存放出错信息字符串
      •  //自动获取网络接口形式
        char errBuf[PCAP_ERRBUF_SIZE], *devStr;
        devStr = pcap_lookupdev(errBuf); //手动获取网络接口形式只需要被devStr赋值即可
        char errBuf[PCAP_ERRBUF_SIZE], *devStr = “eth0”;
    • 获取网络号(ip地址)和掩码
      • int pcap_lookupnet(char* device,bpf_u_int32 *netp,bpf_u_int32 *maskp,char *errbuf);
      • 功能:获取指定网卡的ip地址,子网掩码
      • 参数:device:网络设备名,为第一步获取的网络接口字符串,也可以人为指定,如“eth0”;
      • netp:存放ip地址的指针,buf_u_int32为32位无符号整型
      • maskp:存放子网掩码的指针
      • errbuf:存放出错信息
      • 返回值:成功返回0,失败返回1
      •  char error_content[PCAP_ERRBUF_SIZE] = {}; // 出错信息
        char *dev = pcap_lookupdev(error_content);
        if(NULL == dev)
        {
        printf(error_content);
        exit(-);
        } bpf_u_int32 netp = , maskp = ;
        pcap_t * pcap_handle = NULL;
        int ret = ; //获得网络号和掩码
        ret = pcap_lookupnet(dev, &netp, &maskp, error_content);
        if(ret == -)
        {
        printf(error_content);
        exit(-);
        }
    • 打开网络接口
      • pcap_t *pcap_open_live(const char * device,int snaplen,int promisc,int to_ms,char *errbuf);
      • 功能:打开一个用于捕获数据的网络端口
      • 参数:device:网络接口的名字,为第一步获取的网络接口字符串,也可以人为指定,如:”eth0“
      • snaplen:捕获数据包的长度,不能大于65535个字节
      • promise:”1“代表混杂模式,其他值代表非混杂模式
      • to_ms:指定需要等地啊的毫秒数,超过这个时间后,获得数据包的函数会立即返回,0表示一直等待直到有数据包到来
      • errbuf:存储错误信息
      • 返回值:返回pcap_t类型指针,后面的所有操作都要使用这个指针。
      •  char error_content[PCAP_ERRBUF_SIZE] = {}; // 出错信息
        char *dev = pcap_lookupdev(error_content); // 获取网络接口
        if(NULL == dev)
        {
        printf(error_content);
        exit(-);
        } // 打开网络接口
        pcap_t * pcap_handle = pcap_open_live(dev, , , , error_content);
        if(NULL == pcap_handle)
        {
        printf(error_content);
        exit(-);
        }
    • 获取数据包:
      • 方法一:const u_char *pcap_next(pcap_t *p,struct pcap_pkthdr *h);
      • 功能:捕获一个网络数据包,收到一个数据包立即返回
      • 参数:p:pcap_open_live()返回的pcap_t类型的指针
      • h:数据包头
      • pcap_pkthdr类型的定义如下:
      •  struct pcap_pkthdr
        {
        struct timeval ts; // 抓到包的时间
        bpf_u_int32 caplen; // 表示抓到的数据长度
        bpf_u_int32 len; // 表示数据包的实际长度
        }
      • len和caplen的区别:因为在某些情况下不能保证捕获的包是完整的,例如一个包长1480,但是你捕获到1000的时候,可能因为某些原因就终止捕获了,所以caplen是记录实际捕获的包长,也就是1000,而len就是1480
      • 返回值:成功则返回捕获数据包的地址,失败返回NULL
      •  const unsigned char *p_packet_content = NULL; // 保存接收到的数据包的起始地址
        pcap_t *pcap_handle = NULL;
        struct pcap_pkthdr protocol_header; pcap_handle = pcap_open_live("eth0", , , ,NULL); p_packet_content = pcap_next(pcap_handle, &protocol_header);
        //p_packet_content 所捕获数据包的地址 printf("Capture Time is :%s",ctime((const time_t *)&protocol_header.ts.tv_sec)); // 时间
        printf("Packet Lenght is :%d\n",protocol_header.len); // 数据包的实际长度
      • 方法二:int pcap_loop(pcap_t *p,int cnt,pcap_handler callback,u_char *user);
      • 功能:循环捕获网络数据包,直到遇到错误或者满足退出条件,每次捕获一个数据包就会调用callback指定的回调函数,所以,可以在回调函数中进行数据包的处理操作。
      • 参数:p:pcap_open_live()返回的pcap_t类型的指针
      • cnt:指定捕获数据包的个数,一旦抓到cnt个数据包,pcap_loop立即返回,如果是-1,就会一直捕获直到出错
      • callback:回调函数,名字任意,根据需要自行取名
      • user:向回调函数中传递的参数
      • callback回调函数的定义:
        • void callback(u_char *userarg,const struct pcap_pkthdr *pkthdr,const u_char *packet)
        • userarg:pcap_loop()的最后一个参数,当收到足够数量的包后pcap_loop会调用callback回调函数,同时将pcap_loop()的user参数传递给它
        • pkthdr:是收到数据包的pcap_pkthdr类型的指针,和pcap_next()第二个参数是一样的
        • packet:收到的数据包数据
        • 返回值:成功返回0,失败返回负数
      • 方法三:int pcap_dispatch(pcap_t *p,int cnt,pcap_handler callback,u_char *user);
        • 这个函数和pcap_loop()非常类似,只是在超过to_ms毫秒后就会返回(to_ms是pcap_open_live()的第四个参数)
    • 释放网络接口:
      • void pcap_close(pcap_t *p);
      • 功能:关闭pcap_open_live()打开的网络接口,并释放相关资源
      • 参数:p:需要关闭的网络接口,pcap_open_live()的返回值
      •  // 打开网络接口
        pcap_t * pcap_handle = pcap_open_live("eth0", , , , error_content);
        if(NULL == pcap_handle)
        {
        printf(error_content);
        exit(-);
        } //// ……
        //// …… pcap_close(pcap_handle); //释放网络接口
  • //接收一个数据包
    #include <stdio.h>
    #include <pcap.h>
    #include <arpa/inet.h>
    #include <time.h>
    #include <stdlib.h>
    struct ether_header
    {
    unsigned char ether_dhost[]; //目的mac
    unsigned char ether_shost[]; //源mac
    unsigned short ether_type; //以太网类型
    };
    #define BUFSIZE 1514 int main(int argc,char *argv[])
    {
    pcap_t * pcap_handle = NULL;
    char error_content[] = ""; // 出错信息
    const unsigned char *p_packet_content = NULL; // 保存接收到的数据包的起始地址
    unsigned char *p_mac_string = NULL; // 保存mac的地址,临时变量
    unsigned short ethernet_type = ; // 以太网类型
    char *p_net_interface_name = NULL; // 接口名字
    struct pcap_pkthdr protocol_header;
    struct ether_header *ethernet_protocol; //获得接口名
    p_net_interface_name = pcap_lookupdev(error_content);
    if(NULL == p_net_interface_name)
    {
    perror("pcap_lookupdev");
    exit(-);
    } //打开网络接口
    pcap_handle = pcap_open_live(p_net_interface_name,BUFSIZE,,,error_content);
    p_packet_content = pcap_next(pcap_handle,&protocol_header); printf("------------------------------------------------------------------------\n");
    printf("capture a Packet from p_net_interface_name :%s\n",p_net_interface_name);
    printf("Capture Time is :%s",ctime((const time_t *)&protocol_header.ts.tv_sec));
    printf("Packet Lenght is :%d\n",protocol_header.len); /*
    *分析以太网中的 源mac、目的mac
    */
    ethernet_protocol = (struct ether_header *)p_packet_content;
    p_mac_string = (unsigned char *)ethernet_protocol->ether_shost;//获取源mac
    printf("Mac Source Address is %02x:%02x:%02x:%02x:%02x:%02x\n",*(p_mac_string+),*(p_mac_string+),*(p_mac_string+),*(p_mac_string+),*(p_mac_string+),*(p_mac_string+));
    p_mac_string = (unsigned char *)ethernet_protocol->ether_dhost;//获取目的mac
    printf("Mac Destination Address is %02x:%02x:%02x:%02x:%02x:%02x\n",*(p_mac_string+),*(p_mac_string+),*(p_mac_string+),*(p_mac_string+),*(p_mac_string+),*(p_mac_string+)); /*
    *获得以太网的数据包的地址,然后分析出上层网络协议的类型
    */
    ethernet_type = ntohs(ethernet_protocol->ether_type);
    printf("Ethernet type is :%04x\t",ethernet_type);
    switch(ethernet_type)
    {
    case 0x0800:printf("The network layer is IP protocol\n");break;//ip
    case 0x0806:printf("The network layer is ARP protocol\n");break;//arp
    case 0x0835:printf("The network layer is RARP protocol\n");break;//rarp
    default:printf("The network layer unknow!\n");break;
    } pcap_close(pcap_handle);
    return ;
    }
  • //接收多个数据包 #include <stdio.h>
    #include <pcap.h>
    #include <arpa/inet.h>
    #include <time.h>
    #include <stdlib.h> #define BUFSIZE 1514 struct ether_header
    {
    unsigned char ether_dhost[]; //目的mac
    unsigned char ether_shost[]; //源mac
    unsigned short ether_type; //以太网类型
    }; /*******************************回调函数************************************/
    void ethernet_protocol_callback(unsigned char *argument,const struct pcap_pkthdr *packet_heaher,const unsigned char *packet_content)
    {
    unsigned char *mac_string; //
    struct ether_header *ethernet_protocol;
    unsigned short ethernet_type; //以太网类型
    printf("----------------------------------------------------\n");
    printf("%s\n", ctime((time_t *)&(packet_heaher->ts.tv_sec))); //转换时间
    ethernet_protocol = (struct ether_header *)packet_content; mac_string = (unsigned char *)ethernet_protocol->ether_shost;//获取源mac地址
    printf("Mac Source Address is %02x:%02x:%02x:%02x:%02x:%02x\n",*(mac_string+),*(mac_string+),*(mac_string+),*(mac_string+),*(mac_string+),*(mac_string+));
    mac_string = (unsigned char *)ethernet_protocol->ether_dhost;//获取目的mac
    printf("Mac Destination Address is %02x:%02x:%02x:%02x:%02x:%02x\n",*(mac_string+),*(mac_string+),*(mac_string+),*(mac_string+),*(mac_string+),*(mac_string+)); ethernet_type = ntohs(ethernet_protocol->ether_type);//获得以太网的类型
    printf("Ethernet type is :%04x\n",ethernet_type);
    switch(ethernet_type)
    {
    case 0x0800:printf("The network layer is IP protocol\n");break;//ip
    case 0x0806:printf("The network layer is ARP protocol\n");break;//arp
    case 0x0835:printf("The network layer is RARP protocol\n");break;//rarp
    default:break;
    }
    usleep(*);
    } int main(int argc, char *argv[])
    {
    char error_content[]; //出错信息
    pcap_t * pcap_handle;
    unsigned char *mac_string;
    unsigned short ethernet_type; //以太网类型
    char *net_interface = NULL; //接口名字
    struct pcap_pkthdr protocol_header;
    struct ether_header *ethernet_protocol; //获取网络接口
    net_interface = pcap_lookupdev(error_content);
    if(NULL == net_interface)
    {
    perror("pcap_lookupdev");
    exit(-);
    } pcap_handle = pcap_open_live(net_interface,BUFSIZE,,,error_content);//打开网络接口 if(pcap_loop(pcap_handle,-,ethernet_protocol_callback,NULL) < )
    {
    perror("pcap_loop");
    } pcap_close(pcap_handle);
    return ;
    }
  • 过滤数据包:
    • 设置过滤条件:举一些例子:
      • src host 192.168.1.177:只接收源ip地址是192.168.1.177的数据包
      • 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个字节)
    • 编译BPF过滤规则:
      • int pcap_compile(pcap_t *p,struct bpf_program *fp,char *buf,int optimize,bpf_u_int32 mask);
      • 参数:
        • p:pcap_open_live()返回的pcap_t类型的指针
        • fp:存放编译后的bpf,应用过来规则时需要使用这个指针
        • buf:过滤规则
        • optimize:是否需要优化过滤表达式
        • mask:指定本地网络的网络掩码,不需要时可写0
      • 返回值:成功返回0,失败返回-1
    • 应用BPF过滤规则:
      • int pcap_setfilter(pcap_t *p,struct bpf_program *fp);
      • 功能:应用BPF过滤规则
      • 参数:p:pcap_open_live()返回的pcap_t类型的指针
      • fp:pcap_compile()的第二个参数
      • 返回值:成功返回0,失败返回-1
      •  #include <pcap.h>
        #include <time.h>
        #include <stdlib.h>
        #include <stdio.h> void getPacket(u_char * arg, const struct pcap_pkthdr * pkthdr, const u_char * packet)
        {
        int * id = (int *)arg; printf("id: %d\n", ++(*id));
        printf("Packet length: %d\n", pkthdr->len);
        printf("Number of bytes: %d\n", pkthdr->caplen);
        printf("Recieved time: %s", ctime((const time_t *)&pkthdr->ts.tv_sec)); int i;
        for(i=; i<pkthdr->len; ++i)
        {
        printf(" %02x", packet[i]);
        if( (i + ) % == )
        {
        printf("\n");
        }
        } printf("\n\n");
        } int main()
        {
        char errBuf[PCAP_ERRBUF_SIZE], * devStr; /* get a device */
        devStr = pcap_lookupdev(errBuf); if(devStr)
        {
        printf("success: device: %s\n", devStr);
        }
        else
        {
        printf("error: %s\n", errBuf);
        exit();
        } /* open a device, wait until a packet arrives */
        pcap_t * device = pcap_open_live(devStr, , , , errBuf); if(!device)
        {
        printf("error: pcap_open_live(): %s\n", errBuf);
        exit();
        } /* construct a filter */
        struct bpf_program filter;
        pcap_compile(device, &filter, "dst port 80", , );
        pcap_setfilter(device, &filter); /* wait loop forever */
        int id = ;
        pcap_loop(device, -, getPacket, (u_char*)&id); pcap_close(device); return ;
        }

利用libpcap抓取数据包的更多相关文章

  1. 利用Fiddler抓取websocket包

    一.利用fiddler抓取websockt包 打开Fiddler,点开菜单栏的Rules,选择Customize Rules... 这时会打开CustomRules.js文件,在class Handl ...

  2. Android移动网络如何抓取数据包

    1)下载tcpdump工具 tcpdump(dump the traffic on a network)是Linux中强大的网络数据采集分析工具之一,可以将网络中传送的数据包头完全截获下来提供分析.它 ...

  3. tcpdump 基于mac地址抓取数据包

    1.刚刚接触tcpdump时,常用tcpdump -i eth1 host 192.168.1.1 这个命令基于ip地址抓取数据包信息. tcpdump -i eth1(接口名称) host 192. ...

  4. Fiddler抓取数据包分析案例

    案例:利用Fiddler抓取苏宁易购网站数据包分析 抓包软件:Fiddler4 请求名字:www.suning.com 详细内容: 一.了解数据包区域的字段含义 图1数据包区域 #:顺序号,按照抓包的 ...

  5. 利用tcpdump抓取网络包

    1.下载并安装tcpdump 下载地址:tcpdump 安装tcpdump,连接adb adb push tcpdump /data/local/tcpdump adb shell chmod 675 ...

  6. 利用libpcap抓取QQ号码信息

    最近想在QQ登录时把QQ号码信息记录下来,百度了很多都没有找到具体方式,最近用Wireshark分析报文+libpcap库嗅探实现了这个小功能. 通讯背景: QQ客户端在通讯时使用UDP协议,其中数据 ...

  7. 使用Wireshark 抓取数据包

    Wireshark 是一个网络封包分析软件.网络封包分析软件的功能是获取网络封包,并尽可能显示出最为详细的网络封包资料.Wireshark使用WinPCAP作为接口,直接与网卡进行数据报文交换. 一  ...

  8. WinPcap抓取数据包

    #WinPcap和Libpcap的最强大的特性之一,是拥有过滤数据包的引擎. 它提供了有效的方法去获取网络中的某些数据包,这也是WinPcap捕获机制中的一个组成部分. 用来过滤数据包的函数是#

  9. iPhone 手机用 Fiddler 抓取数据包 问题

    近日公司服务升级,将所有的接口请求由HTTP升级为了HTTPS,升级后在手机中安装了Fiddler的证书,Android端抓取HTTPS请求一切正常,可是在ios端抓取HTTPS请求时一直提示“此服务 ...

随机推荐

  1. 【capstone/ropgadget】环境配置

    具体环境配置可参考 https://github.com/JonathanSalwan/ROPgadget/tree/master 作者给出的安装方式 但具体配置中出现了问题,如引用时出现如下错误: ...

  2. 10. Regular Expression Matching正则表达式匹配

    Implement regular expression matching with support for '.' and '*'. '.' Matches any single character ...

  3. /etc/fstab 只读无法修改的解决办法

    在做saltstack的时候不小心误把/etc/fstab给注释了 在命令补全的时候开始报错:[root@kafka2 ~]# cat /et-bash: cannot create temp fil ...

  4. jpa自定义sql语句

    /** * 查询还没生成索引的帖子 * @return */ @Query(value = "SELECT * FROM t_article WHERE index_state=0" ...

  5. 060、Java中定义有返回值有参数的方法

    01.代码如下: package TIANPAN; /** * 此处为文档注释 * * @author 田攀 微信382477247 */ public class TestDemo { public ...

  6. 原生JS 实现 dom ready

    记录一下项目技术问题: 记得:放在head标签内的脚本,第一时间执行 var baseTools = { // dom ready ready: function( f ){ var ie = !!( ...

  7. 五、Vue:使用axios库进行get和post、用拦截器对请求和响应进行预处理、Mock(数据模拟)

    一.axios [应用]进行请求和传表单 [axios中文档]:https://www.kancloud.cn/yunye/axios/234845 [vue-axios]:https://cn.vu ...

  8. MQTT 协议学习:005-发布消息 与 对应报文 (PUBLISH、PUBACK、PUBREC、PUBREL)

    背景 当有订阅者订阅了有关的主题以后,通过发布消息的消息的动作,可以让订阅者收到对应主题的消息. 根据不同的QoS 等级,通信的动作也略有不同. PUBLISH – 发布消息 报文 PUBLISH控制 ...

  9. P1081 检查密码

    P1081 检查密码 转跳点:

  10. 洛谷 P2549 计算器写作文

    题目传送门 解题思路: 背包,f[i]表示计算器位数为i时,可获得的最大分值. 本题与01背包不同的地方在于,物品的摆放顺序对答案是有影响的,例如两个字符串a,b,那么就会出现a+b和b+a两种情况( ...