Chinaunix首页 | 论坛 | 博客
  • 博客访问: 116440
  • 博文数量: 43
  • 博客积分: 2500
  • 博客等级: 少校
  • 技术积分: 530
  • 用 户 组: 普通用户
  • 注册时间: 2008-09-04 16:56
文章分类

全部博文(43)

文章存档

2011年(1)

2010年(2)

2008年(40)

我的朋友

分类: C/C++

2008-09-26 21:34:18

循序渐进学习使用WINPCAP(六)

现在经过上几节的学习能够进行数据报的捕获和过滤了,我们想用一个简单的"real world"程序将我们所学的

知识应用于实际。
这一节里我们将利用以前的代码并将其引申从而建立一个更实用的程序。该程序的主要目的是如何显示出所捕

获的数据报的内容,尤其是对它的协议头的分析和说明。这个程序名叫UDPdump它将在屏幕上显示出我们网络上

UDP数据的信息。
在此我们选择解析UDP而不用TCP因为他比TCP简单更加的直观明了。下面让我们来看看原代码。




/*
* Copyright (c) 1999 - 2002
* Politecnico di Torino. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that: (1) source code distributions
* retain the above copyright notice and this paragraph in its entirety, (2)
* distributions including binary code include the above copyright notice and
* this paragraph in its entirety in the documentation or other materials
* provided with the distribution, and (3) all advertising materials mentioning
* features or use of this software display the following acknowledgement:
* ``This product includes software developed by the Politecnico
* di Torino, and its contributors.'' Neither the name of
* the University nor the names of its contributors may be used to endorse
* or promote products derived from this software without specific prior
* written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/

#include "pcap.h"

/* 4 BIT的IP头定义 */
typedef struct ip_address{
  u_char byte1;
  u_char byte2;
  u_char byte3;
  u_char byte4;
}ip_address;

/* IPv4 头的定义 */
typedef struct ip_header{
  u_char ver_ihl;     // 4 bit的版本信息 + 4 bits的头长
  u_char tos;         // TOS类型
  u_short tlen;       // 总长度
  u_short identification; // Identification
  u_short flags_fo;     // Flags (3 bits) + Fragment offset (13 bits)
  u_char ttl;         // 生存期
  u_char proto;       // 后面的协议信息
  u_short crc;         // 校验和
  ip_address saddr;     // 源IP
  ip_address daddr;     // 目的IP
  u_int   op_pad;       // Option + Padding
}ip_header;

/* UDP header*/
typedef struct udp_header{
  u_short sport;       // Source port
  u_short dport;       // Destination port
  u_short len;         // Datagram length
  u_short crc;         // Checksum
}udp_header;

/* 定义处理包的函数 */
void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data);


main()
{
  pcap_if_t *alldevs;
  pcap_if_t *d;
  int inum;
  int i=0;
  pcap_t *adhandle;
  char errbuf[PCAP_ERRBUF_SIZE];
  u_int netmask;
  char packet_filter[] = "ip and udp";
  struct bpf_program fcode;

  /* Retrieve the device list */
  if (pcap_findalldevs(&alldevs, errbuf) == -1)
  {
    fprintf(stderr,"Error in pcap_findalldevs: %s\n", errbuf);
    exit(1);
  }
 
  /* Print the list */
  for(d=alldevs; d; d=d->next)
  {
    printf("%d. %s", ++i, d->name);
    if (d->description)
        printf(" (%s)\n", d->description);
    else
        printf(" (No description available)\n");
  }

  if(i==0)
  {
    printf("\nNo interfaces found! Make sure WinPcap is installed.\n");
    return -1;
  }
 
  printf("Enter the interface number (1-%d):",i);
  scanf("%d", &inum);
 
  if(inum < 1 || inum > i)
  {
    printf("\nInterface number out of range.\n");
    /* Free the device list */
    pcap_freealldevs(alldevs);
    return -1;
  }

  /* Jump to the selected adapter */
  for(d=alldevs, i=0; i< inum-1 ;d=d->next, i++);
 
  /* Open the adapter */
  if ( (adhandle= pcap_open_live(d->name, // name of the device
                  65536,   // portion of the packet to capture.
                          // 65536 grants that the whole packet will be captured on

all the MACs.
                  1,       // promiscuous mode
                  1000,     // read timeout
                  errbuf   // error buffer
                  ) ) == NULL)
  {
    fprintf(stderr,"\nUnable to open the adapter. %s is not supported by WinPcap\n");
    /* Free the device list */
    pcap_freealldevs(alldevs);
    return -1;
  }
 
  /* Check the link layer. We support only Ethernet for simplicity. */
  if(pcap_datalink(adhandle) != DLT_EN10MB)
  {
    fprintf(stderr,"\nThis program works only on Ethernet networks.\n");
    /* Free the device list */
    pcap_freealldevs(alldevs);
    return -1;
  }
 
  if(d->addresses != NULL)
    /* Retrieve the mask of the first address of the interface */
    netmask=((struct sockaddr_in *)(d->addresses->netmask))->sin_addr.S_un.S_addr;
  else
    /* If the interface is without addresses we suppose to be in a C class network */
    netmask=0xffffff;


  //compile the filter
  if(pcap_compile(adhandle, &fcode, packet_filter, 1, netmask) <0 ){
    fprintf(stderr,"\nUnable to compile the packet filter. Check the syntax.\n");
    /* Free the device list */
    pcap_freealldevs(alldevs);
    return -1;
  }
 
  //set the filter
  if(pcap_setfilter(adhandle, &fcode)<0){
    fprintf(stderr,"\nError setting the filter.\n");
    /* Free the device list */
    pcap_freealldevs(alldevs);
    return -1;
  }
 
  printf("\nlistening on %s...\n", d->description);
 
  /* At this point, we don't need any more the device list. Free it */
  pcap_freealldevs(alldevs);
 
  /* start the capture */
  pcap_loop(adhandle, 0, packet_handler, NULL);
 
  return 0;
}

/* Callback function invoked by libpcap for every incoming packet */
void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data)
{
  struct tm *ltime;
  char timestr[16];
  ip_header *ih;
  udp_header *uh;
  u_int ip_len;
  u_short sport,dport;

  /* convert the timestamp to readable format */
  ltime=localtime(&header->ts.tv_sec);
  strftime( timestr, sizeof timestr, "%H:%M:%S", ltime);

  /* print timestamp and length of the packet */
  printf("%s.%.6d len:%d ", timestr, header->ts.tv_usec, header->len);

  /* 找到IP头的位置 */
  ih = (ip_header *) (pkt_data +
    14); //14为以太头的长度

  /* 找到UDP的位置 */
  ip_len = (ih->ver_ihl & 0xf) * 4;
  uh = (udp_header *) ((u_char*)ih + ip_len);

  /* 将端口信息从网络型转变为主机顺序 */
  sport = ntohs( uh->sport );
  dport = ntohs( uh->dport );

  /* print ip addresses and udp ports */
  printf("%d.%d.%d.%d.%d -> %d.%d.%d.%d.%d\n",
    ih->saddr.byte1,
    ih->saddr.byte2,
    ih->saddr.byte3,
    ih->saddr.byte4,
    sport,
    ih->daddr.byte1,
    ih->daddr.byte2,
    ih->daddr.byte3,
    ih->daddr.byte4,
    dport);
}

首先我门设置UDP过滤,用这种方法我们确保packet_handler()只接受到基于IPV4的UDP数据。我们同样定义了

两个数据结构来描述IP 和UDP的头部信息,packet_handler()用这两个结构来定位头部的各种字段。
packet_handler()虽然只是限于处理一些UDP数据但却显示了复杂的嗅探器如tcpdump/WinDump的工作原理。
首先我们对MAC地址的头部并不感兴趣所以我们跳过它。不过在开始捕获之前我们用pcap_datalink()来检查MAC

层,所以以上的程序只能够工作在Ethernet networks上,再次我们确保MAC头为14 bytes。
MAC头之后是IP头,我们从中提取出了目的地址。IP之后是UDP,在确定UDP的位置时有点复杂,因为IP的长度以

为版本的不同而不同,所以我们用头长字段来定位UDP,一旦我们确定了UDP的起始位置,我们就可以解析出原

和目的端口。
下面是我们打印出来的一些结果:

1. {A7FD048A-5D4B-478E-B3C1-34401AC3B72F} (Xircom t 10/100 Adapter)
Enter the interface number (1-2):1

listening on Xircom CardBus Ethernet 10/100 Adapter...
16:13:15.312784 len:87 130.192.31.67.2682 -> 130.192.3.21.53
16:13:15.314796 len:137 130.192.3.21.53 -> 130.192.31.67.2682
16:13:15.322101 len:78 130.192.31.67.2683 -> 130.192.3.21.53

上面每一行都显示出不同的数据包的内容.

dahubaobao 2004-11-18 00:55
循序渐进学习使用WINPCAP(七)

通过以前的学习我门已经熟悉了从网卡上捕获数据包,现在我门将学习如何处理数据包。WINPCAP为我们提供了很多API来将流经网络的数据包保存到一个堆文件并读取堆的内容。这一节将讲述如何使用所有的这些API。
这种文件的格式很简单,但包含了所捕获的数据报的二进制内容,这种文件格式也是很多网络工具的标准如WinDump, Ethereal 还有 Snort等.


关于如何将数据包保存到文件:

首先我们看看如何以LIBPCAP的格式写数据包。
下面的例子演示了如何从指定的接口上捕获数据包并将它们存储到一个指定的文件。


#include "pcap.h"

/* 定义处理数据的函数原形 */
void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data);

main(int argc, char **argv)
{
  pcap_if_t *alldevs;
  pcap_if_t *d;
  int inum;
  int i=0;
  pcap_t *adhandle;//定义文件句柄
  char errbuf[PCAP_ERRBUF_SIZE];
  pcap_dumper_t *dumpfile;


 
  /* 检查命令行参数 是否带有文件名*/
  if(argc != 2){

    printf("usage: %s filename", argv[0]);
    return -1;

  }
 
  /* 获得驱动列表 */
  if (pcap_findalldevs(&alldevs, errbuf) == -1)
  {
    fprintf(stderr,"Error in pcap_findalldevs: %s\n", errbuf);
    exit(1);
  }
 
  /* 打印 list */
  for(d=alldevs; d; d=d->next)
  {
    printf("%d. %s", ++i, d->name);
    if (d->description)
        printf(" (%s)\n", d->description);
    else
        printf(" (No description available)\n");
  }

  if(i==0)
  {
    printf("\nNo interfaces found! Make sure WinPcap is installed.\n");
    return -1;
  }
 
  printf("Enter the interface number (1-%d):",i);
  scanf("%d", &inum);
 
  if(inum < 1 || inum > i)
  {
    printf("\nInterface number out of range.\n");
    /* Free the device list */
    pcap_freealldevs(alldevs);
    return -1;
  }
   
  /* 跳转到指定的网卡 */
  for(d=alldevs, i=0; i< inum-1 ;d=d->next, i++);
 
  /* Open the adapter */
  if ( (adhandle = pcap_open_live(d->name, // name of the device
                  65536,   // portion of the packet to capture.
                          // 65536 grants that the whole packet will be captured on all the MACs.
                  1,       // promiscuous mode
                  1000,     // read timeout
                  errbuf   // error buffer
                  ) ) == NULL)
  {
    fprintf(stderr,"\nUnable to open the adapter. %s is not supported by WinPcap\n");
    /* Free the device list */
    pcap_freealldevs(alldevs);
    return -1;
  }

  /* 打开文件 */
  dumpfile = pcap_dump_open(adhandle, argv[1]);
  if(dumpfile==NULL){
    fprintf(stderr,"\nError opening output file\n");
    return -1;
  }
 
  printf("\nlistening on %s...\n", d->description);
 
  /* At this point, we don't need any more the device list. Free it */
  pcap_freealldevs(alldevs);
 
  /* 循环捕获数据并调用packet_handler函数把数据存储到堆文件 */
  pcap_loop(adhandle, 0, packet_handler, (unsigned char *)dumpfile);

  return 0;
}

/* Callback function invoked by libpcap for every incoming packet */

void packet_handler(u_char *dumpfile, const struct pcap_pkthdr *header, const u_char *pkt_data)
{
  /* 此函数功能将数据报存储到堆文件 */
  pcap_dump(dumpfile, header, pkt_data);
}

正如你看到的那样该程序的结构非常类似与以前的例子,区别是:
一旦打开网卡就调用pcap_dump_open()来打开一个文件,该调用将文件和某个网卡相关联。
packet_handler()内部通过调用pcap_dump()来将捕获的数据报存储到文件。pcap_dump()的参数和 packet_handler()一样,所以用起来比较方便。

从文件读数据包:

下面我们来看如何从文件读取数据内容。下面的代码打开了一个堆文件并打印了其中的每个包内容。
pcap_open_offline()用来打开一个堆文件,之后用pcap_loop()来循环从文件中读取数据。你能发现读取脱机的数据几乎和实时的从网卡上读取一摸一样。



#include
#include

#define LINE_LEN 16

void dispatcher_handler(u_char *, const struct pcap_pkthdr *, const u_char *);

main(int argc, char **argv) {
 
  pcap_t *fp;
  char errbuf[PCAP_ERRBUF_SIZE];


  if(argc != 2){

    printf("usage: %s filename", argv[0]);
    return -1;

  }
 
  /* 打开一个存储有数据的堆文件 */
  if ( (fp = pcap_open_offline(argv[1], errbuf) ) == NULL)
  {
    fprintf(stderr,"\nError opening dump file\n");
    return -1;
  }
 
  // 读取数据直到遇到 EOF标志。
  pcap_loop(fp, 0, dispatcher_handler, NULL);

  return 0;
}



void dispatcher_handler(u_char *temp1,
                const struct pcap_pkthdr *header, const u_char *pkt_data)
{
  u_int i=0;
 
  /* print pkt timestamp and pkt len */
  printf("%ld:%ld (%ld)\n", header->ts.tv_sec, header->ts.tv_usec, header->len);      
 
  /* Print the packet */
  for (i=1; (i < header->caplen + 1 ) ; i++)
  {
    printf("%.2x ", pkt_data[i-1]);
    if ( (i % LINE_LEN) == 0) printf("\n");
  }
 
  printf("\n\n");  
 
}


下面的代码具有一样的作用,只不过是用pcap_next_ex()来代替pcap_loop()循环读取数据而已。




#include
#include

#define LINE_LEN 16

main(int argc, char **argv) {
 
  pcap_t *fp;
  char errbuf[PCAP_ERRBUF_SIZE];
  struct pcap_pkthdr *header;
  u_char *pkt_data;
  u_int i=0;
  int res;

  if(argc != 2){

    printf("usage: %s filename", argv[0]);
    return -1;

  }
 
  /* Open a capture file */
  if ( (fp = pcap_open_offline(argv[1], errbuf) ) == NULL)
  {
    fprintf(stderr,"\nError opening dump file\n");
    return -1;
  }
 
  /* Retrieve the packets from the file */
  while((res = pcap_next_ex( fp, &header, &pkt_data)) >= 0){
    /* print pkt timestamp and pkt len */
    printf("%ld:%ld (%ld)\n", header->ts.tv_sec, header->ts.tv_usec, header->len);      
   
    /* Print the packet */
    for (i=1; (i < header->caplen + 1 ) ; i++)
    {
        printf("%.2x ", pkt_data[i-1]);
        if ( (i % LINE_LEN) == 0) printf("\n");
    }
   
    printf("\n\n");  
  }
 
 
  if(res == -1){
    printf("Error reading the packets: %s\n", pcap_geterr(fp));
  }
 
  return 0;
}



用pcap_live_dump将数据写到文件:
WinPcap的最新版本提供了一个进一步的方法来将数据包存储到磁盘,就是使用pcap_live_dump()函数。他需要三个参数:一个文件名,和一个该文件允许的最大长度还有一个参数是该文件所允许的最大包的数量。对这些参数来说 0 意味着没有最大限制。注:我们可以在调用pcap_live_dump()前设置一个过滤器来定义哪些数据报需要存储。

pcap_live_dump() 是非阻塞的,所以他会立刻返回:数据的存储过程将会异步的进行,直到文件到达了指定的最大长度或最大数据报的数目为止。
应用程序能够用pcap_live_dump_ended()来等检查是否数据存储完毕,如果你指定的最大长度参数和数据报数量为0,那么该操作将永远阻塞。

pcap_live_dump() 和 pcap_dump()的不同从设置的最大极限来说就是性能的问题。pcap_live_dump()采用WinPcap NPF驱动来从内核级的层次上向文件中写数据,从而使内存拷贝最小化。
显然,这些特点当前在其他的操作系统下是不能够实现的,pcap_live_dump()是WinPcap所特有的,而且只能够应用于Win32环境
阅读(581) | 评论(0) | 转发(0) |
0

上一篇:winpcap(转载2)

下一篇:不想再次相遇

给主人留下些什么吧!~~