winpcap捕获数据包

//技术手册:http://www.ferrisxu.com/WinPcap/html/index.html



 

Winpcap配置编程环境以及出错处理
2011-04-20 11:58

Winpcap配置编程环境:

安装Winpcap,接着:
1> 从WWW.WINPCAP.ORG 上下载WINPCAP SDK -WpdPack,WinPcap SDk 里面包含库
文件,头文件,文档文件和一些例子。解压到一个指定的目录。解压缩后把Include 目录添
加到IDE 的包含文件中(VC6.0 Tools->Option->Directory; VS 2003/2005/2008 工具->选项->项目
和解决方案/ 项目->VC++ 目录); lib 目录添加为新的库文件目录( VC6.0
Tools->Option->Directory; VS 2003/2005/2008 工具->选项->项目和解决方案/项目->VC++目录);

添加wpcap.lib和ws2_32.lib(工程属性->链接器->输入->附加依赖项)

 


2> 如果一个源文件使用了WinPcap 提供的库函数,那么就需要在该文件开始的位置添加
pcap.h 包含文件(或者在引用的文件中),即#include “pcap.h”
也许会出现下面的错误:
fatal error C1083: 无法打开包括文件:“pcap.h”: No such file or directory
这个错误表明找不到pcap.h 文件,这个头文件在驱动程序安装完成后也是没有的,它是开
发包里面的一个头文件,所以,如果要运行程序还需要到官方网站上去下载WinPcap SDK,
并按步骤1 添加到项目中。
3> 在程序中添加wpcap.lib。如果出现下面错误
error LNK2019: 无法解析的外部符号_pcap_findalldevs_ex,该符号在函数XXX 中被引用,
如果发生上面的错误就表明缺少库文件, 需要添加wpcap.lib 到工程中(VC6.0
Project->Settings->Link->Object/library modules; VS 2003/2005/2008 项目->添加现有项->所有文件)

 

4> 新的版本里WinPcap 支持远程数据包获取,所以还应当添加预处理定义WPCAP和HAVE_REMOTE
添加时注意必须以如下图的格式为好:


否则会发生下面的错误

error C2065: “PCAP_SRC_IF_STRING”: 未声明的标识符
error C3861: “pcap_findalldevs_ex”: 找不到标识符
error C2065: “PCAP_OPENFLAG_PROMISCUOUS”: 未声明的标识符
error C3861: “pcap_open”: 找不到标识符

5> 如果还有问题,可以到WinPcaP 官方网站上找FAQ。

 

 

如果应用程序出现一下提示,那就是没有安装驱动程序的原因了。
也可以不安装WinPcap驱动程序。但是需要把上面提到的四个动态链接库文件拷贝到系统分区/WINDOWS/system32目录下。(似乎有些问题)

pcap_open()
pcap_open() 为捕获/发送数据打开一个普通的源。pcap_open()能够替代所有的pcap_open_xxx()函数,它隐藏了不同的pcap_open_xxx()之间的差异,所以程序员不必使用不同的open函数。

pcap_open(source,snaplen,flags,read-timeout,auth,errbuf);

source:的是包含要打开的源名称的以’\0’结尾的字符串。源名称得包含新的源规范语法 (Source Specification Syntax),并且它不能为NULL。为了方便的使用源语法,请记住: (1)pcap_findalldevs_ex()返回的适配器(网卡)可以直接被pcap_open()使用;(2)万一用户想传递他自己的源字符串给 pcap_open(),pcap_createsrcstr()可以创建正确的源标识。

snaplen:需要保留的数据包的长度。对每一个过滤器接收到的数据包,第一个‘snaplen’字节的内容将被保存到缓冲区,并且传递给用户程 序。例如,snaplen等于100,那么仅仅每一个数据包的第一个100字节的内容被保存。简言之就是从每一个包的开头到snaplen的那段内容将被 保存。

flags:保存一些由于抓包需要的标志。Winpcap定义了三种标志:

l         PCAP_OPENFLAG_PROMISCUOUS:1,它定义了适配器(网卡)是否进入混杂模式(promiscuous mode)。

l         PCAP_OPENFLAG_DATATX_UDP:2,它定义了数据传输(假如是远程抓包)是否用UDP协议来处理。

l         PCAP_OPENFLAG_NOCAPTURE_RPCAP:4,它定义了远程探测器是否捕获它自己产生的数据包。

read_timeout:以毫秒为单位。read timeout被用来设置在遇到一个数据包的时候读操作不必立即返回,而是等待一段时间,让更 多的数据包到来后从OS内核一次读多个数据包。并非所有的平台都支持read timeout;在不支持read timeout的平台上它将被忽略。

auth:一个指向’struct pcap_rmtauth’的指针,保存当一个用户登录到某个远程机器上时的必要信息。假如不是远程抓包,该指针被设置为NULL。

errbuf:一个指向用户申请的缓冲区的指针,存放当该函数出错时的错误信息。

返回值是一个’pcap_t’指针(就是我们一般所说的网卡的指针),它可以作为下一步调用(例如pcap_compile()等)的参数,并且指定了一个已经打开的Winpcap会话。在遇到问题的情况下,它返回NULL并且’errbuf’变量保存了错误信息。

 

 

如何获取数据包的内容呢?

我们首先来看看包的结构:


winpcap捕获数据包_第1张图片
 
winpcap捕获数据包_第2张图片
 
winpcap捕获数据包_第3张图片
 
winpcap捕获数据包_第4张图片
当我们过滤掉:以太头,ip,tcp/udp 之后就是我们需要的数据包的内容了



源代码:

#include "stdafx.h"
#include
using namespace std;
#include
using namespace std;
#include
#include
#include
#include
#pragma comment(lib,"ws2_32.lib")
#define MAX_PRINT 80
#define MAX_LINE 16


#pragma once

 

typedef unsigned char u_int8_t;
typedef unsigned short int u_int16_t;
typedef unsigned int u_int32_t;
#define  ETH_ALEN 6

struct iphdr
{
#if __BYTE_ORDER == __LITTLE_ENDIAN
 unsigned int ihl:4;
 unsigned int version:4;
#elif __BYTE_ORDER == __BIG_ENDIAN
 unsigned int version:4;
 unsigned int ihl:4;
#else
# error "Please fix "
#endif
 u_int8_t tos;
 u_int16_t tot_len;
 u_int16_t id;
 u_int16_t frag_off;
 u_int8_t ttl;
 u_int8_t protocol;
 u_int16_t check;
 u_int32_t saddr;
 u_int32_t daddr;
 /*The options start here. */
};

# ifdef __FAVOR_BSD
typedef u_int32_t tcp_seq;
/*
* TCP header.
* Per RFC 793, September, 1981.
*/
struct tcphdr
{
 u_int16_t th_sport;         /* source port */
 u_int16_t th_dport;         /* destination port */
 tcp_seq th_seq;             /* sequence number */
 tcp_seq th_ack;             /* acknowledgement number */
#  if __BYTE_ORDER == __LITTLE_ENDIAN
 u_int8_t th_x2:4;           /* (unused) */
 u_int8_t th_off:4;          /* data offset */
#  endif
#  if __BYTE_ORDER == __BIG_ENDIAN
 u_int8_t th_off:4;          /* data offset */
 u_int8_t th_x2:4;           /* (unused) */
#  endif
 u_int8_t th_flags;
#  define TH_FIN        0x01
#  define TH_SYN        0x02
#  define TH_RST        0x04
#  define TH_PUSH       0x08
#  define TH_ACK        0x10
#  define TH_URG        0x20
 u_int16_t th_win;           /* window */
 u_int16_t th_sum;           /* checksum */
 u_int16_t th_urp;           /* urgent pointer */
};

# else /* !__FAVOR_BSD */
struct tcphdr
{
 u_int16_t source;
 u_int16_t dest;
 u_int32_t seq;
 u_int32_t ack_seq;
#  if __BYTE_ORDER == __LITTLE_ENDIAN
 u_int16_t res1:4;
 u_int16_t doff:4;
 u_int16_t fin:1;
 u_int16_t syn:1;
 u_int16_t rst:1;
 u_int16_t psh:1;
 u_int16_t ack:1;
 u_int16_t urg:1;
 u_int16_t res2:2;
#  elif __BYTE_ORDER == __BIG_ENDIAN
 u_int16_t doff:4;
 u_int16_t res1:4;
 u_int16_t res2:2;
 u_int16_t urg:1;
 u_int16_t ack:1;
 u_int16_t psh:1;
 u_int16_t rst:1;
 u_int16_t syn:1;
 u_int16_t fin:1;
#  else
#   error "Adjust your defines"
#  endif
 u_int16_t window;
 u_int16_t check;
 u_int16_t urg_ptr;
};
#endif

#define ETH_P_802_3     0x0001          /* Dummy type for 802.3 frames  */
#define ETH_P_AX25      0x0002          /* Dummy protocol id for AX.25  */
#define ETH_P_ALL       0x0003          /* Every packet (be careful!!!) */
#define ETH_P_802_2     0x0004          /* 802.2 frames                 */
#define ETH_P_SNAP      0x0005          /* Internal only                */
#define ETH_P_DDCMP     0x0006          /* DEC DDCMP: Internal only     */
#define ETH_P_WAN_PPP   0x0007          /* Dummy type for WAN PPP frames*/
#define ETH_P_PPP_MP    0x0008          /* Dummy type for PPP MP frames */
#define ETH_P_LOCALTALK 0x0009          /* Localtalk pseudo type        */
#define ETH_P_CAN       0x000C          /* Controller Area Network      */
#define ETH_P_PPPTALK   0x0010          /* Dummy type for Atalk over PPP*/
#define ETH_P_TR_802_2  0x0011          /* 802.2 frames                 */
#define ETH_P_MOBITEX   0x0015          /* Mobitex (
[email protected] )       */
#define ETH_P_CONTROL   0x0016          /* Card specific control frames */
#define ETH_P_IRDA      0x0017          /* Linux-IrDA                   */
#define ETH_P_ECONET    0x0018          /* Acorn Econet                 */
#define ETH_P_HDLC      0x0019          /* HDLC frames                  */
#define ETH_P_ARCNET    0x001A          /* 1A for ArcNet :-)            */
struct ethhdr {
 unsigned char   h_dest[ETH_ALEN];       /* destination eth addr */
 unsigned char   h_source[ETH_ALEN];     /* source ether addr    */
 u_int16_t          h_proto;                /* packet type ID field */
} ;

 

/* 4字节的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 bits) + 首部长度 (4 bits)
    u_char  tos;            // 服务类型(Type of service)
    u_short tlen;           // 总长(Total length)
    u_short identification; // 标识(Identification)
    u_short flags_fo;       // 标志位(Flags) (3 bits) + 段偏移量(Fragment offset) (13 bits)
    u_char  ttl;            // 存活时间(Time to live)
    u_char  proto;          // 协议(Protocol)
    u_short crc;            // 首部校验和(Header checksum)
    ip_address  saddr;      // 源地址(Source address)
    ip_address  daddr;      // 目的地址(Destination address)
    u_int   op_pad;         // 选项与填充(Option + Padding)
}ip_header;

/* UDP 首部*/
typedef struct udp_header{
    u_short sport;          // 源端口(Source port)
    u_short dport;          // 目的端口(Destination port)
    u_short len;            // UDP数据包长度(Datagram length)
    u_short crc;            // 校验和(Checksum)
}udp_header;



/* packet handler 函数原型 */
void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data);

int 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[] = "dst host 192.168.0.1";//过了条件port 20 and ip and tcp dst host 127.0.0.1
struct bpf_program fcode;

    
    /* 获取本机设备列表 */
    if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1)
    {
        fprintf(stderr,"Error in pcap_findalldevs: %s\n", errbuf);
        exit(1);
    }
    
    /* 打印列表 */
    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");
        /* 释放设备列表 */
        pcap_freealldevs(alldevs);
        return -1;
    }
    
    /* 跳转到选中的适配器 */
    for(d=alldevs, i=0; i< inum-1 ;d=d->next, i++);
    
    /* 打开设备 */
    if ( (adhandle= pcap_open(d->name,          // 设备名
                              65536,            // 65535保证能捕获到不同数据链路层上的每个数据包的全部内容
                                 PCAP_OPENFLAG_NOCAPTURE_RPCAP,//PCAP_OPENFLAG_PROMISCUOUS,    // 混杂模式
                              1000,             // 读取超时时间
                              NULL,             // 远程机器验证
                              errbuf            // 错误缓冲池
                              ) ) == NULL)
    {
        fprintf(stderr,"\nUnable to open the adapter. %s is not supported by WinPcap\n", d->name);
        /* 释放设备列表 */
        pcap_freealldevs(alldevs);
        return -1;
    }
    


      /* 检查数据链路层,为了简单,我们只考虑以太网 */
    if(pcap_datalink(adhandle) != DLT_EN10MB)
    {
        fprintf(stderr,"\nThis program works only on Ethernet networks.\n");
        /* 释放设备列表 */
        pcap_freealldevs(alldevs);
        return -1;
    }
    
    if(d->addresses != NULL)
        /* 获得接口第一个地址的掩码 */
        netmask=((struct sockaddr_in *)(d->addresses->netmask))->sin_addr.S_un.S_addr;
    else
        /* 如果接口没有地址,那么我们假设一个C类的掩码 */
        netmask=0xffffff;


    //编译过滤器
    if (pcap_compile(adhandle, &fcode, packet_filter, 1, netmask) <0 )
    {
        fprintf(stderr,"\nUnable to compile the packet filter. Check the syntax.\n");
        /* 释放设备列表 */
        pcap_freealldevs(alldevs);
        return -1;
    }
    
    //设置过滤器
    if (pcap_setfilter(adhandle, &fcode)<0)
    {
        fprintf(stderr,"\nError setting the filter.\n");
        /* 释放设备列表 */
        pcap_freealldevs(alldevs);
        return -1;
    }
    




    printf("\nlistening on %s...\n", d->description);
    
    /* 释放设备列表 */
    pcap_freealldevs(alldevs);
    
    /* 开始捕获 */
    pcap_loop(adhandle, 0, packet_handler, NULL);
    
    return 0;
}


/* 每次捕获到数据包时,libpcap都会自动调用这个回调函数 */
void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data)
{
    /*struct tm *ltime;
    char timestr[16];
    time_t local_tv_sec;
    
    // 将时间戳转换成可识别的格式
    local_tv_sec = header->ts.tv_sec;
    ltime=localtime(&local_tv_sec);
    strftime( timestr, sizeof timestr, "%H:%M:%S", ltime);
    
    printf("%s,%.6d len:%d\n", timestr, header->ts.tv_usec, header->len);*/

    struct tm *ltime;
    struct iphdr *ip;
    struct ethhdr *eth;
    struct tcphdr *tcp;

    char timestr[16];
    ip_header *ih;
    udp_header *uh;
    u_int ip_len;
    u_short sport,dport;
    time_t local_tv_sec;
    
    
    pkt_data=(pkt_data + sizeof(struct ethhdr) + sizeof(struct iphdr)+sizeof(struct tcphdr))-4;//-4 表示移动32位
    
    const char* target="corporation/login_corporation.yizhi?method=verify";
    
    if(!(strstr((char*)pkt_data,target)))
    {
        return;
    }


    if()

        oa/corporation/login_corporation.yizhi?method=verify
    cout<
    

}

 

你可能感兴趣的:(C++)