libpcap编程:Sniffer

主要实现运用libpcap(winpcap)的api进行数据链路层的抓包操作,并一层层进行解析,解析TCP, UDP, ICMP等协议。

主要代码及解析如下:

    /*************************************************************************
        > File Name: sniffer.c
        > Author: PCB
        > Mail: [email protected]
        > Created Time: 2016年04月10日 星期日 20时40分52秒
     ************************************************************************/

    #include<stdio.h>
    #include<pcap.h>
    #include<string.h>
    #include<stdlib.h>
    #include<unistd.h>
    #include<signal.h>
    #include<sys/socket.h>
    #include<netinet/in.h>
    #include<arpa/inet.h>
    #include<netinet/ip.h>
    #include<netinet/tcp.h>
    #include<netinet/udp.h>
    #include<netinet/ip_icmp.h>

    pcap_t* pd;
    int linkhdrlen; //链路层的长度

    /*
     * device: 要连接的设备名字
     * bpfstr: 要过滤的规则
     * */
    pcap_t* open_pcap_socket(char* device, const char* bpfstr)
    {
    char errbuf[PCAP_ERRBUF_SIZE];   //存放出错信息
    pcap_t* pd;
    uint32_t srcip, netmask; //存放源的ip地址和子网掩码
    struct bpf_program bpf; //存取bpf过滤规则的一个结构

    //找到一个设备
    if(!*device && !(device == pcap_lookupdev(errbuf)))
    {
    printf("pcap_lookupdev(): %s\n", errbuf);
    return NULL;
    }

    //打开该设备,并且等到一个包到来
    if((pd = pcap_open_live(device, BUFSIZ, 1, 0, errbuf)) == NULL)
    {
    printf("pcap_open_live(): %s\n", errbuf);
    return NULL;
    }

    //得到设备的ip地址和ip的掩码
    if(pcap_lookupnet(device, &srcip, &netmask, errbuf) < 0)
    {
    printf("pcap_lookupnet: %s\n", errbuf);
      return NULL;
    }
    //应用过滤规则
    if(pcap_compile(pd, &bpf, (char*)bpfstr, 0, netmask))
    {
    printf("pcap_compile(): %s\n", pcap_geterr(pd));
    return NULL;
    }

    if(pcap_setfilter(pd, &bpf)<0)
    {
    printf("pcap_setfilter(): %s\n", pcap_geterr(pd));
    return NULL;
    }

    //printf("Open_pcapSocket()\n");

    return pd;
    }//end open_pcap_socket(char* device, const char* bpfstr);

    void capture_loop(pcap_t* pd, int packets, pcap_handler func)
    {
    int linktype;

    //判断数据链路层的类型
    if((linktype = pcap_datalink(pd)) < 0)
    {
    printf("pcap_datalink(): %s\n", pcap_geterr(pd));
    return;
    }

    switch(linktype)
    {
    case DLT_NULL://BSD回路封装;链路层协议是一个4字节的域
    linkhdrlen = 4;
    break;

    case DLT_EN10MB: //以太网,链路层协议是一个14字节的域
    linkhdrlen = 14;
    break;

    case DLT_SLIP: 
    case DLT_PPP:
    linkhdrlen = 24;  //链路层长度为24个字节
    break;

    default:
    printf("Unsupport datalink (%d)\n", linktype);
    return;

    }

    //开始抓包
    if(pcap_loop(pd, packets, func, 0) < 0)
    printf("pcap_loop failed: %s\n", pcap_geterr(pd));
    }

    /*
     * 解析包的程序*/
    void parse_packet(u_char *user, struct pcap_pkthdr* packethdr, u_char* packetptr)
    {
    struct ip* iphdr;  //netinet库的ip结构
    struct icmphdr* icmphdr;  //icmp结构
    struct tcphdr* tcphdr;//tcp结构
    struct udphdr* udphdr;//udp结构
    char iphdrInfo[256], srcip[256], dstip[256];
    unsigned short id, seq;
    packetptr += linkhdrlen; //过滤掉数据链路层的数据
    iphdr = (struct ip*)packetptr;
    strcpy(srcip, inet_ntoa(iphdr->ip_src));
    strcpy(dstip, inet_ntoa(iphdr->ip_dst));

    sprintf(iphdrInfo, "ID:%d TOS:0x%x, TTL:%d, Iplen:%d Dglen:%d",
       ntohs(iphdr->ip_id), iphdr->ip_tos, iphdr->ip_ttl, 4*iphdr->ip_hl, ntohs(iphdr->ip_len)
       );

    //将ip的字段跳过去
    packetptr += 4*iphdr->ip_hl;
    switch(iphdr->ip_p)
    {
    case IPPROTO_TCP:  //如果是TCP协议
    tcphdr = (struct tcphdr*)packetptr;
    printf("TCP %s:%d -> %s:%d\n", srcip, ntohs(tcphdr->source), dstip, ntohs(tcphdr->dest));
    printf("%s\n", iphdrInfo);
    printf("%c%c%c%c%c%c Seq: 0x%x Ack: 0x%x Win: 0x%x TcpLen: %d\n", (tcphdr->urg ? 'U' : '*'),
       (tcphdr->ack ? 'A' : '*'),
       (tcphdr->psh ? 'P' : '*'),
       (tcphdr->rst ? 'R' : '*'),
       (tcphdr->syn ? 'S' : '*'),
       (tcphdr->fin ? 'F' : '*'),
       ntohl(tcphdr->seq), ntohl(tcphdr->ack_seq), ntohs(tcphdr->window), 4*tcphdr->doff
      );
    break;

    case IPPROTO_UDP: //UDP协议
    udphdr = (struct udphdr*) packetptr;
    printf("UDP %s:%d -> %s:%d\n", srcip, ntohs(udphdr->source),
      dstip, ntohs(udphdr->dest)
      );
    printf("%s\n", iphdrInfo);
    break;

    case IPPROTO_ICMP: //ICMP协议
    icmphdr = (struct icmphdr*)packetptr;
    printf("ICMP %s -> %s\n", srcip, dstip);
    printf("%s\n", iphdrInfo);
    memcpy(&id, (u_char*)icmphdr+4, 2);
    memcpy(&seq, (u_char*)icmphdr+6, 2);
    printf("Type:%d Code:%d ID:%d Seq:%d\n", icmphdr->type, icmphdr->code, ntohs(id), ntohs(seq));
    break;


    }
    printf("---------------------------------------------\n\n");
    }

    void quiteOut(int signo)
    {
    struct pcap_stat stats;
    if(pcap_stats(pd, &stats) >= 0)
    {
    printf("%d packets received\n", stats.ps_recv);
    printf("%d packtes dropped\n\n", stats.ps_drop);
    }
    pcap_close(pd);
    exit(0);
    }


    int main(int argc, char **argv)
    {
    char interface[256] = "", bpfstr[256] = "";

    int packets = 0, c,i;

    while((c = getopt(argc, argv, "i:n:") ) != -1)  //读取参数,当为-i时,指定接口,当为-n时,指定读取多少packets
    {
    switch(c)
    {
    case 'i':
    strcpy(interface, optarg);
    break;
    case 'n':
    packets = atoi(optarg);
    break;
    }
    }

    //获取过滤的字符串
    for(i = optind; i < argc; i++)
    {
    strcat(bpfstr, argv[i]);
    strcat(bpfstr, " ");
    }

    //连接接口
    if((pd = open_pcap_socket(interface, bpfstr)))
    {
    /*/
     * 绑定中断事件给quiteOut函数*/

    signal(SIGINT, quiteOut);
    signal(SIGTERM, quiteOut);
    signal(SIGQUIT, quiteOut);

    capture_loop(pd, packets, (pcap_handler)parse_packet);
    quiteOut(0);
    }
    exit(0);

    }

主要使用:

该程序支持监听的接口及一共要接收的数据包的大小,并且提供过滤功能,具体使用如下:

sudo ./sniffer -i eth0 tcp port 80

运行结果如下:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章