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

運行結果如下:

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