| 网站首页 | 新闻中心 | 系统安全 | 网络安全 | 安全技术 | 下载中心 | 
课件制作网.
收藏本站
设为首页
安全365
使用pcap编写自己的sniffer
使用pcap编写自己的sniffer
作者:佚名 文章来源:不详 点击数: 更新时间:2007-1-19 10:18:32

    1、 用户在命令行指定定监听的网络接口:

#include <stdio.h>
#include <pcap.h>
int main(int argc, char *argv[])
{
    char *dev = argv[1];
    printf("Device: %s\n", dev);
    return(0);
}

    用户通过命令行参数传入监听接口。
    译注:在实际的项目开发中务必对命令行参数进行判断:

      if (argc < 2) {

            printf(“Usage: %s <option>\n”, argv[0]);

            exit(1);

      }

    2、  通过pcap引擎设定监听的网络接口:

#include <stdio.h>
#include <pcap.h>
int main()
{
    char *dev, errbuf[PCAP_ERRBUF_SIZE];
    dev = pcap_lookupdev(errbuf);
    printf("Device: %s\n", dev);
    return(0);
}

    在这种情况下,pcap引擎自己设置用来监听的接口。但是errbuf字符串用来做什么呢?大多数的pcap函数允许我们传递这样一个字符串作为其参数。这个字符串参数用来在pcap函数调用失败以后用来设置出错信息。在上面的例子中,如果pcap_lookup函数调用失败,出错信息将被保存在errbuf中。
    译注:增加的错误检查的代码如下:

       if (NULL == (dev = pcap_lookupdev(errbuf))) {

              fprintf(stderr, “pcap_lookupdev() error: %s\n”, errbuf);

              exit(-1);

       }

       printf(“Device: %s\n”, dev);

 

Opening the device for sniffing
    创建sniff会话的任务非常简单。我们使用pcap_open_live()创建sniff会话。函数原型:

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

       device:上节中我们制定的监听设备接口;

       snaplen:制定pcap捕获的最大数目的网络数据包;

       promisc:>0指定device接口工作在混杂模式(promiscous Mode);

       to_ms:制定经过特定时间(ms)后读超时;0表示遇到错误退出,-1指定永不超时;

       ebuf:制定用来存储出错信息的字符串

       pcap_t:返回值为用于监听的pcap会话。

    示例代码:

#include <pcap.h>
    ...
    pcap_t *handle;
    handle = pcap_open_live(somedev, BUFSIZ, 1, 0, errbuf);
    上面的代码打开somedev指定的设备并读取(捕获)BUFSIZ字节,同时我们设置接口工作在混杂模式,一直监听到有任何错误发生则退出,并将出错信息保存在errbuf指定的字符串中。
    关于混杂模式vs.非混杂模式:通常情况在非混杂模式下仅监听直接发往主机的数据包:发往、源自或通过主机路由的数据包都将被pcap捕获;混杂模式下,所有发送到物理链路上的数据包都将被捕获。在一个共享式的网络环境中,这将导致整个网络的数据流被监听。混合监听模式是可以被检测的:可以通过测试强可靠性来发现网络中是否有主机正在以混合模式监听,另外混杂工作模式仅仅在非交换式的网络中有效,而且在一个高负载的网络环境中,混杂模式将消耗大量的系统资源。
    Filter traffic
    通常我们只对特定网络通信感兴趣。比如我们只打算监听Telnet服务(port 23)以捕获用户名和口令信息。获知对FTP(port 21)或DNS(UDP port 53)数据流感兴趣。可以通过pcap_compile()和pcap_setfilter来设置数据流过滤规则(filter)
    函数原型:
    int pcap_compile(pcap_t *p, struct bpf_program *fp, char *str, int optimize, bpf_u_int32 netmask)
    p:表示pcap会话句柄;
    fp:存放编译以后的规则;
    str:规则表达式格式的过滤规则(filter),同tcpdump中的filter;

optimize:制定优化选项:0 false, 1 true;

netmask:监听接口的网络掩码;
 
    返回值:-1表示操作失败,其他值表成功。

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

              p:表示pcap的会话句柄;

              fp:表示经过编译后的过滤规则;

              返回值:-1表示操作失败,其他值表成功。
    示例代码:

#include <pcap.h>
    ...
    pcap_t *handle;                           /* Session handle */
    char dev[] = "rl0";                        /* Device to sniff on */
    char errbuf[PCAP_ERRBUF_SIZE];    /* Error string */
    struct bpf_program filter;               /* The compiled filter expression */
    char filter_app[] = "port 23";          /* The filter expression */
    bpf_u_int32 mask;                        /* The netmask of our sniffing device */
    bpf_u_int32 net;                           /* The IP of our sniffing device */
    pcap_lookupnet(dev, &net, &mask, errbuf);
    handle = pcap_open_live(dev, BUFSIZ, 1, 0, errbuf);
    pcap_compile(handle, &filter, filter_app, 0, net);
    pcap_setfilter(handle, &filter);      
    上面的代码设备rl0上以混杂模式监听所有发往或源自端口23的数据包。Pcap_lookupnet()函数返回给定接口的IP地址和子网掩码。
    The actual sniffing
    现在我们开始准备捕获数据包:有两种方法可以用来捕获数据包。要么一次捕获一个满足条件的数据包,要么进入一个循环过程捕获指定数量数据包然后退出。首先来了解使用pcap_next()一次捕获单一数据包。
    函数原型:

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

       p:pcap会话句柄;

       h:指向pcap_pkthdr接口的指针,在此结构中保存了所捕获的数据包的通用信息。包括:时间信息、数据包的长度和包头部分的长度(结构定义在后面定义)。

       返回值:返回指向实际捕获的数据包的u_char *型指针。

代码示例:

#include <pcap.h>
    #include <stdio.h>
    int main()
    {
        pcap_t *handle;                        /* Session handle */
        char *dev;                                /* The device to sniff on */
        char errbuf[PCAP_ERRBUF_SIZE]; /* Error string */
        struct bpf_program filter;            /* The compiled filter */
        char filter_app[] = "port 23";       /* The filter expression */
        bpf_u_int32 mask;                     /* Our netmask */
        bpf_u_int32 net;                        /* Our IP */
        struct pcap_pkthdr header;          /* The header that pcap gives us */
        const u_char *packet;                 /* The actual packet */
        /* Define the device */
        dev = pcap_lookupdev(errbuf);
        /* Find the properties for the device */
        pcap_lookupnet(dev, &net, &mask, errbuf);
        /* Open the session in promiscuous mode */
        handle = pcap_open_live(dev, BUFSIZ, 1, 0, errbuf);
        /* Compile and apply the filter */
        pcap_compile(handle, &filter, filter_app, 0, net);
        pcap_setfilter(handle, &filter);
        /* Grab a packet */
        packet = pcap_next(handle, &header);
        /* Print its length */
        printf("Jacked a packet with length of [%d]\n", header.len);
        /* And close the session */
        pcap_close(handle);
        return(0);
    }

    上面的代码将所有从pcap_lookupdev()返回的接口置于混杂模式监听状态。Pcap捕获端口23的一个数据包并打印该包的长度。然后调用pcap_close()关闭pcap会话。

 

    当然我们可以使用更复杂和更强大的功能pcap_loop和pcap_dispatch。通常很少有sniffer使用pcap_next,他们更通常的使用pcap_loop或pcap_dispatch。为便于理解这两个函数,需要现了解回调函数的概念。
    回调函数并不是一个新概念,在很多的API中都使用了回调函数的概念。可以通过pcap_loop或pcap_dispatch定义用户自己的回调函数。事实上pcap_loop和pcap_dispatch的功能非常相似,当pcap捕获的满足规则的数据包时,着两个函数将调用我们自己定义的回调函数执行我们自己的处理。
    函数原型:

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

p:pcap会话句柄;

cnt:定义sniff捕获的数据包的数目;

callback:自定义的回调函数句柄;

user:传递给回调函数的参数,如没有参数可以设为NULL;
    函数pcap_dispatch和pcap_loop的用法几乎相同,两者之间的唯一的差别是处理超时的方式不同(在pcap_open_live()中设置的超时参数将在这里起作用:pcap_loop将忽略超时参数而pcap_dispatch在制定时间到时将产生读超时的错误)。查阅pcap的帮助获得更多信息。
    回调函数的原型:

void got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet);

       args:对应于pcap_loop中的最后一个参数;

       header:指向pcap数据包包头的指针;

       packet:指向pcap捕获到的数据包的指针,packet指针指向的字符串包含了整个数据包;

       返回值:回调函数不能返回任何值。

    定义回调函数时,需要严格遵守原型定义,否则pcap_loop将不能正确调用回调函数。

       pcap_pkthdr结构的定义如下:

       struct pcap_pkthdr {
              struct timeval ts; /* time stamp */
              bpf_u_int32 caplen; /* length of portion present */
              bpf_u_int32 len; /* length this packet (off wire) */

};

 

    怎样使用(处理)packet指针变量呢?一个packet指针所指的结构包含了很多属性,它并不是一个真正的字符串,而是多个结构组成的集合(比如:一个TCP/IP数据包包括以太网头、IP包头、TCP头和数据包中有效的数据负载)。首先需要定义这些结构:

/* Ethernet header */

文章录入:admin    责任编辑:admin 
  • 上一篇文章:

  • 下一篇文章:
  • 【字体: 】【发表评论】【加入收藏】【告诉好友】【打印此文】【关闭窗口
      网友评论:(只显示最新10条。评论内容只代表网友观点,与本站立场无关!)
     
     
     
    IBM Lotus Notes NRPC协
    PHP Classifieds catid_
    Cisco Router Web Setup
    ipc$常见问题和回答
    APPC:高级程序间通信协
    ISO-IP CLNP:无连接网络
    IPCP 和 IPv6CP协议
    RPC协议
    IBM Lotus Notes NRPC协
    Php5 GPC绕过缺陷

    Copyright © 2006-2008 www.anquan365.com 安全365
    建议使用1024*768分辨率及第三方浏览器对本站进行浏览